1dd2a3cdaSBram Moolenaar" Vim indent file 2dd2a3cdaSBram Moolenaar" Language: Javascript 3*e4a3bcf2SBram Moolenaar" Maintainer: Chris Paul ( https://github.com/bounceme ) 409521313SBram Moolenaar" URL: https://github.com/pangloss/vim-javascript 5*e4a3bcf2SBram Moolenaar" Last Change: August 25, 2016 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() 15*e4a3bcf2SBram Moolenaarsetlocal nolisp noautoindent nosmartindent 1609521313SBram Moolenaarsetlocal indentkeys=0{,0},0),0],:,!^F,o,O,e 173acfc304SBram Moolenaarsetlocal cinoptions+=j1,J1 18dd2a3cdaSBram Moolenaar 19*e4a3bcf2SBram Moolenaarlet b:undo_indent = 'setlocal indentexpr< smartindent< autoindent< indentkeys< cinoptions<' 2009521313SBram Moolenaar 2109521313SBram Moolenaar" Only define the function once. 2209521313SBram Moolenaarif exists('*GetJavascriptIndent') 2309521313SBram Moolenaar finish 2409521313SBram Moolenaarendif 2509521313SBram Moolenaar 2609521313SBram Moolenaarlet s:cpo_save = &cpo 2709521313SBram Moolenaarset cpo&vim 2809521313SBram Moolenaar 2909521313SBram Moolenaar" Get shiftwidth value 3009521313SBram Moolenaarif exists('*shiftwidth') 3109521313SBram Moolenaar function s:sw() 3209521313SBram Moolenaar return shiftwidth() 3309521313SBram Moolenaar endfunction 3409521313SBram Moolenaarelse 3509521313SBram Moolenaar function s:sw() 3609521313SBram Moolenaar return &sw 3709521313SBram Moolenaar endfunction 3809521313SBram Moolenaarendif 3909521313SBram Moolenaar 40*e4a3bcf2SBram Moolenaarlet s:line_pre = '^\s*\%(\%(\%(\/\*.\{-}\)\=\*\+\/\s*\)\=\)\@>' 4109521313SBram Moolenaarlet s:expr_case = s:line_pre . '\%(\%(case\>.\+\)\|default\)\s*:' 4209521313SBram Moolenaar" Regex of syntax group names that are or delimit string or are comments. 4309521313SBram Moolenaarlet s:syng_strcom = '\%(s\%(tring\|pecial\)\|comment\|regex\|doc\|template\)' 4409521313SBram Moolenaar 4509521313SBram Moolenaar" Regex of syntax group names that are strings or documentation. 4609521313SBram Moolenaarlet s:syng_comment = '\%(comment\|doc\)' 4709521313SBram Moolenaar 4809521313SBram Moolenaar" Expression used to check whether we should skip a match with searchpair(). 49*e4a3bcf2SBram Moolenaarlet s:skip_expr = "synIDattr(synID(line('.'),col('.'),0),'name') =~? '".s:syng_strcom."'" 5009521313SBram Moolenaar 5109521313SBram Moolenaarif has('reltime') 52*e4a3bcf2SBram Moolenaar function s:GetPair(start,end,flags,time) 53*e4a3bcf2SBram Moolenaar return searchpair(a:start,'',a:end,a:flags,s:skip_expr,max([prevnonblank(v:lnum) - 2000,0]),a:time) 5409521313SBram Moolenaar endfunction 55*e4a3bcf2SBram Moolenaarelse 56*e4a3bcf2SBram Moolenaar function s:GetPair(start,end,flags,n) 57*e4a3bcf2SBram Moolenaar return searchpair(a:start,'',a:end,a:flags,0,max([prevnonblank(v:lnum) - 2000,0])) 58*e4a3bcf2SBram Moolenaar endfunction 59*e4a3bcf2SBram Moolenaarendif 6009521313SBram Moolenaar 61*e4a3bcf2SBram Moolenaarlet s:line_term = '\s*\%(\%(\/\%(\%(\*.\{-}\*\/\)\|\%(\*\+\)\)\)\s*\)\=$' 6209521313SBram Moolenaar 6309521313SBram Moolenaar" configurable regexes that define continuation lines, not including (, {, or [. 6409521313SBram Moolenaarif !exists('g:javascript_opfirst') 65*e4a3bcf2SBram Moolenaar let g:javascript_opfirst = '\%([<>,:?^%|*&]\|\/[^/*]\|\([-.+]\)\1\@!\|=>\@!\|in\%(stanceof\)\=\>\)' 6609521313SBram Moolenaarendif 6709521313SBram Moolenaarif !exists('g:javascript_continuation') 68*e4a3bcf2SBram Moolenaar let g:javascript_continuation = '\%([<=,.?/*:^%|&]\|+\@<!+\|-\@<!-\|=\@<!>\|\<in\%(stanceof\)\=\)' 6909521313SBram Moolenaarendif 70*e4a3bcf2SBram Moolenaar 71*e4a3bcf2SBram Moolenaarlet g:javascript_opfirst = s:line_pre . g:javascript_opfirst 7209521313SBram Moolenaarlet g:javascript_continuation .= s:line_term 7309521313SBram Moolenaar 74*e4a3bcf2SBram Moolenaarfunction s:OneScope(lnum,text,add) 75*e4a3bcf2SBram Moolenaar return a:text =~# '\%(\<else\|\<do\|=>\)' . s:line_term ? 'no b' : 7609521313SBram Moolenaar \ ((a:add && a:text =~ s:line_pre . '$' && search('\%' . s:PrevCodeLine(a:lnum - 1) . 'l.)' . s:line_term)) || 7709521313SBram Moolenaar \ cursor(a:lnum, match(a:text, ')' . s:line_term)) > -1) && 78*e4a3bcf2SBram Moolenaar \ s:GetPair('(', ')', 'cbW', 100) > 0 && search('\C\l\+\_s*\%#','bW') && 79*e4a3bcf2SBram Moolenaar \ (a:add || ((expand('<cword>') !=# 'while' || !s:GetPair('\C\<do\>', '\C\<while\>','nbW',100)) && 80*e4a3bcf2SBram Moolenaar \ (expand('<cword>') !=# 'each' || search('\C\<for\_s\+\%#','nbW')))) ? expand('<cword>') : '' 81*e4a3bcf2SBram Moolenaarendfunction 82*e4a3bcf2SBram Moolenaar 83*e4a3bcf2SBram Moolenaar" https://github.com/sweet-js/sweet.js/wiki/design#give-lookbehind-to-the-reader 84*e4a3bcf2SBram Moolenaarfunction s:IsBlock() 85*e4a3bcf2SBram Moolenaar return getline(line('.'))[col('.')-1] == '{' && !search( 86*e4a3bcf2SBram Moolenaar \ '\C\%(\<return\s*\|\%([-=~!<*+,.?^%|&\[(]\|=\@<!>\|\*\@<!\/\|\<\%(var\|const\|let\|import\|export\%(\_s\+default\)\=\|yield\|delete\|void\|t\%(ypeof\|hrow\)\|new\|in\%(stanceof\)\=\)\)\_s*\)\%#','bnW') && 87*e4a3bcf2SBram Moolenaar \ (!search(':\_s*\%#','bW') || (!s:GetPair('[({[]','[])}]','bW',200) || s:IsBlock())) 8809521313SBram Moolenaarendfunction 8909521313SBram Moolenaar 9009521313SBram Moolenaar" Auxiliary Functions {{{2 9109521313SBram Moolenaar 9209521313SBram Moolenaar" Find line above 'lnum' that isn't empty, in a comment, or in a string. 9309521313SBram Moolenaarfunction s:PrevCodeLine(lnum) 9409521313SBram Moolenaar let l:lnum = prevnonblank(a:lnum) 95*e4a3bcf2SBram Moolenaar while l:lnum 9609521313SBram Moolenaar if synIDattr(synID(l:lnum,matchend(getline(l:lnum), '^\s*[^''"]'),0),'name') !~? s:syng_strcom 97*e4a3bcf2SBram Moolenaar return l:lnum 9809521313SBram Moolenaar endif 9909521313SBram Moolenaar let l:lnum = prevnonblank(l:lnum - 1) 10009521313SBram Moolenaar endwhile 10109521313SBram Moolenaarendfunction 10209521313SBram Moolenaar 10309521313SBram Moolenaar" Check if line 'lnum' has a balanced amount of parentheses. 10409521313SBram Moolenaarfunction s:Balanced(lnum) 105*e4a3bcf2SBram Moolenaar let [open_0,open_2,open_4] = [0,0,0] 10609521313SBram Moolenaar let l:line = getline(a:lnum) 10709521313SBram Moolenaar let pos = match(l:line, '[][(){}]', 0) 10809521313SBram Moolenaar while pos != -1 10909521313SBram Moolenaar if synIDattr(synID(a:lnum,pos + 1,0),'name') !~? s:syng_strcom 11009521313SBram Moolenaar let idx = stridx('(){}[]', l:line[pos]) 11109521313SBram Moolenaar if idx % 2 == 0 11209521313SBram Moolenaar let open_{idx} = open_{idx} + 1 11309521313SBram Moolenaar else 11409521313SBram Moolenaar let open_{idx - 1} = open_{idx - 1} - 1 11509521313SBram Moolenaar endif 11609521313SBram Moolenaar endif 11709521313SBram Moolenaar let pos = match(l:line, '[][(){}]', pos + 1) 11809521313SBram Moolenaar endwhile 11909521313SBram Moolenaar return (!open_4 + !open_2 + !open_0) - 2 12009521313SBram Moolenaarendfunction 12109521313SBram Moolenaar" }}} 12209521313SBram Moolenaar 12309521313SBram Moolenaarfunction GetJavascriptIndent() 12409521313SBram Moolenaar if !exists('b:js_cache') 12509521313SBram Moolenaar let b:js_cache = [0,0,0] 12609521313SBram Moolenaar endif 12709521313SBram Moolenaar " Get the current line. 12809521313SBram Moolenaar let l:line = getline(v:lnum) 12909521313SBram Moolenaar let syns = synIDattr(synID(v:lnum, 1, 0), 'name') 13009521313SBram Moolenaar 13109521313SBram Moolenaar " start with strings,comments,etc.{{{2 132*e4a3bcf2SBram Moolenaar if (l:line !~ '^[''"`]' && syns =~? '\%(string\|template\)') || 13309521313SBram Moolenaar \ (l:line !~ '^\s*[/*]' && syns =~? s:syng_comment) 13409521313SBram Moolenaar return -1 13509521313SBram Moolenaar endif 13609521313SBram Moolenaar if l:line !~ '^\%(\/\*\|\s*\/\/\)' && syns =~? s:syng_comment 13709521313SBram Moolenaar return cindent(v:lnum) 13809521313SBram Moolenaar endif 13909521313SBram Moolenaar let l:lnum = s:PrevCodeLine(v:lnum - 1) 14009521313SBram Moolenaar if l:lnum == 0 14109521313SBram Moolenaar return 0 14209521313SBram Moolenaar endif 14309521313SBram Moolenaar 14409521313SBram Moolenaar if (l:line =~# s:expr_case) 14509521313SBram Moolenaar let cpo_switch = &cpo 14609521313SBram Moolenaar set cpo+=% 14709521313SBram Moolenaar let ind = cindent(v:lnum) 14809521313SBram Moolenaar let &cpo = cpo_switch 14909521313SBram Moolenaar return ind 15009521313SBram Moolenaar endif 15109521313SBram Moolenaar "}}} 15209521313SBram Moolenaar 15309521313SBram Moolenaar " the containing paren, bracket, curly. Memoize, last lineNr either has the 15409521313SBram Moolenaar " same scope or starts a new one, unless if it closed a scope. 15509521313SBram Moolenaar call cursor(v:lnum,1) 156*e4a3bcf2SBram Moolenaar if b:js_cache[0] >= l:lnum && b:js_cache[0] < v:lnum && b:js_cache[0] && 15709521313SBram Moolenaar \ (b:js_cache[0] > l:lnum || s:Balanced(l:lnum) > 0) 15809521313SBram Moolenaar let num = b:js_cache[1] 15909521313SBram Moolenaar elseif syns != '' && l:line[0] =~ '\s' 16009521313SBram Moolenaar let pattern = syns =~? 'block' ? ['{','}'] : syns =~? 'jsparen' ? ['(',')'] : 16109521313SBram Moolenaar \ syns =~? 'jsbracket'? ['\[','\]'] : ['[({[]','[])}]'] 162*e4a3bcf2SBram Moolenaar let num = s:GetPair(pattern[0],pattern[1],'bW',2000) 16309521313SBram Moolenaar else 164*e4a3bcf2SBram Moolenaar let num = s:GetPair('[({[]','[])}]','bW',2000) 16509521313SBram Moolenaar endif 16609521313SBram Moolenaar let b:js_cache = [v:lnum,num,line('.') == v:lnum ? b:js_cache[2] : col('.')] 16709521313SBram Moolenaar 16809521313SBram Moolenaar if l:line =~ s:line_pre . '[])}]' 16909521313SBram Moolenaar return indent(num) 17009521313SBram Moolenaar endif 17109521313SBram Moolenaar 172*e4a3bcf2SBram Moolenaar call cursor(b:js_cache[1],b:js_cache[2]) 173*e4a3bcf2SBram Moolenaar 174*e4a3bcf2SBram Moolenaar let swcase = getline(l:lnum) =~# s:expr_case 175*e4a3bcf2SBram Moolenaar let pline = swcase ? getline(l:lnum) : substitute(getline(l:lnum), '\%(:\@<!\/\/.*\)$', '','') 176*e4a3bcf2SBram Moolenaar let inb = num == 0 || num < l:lnum && ((l:line !~ s:line_pre . ',' && pline !~ ',' . s:line_term) || s:IsBlock()) 177*e4a3bcf2SBram Moolenaar let switch_offset = num == 0 || s:OneScope(num, strpart(getline(num),0,b:js_cache[2] - 1),1) !=# 'switch' ? 0 : 178*e4a3bcf2SBram Moolenaar \ &cino !~ ':' || !has('float') ? s:sw() : 17909521313SBram Moolenaar \ float2nr(str2float(matchstr(&cino,'.*:\zs[-0-9.]*')) * (&cino =~# '.*:[^,]*s' ? s:sw() : 1)) 18009521313SBram Moolenaar 18109521313SBram Moolenaar " most significant, find the indent amount 182*e4a3bcf2SBram Moolenaar if inb && !swcase && ((l:line =~# g:javascript_opfirst || pline =~# g:javascript_continuation) || 183*e4a3bcf2SBram Moolenaar \ num < l:lnum && s:OneScope(l:lnum,pline,0) =~# '\<\%(for\|each\|if\|let\|no\sb\|w\%(hile\|ith\)\)\>' && 184*e4a3bcf2SBram Moolenaar \ l:line !~ s:line_pre . '{') 18509521313SBram Moolenaar return (num > 0 ? indent(num) : -s:sw()) + (s:sw() * 2) + switch_offset 18609521313SBram Moolenaar elseif num > 0 18709521313SBram Moolenaar return indent(num) + s:sw() + switch_offset 18809521313SBram Moolenaar endif 18909521313SBram Moolenaar 19009521313SBram Moolenaarendfunction 19109521313SBram Moolenaar 19209521313SBram Moolenaar 19309521313SBram Moolenaarlet &cpo = s:cpo_save 19409521313SBram Moolenaarunlet s:cpo_save 195