1dd2a3cdaSBram Moolenaar" Vim indent file 2dd2a3cdaSBram Moolenaar" Language: Javascript 3*09521313SBram Moolenaar" Maintainer: vim-javascript community 4*09521313SBram Moolenaar" URL: https://github.com/pangloss/vim-javascript 5*09521313SBram Moolenaar" Last Change: August 12, 2016 6dd2a3cdaSBram Moolenaar 7dd2a3cdaSBram Moolenaar" Only load this indent file when no other was loaded. 8*09521313SBram Moolenaarif exists('b:did_indent') 9dd2a3cdaSBram Moolenaar finish 10dd2a3cdaSBram Moolenaarendif 11dd2a3cdaSBram Moolenaarlet b:did_indent = 1 12dd2a3cdaSBram Moolenaar 13*09521313SBram Moolenaar" Now, set up our indentation expression and keys that trigger it. 14*09521313SBram Moolenaarsetlocal indentexpr=GetJavascriptIndent() 15*09521313SBram Moolenaarsetlocal nolisp 16*09521313SBram Moolenaarsetlocal indentkeys=0{,0},0),0],:,!^F,o,O,e 173acfc304SBram Moolenaarsetlocal cinoptions+=j1,J1 18dd2a3cdaSBram Moolenaar 19*09521313SBram Moolenaarlet b:undo_indent = 'setlocal indentexpr< indentkeys< cinoptions<' 20*09521313SBram Moolenaar 21*09521313SBram Moolenaar" Only define the function once. 22*09521313SBram Moolenaarif exists('*GetJavascriptIndent') 23*09521313SBram Moolenaar finish 24*09521313SBram Moolenaarendif 25*09521313SBram Moolenaar 26*09521313SBram Moolenaarlet s:cpo_save = &cpo 27*09521313SBram Moolenaarset cpo&vim 28*09521313SBram Moolenaar 29*09521313SBram Moolenaar" Get shiftwidth value 30*09521313SBram Moolenaarif exists('*shiftwidth') 31*09521313SBram Moolenaar function s:sw() 32*09521313SBram Moolenaar return shiftwidth() 33*09521313SBram Moolenaar endfunction 34*09521313SBram Moolenaarelse 35*09521313SBram Moolenaar function s:sw() 36*09521313SBram Moolenaar return &sw 37*09521313SBram Moolenaar endfunction 38*09521313SBram Moolenaarendif 39*09521313SBram Moolenaar 40*09521313SBram Moolenaarlet s:line_pre = '^\s*\%(\/\*.\{-}\*\/\s*\)*' 41*09521313SBram Moolenaarlet s:expr_case = s:line_pre . '\%(\%(case\>.\+\)\|default\)\s*:' 42*09521313SBram Moolenaar" Regex of syntax group names that are or delimit string or are comments. 43*09521313SBram Moolenaarlet s:syng_strcom = '\%(s\%(tring\|pecial\)\|comment\|regex\|doc\|template\)' 44*09521313SBram Moolenaar 45*09521313SBram Moolenaar" Regex of syntax group names that are strings or documentation. 46*09521313SBram Moolenaarlet s:syng_comment = '\%(comment\|doc\)' 47*09521313SBram Moolenaar 48*09521313SBram Moolenaar" Expression used to check whether we should skip a match with searchpair(). 49*09521313SBram Moolenaarlet s:skip_expr = "line('.') < (prevnonblank(v:lnum) - 2000) ? dummy : synIDattr(synID(line('.'),col('.'),0),'name') =~? '".s:syng_strcom."'" 50*09521313SBram Moolenaar 51*09521313SBram Moolenaarfunction s:lookForParens(start,end,flags,time) 52*09521313SBram Moolenaar if has('reltime') 53*09521313SBram Moolenaar return searchpair(a:start,'',a:end,a:flags,s:skip_expr,0,a:time) 54*09521313SBram Moolenaar else 55*09521313SBram Moolenaar return searchpair(a:start,'',a:end,a:flags,0,0) 56*09521313SBram Moolenaar endif 57*09521313SBram Moolenaarendfunction 58*09521313SBram Moolenaar 59*09521313SBram Moolenaarlet s:line_term = '\%(\s*\%(\/\*.\{-}\*\/\s*\)\=\)\@>$' 60*09521313SBram Moolenaar 61*09521313SBram Moolenaar" configurable regexes that define continuation lines, not including (, {, or [. 62*09521313SBram Moolenaarif !exists('g:javascript_opfirst') 63*09521313SBram Moolenaar let g:javascript_opfirst = '\%([<>,:?^%]\|\([-/.+]\)\%(\1\|\*\|\/\)\@!\|\*\/\@!\|=>\@!\||\|&\|in\%(stanceof\)\=\>\)' 64*09521313SBram Moolenaarendif 65*09521313SBram Moolenaarlet g:javascript_opfirst = s:line_pre . g:javascript_opfirst 66*09521313SBram Moolenaar 67*09521313SBram Moolenaarif !exists('g:javascript_continuation') 68*09521313SBram Moolenaar let g:javascript_continuation = '\%([<*,.?:^%]\|+\@<!+\|-\@<!-\|=\@<!>\|\*\@<!\/\|=\||\|&\|\<in\%(stanceof\)\=\)' 69*09521313SBram Moolenaarendif 70*09521313SBram Moolenaarlet g:javascript_continuation .= s:line_term 71*09521313SBram Moolenaar 72*09521313SBram Moolenaarfunction s:Onescope(lnum,text,add) 73*09521313SBram Moolenaar return a:text =~# '\%(\<else\|\<do\|=>' . (a:add ? '\|\<try\|\<finally' : '' ) . '\)' . s:line_term || 74*09521313SBram Moolenaar \ ((a:add && a:text =~ s:line_pre . '$' && search('\%' . s:PrevCodeLine(a:lnum - 1) . 'l.)' . s:line_term)) || 75*09521313SBram Moolenaar \ cursor(a:lnum, match(a:text, ')' . s:line_term)) > -1) && 76*09521313SBram Moolenaar \ s:lookForParens('(', ')', 'cbW', 100) > 0 && search((a:add ? 77*09521313SBram Moolenaar \ '\%(function\*\|[[:lower:][:upper:]_$][[:digit:][:lower:][:upper:]_$]*\)' : 78*09521313SBram Moolenaar \ '\<\%(for\%(\s\+each\)\=\|if\|let\|w\%(hile\|ith\)\)') . '\_s*\%#\C','bW') && 79*09521313SBram Moolenaar \ (a:add || (expand('<cword>') ==# 'while' ? !s:lookForParens('\<do\>\C', '\<while\>\C','bW',100) : 1)) 80*09521313SBram Moolenaarendfunction 81*09521313SBram Moolenaar 82*09521313SBram Moolenaar" Auxiliary Functions {{{2 83*09521313SBram Moolenaar 84*09521313SBram Moolenaar" strip line of comment 85*09521313SBram Moolenaarfunction s:StripLine(c) 86*09521313SBram Moolenaar return a:c !~# s:expr_case ? substitute(a:c, '\%(:\@<!\/\/.*\)$', '','') : a:c 87*09521313SBram Moolenaarendfunction 88*09521313SBram Moolenaar 89*09521313SBram Moolenaar" Find line above 'lnum' that isn't empty, in a comment, or in a string. 90*09521313SBram Moolenaarfunction s:PrevCodeLine(lnum) 91*09521313SBram Moolenaar let l:lnum = prevnonblank(a:lnum) 92*09521313SBram Moolenaar while l:lnum > 0 93*09521313SBram Moolenaar if synIDattr(synID(l:lnum,matchend(getline(l:lnum), '^\s*[^''"]'),0),'name') !~? s:syng_strcom 94*09521313SBram Moolenaar break 95*09521313SBram Moolenaar endif 96*09521313SBram Moolenaar let l:lnum = prevnonblank(l:lnum - 1) 97*09521313SBram Moolenaar endwhile 98*09521313SBram Moolenaar return l:lnum 99*09521313SBram Moolenaarendfunction 100*09521313SBram Moolenaar 101*09521313SBram Moolenaar" Check if line 'lnum' has a balanced amount of parentheses. 102*09521313SBram Moolenaarfunction s:Balanced(lnum) 103*09521313SBram Moolenaar let open_0 = 0 104*09521313SBram Moolenaar let open_2 = 0 105*09521313SBram Moolenaar let open_4 = 0 106*09521313SBram Moolenaar let l:line = getline(a:lnum) 107*09521313SBram Moolenaar let pos = match(l:line, '[][(){}]', 0) 108*09521313SBram Moolenaar while pos != -1 109*09521313SBram Moolenaar if synIDattr(synID(a:lnum,pos + 1,0),'name') !~? s:syng_strcom 110*09521313SBram Moolenaar let idx = stridx('(){}[]', l:line[pos]) 111*09521313SBram Moolenaar if idx % 2 == 0 112*09521313SBram Moolenaar let open_{idx} = open_{idx} + 1 113*09521313SBram Moolenaar else 114*09521313SBram Moolenaar let open_{idx - 1} = open_{idx - 1} - 1 115*09521313SBram Moolenaar endif 116*09521313SBram Moolenaar endif 117*09521313SBram Moolenaar let pos = match(l:line, '[][(){}]', pos + 1) 118*09521313SBram Moolenaar endwhile 119*09521313SBram Moolenaar return (!open_4 + !open_2 + !open_0) - 2 120*09521313SBram Moolenaarendfunction 121*09521313SBram Moolenaar" }}} 122*09521313SBram Moolenaar 123*09521313SBram Moolenaarfunction GetJavascriptIndent() 124*09521313SBram Moolenaar if !exists('b:js_cache') 125*09521313SBram Moolenaar let b:js_cache = [0,0,0] 126*09521313SBram Moolenaar endif 127*09521313SBram Moolenaar " Get the current line. 128*09521313SBram Moolenaar let l:line = getline(v:lnum) 129*09521313SBram Moolenaar let syns = synIDattr(synID(v:lnum, 1, 0), 'name') 130*09521313SBram Moolenaar 131*09521313SBram Moolenaar " start with strings,comments,etc.{{{2 132*09521313SBram Moolenaar if (l:line !~ '^[''"`]' && syns =~? 'string\|template') || 133*09521313SBram Moolenaar \ (l:line !~ '^\s*[/*]' && syns =~? s:syng_comment) 134*09521313SBram Moolenaar return -1 135*09521313SBram Moolenaar endif 136*09521313SBram Moolenaar if l:line !~ '^\%(\/\*\|\s*\/\/\)' && syns =~? s:syng_comment 137*09521313SBram Moolenaar return cindent(v:lnum) 138*09521313SBram Moolenaar endif 139*09521313SBram Moolenaar let l:lnum = s:PrevCodeLine(v:lnum - 1) 140*09521313SBram Moolenaar if l:lnum == 0 141*09521313SBram Moolenaar return 0 142*09521313SBram Moolenaar endif 143*09521313SBram Moolenaar 144*09521313SBram Moolenaar if (l:line =~# s:expr_case) 145*09521313SBram Moolenaar let cpo_switch = &cpo 146*09521313SBram Moolenaar set cpo+=% 147*09521313SBram Moolenaar let ind = cindent(v:lnum) 148*09521313SBram Moolenaar let &cpo = cpo_switch 149*09521313SBram Moolenaar return ind 150*09521313SBram Moolenaar endif 151*09521313SBram Moolenaar "}}} 152*09521313SBram Moolenaar 153*09521313SBram Moolenaar " the containing paren, bracket, curly. Memoize, last lineNr either has the 154*09521313SBram Moolenaar " same scope or starts a new one, unless if it closed a scope. 155*09521313SBram Moolenaar call cursor(v:lnum,1) 156*09521313SBram Moolenaar if b:js_cache[0] >= l:lnum && b:js_cache[0] <= v:lnum && b:js_cache[0] && 157*09521313SBram Moolenaar \ (b:js_cache[0] > l:lnum || s:Balanced(l:lnum) > 0) 158*09521313SBram Moolenaar let num = b:js_cache[1] 159*09521313SBram Moolenaar elseif syns != '' && l:line[0] =~ '\s' 160*09521313SBram Moolenaar let pattern = syns =~? 'block' ? ['{','}'] : syns =~? 'jsparen' ? ['(',')'] : 161*09521313SBram Moolenaar \ syns =~? 'jsbracket'? ['\[','\]'] : ['[({[]','[])}]'] 162*09521313SBram Moolenaar let num = s:lookForParens(pattern[0],pattern[1],'bW',2000) 163*09521313SBram Moolenaar else 164*09521313SBram Moolenaar let num = s:lookForParens('[({[]','[])}]','bW',2000) 165*09521313SBram Moolenaar endif 166*09521313SBram Moolenaar let b:js_cache = [v:lnum,num,line('.') == v:lnum ? b:js_cache[2] : col('.')] 167*09521313SBram Moolenaar 168*09521313SBram Moolenaar if l:line =~ s:line_pre . '[])}]' 169*09521313SBram Moolenaar return indent(num) 170*09521313SBram Moolenaar endif 171*09521313SBram Moolenaar 172*09521313SBram Moolenaar let pline = s:StripLine(getline(l:lnum)) 173*09521313SBram Moolenaar let inb = num == 0 ? 1 : (s:Onescope(num, s:StripLine(strpart(getline(num),0,b:js_cache[2] - 1)),1) || 174*09521313SBram Moolenaar \ (l:line !~ s:line_pre . ',' && pline !~ ',' . s:line_term)) && num < l:lnum 175*09521313SBram Moolenaar let switch_offset = (!inb || num == 0) || expand("<cword>") !=# 'switch' ? 0 : &cino !~ ':' || !has('float') ? s:sw() : 176*09521313SBram Moolenaar \ float2nr(str2float(matchstr(&cino,'.*:\zs[-0-9.]*')) * (&cino =~# '.*:[^,]*s' ? s:sw() : 1)) 177*09521313SBram Moolenaar 178*09521313SBram Moolenaar " most significant, find the indent amount 179*09521313SBram Moolenaar if (inb && (l:line =~# g:javascript_opfirst || 180*09521313SBram Moolenaar \ (pline =~# g:javascript_continuation && pline !~# s:expr_case && (pline !~ ':' . s:line_term || l:line !~# 181*09521313SBram Moolenaar \ s:line_pre . '\%(d\%(o\|ebugger\)\|else\|f\%(or\|inally\)\|if\|let\|switch\|t\%(hrow\|ry\)\|w\%(hile\|ith\)\)\>')))) || 182*09521313SBram Moolenaar \ (num < l:lnum && s:Onescope(l:lnum,pline,0) && l:line !~ s:line_pre . '{') 183*09521313SBram Moolenaar return (num > 0 ? indent(num) : -s:sw()) + (s:sw() * 2) + switch_offset 184*09521313SBram Moolenaar elseif num > 0 185*09521313SBram Moolenaar return indent(num) + s:sw() + switch_offset 186*09521313SBram Moolenaar endif 187*09521313SBram Moolenaar 188*09521313SBram Moolenaarendfunction 189*09521313SBram Moolenaar 190*09521313SBram Moolenaar 191*09521313SBram Moolenaarlet &cpo = s:cpo_save 192*09521313SBram Moolenaarunlet s:cpo_save 193