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*f0b03c4eSBram Moolenaar" Last Change: December 4, 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)
173c2881dcSBram Moolenaar" Testable with something like:
183c2881dcSBram Moolenaar" vim  -eNs "+filetype plugin indent on" "+syntax on" "+set ft=javascript" \
193c2881dcSBram Moolenaar"       "+norm! gg=G" '+%print' '+:q!' testfile.js \
203c2881dcSBram 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
32*f0b03c4eSBram Moolenaar" indent correctly if inside <script>
33*f0b03c4eSBram Moolenaar" vim/vim@690afe1 for the switch from cindent
34*f0b03c4eSBram Moolenaar" overridden with b:html_indent_script1
35*f0b03c4eSBram Moolenaarcall extend(g:,{'html_indent_script1': 'inc'},'keep')
36*f0b03c4eSBram Moolenaar
37*f0b03c4eSBram Moolenaar" Regex of syntax group names that are or delimit string or are comments.
38*f0b03c4eSBram Moolenaarlet s:bvars = {
39*f0b03c4eSBram Moolenaar      \ 'syng_strcom': 'string\|comment\|regex\|special\|doc\|template\%(braces\)\@!',
40*f0b03c4eSBram Moolenaar      \ 'syng_str': 'string\|template\|special' }
41*f0b03c4eSBram Moolenaar" template strings may want to be excluded when editing graphql:
42*f0b03c4eSBram Moolenaar" au! Filetype javascript let b:syng_str = '^\%(.*template\)\@!.*string\|special'
43*f0b03c4eSBram Moolenaar" au! Filetype javascript let b:syng_strcom = '^\%(.*template\)\@!.*string\|comment\|regex\|special\|doc'
44*f0b03c4eSBram Moolenaar
45*f0b03c4eSBram Moolenaarfunction s:GetVars()
46*f0b03c4eSBram Moolenaar  call extend(b:,extend(s:bvars,{'js_cache': [0,0,0]}),'keep')
47*f0b03c4eSBram Moolenaarendfunction
48*f0b03c4eSBram Moolenaar
4909521313SBram Moolenaar" Get shiftwidth value
5009521313SBram Moolenaarif exists('*shiftwidth')
5109521313SBram Moolenaar  function s:sw()
5209521313SBram Moolenaar    return shiftwidth()
5309521313SBram Moolenaar  endfunction
5409521313SBram Moolenaarelse
5509521313SBram Moolenaar  function s:sw()
5637c64c78SBram Moolenaar    return &l:shiftwidth ? &l:shiftwidth : &l:tabstop
5709521313SBram Moolenaar  endfunction
5809521313SBram Moolenaarendif
5909521313SBram Moolenaar
603c2881dcSBram Moolenaar" Performance for forwards search(): start search at pos rather than masking
613c2881dcSBram Moolenaar" matches before pos.
623c2881dcSBram Moolenaarlet s:z = has('patch-7.4.984') ? 'z' : ''
633c2881dcSBram Moolenaar
6409521313SBram Moolenaar" Expression used to check whether we should skip a match with searchpair().
6537c64c78SBram Moolenaarlet s:skip_expr = "s:SynAt(line('.'),col('.')) =~? b:syng_strcom"
6637c64c78SBram Moolenaarlet s:in_comm = s:skip_expr[:-14] . "'comment\\|doc'"
6709521313SBram Moolenaar
6837c64c78SBram Moolenaarlet s:rel = has('reltime')
6937c64c78SBram Moolenaar" searchpair() wrapper
7037c64c78SBram Moolenaarif s:rel
7137c64c78SBram Moolenaar  function s:GetPair(start,end,flags,skip)
7237c64c78SBram Moolenaar    return searchpair('\m'.a:start,'','\m'.a:end,a:flags,a:skip,s:l1,a:skip ==# 's:SkipFunc()' ? 2000 : 200)
733c2881dcSBram Moolenaar  endfunction
7468563937SBram Moolenaarelse
7537c64c78SBram Moolenaar  function s:GetPair(start,end,flags,skip)
7637c64c78SBram Moolenaar    return searchpair('\m'.a:start,'','\m'.a:end,a:flags,a:skip,s:l1)
7737c64c78SBram Moolenaar  endfunction
7837c64c78SBram Moolenaarendif
7937c64c78SBram Moolenaar
8037c64c78SBram Moolenaarfunction s:SynAt(l,c)
8137c64c78SBram Moolenaar  let byte = line2byte(a:l) + a:c - 1
8237c64c78SBram Moolenaar  let pos = index(s:synid_cache[0], byte)
8337c64c78SBram Moolenaar  if pos == -1
8437c64c78SBram Moolenaar    let s:synid_cache[:] += [[byte], [synIDattr(synID(a:l, a:c, 0), 'name')]]
8537c64c78SBram Moolenaar  endif
8637c64c78SBram Moolenaar  return s:synid_cache[1][pos]
8737c64c78SBram Moolenaarendfunction
8837c64c78SBram Moolenaar
8937c64c78SBram Moolenaarfunction s:ParseCino(f)
9037c64c78SBram Moolenaar  let [divider, n, cstr] = [0] + matchlist(&cino,
9137c64c78SBram Moolenaar        \ '\%(.*,\)\=\%(\%d'.char2nr(a:f).'\(-\)\=\([.s0-9]*\)\)\=')[1:2]
9237c64c78SBram Moolenaar  for c in split(cstr,'\zs')
9337c64c78SBram Moolenaar    if c == '.' && !divider
9437c64c78SBram Moolenaar      let divider = 1
9537c64c78SBram Moolenaar    elseif c ==# 's'
9637c64c78SBram Moolenaar      if n !~ '\d'
9737c64c78SBram Moolenaar        return n . s:sw() + 0
9837c64c78SBram Moolenaar      endif
9937c64c78SBram Moolenaar      let n = str2nr(n) * s:sw()
10037c64c78SBram Moolenaar      break
10137c64c78SBram Moolenaar    else
10237c64c78SBram Moolenaar      let [n, divider] .= [c, 0]
10337c64c78SBram Moolenaar    endif
10437c64c78SBram Moolenaar  endfor
10537c64c78SBram Moolenaar  return str2nr(n) / max([str2nr(divider),1])
10637c64c78SBram Moolenaarendfunction
10737c64c78SBram Moolenaar
10837c64c78SBram Moolenaar" Optimized {skip} expr, only callable from the search loop which
10937c64c78SBram Moolenaar" GetJavascriptIndent does to find the containing [[{(] (side-effects)
11037c64c78SBram Moolenaarfunction s:SkipFunc()
11137c64c78SBram Moolenaar  if s:top_col == 1
11237c64c78SBram Moolenaar    throw 'out of bounds'
113*f0b03c4eSBram Moolenaar  elseif s:check_in
11437c64c78SBram Moolenaar    if eval(s:skip_expr)
11537c64c78SBram Moolenaar      return 1
11637c64c78SBram Moolenaar    endif
11737c64c78SBram Moolenaar    let s:check_in = 0
11837c64c78SBram Moolenaar  elseif getline('.') =~ '\%<'.col('.').'c\/.\{-}\/\|\%>'.col('.').'c[''"]\|\\$'
11937c64c78SBram Moolenaar    if eval(s:skip_expr)
12037c64c78SBram Moolenaar      return 1
12137c64c78SBram Moolenaar    endif
122*f0b03c4eSBram Moolenaar  elseif search('\m`\|\${\|\*\/','nW'.s:z,s:looksyn)
123*f0b03c4eSBram Moolenaar    if eval(s:skip_expr)
12437c64c78SBram Moolenaar      let s:check_in = 1
12537c64c78SBram Moolenaar      return 1
12637c64c78SBram Moolenaar    endif
127*f0b03c4eSBram Moolenaar  else
128*f0b03c4eSBram Moolenaar    let s:synid_cache[:] += [[line2byte('.') + col('.') - 1], ['']]
129*f0b03c4eSBram Moolenaar  endif
13037c64c78SBram Moolenaar  let [s:looksyn, s:top_col] = getpos('.')[1:2]
13137c64c78SBram Moolenaarendfunction
13237c64c78SBram Moolenaar
13337c64c78SBram Moolenaarfunction s:AlternatePair()
13437c64c78SBram Moolenaar  let [pat, l:for] = ['[][(){};]', 2]
13537c64c78SBram Moolenaar  while s:SearchLoop(pat,'bW','s:SkipFunc()')
13637c64c78SBram Moolenaar    if s:LookingAt() == ';'
13737c64c78SBram Moolenaar      if !l:for
13837c64c78SBram Moolenaar        if s:GetPair('{','}','bW','s:SkipFunc()')
13968563937SBram Moolenaar          return
14068563937SBram Moolenaar        endif
14137c64c78SBram Moolenaar        break
14237c64c78SBram Moolenaar      else
14337c64c78SBram Moolenaar        let [pat, l:for] = ['[{}();]', l:for - 1]
14437c64c78SBram Moolenaar      endif
14537c64c78SBram Moolenaar    else
14637c64c78SBram Moolenaar      let idx = stridx('])}',s:LookingAt())
14737c64c78SBram Moolenaar      if idx == -1
14837c64c78SBram Moolenaar        return
14937c64c78SBram Moolenaar      elseif !s:GetPair(['\[','(','{'][idx],'])}'[idx],'bW','s:SkipFunc()')
15037c64c78SBram Moolenaar        break
15137c64c78SBram Moolenaar      endif
15237c64c78SBram Moolenaar    endif
15368563937SBram Moolenaar  endwhile
15437c64c78SBram Moolenaar  throw 'out of bounds'
15568563937SBram Moolenaarendfunction
15668563937SBram Moolenaar
15737c64c78SBram Moolenaarfunction s:Nat(int)
15837c64c78SBram Moolenaar  return a:int * (a:int > 0)
15968563937SBram Moolenaarendfunction
16068563937SBram Moolenaar
16137c64c78SBram Moolenaarfunction s:LookingAt()
16268563937SBram Moolenaar  return getline('.')[col('.')-1]
16368563937SBram Moolenaarendfunction
16468563937SBram Moolenaar
16537c64c78SBram Moolenaarfunction s:Token()
16637c64c78SBram Moolenaar  return s:LookingAt() =~ '\k' ? expand('<cword>') : s:LookingAt()
16768563937SBram Moolenaarendfunction
16868563937SBram Moolenaar
169*f0b03c4eSBram Moolenaarfunction s:PreviousToken(...)
170*f0b03c4eSBram Moolenaar  let [l:pos, tok] = [getpos('.'), '']
17137c64c78SBram Moolenaar  if search('\m\k\{1,}\|\S','ebW')
172*f0b03c4eSBram Moolenaar    if getline('.')[col('.')-2:col('.')-1] == '*/'
173*f0b03c4eSBram Moolenaar      if eval(s:in_comm) && !s:SearchLoop('\S\ze\_s*\/[/*]','bW',s:in_comm)
174*f0b03c4eSBram Moolenaar        call setpos('.',l:pos)
1753c2881dcSBram Moolenaar      else
176*f0b03c4eSBram Moolenaar        let tok = s:Token()
177*f0b03c4eSBram Moolenaar      endif
178*f0b03c4eSBram Moolenaar    else
179*f0b03c4eSBram Moolenaar      let two = a:0 || line('.') != l:pos[1] ? strridx(getline('.')[:col('.')],'//') + 1 : 0
180*f0b03c4eSBram Moolenaar      if two && eval(s:in_comm)
181*f0b03c4eSBram Moolenaar        call cursor(0,two)
182*f0b03c4eSBram Moolenaar        let tok = s:PreviousToken(1)
183*f0b03c4eSBram Moolenaar        if tok is ''
184*f0b03c4eSBram Moolenaar          call setpos('.',l:pos)
185*f0b03c4eSBram Moolenaar        endif
186*f0b03c4eSBram Moolenaar      else
187*f0b03c4eSBram Moolenaar        let tok = s:Token()
1883c2881dcSBram Moolenaar      endif
1893c2881dcSBram Moolenaar    endif
190*f0b03c4eSBram Moolenaar  endif
191*f0b03c4eSBram Moolenaar  return tok
19268563937SBram Moolenaarendfunction
19368563937SBram Moolenaar
19437c64c78SBram Moolenaarfunction s:Pure(f,...)
19537c64c78SBram Moolenaar  return eval("[call(a:f,a:000),cursor(a:firstline,".col('.').")][0]")
19637c64c78SBram Moolenaarendfunction
19737c64c78SBram Moolenaar
19837c64c78SBram Moolenaarfunction s:SearchLoop(pat,flags,expr)
19937c64c78SBram Moolenaar  return s:GetPair(a:pat,'\_$.',a:flags,a:expr)
20037c64c78SBram Moolenaarendfunction
20137c64c78SBram Moolenaar
20237c64c78SBram Moolenaarfunction s:ExprCol()
203*f0b03c4eSBram Moolenaar  if getline('.')[col('.')-2] == ':'
204*f0b03c4eSBram Moolenaar    return 1
205*f0b03c4eSBram Moolenaar  endif
2063c2881dcSBram Moolenaar  let bal = 0
207*f0b03c4eSBram Moolenaar  while s:SearchLoop('[{}?:]','bW',s:skip_expr)
20837c64c78SBram Moolenaar    if s:LookingAt() == ':'
209*f0b03c4eSBram Moolenaar      if getline('.')[col('.')-2] == ':'
210*f0b03c4eSBram Moolenaar        call cursor(0,col('.')-1)
211*f0b03c4eSBram Moolenaar        continue
212*f0b03c4eSBram Moolenaar      endif
21337c64c78SBram Moolenaar      let bal -= 1
21437c64c78SBram Moolenaar    elseif s:LookingAt() == '?'
215*f0b03c4eSBram Moolenaar      if getline('.')[col('.'):col('.')+1] =~ '^\.\d\@!'
216*f0b03c4eSBram Moolenaar        continue
217*f0b03c4eSBram Moolenaar      elseif !bal
218*f0b03c4eSBram Moolenaar        return 1
21937c64c78SBram Moolenaar      endif
220*f0b03c4eSBram Moolenaar      let bal += 1
22137c64c78SBram Moolenaar    elseif s:LookingAt() == '{'
222*f0b03c4eSBram Moolenaar      return !s:IsBlock()
22337c64c78SBram Moolenaar    elseif !s:GetPair('{','}','bW',s:skip_expr)
22437c64c78SBram Moolenaar      break
22537c64c78SBram Moolenaar    endif
2263c2881dcSBram Moolenaar  endwhile
22768563937SBram Moolenaarendfunction
22809521313SBram Moolenaar
22909521313SBram Moolenaar" configurable regexes that define continuation lines, not including (, {, or [.
23068563937SBram Moolenaarlet s:opfirst = '^' . get(g:,'javascript_opfirst',
23137c64c78SBram Moolenaar      \ '\C\%([<>=,.?^%|/&]\|\([-:+]\)\1\@!\|\*\+\|!=\|in\%(stanceof\)\=\>\)')
23268563937SBram Moolenaarlet s:continuation = get(g:,'javascript_continuation',
23337c64c78SBram Moolenaar      \ '\C\%([<=,.~!?/*^%|&:]\|+\@<!+\|-\@<!-\|=\@<!>\|\<\%(typeof\|new\|delete\|void\|in\|instanceof\|await\)\)') . '$'
234e4a3bcf2SBram Moolenaar
235*f0b03c4eSBram Moolenaarfunction s:Continues()
236*f0b03c4eSBram Moolenaar  let tok = matchstr(strpart(getline('.'),col('.')-15,15),s:continuation)
23737c64c78SBram Moolenaar  if tok =~ '[a-z:]'
23837c64c78SBram Moolenaar    return tok == ':' ? s:ExprCol() : s:PreviousToken() != '.'
23937c64c78SBram Moolenaar  elseif tok !~ '[/>]'
24037c64c78SBram Moolenaar    return tok isnot ''
2413c2881dcSBram Moolenaar  endif
242*f0b03c4eSBram Moolenaar  return s:SynAt(line('.'),col('.')) !~? (tok == '>' ? 'jsflow\|^html' : 'regex')
24309521313SBram Moolenaarendfunction
24409521313SBram Moolenaar
24509521313SBram Moolenaar" Check if line 'lnum' has a balanced amount of parentheses.
246*f0b03c4eSBram Moolenaarfunction s:Balanced(lnum,line)
247*f0b03c4eSBram Moolenaar  let l:open = 0
248*f0b03c4eSBram Moolenaar  let pos = match(a:line, '[][(){}]')
24909521313SBram Moolenaar  while pos != -1
25037c64c78SBram Moolenaar    if s:SynAt(a:lnum,pos + 1) !~? b:syng_strcom
251*f0b03c4eSBram Moolenaar      let l:open += match(' ' . a:line[pos],'[[({]')
25268563937SBram Moolenaar      if l:open < 0
25368563937SBram Moolenaar        return
25409521313SBram Moolenaar      endif
25509521313SBram Moolenaar    endif
256*f0b03c4eSBram Moolenaar    let pos = match(a:line, !l:open ? '[][(){}]' : '()' =~ a:line[pos] ?
257*f0b03c4eSBram Moolenaar          \ '[()]' : '{}' =~ a:line[pos] ? '[{}]' : '[][]', pos + 1)
25809521313SBram Moolenaar  endwhile
25968563937SBram Moolenaar  return !l:open
26009521313SBram Moolenaarendfunction
26168563937SBram Moolenaar
26237c64c78SBram Moolenaarfunction s:OneScope()
26337c64c78SBram Moolenaar  if s:LookingAt() == ')' && s:GetPair('(', ')', 'bW', s:skip_expr)
26437c64c78SBram Moolenaar    let tok = s:PreviousToken()
26537c64c78SBram Moolenaar    return (count(split('for if let while with'),tok) ||
26637c64c78SBram Moolenaar          \ tok =~# '^await$\|^each$' && s:PreviousToken() ==# 'for') &&
26737c64c78SBram Moolenaar          \ s:Pure('s:PreviousToken') != '.' && !(tok == 'while' && s:DoWhile())
26837c64c78SBram Moolenaar  elseif s:Token() =~# '^else$\|^do$'
26937c64c78SBram Moolenaar    return s:Pure('s:PreviousToken') != '.'
270*f0b03c4eSBram Moolenaar  elseif strpart(getline('.'),col('.')-2,2) == '=>'
271*f0b03c4eSBram Moolenaar    call cursor(0,col('.')-1)
272*f0b03c4eSBram Moolenaar    if s:PreviousToken() == ')'
273*f0b03c4eSBram Moolenaar      return s:GetPair('(', ')', 'bW', s:skip_expr)
27468563937SBram Moolenaar    endif
275*f0b03c4eSBram Moolenaar    return 1
276*f0b03c4eSBram Moolenaar  endif
27768563937SBram Moolenaarendfunction
27868563937SBram Moolenaar
27937c64c78SBram Moolenaarfunction s:DoWhile()
28037c64c78SBram Moolenaar  let cpos = searchpos('\m\<','cbW')
281*f0b03c4eSBram Moolenaar  while s:SearchLoop('\C[{}]\|\<\%(do\|while\)\>','bW',s:skip_expr)
282*f0b03c4eSBram Moolenaar    if s:LookingAt() =~ '\a'
283*f0b03c4eSBram Moolenaar      if s:Pure('s:IsBlock')
284*f0b03c4eSBram Moolenaar        if s:LookingAt() ==# 'd'
28537c64c78SBram Moolenaar          return 1
28637c64c78SBram Moolenaar        endif
287*f0b03c4eSBram Moolenaar        break
28837c64c78SBram Moolenaar      endif
289*f0b03c4eSBram Moolenaar    elseif s:LookingAt() != '}' || !s:GetPair('{','}','bW',s:skip_expr)
290*f0b03c4eSBram Moolenaar      break
291*f0b03c4eSBram Moolenaar    endif
292*f0b03c4eSBram Moolenaar  endwhile
293*f0b03c4eSBram Moolenaar  call call('cursor',cpos)
29437c64c78SBram Moolenaarendfunction
29537c64c78SBram Moolenaar
29637c64c78SBram Moolenaar" returns total offset from braceless contexts. 'num' is the lineNr which
29737c64c78SBram Moolenaar" encloses the entire context, 'cont' if whether a:firstline is a continued
29837c64c78SBram Moolenaar" expression, which could have started in a braceless context
299*f0b03c4eSBram Moolenaarfunction s:IsContOne(cont)
300*f0b03c4eSBram Moolenaar  let [l:num, b_l] = [b:js_cache[1] + !b:js_cache[1], 0]
301*f0b03c4eSBram Moolenaar  let pind = b:js_cache[1] ? indent(b:js_cache[1]) + s:sw() : 0
30237c64c78SBram Moolenaar  let ind = indent('.') + !a:cont
30337c64c78SBram Moolenaar  while line('.') > l:num && ind > pind || line('.') == l:num
30437c64c78SBram Moolenaar    if indent('.') < ind && s:OneScope()
30537c64c78SBram Moolenaar      let b_l += 1
30637c64c78SBram Moolenaar    elseif !a:cont || b_l || ind < indent(a:firstline)
30737c64c78SBram Moolenaar      break
30837c64c78SBram Moolenaar    else
30937c64c78SBram Moolenaar      call cursor(0,1)
31037c64c78SBram Moolenaar    endif
31137c64c78SBram Moolenaar    let ind = min([ind, indent('.')])
31237c64c78SBram Moolenaar    if s:PreviousToken() is ''
31368563937SBram Moolenaar      break
31468563937SBram Moolenaar    endif
31568563937SBram Moolenaar  endwhile
31637c64c78SBram Moolenaar  return b_l
31737c64c78SBram Moolenaarendfunction
31837c64c78SBram Moolenaar
31937c64c78SBram Moolenaarfunction s:IsSwitch()
320*f0b03c4eSBram Moolenaar  call call('cursor',b:js_cache[1:])
321*f0b03c4eSBram Moolenaar  return search('\m\C\%#.\_s*\%(\%(\/\/.*\_$\|\/\*\_.\{-}\*\/\)\@>\_s*\)*\%(case\|default\)\>','nWc'.s:z)
32268563937SBram Moolenaarendfunction
32368563937SBram Moolenaar
32468563937SBram Moolenaar" https://github.com/sweet-js/sweet.js/wiki/design#give-lookbehind-to-the-reader
32568563937SBram Moolenaarfunction s:IsBlock()
32637c64c78SBram Moolenaar  let tok = s:PreviousToken()
32737c64c78SBram Moolenaar  if join(s:stack) =~? 'xml\|jsx' && s:SynAt(line('.'),col('.')-1) =~? 'xml\|jsx'
328*f0b03c4eSBram Moolenaar    let s:in_jsx = 1
32937c64c78SBram Moolenaar    return tok != '{'
33037c64c78SBram Moolenaar  elseif tok =~ '\k'
33137c64c78SBram Moolenaar    if tok ==# 'type'
33237c64c78SBram Moolenaar      return s:Pure('eval',"s:PreviousToken() !~# '^\\%(im\\|ex\\)port$' || s:PreviousToken() == '.'")
33337c64c78SBram Moolenaar    elseif tok ==# 'of'
33437c64c78SBram Moolenaar      return s:Pure('eval',"!s:GetPair('[[({]','[])}]','bW',s:skip_expr) || s:LookingAt() != '(' ||"
33537c64c78SBram Moolenaar            \ ."s:{s:PreviousToken() ==# 'await' ? 'Previous' : ''}Token() !=# 'for' || s:PreviousToken() == '.'")
33668563937SBram Moolenaar    endif
33737c64c78SBram Moolenaar    return index(split('return const let import export extends yield default delete var await void typeof throw case new in instanceof')
33837c64c78SBram Moolenaar          \ ,tok) < (line('.') != a:firstline) || s:Pure('s:PreviousToken') == '.'
33937c64c78SBram Moolenaar  elseif tok == '>'
34037c64c78SBram Moolenaar    return getline('.')[col('.')-2] == '=' || s:SynAt(line('.'),col('.')) =~? 'jsflow\|^html'
34137c64c78SBram Moolenaar  elseif tok == '*'
34237c64c78SBram Moolenaar    return s:Pure('s:PreviousToken') == ':'
34337c64c78SBram Moolenaar  elseif tok == ':'
34437c64c78SBram Moolenaar    return s:Pure('eval',"s:PreviousToken() =~ '^\\K\\k*$' && !s:ExprCol()")
34537c64c78SBram Moolenaar  elseif tok == '/'
34637c64c78SBram Moolenaar    return s:SynAt(line('.'),col('.')) =~? 'regex'
34737c64c78SBram Moolenaar  elseif tok !~ '[=~!<,.?^%|&([]'
34837c64c78SBram Moolenaar    return tok !~ '[-+]' || line('.') != a:firstline && getline('.')[col('.')-2] == tok
34968563937SBram Moolenaar  endif
35068563937SBram Moolenaarendfunction
35109521313SBram Moolenaar
35209521313SBram Moolenaarfunction GetJavascriptIndent()
353*f0b03c4eSBram Moolenaar  call s:GetVars()
35437c64c78SBram Moolenaar  let s:synid_cache = [[],[]]
35537c64c78SBram Moolenaar  let l:line = getline(v:lnum)
3563c2881dcSBram Moolenaar  " use synstack as it validates syn state and works in an empty line
35737c64c78SBram Moolenaar  let s:stack = [''] + map(synstack(v:lnum,1),"synIDattr(v:val,'name')")
35809521313SBram Moolenaar
35968563937SBram Moolenaar  " start with strings,comments,etc.
36037c64c78SBram Moolenaar  if s:stack[-1] =~? 'comment\|doc'
36168563937SBram Moolenaar    if l:line =~ '^\s*\*'
36268563937SBram Moolenaar      return cindent(v:lnum)
36368563937SBram Moolenaar    elseif l:line !~ '^\s*\/[/*]'
36409521313SBram Moolenaar      return -1
36509521313SBram Moolenaar    endif
36637c64c78SBram Moolenaar  elseif s:stack[-1] =~? b:syng_str
367*f0b03c4eSBram Moolenaar    if b:js_cache[0] == v:lnum - 1 && s:Balanced(v:lnum-1,getline(v:lnum-1))
36868563937SBram Moolenaar      let b:js_cache[0] = v:lnum
36968563937SBram Moolenaar    endif
37068563937SBram Moolenaar    return -1
37109521313SBram Moolenaar  endif
37237c64c78SBram Moolenaar
37337c64c78SBram Moolenaar  let s:l1 = max([0,prevnonblank(v:lnum) - (s:rel ? 2000 : 1000),
37437c64c78SBram Moolenaar        \ get(get(b:,'hi_indent',{}),'blocklnr')])
37537c64c78SBram Moolenaar  call cursor(v:lnum,1)
37637c64c78SBram Moolenaar  if s:PreviousToken() is ''
37768563937SBram Moolenaar    return
37809521313SBram Moolenaar  endif
37937c64c78SBram Moolenaar  let [l:lnum, pline] = [line('.'), getline('.')[:col('.')-1]]
38009521313SBram Moolenaar
38168563937SBram Moolenaar  let l:line = substitute(l:line,'^\s*','','')
38237c64c78SBram Moolenaar  let l:line_raw = l:line
38368563937SBram Moolenaar  if l:line[:1] == '/*'
38468563937SBram Moolenaar    let l:line = substitute(l:line,'^\%(\/\*.\{-}\*\/\s*\)*','','')
38509521313SBram Moolenaar  endif
38668563937SBram Moolenaar  if l:line =~ '^\/[/*]'
38768563937SBram Moolenaar    let l:line = ''
38868563937SBram Moolenaar  endif
38909521313SBram Moolenaar
39068563937SBram Moolenaar  " the containing paren, bracket, or curly. Many hacks for performance
39137c64c78SBram Moolenaar  call cursor(v:lnum,1)
3923c2881dcSBram Moolenaar  let idx = index([']',')','}'],l:line[0])
39337c64c78SBram Moolenaar  if b:js_cache[0] > l:lnum && b:js_cache[0] < v:lnum ||
394*f0b03c4eSBram Moolenaar        \ b:js_cache[0] == l:lnum && s:Balanced(l:lnum,pline)
39568563937SBram Moolenaar    call call('cursor',b:js_cache[1:])
39609521313SBram Moolenaar  else
39737c64c78SBram Moolenaar    let [s:looksyn, s:top_col, s:check_in, s:l1] = [v:lnum - 1,0,0,
39837c64c78SBram Moolenaar          \ max([s:l1, &smc ? search('\m^.\{'.&smc.',}','nbW',s:l1 + 1) + 1 : 0])]
39937c64c78SBram Moolenaar    try
40037c64c78SBram Moolenaar      if idx != -1
40137c64c78SBram Moolenaar        call s:GetPair(['\[','(','{'][idx],'])}'[idx],'bW','s:SkipFunc()')
40237c64c78SBram Moolenaar      elseif getline(v:lnum) !~ '^\S' && s:stack[-1] =~? 'block\|^jsobject$'
40337c64c78SBram Moolenaar        call s:GetPair('{','}','bW','s:SkipFunc()')
40468563937SBram Moolenaar      else
40537c64c78SBram Moolenaar        call s:AlternatePair()
40609521313SBram Moolenaar      endif
40737c64c78SBram Moolenaar    catch /^\Cout of bounds$/
40837c64c78SBram Moolenaar      call cursor(v:lnum,1)
40937c64c78SBram Moolenaar    endtry
41037c64c78SBram Moolenaar    let b:js_cache[1:] = line('.') == v:lnum ? [0,0] : getpos('.')[1:2]
41109521313SBram Moolenaar  endif
41209521313SBram Moolenaar
41337c64c78SBram Moolenaar  let [b:js_cache[0], num] = [v:lnum, b:js_cache[1]]
41468563937SBram Moolenaar
415*f0b03c4eSBram Moolenaar  let [num_ind, is_op, b_l, l:switch_offset, s:in_jsx] = [s:Nat(indent(num)),0,0,0,0]
41637c64c78SBram Moolenaar  if !num || s:LookingAt() == '{' && s:IsBlock()
4173c2881dcSBram Moolenaar    let ilnum = line('.')
418*f0b03c4eSBram Moolenaar    if num && !s:in_jsx && s:LookingAt() == ')' && s:GetPair('(',')','bW',s:skip_expr)
41937c64c78SBram Moolenaar      if ilnum == num
42037c64c78SBram Moolenaar        let [num, num_ind] = [line('.'), indent('.')]
42168563937SBram Moolenaar      endif
42237c64c78SBram Moolenaar      if idx == -1 && s:PreviousToken() ==# 'switch' && s:IsSwitch()
42337c64c78SBram Moolenaar        let l:switch_offset = &cino !~ ':' ? s:sw() : s:ParseCino(':')
42468563937SBram Moolenaar        if pline[-1:] != '.' && l:line =~# '^\%(default\|case\)\>'
42537c64c78SBram Moolenaar          return s:Nat(num_ind + l:switch_offset)
42637c64c78SBram Moolenaar        elseif &cino =~ '='
42737c64c78SBram Moolenaar          let l:case_offset = s:ParseCino('=')
42868563937SBram Moolenaar        endif
42968563937SBram Moolenaar      endif
43068563937SBram Moolenaar    endif
43137c64c78SBram Moolenaar    if idx == -1 && pline[-1:] !~ '[{;]'
432*f0b03c4eSBram Moolenaar      call cursor(l:lnum, len(pline))
43337c64c78SBram Moolenaar      let sol = matchstr(l:line,s:opfirst)
43437c64c78SBram Moolenaar      if sol is '' || sol == '/' && s:SynAt(v:lnum,
43537c64c78SBram Moolenaar            \ 1 + len(getline(v:lnum)) - len(l:line)) =~? 'regex'
436*f0b03c4eSBram Moolenaar        if s:Continues()
43737c64c78SBram Moolenaar          let is_op = s:sw()
43868563937SBram Moolenaar        endif
439*f0b03c4eSBram Moolenaar      elseif num && sol =~# '^\%(in\%(stanceof\)\=\|\*\)$' &&
440*f0b03c4eSBram Moolenaar            \ s:LookingAt() == '}' && s:GetPair('{','}','bW',s:skip_expr) &&
441*f0b03c4eSBram Moolenaar            \ s:PreviousToken() == ')' && s:GetPair('(',')','bW',s:skip_expr) &&
442*f0b03c4eSBram Moolenaar            \ (s:PreviousToken() == ']' || s:LookingAt() =~ '\k' &&
443*f0b03c4eSBram Moolenaar            \ s:{s:PreviousToken() == '*' ? 'Previous' : ''}Token() !=# 'function')
44437c64c78SBram Moolenaar        return num_ind + s:sw()
44537c64c78SBram Moolenaar      else
44637c64c78SBram Moolenaar        let is_op = s:sw()
44737c64c78SBram Moolenaar      endif
44837c64c78SBram Moolenaar      call cursor(l:lnum, len(pline))
449*f0b03c4eSBram Moolenaar      let b_l = s:Nat(s:IsContOne(is_op) - (!is_op && l:line =~ '^{')) * s:sw()
45037c64c78SBram Moolenaar    endif
45137c64c78SBram Moolenaar  elseif idx.s:LookingAt().&cino =~ '^-1(.*(' && (search('\m\S','nbW',num) || s:ParseCino('U'))
45237c64c78SBram Moolenaar    let pval = s:ParseCino('(')
45337c64c78SBram Moolenaar    if !pval
45437c64c78SBram Moolenaar      let [Wval, vcol] = [s:ParseCino('W'), virtcol('.')]
45537c64c78SBram Moolenaar      if search('\m\S','W',num)
45637c64c78SBram Moolenaar        return s:ParseCino('w') ? vcol : virtcol('.')-1
45737c64c78SBram Moolenaar      endif
45837c64c78SBram Moolenaar      return Wval ? s:Nat(num_ind + Wval) : vcol
45937c64c78SBram Moolenaar    endif
46037c64c78SBram Moolenaar    return s:Nat(num_ind + pval + searchpair('\m(','','\m)','nbrmW',s:skip_expr,num) * s:sw())
46168563937SBram Moolenaar  endif
46268563937SBram Moolenaar
46368563937SBram Moolenaar  " main return
46437c64c78SBram Moolenaar  if l:line =~ '^[])}]\|^|}'
465*f0b03c4eSBram Moolenaar    if l:line_raw[0] == ')'
46637c64c78SBram Moolenaar      if s:ParseCino('M')
46737c64c78SBram Moolenaar        return indent(l:lnum)
468*f0b03c4eSBram Moolenaar      elseif num && &cino =~# 'm' && !s:ParseCino('m')
46937c64c78SBram Moolenaar        return virtcol('.') - 1
47068563937SBram Moolenaar      endif
47137c64c78SBram Moolenaar    endif
47237c64c78SBram Moolenaar    return num_ind
47337c64c78SBram Moolenaar  elseif num
47437c64c78SBram Moolenaar    return s:Nat(num_ind + get(l:,'case_offset',s:sw()) + l:switch_offset + b_l + is_op)
47537c64c78SBram Moolenaar  endif
47637c64c78SBram Moolenaar  return b_l + is_op
47709521313SBram Moolenaarendfunction
47809521313SBram Moolenaar
47909521313SBram Moolenaarlet &cpo = s:cpo_save
48009521313SBram Moolenaarunlet s:cpo_save
481