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