diff options
Diffstat (limited to 'indent')
-rw-r--r-- | indent/javascript.vim | 241 |
1 files changed, 113 insertions, 128 deletions
diff --git a/indent/javascript.vim b/indent/javascript.vim index 68e21ed2..24f394c1 100644 --- a/indent/javascript.vim +++ b/indent/javascript.vim @@ -18,7 +18,7 @@ setlocal nosmartindent " Now, set up our indentation expression and keys that trigger it. setlocal indentexpr=GetJavascriptIndent() setlocal formatexpr=Fixedgq(v:lnum,v:count) -setlocal indentkeys=0{,0},0),0],0\,:,!^F,o,O,e +setlocal indentkeys=0{,0},0),0],0\,*<Return>,:,!^F,o,O,e setlocal cinoptions+=j1,J1 " Only define the function once. @@ -44,23 +44,28 @@ endif " ============ let s:line_pre = '^\s*\%(\/\*.*\*\/\s*\)*' -let s:js_keywords = s:line_pre . '\%(break\|catch\|const\|continue\|debugger\|delete\|do\|else\|finally\|for\|function\|if\|in\|instanceof\|let\|new\|return\|switch\|this\|throw\|try\|typeof\|var\|void\|while\|with\)\>\C' +let s:js_keywords = s:line_pre . '\%(break\|import\|export\|catch\|const\|continue\|debugger\|delete\|do\|else\|finally\|for\|function\|if\|in\|instanceof\|let\|new\|return\|switch\|this\|throw\|try\|typeof\|var\|void\|while\|with\)\>\C' let s:expr_case = s:line_pre . '\%(case\s\+[^\:]*\|default\)\s*:\s*\C' " Regex of syntax group names that are or delimit string or are comments. -let s:syng_strcom = '\%(string\|regex\|comment\|template\)\c' +let s:syng_strcom = '\%(string\|regex\|special\|doc\|comment\|template\)\c' " Regex of syntax group names that are strings. let s:syng_string = 'regex\c' " Regex of syntax group names that are strings or documentation. -let s:syng_multiline = '\%(comment\|doc\)\c' - -" Regex of syntax group names that are line comment. -let s:syng_linecom = 'linecomment\c' +let s:syng_comment = '\%(comment\|doc\)\c' " Expression used to check whether we should skip a match with searchpair(). let s:skip_expr = "synIDattr(synID(line('.'),col('.'),1),'name') =~ '".s:syng_strcom."'" +func s:lookForParens(start,end,flags,stop) + try + return searchpair(a:start,'',a:end,a:flags,s:skip_expr,a:stop,300) + catch /E118/ + return searchpair(a:start,'',a:end,a:flags,0,a:stop) + endtry +endfunc + let s:line_term = '\s*\%(\%(\/\/.*\)\=\|\%(\/\*.*\*\/\s*\)*\)$' " Regex that defines continuation lines, not including (, {, or [. @@ -75,7 +80,7 @@ function s:Onescope(lnum) let mypos = col('.') call cursor(a:lnum, 1) if search('\<\%(while\|for\|if\)\>\s*(\C', 'ce', a:lnum) > 0 && - \ searchpair('(', '', ')', 'W', s:skip_expr, a:lnum) > 0 && + \ s:lookForParens('(', ')', 'W', a:lnum) > 0 && \ col('.') == strlen(s:RemoveTrailingComments(getline(a:lnum))) call cursor(a:lnum, mypos) return 1 @@ -88,7 +93,7 @@ endfunction " Regex that defines blocks. let s:block_regex = '[{([]' . s:line_term -let s:operator_first = s:line_pre . '\%([.,:?]\|\([-/+*]\)\%(\1\|\*\|\/\)\@!\|||\|&&\)' +let s:operator_first = s:line_pre . '\%([,:?]\|\([-/.+*]\)\%(\1\|\*\|\/\)\@!\|||\|&&\)' let s:var_stmt = s:line_pre . '\%(const\|let\|var\)\s\+\C' @@ -108,32 +113,23 @@ function s:IsInString(lnum, col) endfunction " Check if the character at lnum:col is inside a multi-line comment. -function s:IsInMultilineComment(lnum, col) - return !s:IsLineComment(a:lnum, a:col) && synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_multiline -endfunction - -" Check if the character at lnum:col is a line comment. -function s:IsLineComment(lnum, col) - return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_linecom +function s:IsInComment(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_comment endfunction " Find line above 'lnum' that isn't empty, in a comment, or in a string. function s:PrevNonBlankNonString(lnum) - let in_block = 0 let lnum = prevnonblank(a:lnum) while lnum > 0 - " Go in and out of blocks comments as necessary. - " If the line isn't empty (with opt. comment) or in a string, end search. let line = getline(lnum) - if s:IsInMultilineComment(lnum, matchend(line, '^\s*/\*') - 1) && line !~ s:line_pre . '$' - if in_block - let in_block = 0 - else - break - endif - elseif !in_block && s:IsInMultilineComment(lnum, match(line, '\*/\s*$') + 1) && line !~ s:line_pre . '$' - let in_block = 1 - elseif !in_block && line !~ s:line_pre . '\%(//\).*$' && !(s:IsInStringOrComment(lnum, 1) && s:IsInStringOrComment(lnum, strlen(line))) + let com = match(line, '\%(\/\*.*\)\@<!\*\/') + 1 + if s:IsInComment(lnum, com) + call cursor(lnum, com) + let parlnum = search('\%(\/\/.*\)\@<!\/\*', 'nbW') + if parlnum > 0 + let lnum = parlnum + end + elseif line !~ '^' . s:line_term && !s:IsInStringOrComment(lnum,1) break endif let lnum = prevnonblank(lnum - 1) @@ -150,21 +146,18 @@ function s:GetMSL(lnum, in_one_line_scope) " If we have a continuation line, or we're in a string, use line as MSL. " Otherwise, terminate search as we have found our MSL already. let line = getline(lnum) - let col = match(line, s:continuation_regex) + 1 - let coal = match(line, s:comma_last) + 1 let line2 = getline(msl) - let col2 = matchend(line2, ')') - if ((col > 0 && !s:IsInStringOrComment(lnum, col) || coal > 0 && !s:IsInStringOrComment(lnum,coal)) && + if ((s:Match(lnum,s:continuation_regex) || s:Match(lnum, s:comma_last)) && \ !s:Match(lnum, s:expr_case)) || s:IsInString(lnum, strlen(line)) let msl = lnum - - " if there are more closing brackets, continue from the line which has the matching opening bracket - elseif col2 > 0 && !s:IsInStringOrComment(msl, col2) && s:LineHasOpeningBrackets(msl)[0] == '2' && !a:in_one_line_scope - call cursor(msl, 1) - if searchpair('(', '', ')', 'bW', s:skip_expr) > 0 - let lnum = line('.') - let msl = lnum - endif + if s:Match(lnum, s:line_pre . '[]})]') && !a:in_one_line_scope + call cursor(lnum,1) + let parlnum = s:lookForParens('(\|{\|\[', ')\|}\|\]', 'nbW', 0) + if parlnum > 0 + let lnum = parlnum + continue + end + end else @@ -193,18 +186,22 @@ endfunction " Find if the string is inside var statement (but not the first string) function s:InMultiVarStatement(lnum, cont, prev) let lnum = s:PrevNonBlankNonString(a:lnum - 1) + let cont = a:cont + let prev = a:prev " let type = synIDattr(synID(lnum, indent(lnum) + 1, 0), 'name') " loop through previous expressions to find a var statement - while lnum > 0 && (s:Match(lnum, s:comma_last) ||(a:cont && getline(lnum) =~ s:line_pre . '}') || - \ s:Match(lnum,s:continuation_regex)) || (a:prev && (s:Match(a:prev, s:comma_last) || - \ s:Match(a:prev,s:continuation_regex))) + while lnum > 0 && (s:Match(lnum, s:comma_last) ||(cont && getline(lnum) =~ s:line_pre . '[]})]') || + \ s:Match(lnum,s:continuation_regex)) || (prev && (s:Match(prev, s:comma_last) || + \ s:Match(prev,s:continuation_regex))) " if the line is a js keyword - if a:cont + if cont + let cont = 0 call cursor(lnum,1) - if searchpair('{', '', '}', 'bW', s:skip_expr) > 0 - let lnum = line('.') + let parlnum = s:lookForParens('(\|{\|\[', ')\|}\|\]', 'nbW', 0) + if parlnum > 0 + let lnum = parlnum end end if s:Match(lnum, s:js_keywords) @@ -221,33 +218,13 @@ function s:InMultiVarStatement(lnum, cont, prev) end endif let lnum = s:PrevNonBlankNonString(lnum - 1) + let prev = prev && lnum > 0 ? prev : 0 endwhile " beginning of program, not a var return 0 endfunction -" Find line above with beginning of the var statement or returns 0 if it's not"{{{2 -" this statement -" function s:GetVarIndent(lnum) -" let lvar = s:InMultiVarStatement(a:lnum, 0,0) -" let prev_lnum = s:PrevNonBlankNonString(a:lnum - 1) - -" if lvar -" let line = s:RemoveTrailingComments(getline(prev_lnum)) - -" " if the previous line doesn't end in a comma, return to regular indent -" if (line !~ s:comma_last) -" return indent(prev_lnum) - s:sw() -" else -" return indent(lvar) + s:sw() -" endif -" endif - -" return -1 -" endfunction"}}} - - " Check if line 'lnum' has more opening brackets than closing ones. function s:LineHasOpeningBrackets(lnum) let open_0 = 0 @@ -347,20 +324,22 @@ function GetJavascriptIndent() let line = getline(v:lnum) " previous nonblank line number let prevline = prevnonblank(v:lnum - 1) - + " previous line of code + let lnum = s:PrevNonBlankNonString(v:lnum - 1) + " to not change multiline string values - if synIDattr(synID(v:lnum, 1, 1), 'name') =~? 'string\|template' && line !~ s:line_pre . '[''"`]' - return indent(v:lnum) + if line !~ '^[''"`]' && synIDattr(synID(v:lnum, 1, 1), 'name') =~? 'string\|template' + return -1 endif " If we are in a multi-line comment, cindent does the right thing. - if s:IsInMultilineComment(v:lnum, 1) && !s:IsLineComment(v:lnum, 1) && - \ s:IsInMultilineComment(v:lnum, match(line, '\s*$')) && line !~ '^\/\*' + if line !~ '^\%(\/\*\|\s*\/\/\)' && s:IsInComment(v:lnum, 1) return cindent(v:lnum) endif " single opening bracket will assume you want a c style of indenting - if s:Match(v:lnum, s:line_pre . '{' . s:line_term) + if s:Match(v:lnum, s:line_pre . '{' . s:line_term) && !s:Match(lnum,s:block_regex) && + \ !s:Match(lnum,s:comma_last) return cindent(v:lnum) endif @@ -370,52 +349,60 @@ function GetJavascriptIndent() endif " If we got a closing bracket on an empty line, find its match and indent - " according to it. For parentheses we indent to its column - 1, for the - " others we indent to the containing line's MSL's level. Return -1 if fail. - let col = matchend(line, s:line_pre . '[]})]') - if col > 0 && !s:IsInStringOrComment(v:lnum, col) - call cursor(v:lnum, col) - - - let bs = strpart('(){}[]', stridx(')}]', line[col - 1]) * 2, 2) - if searchpair(escape(bs[0], '\['), '', bs[1], 'bW', s:skip_expr) > 0 - let ind = s:InMultiVarStatement(line('.'), 0, 0) ? indent(line('.')) : indent(s:GetMSL(line('.'), 0)) - endif + " according to it. + let col = s:Match(v:lnum, s:line_pre . '[]})]') + if col > 0 + let parlnum = v:lnum + while col + call cursor(parlnum, 1) + let parlnum = s:lookForParens('(\|{\|\[', ')\|}\|\]', 'nbW', 0) + let col = s:Match(parlnum, s:line_pre . '[]})]') + if col + continue + end + if parlnum > 0 + let ind = s:InMultiVarStatement(parlnum, 0, 0)|| s:LineHasOpeningBrackets(parlnum) !~ '2' + \ ? indent(parlnum) : indent(s:GetMSL(parlnum, 0)) + endif + endwhile return ind endif + " If line starts with an operator... if (line =~ s:operator_first) - if (s:Match(prevline, s:operator_first)) + if (s:Match(lnum, s:operator_first) || s:Match(lnum, s:line_pre . '[])}]')) " and so does previous line, don't indent - return indent(prevline) + return indent(lnum) end - let counts = s:LineHasOpeningBrackets(prevline) - if counts[0] == '2' || counts[1] == '2' || counts[2] == '2' - call cursor(prevline, 1) + let counts = s:LineHasOpeningBrackets(lnum) + if counts =~ '2' + call cursor(lnum, 1) " Search for the opening tag - let bs = strpart('(){}[]', stridx(')}]', line[col - 1]) * 2, 2) - if searchpair(escape(bs[0], '\['), '', bs[1], 'bW', s:skip_expr) > 0 && s:Match(line('.'), s:operator_first) - return indent(line('.')) + let parlnum = s:lookForParens('(\|{\|\[', ')\|}\|\]', 'nbW', 0) + if parlnum > 0 + return !s:Match(parlnum, s:operator_first) && + \ synIDattr(synID(v:lnum, 1, 1), 'name') !~? 'jsbracket\|jsparen\|jsobject' ? + \ indent(lnum) + s:sw() : indent(parlnum) end - elseif counts[0] != '1' && counts[1] != '1' && counts[2] != '1' - " otherwise, indent 1 level - return indent(prevline) + s:sw() + elseif synIDattr(synID(v:lnum, 1, 1), 'name') !~? 'jsbracket\|jsparen\|jsobject' + " otherwise, if not in an key/val;array item;param, indent 1 level + return indent(lnum) + s:sw() end " If previous line starts with an operator... - elseif (s:Match(prevline, s:operator_first) && !s:Match(prevline,s:continuation_regex))||getline(prevline) =~ ');\=' . s:line_term - let counts = s:LineHasOpeningBrackets(prevline) - if counts[0] == '2' && !s:Match(prevline, s:operator_first) - call cursor(prevline, 1) + elseif (s:Match(lnum, s:operator_first) && !s:Match(lnum,s:continuation_regex))||getline(lnum) =~ ');\=' . s:line_term + let counts = s:LineHasOpeningBrackets(lnum) + if counts[0] == '2' && !s:Match(lnum, s:operator_first) + call cursor(lnum, 1) " Search for the opening tag - let mnum = searchpair('(', '', ')', 'bW', s:skip_expr) + let mnum = s:lookForParens('(', ')', 'nbW', 0) if mnum > 0 && s:Match(mnum, s:operator_first) return indent(mnum) - s:sw() end - elseif s:Match(prevline, s:operator_first) - if counts[0] != '1' && counts[1] != '1' && counts[2] != '1' - return indent(prevline) - s:sw() + elseif s:Match(lnum, s:operator_first) + if counts !~ '1' + return indent(lnum) - s:sw() end end end @@ -426,12 +413,12 @@ function GetJavascriptIndent() " If the line is empty and the previous nonblank line was a multi-line " comment, use that comment's indent. Deduct one char to account for the " space in ' */'. - if line =~ '^\s*$' && s:IsInMultilineComment(prevline, 1) + if line =~ '^\s*$' && getline(prevline) !~ '\%(\%(^\s*\/\/\|\/\*\).*\)\@<!\*\/' && + \ s:IsInComment(prevline, 1) return indent(prevline) - 1 endif " Find a non-blank, non-multi-line string line above the current line. - let lnum = s:PrevNonBlankNonString(v:lnum - 1) " If the line is empty and inside a string, use the previous line. if line =~ '^\s*$' && lnum != prevline @@ -443,10 +430,20 @@ function GetJavascriptIndent() return 0 endif +" foo('foo', +" bar('bar', function() { +" hi(); +" }) +" ); +" function (a, b, c, d, +" e, f, g) { +" console.log('inner'); +" } " If the previous line ended with a block opening, add a level of indent. if s:Match(lnum, s:block_regex) - return s:InMultiVarStatement(lnum, 0, 0) ? indent(lnum) + s:sw() : indent(s:GetMSL(lnum, 0)) + s:sw() + return s:InMultiVarStatement(lnum, 0, 0) || s:LineHasOpeningBrackets(lnum) !~ '2' ? + \ indent(lnum) + s:sw() : indent(s:GetMSL(lnum, 0)) + s:sw() endif " Set up variables for current line. @@ -454,30 +451,18 @@ function GetJavascriptIndent() let ind = indent(lnum) " If the previous line contained an opening bracket, and we are still in it, " add indent depending on the bracket type. - if s:Match(lnum, '[[({})\]]') + if s:Match(lnum, '\%([[({]\)\|\%([^\t \])}][})\]]\)') let counts = s:LineHasOpeningBrackets(lnum) - if counts[0] == '2' - call cursor(lnum, 1) - " Search for the opening tag - if searchpair('(', '', ')', 'bW', s:skip_expr) > 0 - return indent(s:GetMSL(line('.'), 0)) - end - elseif counts[1] == '2' && !s:Match(lnum, s:line_pre . '}') - call cursor(lnum, 1) - " Search for the opening tag - if searchpair('{', '', '}', 'bW', s:skip_expr) > 0 - return indent(s:GetMSL(line('.'), 0)) + if counts =~ '2' + call cursor(lnum,matchend(s:RemoveTrailingComments(line), '.\+\zs[])}]')) + while s:lookForParens('(\|{\|\[', ')\|}\|\]', 'bW', 0) == lnum + call cursor(lnum, matchend(s:RemoveTrailingComments(strpart(line,0,col('.'))), '.*\zs[])}]')) + endwhile + if line('.') < lnum && !s:InMultiVarStatement(line('.'),0,0) + return indent(s:GetMSL(line('.'), 0)) end - elseif counts[2] == '2' && !s:Match(lnum, s:line_pre . ']') - call cursor(lnum, 1) - " Search for the opening tag - if searchpair('\[', '', '\]', 'bW', s:skip_expr) > 0 - return indent(s:GetMSL(line('.'), 0)) - end - elseif counts[1] == '1' || counts[2] == '1' || counts[0] == '1' || s:Onescope(lnum) + elseif counts =~ '1' || s:Onescope(lnum) return ind + s:sw() - else - call cursor(v:lnum, vcol) end end @@ -527,7 +512,7 @@ function! Fixedgq(lnum, count) endif " This gq is only meant to do code with strings, not comments - if s:IsLineComment(a:lnum, l:first_char) || s:IsInMultilineComment(a:lnum, l:first_char) + if s:IsInComment(a:lnum, l:first_char) return 1 endif |