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