1dd2a3cdaSBram Moolenaar" Vim indent file
2dd2a3cdaSBram Moolenaar" Language: Javascript
3e4a3bcf2SBram Moolenaar" Maintainer: Chris Paul ( https://github.com/bounceme )
409521313SBram Moolenaar" URL: https://github.com/pangloss/vim-javascript
5*3c2881dcSBram Moolenaar" Last Change: March 21, 2017
6dd2a3cdaSBram Moolenaar
7dd2a3cdaSBram Moolenaar" Only load this indent file when no other was loaded.
809521313SBram Moolenaarif exists('b:did_indent')
9dd2a3cdaSBram Moolenaar  finish
10dd2a3cdaSBram Moolenaarendif
11dd2a3cdaSBram Moolenaarlet b:did_indent = 1
12dd2a3cdaSBram Moolenaar
1309521313SBram Moolenaar" Now, set up our indentation expression and keys that trigger it.
1409521313SBram Moolenaarsetlocal indentexpr=GetJavascriptIndent()
1568563937SBram Moolenaarsetlocal autoindent nolisp nosmartindent
1668563937SBram Moolenaarsetlocal indentkeys+=0],0)
17*3c2881dcSBram Moolenaar" Testable with something like:
18*3c2881dcSBram Moolenaar" vim  -eNs "+filetype plugin indent on" "+syntax on" "+set ft=javascript" \
19*3c2881dcSBram Moolenaar"       "+norm! gg=G" '+%print' '+:q!' testfile.js \
20*3c2881dcSBram Moolenaar"       | diff -uBZ testfile.js -
21dd2a3cdaSBram Moolenaar
2268563937SBram Moolenaarlet b:undo_indent = 'setlocal indentexpr< smartindent< autoindent< indentkeys<'
2309521313SBram Moolenaar
2409521313SBram Moolenaar" Only define the function once.
2509521313SBram Moolenaarif exists('*GetJavascriptIndent')
2609521313SBram Moolenaar  finish
2709521313SBram Moolenaarendif
2809521313SBram Moolenaar
2909521313SBram Moolenaarlet s:cpo_save = &cpo
3009521313SBram Moolenaarset cpo&vim
3109521313SBram Moolenaar
3209521313SBram Moolenaar" Get shiftwidth value
3309521313SBram Moolenaarif exists('*shiftwidth')
3409521313SBram Moolenaar  function s:sw()
3509521313SBram Moolenaar    return shiftwidth()
3609521313SBram Moolenaar  endfunction
3709521313SBram Moolenaarelse
3809521313SBram Moolenaar  function s:sw()
39*3c2881dcSBram Moolenaar    return &l:shiftwidth == 0 ? &l:tabstop : &l:shiftwidth
4009521313SBram Moolenaar  endfunction
4109521313SBram Moolenaarendif
4209521313SBram Moolenaar
43*3c2881dcSBram Moolenaar" Performance for forwards search(): start search at pos rather than masking
44*3c2881dcSBram Moolenaar" matches before pos.
45*3c2881dcSBram Moolenaarlet s:z = has('patch-7.4.984') ? 'z' : ''
46*3c2881dcSBram Moolenaar
4768563937SBram Moolenaar" searchpair() wrapper
4868563937SBram Moolenaarif has('reltime')
4968563937SBram Moolenaar  function s:GetPair(start,end,flags,skip,time,...)
5068563937SBram Moolenaar    return searchpair('\m'.a:start,'','\m'.a:end,a:flags,a:skip,max([prevnonblank(v:lnum) - 2000,0] + a:000),a:time)
5168563937SBram Moolenaar  endfunction
5268563937SBram Moolenaarelse
5368563937SBram Moolenaar  function s:GetPair(start,end,flags,skip,...)
5468563937SBram Moolenaar    return searchpair('\m'.a:start,'','\m'.a:end,a:flags,a:skip,max([prevnonblank(v:lnum) - 1000,get(a:000,1)]))
5568563937SBram Moolenaar  endfunction
5668563937SBram Moolenaarendif
5768563937SBram Moolenaar
5809521313SBram Moolenaar" Regex of syntax group names that are or delimit string or are comments.
59*3c2881dcSBram Moolenaarlet s:syng_strcom = 'string\|comment\|regex\|special\|doc\|template\%(braces\)\@!'
60*3c2881dcSBram Moolenaarlet s:syng_str = 'string\|template\|special'
6168563937SBram Moolenaarlet s:syng_com = 'comment\|doc'
6209521313SBram Moolenaar" Expression used to check whether we should skip a match with searchpair().
63e4a3bcf2SBram Moolenaarlet s:skip_expr = "synIDattr(synID(line('.'),col('.'),0),'name') =~? '".s:syng_strcom."'"
6409521313SBram Moolenaar
65*3c2881dcSBram Moolenaarfunction s:parse_cino(f) abort
66*3c2881dcSBram Moolenaar  return float2nr(eval(substitute(substitute(join(split(
67*3c2881dcSBram Moolenaar        \ matchstr(&cino,'.*'.a:f.'\zs[^,]*'), 's',1), '*'.s:W)
68*3c2881dcSBram Moolenaar        \ , '^-\=\zs\*','',''), '^-\=\zs\.','0.','')))
69*3c2881dcSBram Moolenaarendfunction
70*3c2881dcSBram Moolenaar
7168563937SBram Moolenaarfunction s:skip_func()
72*3c2881dcSBram Moolenaar  if getline('.') =~ '\%<'.col('.').'c\/.\{-}\/\|\%>'.col('.').'c[''"]\|\\$'
73*3c2881dcSBram Moolenaar    return eval(s:skip_expr)
74*3c2881dcSBram Moolenaar  elseif s:checkIn || search('\m`\|\${\|\*\/','nW'.s:z,s:looksyn)
75*3c2881dcSBram Moolenaar    let s:checkIn = eval(s:skip_expr)
76e4a3bcf2SBram Moolenaar  endif
7768563937SBram Moolenaar  let s:looksyn = line('.')
78*3c2881dcSBram Moolenaar  return s:checkIn
7968563937SBram Moolenaarendfunction
8009521313SBram Moolenaar
8168563937SBram Moolenaarfunction s:alternatePair(stop)
8268563937SBram Moolenaar  let pos = getpos('.')[1:2]
83*3c2881dcSBram Moolenaar  let pat = '[][(){};]'
84*3c2881dcSBram Moolenaar  while search('\m'.pat,'bW',a:stop)
85*3c2881dcSBram Moolenaar    if s:skip_func() | continue | endif
86*3c2881dcSBram Moolenaar    let idx = stridx('])};',s:looking_at())
87*3c2881dcSBram Moolenaar    if idx is 3 | let pat = '[{}()]' | continue | endif
8868563937SBram Moolenaar    if idx + 1
89*3c2881dcSBram Moolenaar      if s:GetPair(['\[','(','{'][idx], '])}'[idx],'bW','s:skip_func()',2000,a:stop) <= 0
9068563937SBram Moolenaar        break
9168563937SBram Moolenaar      endif
9268563937SBram Moolenaar    else
9368563937SBram Moolenaar      return
9468563937SBram Moolenaar    endif
9568563937SBram Moolenaar  endwhile
9668563937SBram Moolenaar  call call('cursor',pos)
9768563937SBram Moolenaarendfunction
9868563937SBram Moolenaar
9968563937SBram Moolenaarfunction s:save_pos(f,...)
10068563937SBram Moolenaar  let l:pos = getpos('.')[1:2]
10168563937SBram Moolenaar  let ret = call(a:f,a:000)
10268563937SBram Moolenaar  call call('cursor',l:pos)
10368563937SBram Moolenaar  return ret
10468563937SBram Moolenaarendfunction
10568563937SBram Moolenaar
10668563937SBram Moolenaarfunction s:syn_at(l,c)
10768563937SBram Moolenaar  return synIDattr(synID(a:l,a:c,0),'name')
10868563937SBram Moolenaarendfunction
10968563937SBram Moolenaar
11068563937SBram Moolenaarfunction s:looking_at()
11168563937SBram Moolenaar  return getline('.')[col('.')-1]
11268563937SBram Moolenaarendfunction
11368563937SBram Moolenaar
11468563937SBram Moolenaarfunction s:token()
11568563937SBram Moolenaar  return s:looking_at() =~ '\k' ? expand('<cword>') : s:looking_at()
11668563937SBram Moolenaarendfunction
11768563937SBram Moolenaar
11868563937SBram Moolenaarfunction s:previous_token()
119*3c2881dcSBram Moolenaar  let l:pos = getpos('.')[1:2]
120*3c2881dcSBram Moolenaar  if search('\m\k\{1,}\zs\k\|\S','bW')
121*3c2881dcSBram Moolenaar    if (getline('.')[col('.')-2:col('.')-1] == '*/' || line('.') != l:pos[0] &&
122*3c2881dcSBram Moolenaar          \ getline('.') =~ '\%<'.col('.').'c\/\/') && s:syn_at(line('.'),col('.')) =~? s:syng_com
123*3c2881dcSBram Moolenaar      while search('\m\S\ze\_s*\/[/*]','bW')
124*3c2881dcSBram Moolenaar        if s:syn_at(line('.'),col('.')) !~? s:syng_com
12568563937SBram Moolenaar          return s:token()
12668563937SBram Moolenaar        endif
12768563937SBram Moolenaar      endwhile
128*3c2881dcSBram Moolenaar    else
129*3c2881dcSBram Moolenaar      return s:token()
130*3c2881dcSBram Moolenaar    endif
131*3c2881dcSBram Moolenaar  endif
132*3c2881dcSBram Moolenaar  call call('cursor',l:pos)
13368563937SBram Moolenaar  return ''
13468563937SBram Moolenaarendfunction
13568563937SBram Moolenaar
136*3c2881dcSBram Moolenaarfunction s:expr_col()
137*3c2881dcSBram Moolenaar  if getline('.')[col('.')-2] == ':'
138*3c2881dcSBram Moolenaar    return 1
13968563937SBram Moolenaar  endif
140*3c2881dcSBram Moolenaar  let bal = 0
141*3c2881dcSBram Moolenaar  while search('\m[{}?:;]','bW')
142*3c2881dcSBram Moolenaar    if eval(s:skip_expr) | continue | endif
143*3c2881dcSBram Moolenaar    " switch (looking_at())
144*3c2881dcSBram Moolenaar    exe {   '}': "if s:GetPair('{','}','bW',s:skip_expr,200) <= 0 | return | endif",
145*3c2881dcSBram Moolenaar          \ ';': "return",
146*3c2881dcSBram Moolenaar          \ '{': "return getpos('.')[1:2] != b:js_cache[1:] && !s:IsBlock()",
147*3c2881dcSBram Moolenaar          \ ':': "let bal -= getline('.')[max([col('.')-2,0]):col('.')] !~ '::'",
148*3c2881dcSBram Moolenaar          \ '?': "let bal += 1 | if bal > 0 | return 1 | endif" }[s:looking_at()]
149*3c2881dcSBram Moolenaar  endwhile
15068563937SBram Moolenaarendfunction
15109521313SBram Moolenaar
15209521313SBram Moolenaar" configurable regexes that define continuation lines, not including (, {, or [.
15368563937SBram Moolenaarlet s:opfirst = '^' . get(g:,'javascript_opfirst',
154*3c2881dcSBram Moolenaar      \ '\C\%([<>=,?^%|*/&]\|\([-.:+]\)\1\@!\|!=\|in\%(stanceof\)\=\>\)')
15568563937SBram Moolenaarlet s:continuation = get(g:,'javascript_continuation',
156*3c2881dcSBram Moolenaar      \ '\C\%([-+<>=,.~!?/*^%|&:]\|\<\%(typeof\|new\|delete\|void\|in\|instanceof\|await\)\)') . '$'
157e4a3bcf2SBram Moolenaar
15868563937SBram Moolenaarfunction s:continues(ln,con)
159*3c2881dcSBram Moolenaar  if !cursor(a:ln, match(' '.a:con,s:continuation))
160*3c2881dcSBram Moolenaar    let teol = s:looking_at()
161*3c2881dcSBram Moolenaar    if teol == '/'
162*3c2881dcSBram Moolenaar      return s:syn_at(line('.'),col('.')) !~? 'regex'
163*3c2881dcSBram Moolenaar    elseif teol =~ '[-+>]'
164*3c2881dcSBram Moolenaar      return getline('.')[col('.')-2] != tr(teol,'>','=')
165*3c2881dcSBram Moolenaar    elseif teol =~ '\l'
166*3c2881dcSBram Moolenaar      return s:previous_token() != '.'
167*3c2881dcSBram Moolenaar    elseif teol == ':'
168*3c2881dcSBram Moolenaar      return s:expr_col()
169*3c2881dcSBram Moolenaar    endif
170*3c2881dcSBram Moolenaar    return 1
171*3c2881dcSBram Moolenaar  endif
172e4a3bcf2SBram Moolenaarendfunction
173e4a3bcf2SBram Moolenaar
17468563937SBram Moolenaar" get the line of code stripped of comments and move cursor to the last
17568563937SBram Moolenaar" non-comment char.
17668563937SBram Moolenaarfunction s:Trim(ln)
17768563937SBram Moolenaar  let pline = substitute(getline(a:ln),'\s*$','','')
178*3c2881dcSBram Moolenaar  let l:max = max([strridx(pline,'//'), strridx(pline,'/*')])
179*3c2881dcSBram Moolenaar  while l:max != -1 && s:syn_at(a:ln, strlen(pline)) =~? s:syng_com
180*3c2881dcSBram Moolenaar    let pline = pline[: l:max]
181*3c2881dcSBram Moolenaar    let l:max = max([strridx(pline,'//'), strridx(pline,'/*')])
182*3c2881dcSBram Moolenaar    let pline = substitute(pline[:-2],'\s*$','','')
18368563937SBram Moolenaar  endwhile
184*3c2881dcSBram Moolenaar  return pline is '' || cursor(a:ln,strlen(pline)) ? pline : pline
18509521313SBram Moolenaarendfunction
18609521313SBram Moolenaar
18768563937SBram Moolenaar" Find line above 'lnum' that isn't empty or in a comment
18809521313SBram Moolenaarfunction s:PrevCodeLine(lnum)
189*3c2881dcSBram Moolenaar  let [l:pos, l:n] = [getpos('.')[1:2], prevnonblank(a:lnum)]
19068563937SBram Moolenaar  while l:n
19168563937SBram Moolenaar    if getline(l:n) =~ '^\s*\/[/*]'
19268563937SBram Moolenaar      let l:n = prevnonblank(l:n-1)
193*3c2881dcSBram Moolenaar    elseif stridx(getline(l:n), '*/') + 1 && s:syn_at(l:n,1) =~? s:syng_com
194*3c2881dcSBram Moolenaar      call cursor(l:n,1)
195*3c2881dcSBram Moolenaar      keepjumps norm! [*
196*3c2881dcSBram Moolenaar      let l:n = search('\m\S','nbW')
19768563937SBram Moolenaar    else
198*3c2881dcSBram Moolenaar      break
19968563937SBram Moolenaar    endif
20009521313SBram Moolenaar  endwhile
201*3c2881dcSBram Moolenaar  call call('cursor',l:pos)
202*3c2881dcSBram Moolenaar  return l:n
20309521313SBram Moolenaarendfunction
20409521313SBram Moolenaar
20509521313SBram Moolenaar" Check if line 'lnum' has a balanced amount of parentheses.
20609521313SBram Moolenaarfunction s:Balanced(lnum)
20768563937SBram Moolenaar  let l:open = 0
20809521313SBram Moolenaar  let l:line = getline(a:lnum)
20909521313SBram Moolenaar  let pos = match(l:line, '[][(){}]', 0)
21009521313SBram Moolenaar  while pos != -1
21168563937SBram Moolenaar    if s:syn_at(a:lnum,pos + 1) !~? s:syng_strcom
21268563937SBram Moolenaar      let l:open += match(' ' . l:line[pos],'[[({]')
21368563937SBram Moolenaar      if l:open < 0
21468563937SBram Moolenaar        return
21509521313SBram Moolenaar      endif
21609521313SBram Moolenaar    endif
217*3c2881dcSBram Moolenaar    let pos = match(l:line, (l:open ?
218*3c2881dcSBram Moolenaar          \ '['.escape(tr(l:line[pos],'({[]})',')}][{(').l:line[pos],']').']' :
219*3c2881dcSBram Moolenaar          \ '[][(){}]'), pos + 1)
22009521313SBram Moolenaar  endwhile
22168563937SBram Moolenaar  return !l:open
22209521313SBram Moolenaarendfunction
22368563937SBram Moolenaar
22468563937SBram Moolenaarfunction s:OneScope(lnum)
22568563937SBram Moolenaar  let pline = s:Trim(a:lnum)
22668563937SBram Moolenaar  let kw = 'else do'
22768563937SBram Moolenaar  if pline[-1:] == ')' && s:GetPair('(', ')', 'bW', s:skip_expr, 100) > 0
228*3c2881dcSBram Moolenaar    if s:previous_token() =~# '^\%(await\|each\)$'
22968563937SBram Moolenaar      call s:previous_token()
23068563937SBram Moolenaar      let kw = 'for'
231*3c2881dcSBram Moolenaar    else
232*3c2881dcSBram Moolenaar      let kw = 'for if let while with'
23368563937SBram Moolenaar    endif
23468563937SBram Moolenaar  endif
23568563937SBram Moolenaar  return pline[-2:] == '=>' || index(split(kw),s:token()) + 1 &&
23668563937SBram Moolenaar        \ s:save_pos('s:previous_token') != '.'
23768563937SBram Moolenaarendfunction
23868563937SBram Moolenaar
23968563937SBram Moolenaar" returns braceless levels started by 'i' and above lines * &sw. 'num' is the
24068563937SBram Moolenaar" lineNr which encloses the entire context, 'cont' if whether line 'i' + 1 is
24168563937SBram Moolenaar" a continued expression, which could have started in a braceless context
24268563937SBram Moolenaarfunction s:iscontOne(i,num,cont)
24368563937SBram Moolenaar  let [l:i, l:num, bL] = [a:i, a:num + !a:num, 0]
24468563937SBram Moolenaar  let pind = a:num ? indent(l:num) + s:W : 0
24568563937SBram Moolenaar  let ind = indent(l:i) + (a:cont ? 0 : s:W)
24668563937SBram Moolenaar  while l:i >= l:num && (ind > pind || l:i == l:num)
24768563937SBram Moolenaar    if indent(l:i) < ind && s:OneScope(l:i)
24868563937SBram Moolenaar      let bL += s:W
24968563937SBram Moolenaar      let l:i = line('.')
25068563937SBram Moolenaar    elseif !a:cont || bL || ind < indent(a:i)
25168563937SBram Moolenaar      break
25268563937SBram Moolenaar    endif
25368563937SBram Moolenaar    let ind = min([ind, indent(l:i)])
25468563937SBram Moolenaar    let l:i = s:PrevCodeLine(l:i - 1)
25568563937SBram Moolenaar  endwhile
25668563937SBram Moolenaar  return bL
25768563937SBram Moolenaarendfunction
25868563937SBram Moolenaar
25968563937SBram Moolenaar" https://github.com/sweet-js/sweet.js/wiki/design#give-lookbehind-to-the-reader
26068563937SBram Moolenaarfunction s:IsBlock()
26168563937SBram Moolenaar  if s:looking_at() == '{'
26268563937SBram Moolenaar    let l:n = line('.')
26368563937SBram Moolenaar    let char = s:previous_token()
264*3c2881dcSBram Moolenaar    if match(s:stack,'\cxml\|jsx') + 1 && s:syn_at(line('.'),col('.')-1) =~? 'xml\|jsx'
26568563937SBram Moolenaar      return char != '{'
26668563937SBram Moolenaar    elseif char =~ '\k'
267*3c2881dcSBram Moolenaar      if char ==# 'type'
268*3c2881dcSBram Moolenaar        return s:previous_token() !~# '^\%(im\|ex\)port$'
26968563937SBram Moolenaar      endif
270*3c2881dcSBram Moolenaar      return index(split('return const let import export extends yield default delete var await void typeof throw case new of in instanceof')
271*3c2881dcSBram Moolenaar            \ ,char) < (line('.') != l:n) || s:save_pos('s:previous_token') == '.'
272*3c2881dcSBram Moolenaar    elseif char == '>'
273*3c2881dcSBram Moolenaar      return getline('.')[col('.')-2] == '=' || s:syn_at(line('.'),col('.')) =~? '^jsflow'
274*3c2881dcSBram Moolenaar    elseif char == ':'
275*3c2881dcSBram Moolenaar      return !s:save_pos('s:expr_col')
276*3c2881dcSBram Moolenaar    elseif char == '/'
277*3c2881dcSBram Moolenaar      return s:syn_at(line('.'),col('.')) =~? 'regex'
278*3c2881dcSBram Moolenaar    endif
279*3c2881dcSBram Moolenaar    return char !~ '[=~!<*,?^%|&([]' &&
280*3c2881dcSBram Moolenaar          \ (char !~ '[-+]' || l:n != line('.') && getline('.')[col('.')-2] == char)
28168563937SBram Moolenaar  endif
28268563937SBram Moolenaarendfunction
28309521313SBram Moolenaar
28409521313SBram Moolenaarfunction GetJavascriptIndent()
28568563937SBram Moolenaar  let b:js_cache = get(b:,'js_cache',[0,0,0])
28609521313SBram Moolenaar  " Get the current line.
28768563937SBram Moolenaar  call cursor(v:lnum,1)
28868563937SBram Moolenaar  let l:line = getline('.')
289*3c2881dcSBram Moolenaar  " use synstack as it validates syn state and works in an empty line
290*3c2881dcSBram Moolenaar  let s:stack = map(synstack(v:lnum,1),"synIDattr(v:val,'name')")
291*3c2881dcSBram Moolenaar  let syns = get(s:stack,-1,'')
29209521313SBram Moolenaar
29368563937SBram Moolenaar  " start with strings,comments,etc.
29468563937SBram Moolenaar  if syns =~? s:syng_com
29568563937SBram Moolenaar    if l:line =~ '^\s*\*'
29668563937SBram Moolenaar      return cindent(v:lnum)
29768563937SBram Moolenaar    elseif l:line !~ '^\s*\/[/*]'
29809521313SBram Moolenaar      return -1
29909521313SBram Moolenaar    endif
300*3c2881dcSBram Moolenaar  elseif syns =~? s:syng_str
30168563937SBram Moolenaar    if b:js_cache[0] == v:lnum - 1 && s:Balanced(v:lnum-1)
30268563937SBram Moolenaar      let b:js_cache[0] = v:lnum
30368563937SBram Moolenaar    endif
30468563937SBram Moolenaar    return -1
30509521313SBram Moolenaar  endif
30609521313SBram Moolenaar  let l:lnum = s:PrevCodeLine(v:lnum - 1)
30768563937SBram Moolenaar  if !l:lnum
30868563937SBram Moolenaar    return
30909521313SBram Moolenaar  endif
31009521313SBram Moolenaar
31168563937SBram Moolenaar  let l:line = substitute(l:line,'^\s*','','')
31268563937SBram Moolenaar  if l:line[:1] == '/*'
31368563937SBram Moolenaar    let l:line = substitute(l:line,'^\%(\/\*.\{-}\*\/\s*\)*','','')
31409521313SBram Moolenaar  endif
31568563937SBram Moolenaar  if l:line =~ '^\/[/*]'
31668563937SBram Moolenaar    let l:line = ''
31768563937SBram Moolenaar  endif
31809521313SBram Moolenaar
31968563937SBram Moolenaar  " the containing paren, bracket, or curly. Many hacks for performance
320*3c2881dcSBram Moolenaar  let idx = index([']',')','}'],l:line[0])
32168563937SBram Moolenaar  if b:js_cache[0] >= l:lnum && b:js_cache[0] < v:lnum &&
32268563937SBram Moolenaar        \ (b:js_cache[0] > l:lnum || s:Balanced(l:lnum))
32368563937SBram Moolenaar    call call('cursor',b:js_cache[1:])
32409521313SBram Moolenaar  else
325*3c2881dcSBram Moolenaar    let [s:looksyn, s:checkIn, top] = [v:lnum - 1, 0, (!indent(l:lnum) &&
32668563937SBram Moolenaar          \ s:syn_at(l:lnum,1) !~? s:syng_str) * l:lnum]
32768563937SBram Moolenaar    if idx + 1
32868563937SBram Moolenaar      call s:GetPair(['\[','(','{'][idx],'])}'[idx],'bW','s:skip_func()',2000,top)
329*3c2881dcSBram Moolenaar    elseif getline(v:lnum) !~ '^\S' && syns =~? 'block'
33068563937SBram Moolenaar      call s:GetPair('{','}','bW','s:skip_func()',2000,top)
33168563937SBram Moolenaar    else
33268563937SBram Moolenaar      call s:alternatePair(top)
33309521313SBram Moolenaar    endif
33409521313SBram Moolenaar  endif
33509521313SBram Moolenaar
33668563937SBram Moolenaar  let b:js_cache = [v:lnum] + (line('.') == v:lnum ? [0,0] : getpos('.')[1:2])
33768563937SBram Moolenaar  let num = b:js_cache[1]
33868563937SBram Moolenaar
33968563937SBram Moolenaar  let [s:W, isOp, bL, switch_offset] = [s:sw(),0,0,0]
34068563937SBram Moolenaar  if !num || s:IsBlock()
341*3c2881dcSBram Moolenaar    let ilnum = line('.')
34268563937SBram Moolenaar    let pline = s:save_pos('s:Trim',l:lnum)
34368563937SBram Moolenaar    if num && s:looking_at() == ')' && s:GetPair('(', ')', 'bW', s:skip_expr, 100) > 0
344*3c2881dcSBram Moolenaar      let num = ilnum == num ? line('.') : num
345*3c2881dcSBram Moolenaar      if idx < 0 && s:previous_token() ==# 'switch' && s:previous_token() != '.'
346*3c2881dcSBram Moolenaar        if &cino !~ ':'
34768563937SBram Moolenaar          let switch_offset = s:W
34868563937SBram Moolenaar        else
349*3c2881dcSBram Moolenaar          let switch_offset = max([-indent(num),s:parse_cino(':')])
35068563937SBram Moolenaar        endif
35168563937SBram Moolenaar        if pline[-1:] != '.' && l:line =~# '^\%(default\|case\)\>'
35268563937SBram Moolenaar          return indent(num) + switch_offset
35368563937SBram Moolenaar        endif
35468563937SBram Moolenaar      endif
35568563937SBram Moolenaar    endif
356*3c2881dcSBram Moolenaar    if idx < 0 && pline[-1:] !~ '[{;]'
357*3c2881dcSBram Moolenaar      let isOp = (l:line =~# s:opfirst || s:continues(l:lnum,pline)) * s:W
358*3c2881dcSBram Moolenaar      let bL = s:iscontOne(l:lnum,b:js_cache[1],isOp)
35968563937SBram Moolenaar      let bL -= (bL && l:line[0] == '{') * s:W
36068563937SBram Moolenaar    endif
361*3c2881dcSBram Moolenaar  elseif idx < 0 && getline(b:js_cache[1])[b:js_cache[2]-1] == '(' && &cino =~ '('
362*3c2881dcSBram Moolenaar    let pval = s:parse_cino('(')
363*3c2881dcSBram Moolenaar    return !pval ? (s:parse_cino('w') ? 0 : -(!!search('\m\S','W'.s:z,num))) + virtcol('.') :
364*3c2881dcSBram Moolenaar          \ max([indent('.') + pval + (s:GetPair('(',')','nbrmW',s:skip_expr,100,num) * s:W),0])
36568563937SBram Moolenaar  endif
36668563937SBram Moolenaar
36768563937SBram Moolenaar  " main return
368*3c2881dcSBram Moolenaar  if l:line =~ '^\%([])}]\||}\)'
369*3c2881dcSBram Moolenaar    return max([indent(num),0])
37068563937SBram Moolenaar  elseif num
371*3c2881dcSBram Moolenaar    return indent(num) + s:W + switch_offset + bL + isOp
37268563937SBram Moolenaar  endif
373*3c2881dcSBram Moolenaar  return bL + isOp
37409521313SBram Moolenaarendfunction
37509521313SBram Moolenaar
37609521313SBram Moolenaarlet &cpo = s:cpo_save
37709521313SBram Moolenaarunlet s:cpo_save
378