diff options
author | Adam Stankiewicz <sheerun@sher.pl> | 2016-12-20 20:57:20 +0100 |
---|---|---|
committer | Adam Stankiewicz <sheerun@sher.pl> | 2016-12-20 20:57:20 +0100 |
commit | e404a658b1647fad396a954776eda0bdabf8353c (patch) | |
tree | fcdab0e324fd72015ba656e43bd8f8c243030c14 /indent/javascript.vim | |
parent | 74652b465d7eff97070001317a4ea5557717378d (diff) | |
download | vim-polyglot-e404a658b1647fad396a954776eda0bdabf8353c.tar.gz vim-polyglot-e404a658b1647fad396a954776eda0bdabf8353c.zip |
Update
Diffstat (limited to 'indent/javascript.vim')
-rw-r--r-- | indent/javascript.vim | 338 |
1 files changed, 229 insertions, 109 deletions
diff --git a/indent/javascript.vim b/indent/javascript.vim index e8573790..ea6b551d 100644 --- a/indent/javascript.vim +++ b/indent/javascript.vim @@ -2,9 +2,9 @@ if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'javascript') == " Vim indent file " Language: Javascript -" Maintainer: vim-javascript community +" Maintainer: Chris Paul ( https://github.com/bounceme ) " URL: https://github.com/pangloss/vim-javascript -" Last Change: August 20, 2016 +" Last Change: December 14, 2016 " Only load this indent file when no other was loaded. if exists('b:did_indent') @@ -14,11 +14,10 @@ let b:did_indent = 1 " Now, set up our indentation expression and keys that trigger it. setlocal indentexpr=GetJavascriptIndent() -setlocal nolisp noautoindent nosmartindent -setlocal indentkeys=0{,0},0),0],:,!^F,o,O,e -setlocal cinoptions+=j1,J1 +setlocal autoindent nolisp nosmartindent +setlocal indentkeys+=0],0) -let b:undo_indent = 'setlocal indentexpr< smartindent< autoindent< indentkeys< cinoptions<' +let b:undo_indent = 'setlocal indentexpr< smartindent< autoindent< indentkeys<' " Only define the function once. if exists('*GetJavascriptIndent') @@ -39,159 +38,280 @@ else endfunction endif -let s:line_pre = '^\s*\%(\%(\%(\/\*.\{-}\)\=\*\+\/\s*\)\=\)\@>' -let s:expr_case = s:line_pre . '\%(\%(case\>.\+\)\|default\)\s*:' -" Regex of syntax group names that are or delimit string or are comments. -let s:syng_strcom = '\%(s\%(tring\|pecial\)\|comment\|regex\|doc\|template\)' - -" Regex of syntax group names that are strings or documentation. -let s:syng_comment = '\%(comment\|doc\)' - -" Expression used to check whether we should skip a match with searchpair(). -let s:skip_expr = "synIDattr(synID(line('.'),col('.'),0),'name') =~? '".s:syng_strcom."'" - +" searchpair() wrapper if has('reltime') - function s:GetPair(start,end,flags,time) - return searchpair(a:start,'',a:end,a:flags,s:skip_expr,max([prevnonblank(v:lnum) - 2000,0]),a:time) + function s:GetPair(start,end,flags,skip,time,...) + return searchpair(a:start,'',a:end,a:flags,a:skip,max([prevnonblank(v:lnum) - 2000,0] + a:000),a:time) endfunction else - function s:GetPair(start,end,flags,n) - return searchpair(a:start,'',a:end,a:flags,0,max([prevnonblank(v:lnum) - 2000,0])) + function s:GetPair(start,end,flags,skip,...) + return searchpair(a:start,'',a:end,a:flags,a:skip,max([prevnonblank(v:lnum) - 1000,get(a:000,1)])) endfunction endif -let s:line_term = '\s*\%(\%(\/\%(\%(\*.\{-}\*\/\)\|\%(\*\+\)\)\)\s*\)\=$' +" Regex of syntax group names that are or delimit string or are comments. +let s:syng_strcom = 'string\|comment\|regex\|special\|doc\|template' +let s:syng_str = 'string\|template' +let s:syng_com = 'comment\|doc' +" Expression used to check whether we should skip a match with searchpair(). +let s:skip_expr = "synIDattr(synID(line('.'),col('.'),0),'name') =~? '".s:syng_strcom."'" + +function s:skip_func() + if !s:free || search('`\|\*\/','nW',s:looksyn) + let s:free = !eval(s:skip_expr) + let s:looksyn = s:free ? line('.') : s:looksyn + return !s:free + endif + let s:looksyn = line('.') + return (search('\/','nbW',s:looksyn) || search('[''"\\]','nW',s:looksyn)) && eval(s:skip_expr) +endfunction -" configurable regexes that define continuation lines, not including (, {, or [. -if !exists('g:javascript_opfirst') - let g:javascript_opfirst = '\%([<>,:?^%|*&]\|\([-/.+]\)\1\@!\|=>\@!\|in\%(stanceof\)\=\>\)' -endif -if !exists('g:javascript_continuation') - let g:javascript_continuation = '\%([<=,.?/*:^%|&]\|+\@<!+\|-\@<!-\|=\@<!>\|\<in\%(stanceof\)\=\)' -endif +function s:alternatePair(stop) + while search('[][(){}]','bW',a:stop) + if !s:skip_func() + let idx = stridx('])}',s:looking_at()) + if idx + 1 + if !s:GetPair(['\[','(','{'][idx], '])}'[idx],'bW','s:skip_func()',2000,a:stop) + break + endif + else + return + endif + endif + endwhile + call cursor(v:lnum,1) +endfunction -let g:javascript_opfirst = s:line_pre . g:javascript_opfirst -let g:javascript_continuation .= s:line_term +function s:syn_at(l,c) + return synIDattr(synID(a:l,a:c,0),'name') +endfunction -function s:OneScope(lnum,text,add) - return a:text =~# '\%(\<else\|\<do\|=>\)' . s:line_term ? 'no b' : - \ ((a:add && a:text =~ s:line_pre . '$' && search('\%' . s:PrevCodeLine(a:lnum - 1) . 'l.)' . s:line_term)) || - \ cursor(a:lnum, match(a:text, ')' . s:line_term)) > -1) && - \ s:GetPair('(', ')', 'cbW', 100) > 0 && search('\C\l\+\_s*\%#','bW') && - \ (a:add || ((expand('<cword>') !=# 'while' || !s:GetPair('\C\<do\>', '\C\<while\>','nbW',100)) && - \ expand('cword') !=# 'each' || search('\C\<for\_s\+\%#','nbW'))) ? expand('<cword>') : '' +function s:looking_at() + return getline('.')[col('.')-1] endfunction -" https://github.com/sweet-js/sweet.js/wiki/design#give-lookbehind-to-the-reader -function s:IsBlock() - return getline(line('.'))[col('.')-1] == '{' && !search( - \ '\C\%(\<return\s*\|\%([-=~!<*+,.?^%|&\[(]\|=\@<!>\|\*\@<!\/\|\<\%(var\|const\|let\|yield\|delete\|void\|t\%(ypeof\|hrow\)\|new\|\<in\%(stanceof\)\=\)\)\_s*\)\%#','bnW') && - \ (!search(':\_s*\%#','bW') || (!s:GetPair('[({[]','[])}]','bW',200) || s:IsBlock())) +function s:token() + return s:looking_at() =~ '\k' ? expand('<cword>') : s:looking_at() endfunction -" Auxiliary Functions {{{2 +" NOTE: Moves the cursor, unless a arg is supplied. +function s:previous_token(...) + let l:pos = getpos('.')[1:2] + return [search('.\>\|[^[:alnum:][:space:]_$]','bW') ? + \ (s:looking_at() == '/' || line('.') != l:pos[0] && getline('.') =~ '\/\/') && + \ s:syn_at(line('.'),col('.')) =~? s:syng_com ? + \ search('\_[^/]\zs\/[/*]','bW') ? s:previous_token() : '' + \ : s:token() + \ : ''][a:0 && call('cursor',l:pos)] +endfunction + +" switch case label pattern +let s:case_stmt = '\<\%(case\>\s*[^ \t:].*\|default\s*\):\C' + +function s:label_end(ln,con) + return !cursor(a:ln,match(' '.a:con, '.*\zs' . s:case_stmt . '$')) && + \ (expand('<cword>') !=# 'default' || s:previous_token(1) !~ '[{,.]') +endfunction + +" configurable regexes that define continuation lines, not including (, {, or [. +let s:opfirst = '^' . get(g:,'javascript_opfirst', + \ '\%([<>=,?^%|*/&]\|\([-.:+]\)\1\@!\|!=\|in\%(stanceof\)\=\>\)') +let s:continuation = get(g:,'javascript_continuation', + \ '\%([<=,.~!?/*^%|&:]\|+\@<!+\|-\@<!-\|=\@<!>\|\<\%(typeof\|delete\|void\|in\|instanceof\)\)') . '$' + +function s:continues(ln,con) + return !cursor(a:ln, match(' '.a:con,s:continuation)) && + \ eval((['s:syn_at(line("."),col(".")) !~? "regex"'] + + \ repeat(['s:previous_token() != "."'],5) + [1])[ + \ index(split('/ typeof in instanceof void delete'),s:token())]) +endfunction -" Find line above 'lnum' that isn't empty, in a comment, or in a string. +" get the line of code stripped of comments. if called with two args, leave +" cursor at the last non-comment char. +function s:Trim(ln,...) + let pline = substitute(getline(a:ln),'\s*$','','') + let l:max = max([match(pline,'.*[^/]\zs\/[/*]'),0]) + while l:max && s:syn_at(a:ln, strlen(pline)) =~? s:syng_com + let pline = substitute(strpart(pline, 0, l:max),'\s*$','','') + let l:max = max([match(pline,'.*[^/]\zs\/[/*]'),0]) + endwhile + return !a:0 || cursor(a:ln,strlen(pline)) ? pline : pline +endfunction + +" Find line above 'lnum' that isn't empty or in a comment function s:PrevCodeLine(lnum) - let l:lnum = prevnonblank(a:lnum) - while l:lnum - if synIDattr(synID(l:lnum,matchend(getline(l:lnum), '^\s*[^''"]'),0),'name') !~? s:syng_strcom - return l:lnum - endif - let l:lnum = prevnonblank(l:lnum - 1) + let l:n = prevnonblank(a:lnum) + while getline(l:n) =~ '^\s*\/[/*]' || s:syn_at(l:n,1) =~? s:syng_com + let l:n = prevnonblank(l:n-1) endwhile + return l:n endfunction " Check if line 'lnum' has a balanced amount of parentheses. function s:Balanced(lnum) - let [open_0,open_2,open_4] = [0,0,0] + let l:open = 0 let l:line = getline(a:lnum) let pos = match(l:line, '[][(){}]', 0) while pos != -1 - if synIDattr(synID(a:lnum,pos + 1,0),'name') !~? s:syng_strcom - let idx = stridx('(){}[]', l:line[pos]) - if idx % 2 == 0 - let open_{idx} = open_{idx} + 1 - else - let open_{idx - 1} = open_{idx - 1} - 1 + if s:syn_at(a:lnum,pos + 1) !~? s:syng_strcom + let l:open += match(' ' . l:line[pos],'[[({]') + if l:open < 0 + return endif endif let pos = match(l:line, '[][(){}]', pos + 1) endwhile - return (!open_4 + !open_2 + !open_0) - 2 + return !l:open endfunction -" }}} -function GetJavascriptIndent() - if !exists('b:js_cache') - let b:js_cache = [0,0,0] +function s:OneScope(lnum) + let pline = s:Trim(a:lnum,1) + if pline[-1:] == ')' && s:GetPair('(', ')', 'bW', s:skip_expr, 100) > 0 + let token = s:previous_token() + if index(split('await each'),token) + 1 + return s:previous_token() ==# 'for' + endif + return index(split('for if let while with'),token) + 1 + endif + return eval((['getline(".")[col(".")-2] == "="'] + + \ repeat(['s:previous_token(1) != "."'],2) + [0])[ + \ index(split('> else do'),s:token())]) +endfunction + +" returns braceless levels started by 'i' and above lines * &sw. 'num' is the +" lineNr which encloses the entire context, 'cont' if whether line 'i' + 1 is +" a continued expression, which could have started in a braceless context +function s:iscontOne(i,num,cont) + let [l:i, l:num, bL] = [a:i, a:num + !a:num, 0] + let pind = a:num ? indent(l:num) + s:W : 0 + let ind = indent(l:i) + (a:cont ? 0 : s:W) + while l:i >= l:num && (ind > pind || l:i == l:num) + if indent(l:i) < ind && s:OneScope(l:i) + let bL += s:W + let l:i = line('.') + elseif !a:cont || bL || ind < indent(a:i) + break + endif + let ind = min([ind, indent(l:i)]) + let l:i = s:PrevCodeLine(l:i - 1) + endwhile + return bL +endfunction + +" https://github.com/sweet-js/sweet.js/wiki/design#give-lookbehind-to-the-reader +function s:IsBlock() + let l:ln = line('.') + let char = s:previous_token() + let syn = char =~ '[{>/]' ? s:syn_at(line('.'),col('.')-(char == '{')) : '' + if syn =~? 'xml\|jsx' + return char != '{' + elseif char =~ '\k' + return index(split('return const let import export yield default delete var void typeof throw new in instanceof') + \ ,char) < (0 + (line('.') != l:ln)) || s:previous_token() == '.' + elseif char == '>' + return getline('.')[col('.')-2] == '=' || syn =~? '^jsflow' + elseif char == ':' + return s:label_end(0,strpart(getline('.'),0,col('.'))) endif + return syn =~? 'regex' || char !~ '[-=~!<*+,/?^%|&([]' +endfunction + +function GetJavascriptIndent() + let b:js_cache = get(b:,'js_cache',[0,0,0]) " Get the current line. let l:line = getline(v:lnum) - let syns = synIDattr(synID(v:lnum, 1, 0), 'name') + let syns = s:syn_at(v:lnum, 1) - " start with strings,comments,etc.{{{2 - if (l:line !~ '^[''"`]' && syns =~? '\%(string\|template\)') || - \ (l:line !~ '^\s*[/*]' && syns =~? s:syng_comment) + " start with strings,comments,etc. + if syns =~? s:syng_com + if l:line =~ '^\s*\*' + return cindent(v:lnum) + elseif l:line !~ '^\s*\/[/*]' + return -1 + endif + elseif syns =~? s:syng_str && l:line !~ '^[''"]' + if b:js_cache[0] == v:lnum - 1 && s:Balanced(v:lnum-1) + let b:js_cache[0] = v:lnum + endif return -1 endif - if l:line !~ '^\%(\/\*\|\s*\/\/\)' && syns =~? s:syng_comment - return cindent(v:lnum) - endif let l:lnum = s:PrevCodeLine(v:lnum - 1) - if l:lnum == 0 - return 0 + if !l:lnum + return endif - if (l:line =~# s:expr_case) - let cpo_switch = &cpo - set cpo+=% - let ind = cindent(v:lnum) - let &cpo = cpo_switch - return ind + let l:line = substitute(l:line,'^\s*','','') + if l:line[:1] == '/*' + let l:line = substitute(l:line,'^\%(\/\*.\{-}\*\/\s*\)*','','') + endif + if l:line =~ '^\/[/*]' + let l:line = '' endif - "}}} - " the containing paren, bracket, curly. Memoize, last lineNr either has the - " same scope or starts a new one, unless if it closed a scope. + " the containing paren, bracket, or curly. Many hacks for performance call cursor(v:lnum,1) - if b:js_cache[0] >= l:lnum && b:js_cache[0] < v:lnum && b:js_cache[0] && - \ (b:js_cache[0] > l:lnum || s:Balanced(l:lnum) > 0) - let num = b:js_cache[1] - elseif syns != '' && l:line[0] =~ '\s' - let pattern = syns =~? 'block' ? ['{','}'] : syns =~? 'jsparen' ? ['(',')'] : - \ syns =~? 'jsbracket'? ['\[','\]'] : ['[({[]','[])}]'] - let num = s:GetPair(pattern[0],pattern[1],'bW',2000) + let idx = strlen(l:line) ? stridx('])}',l:line[0]) : -1 + if b:js_cache[0] >= l:lnum && b:js_cache[0] < v:lnum && + \ (b:js_cache[0] > l:lnum || s:Balanced(l:lnum)) + call call('cursor',b:js_cache[1:]) else - let num = s:GetPair('[({[]','[])}]','bW',2000) + let [s:looksyn, s:free, top] = [v:lnum - 1, 1, (!indent(l:lnum) && + \ s:syn_at(l:lnum,1) !~? s:syng_str) * l:lnum] + if idx + 1 + call s:GetPair(['\[','(','{'][idx], '])}'[idx],'bW','s:skip_func()',2000,top) + elseif indent(v:lnum) && syns =~? 'block' + call s:GetPair('{','}','bW','s:skip_func()',2000,top) + else + call s:alternatePair(top) + endif endif - let b:js_cache = [v:lnum,num,line('.') == v:lnum ? b:js_cache[2] : col('.')] - if l:line =~ s:line_pre . '[])}]' - return indent(num) + if idx + 1 + if idx == 2 && search('\S','bW',line('.')) && s:looking_at() == ')' + call s:GetPair('(',')','bW',s:skip_expr,200) + endif + return indent('.') endif - call cursor(b:js_cache[1],b:js_cache[2]) - - let swcase = getline(l:lnum) =~# s:expr_case - let pline = swcase ? getline(l:lnum) : substitute(getline(l:lnum), '\%(:\@<!\/\/.*\)$', '','') - let inb = num == 0 || num < l:lnum && ((l:line !~ s:line_pre . ',' && pline !~ ',' . s:line_term) || s:IsBlock()) - let switch_offset = num == 0 || s:OneScope(num, strpart(getline(num),0,b:js_cache[2] - 1),1) !=# 'switch' ? 0 : - \ &cino !~ ':' || !has('float') ? s:sw() : - \ float2nr(str2float(matchstr(&cino,'.*:\zs[-0-9.]*')) * (&cino =~# '.*:[^,]*s' ? s:sw() : 1)) - - " most significant, find the indent amount - if inb && !swcase && ((l:line =~# g:javascript_opfirst || pline =~# g:javascript_continuation) || - \ num < l:lnum && s:OneScope(l:lnum,pline,0) =~# '\<\%(for\|each\|if\|let\|no\sb\|w\%(hile\|ith\)\)\>' && - \ l:line !~ s:line_pre . '{') - return (num > 0 ? indent(num) : -s:sw()) + (s:sw() * 2) + switch_offset - elseif num > 0 - return indent(num) + s:sw() + switch_offset + let b:js_cache = [v:lnum] + (line('.') == v:lnum ? [0,0] : getpos('.')[1:2]) + let num = b:js_cache[1] + + let [s:W, isOp, bL, switch_offset] = [s:sw(),0,0,0] + if !num || s:looking_at() == '{' && s:IsBlock() + let pline = s:Trim(l:lnum) + if num && s:looking_at() == ')' && s:GetPair('(', ')', 'bW', s:skip_expr, 100) > 0 + let num = line('.') + if s:previous_token() ==# 'switch' + if &cino !~ ':' || !has('float') + let switch_offset = s:W + else + let cinc = matchlist(&cino,'.*:\(-\)\=\([0-9.]*\)\(s\)\=\C') + let switch_offset = float2nr(str2float(cinc[1].(strlen(cinc[2]) ? cinc[2] : strlen(cinc[3]))) + \ * (strlen(cinc[3]) ? s:W : 1)) + endif + if pline[-1:] != '.' && l:line =~# '^' . s:case_stmt + return indent(num) + switch_offset + elseif s:label_end(l:lnum,pline) + return indent(l:lnum) + s:W + endif + endif + endif + if pline[-1:] !~ '[{;]' + let isOp = l:line =~# s:opfirst || s:continues(l:lnum,pline) + let bL = s:iscontOne(l:lnum,num,isOp) + let bL -= (bL && l:line[0] == '{') * s:W + endif endif + " main return + if isOp + return (num ? indent(num) : -s:W) + (s:W * 2) + switch_offset + bL + elseif num + return indent(num) + s:W + switch_offset + bL + endif + return bL endfunction - let &cpo = s:cpo_save unlet s:cpo_save |