@@ -16,7 +16,7 @@ setlocal nosmartindent
1616" Now, set up our indentation expression and keys that trigger it.
1717setlocal indentexpr = GetJavascriptIndent ()
1818setlocal formatexpr = Fixedgq (v: lnum ,v: count )
19- setlocal indentkeys = 0 {,0 },0 ),0 ],0 \, :,! ^F,o ,O,e
19+ setlocal indentkeys = 0 {,0 },0 ),0 ],0 \, * <Return> , :,! ^F,o ,O,e
2020setlocal cinoptions += j1,J1
2121
2222" Only define the function once.
@@ -42,23 +42,28 @@ endif
4242" ============
4343
4444let s: line_pre = ' ^\s*\%(\/\*.*\*\/\s*\)*'
45- 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'
45+ 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'
4646let s: expr_case = s: line_pre . ' \%(case\s\+[^\:]*\|default\)\s*:\s*\C'
4747" Regex of syntax group names that are or delimit string or are comments.
48- let s: syng_strcom = ' \%(string\|regex\|comment\|template\)\c'
48+ let s: syng_strcom = ' \%(string\|regex\|special\| comment\|template\)\c'
4949
5050" Regex of syntax group names that are strings.
5151let s: syng_string = ' regex\c'
5252
5353" Regex of syntax group names that are strings or documentation.
54- let s: syng_multiline = ' \%(comment\|doc\)\c'
55-
56- " Regex of syntax group names that are line comment.
57- let s: syng_linecom = ' linecomment\c'
54+ let s: syng_comment = ' \%(comment\|doc\)\c'
5855
5956" Expression used to check whether we should skip a match with searchpair().
6057let s: skip_expr = " synIDattr(synID(line('.'),col('.'),1),'name') =~ '" .s: syng_strcom ." '"
6158
59+ func s: lookForParens (start ,end ,flags,stop )
60+ try
61+ return searchpair (a: start ,' ' ,a: end ,a: flags ,s: skip_expr ,a: stop ,300 )
62+ catch /E118/
63+ return searchpair (a: start ,' ' ,a: end ,a: flags ,0 ,a: stop )
64+ endtry
65+ endfunc
66+
6267let s: line_term = ' \s*\%(\%(\/\/.*\)\=\|\%(\/\*.*\*\/\s*\)*\)$'
6368
6469" Regex that defines continuation lines, not including (, {, or [.
@@ -73,7 +78,7 @@ function s:Onescope(lnum)
7378 let mypos = col (' .' )
7479 call cursor (a: lnum , 1 )
7580 if search (' \<\%(while\|for\|if\)\>\s*(\C' , ' ce' , a: lnum ) > 0 &&
76- \ searchpair (' (' , ' ' , ' )' , ' W' , s: skip_expr , a: lnum ) > 0 &&
81+ \ s: lookForParens (' (' , ' )' , ' W' , a: lnum ) > 0 &&
7782 \ col (' .' ) == strlen (s: RemoveTrailingComments (getline (a: lnum )))
7883 call cursor (a: lnum , mypos)
7984 return 1
@@ -86,7 +91,7 @@ endfunction
8691" Regex that defines blocks.
8792let s: block_regex = ' [{([]' . s: line_term
8893
89- let s: operator_first = s: line_pre . ' \%([. ,:?]\|\([-/+*]\)\%(\1\|\*\|\/\)\@!\|||\|&&\)'
94+ let s: operator_first = s: line_pre . ' \%([,:?]\|\([-/. +*]\)\%(\1\|\*\|\/\)\@!\|||\|&&\)'
9095
9196let s: var_stmt = s: line_pre . ' \%(const\|let\|var\)\s\+\C'
9297
@@ -106,32 +111,23 @@ function s:IsInString(lnum, col)
106111endfunction
107112
108113" Check if the character at lnum:col is inside a multi-line comment.
109- function s: IsInMultilineComment (lnum, col )
110- return ! s: IsLineComment (a: lnum , a: col ) && synIDattr (synID (a: lnum , a: col , 1 ), ' name' ) = ~ s: syng_multiline
111- endfunction
112-
113- " Check if the character at lnum:col is a line comment.
114- function s: IsLineComment (lnum, col )
115- return synIDattr (synID (a: lnum , a: col , 1 ), ' name' ) = ~ s: syng_linecom
114+ function s: IsInComment (lnum, col )
115+ return synIDattr (synID (a: lnum , a: col , 1 ), ' name' ) = ~ s: syng_comment
116116endfunction
117117
118118" Find line above 'lnum' that isn't empty, in a comment, or in a string.
119119function s: PrevNonBlankNonString (lnum)
120- let in_block = 0
121120 let lnum = prevnonblank (a: lnum )
122121 while lnum > 0
123- " Go in and out of blocks comments as necessary.
124- " If the line isn't empty (with opt. comment) or in a string, end search.
125122 let line = getline (lnum)
126- if s: IsInMultilineComment (lnum, matchend (line , ' ^\s*/\*' ) - 1 ) && line !~ s: line_pre . ' $'
127- if in_block
128- let in_block = 0
129- else
130- break
131- endif
132- elseif ! in_block && s: IsInMultilineComment (lnum, match (line , ' \*/\s*$' ) + 1 ) && line !~ s: line_pre . ' $'
133- let in_block = 1
134- elseif ! in_block && line !~ s: line_pre . ' \%(//\).*$' && ! (s: IsInStringOrComment (lnum, 1 ) && s: IsInStringOrComment (lnum, strlen (line )))
123+ let com = match (line , ' \%(\/\*.*\)\@<!\*\/' ) + 1
124+ if s: IsInComment (lnum, com )
125+ call cursor (lnum, com )
126+ let parlnum = search (' \%(\/\/.*\)\@<!\/\*' , ' nbW' )
127+ if parlnum > 0
128+ let lnum = parlnum
129+ end
130+ elseif line !~ ' ^' . s: line_term && ! s: IsInStringOrComment (lnum,1 )
135131 break
136132 endif
137133 let lnum = prevnonblank (lnum - 1 )
@@ -148,21 +144,18 @@ function s:GetMSL(lnum, in_one_line_scope)
148144 " If we have a continuation line, or we're in a string, use line as MSL.
149145 " Otherwise, terminate search as we have found our MSL already.
150146 let line = getline (lnum)
151- let col = match (line , s: continuation_regex ) + 1
152- let coal = match (line , s: comma_last ) + 1
153147 let line2 = getline (msl)
154- let col2 = matchend (line2, ' )' )
155- if ((col > 0 && ! s: IsInStringOrComment (lnum, col ) || coal > 0 && ! s: IsInStringOrComment (lnum,coal)) &&
148+ if ((s: Match (lnum,s: continuation_regex ) || s: Match (lnum, s: comma_last )) &&
156149 \ ! s: Match (lnum, s: expr_case )) || s: IsInString (lnum, strlen (line ))
157150 let msl = lnum
158-
159- " if there are more closing brackets, continue from the line which has the matching opening bracket
160- elseif col2 > 0 && ! s: IsInStringOrComment (msl, col2) && s: LineHasOpeningBrackets (msl)[ 0 ] == ' 2 ' && ! a: in_one_line_scope
161- call cursor (msl, 1 )
162- if searchpair ( ' ( ' , ' ' , ' ) ' , ' bW ' , s: skip_expr ) > 0
163- let lnum = line ( ' . ' )
164- let msl = lnum
165- endif
151+ if s: Match (lnum, s: line_pre . ' []})] ' ) && ! a: in_one_line_scope
152+ call cursor (lnum, 1 )
153+ let parlnum = s: lookForParens ( ' (\|{\|\[ ' , ' )\|}\|\] ' , ' nbW ' , 0 )
154+ if parlnum > 0
155+ let lnum = parlnum
156+ continue
157+ end
158+ end
166159
167160 else
168161
@@ -191,18 +184,22 @@ endfunction
191184" Find if the string is inside var statement (but not the first string)
192185function s: InMultiVarStatement (lnum, cont, prev )
193186 let lnum = s: PrevNonBlankNonString (a: lnum - 1 )
187+ let cont = a: cont
188+ let prev = a: prev
194189
195190 " let type = synIDattr(synID(lnum, indent(lnum) + 1, 0), 'name')
196191
197192 " loop through previous expressions to find a var statement
198- while lnum > 0 && (s: Match (lnum, s: comma_last ) || (a: cont && getline (lnum) = ~ s: line_pre . ' } ' ) ||
199- \ s: Match (lnum,s: continuation_regex )) || (a: prev && (s: Match (a: prev , s: comma_last ) ||
200- \ s: Match (a: prev ,s: continuation_regex )))
193+ while lnum > 0 && (s: Match (lnum, s: comma_last ) || (cont && getline (lnum) = ~ s: line_pre . ' []})] ' ) ||
194+ \ s: Match (lnum,s: continuation_regex )) || (prev && (s: Match (prev , s: comma_last ) ||
195+ \ s: Match (prev ,s: continuation_regex )))
201196 " if the line is a js keyword
202- if a: cont
197+ if cont
198+ let cont = 0
203199 call cursor (lnum,1 )
204- if searchpair (' {' , ' ' , ' }' , ' bW' , s: skip_expr ) > 0
205- let lnum = line (' .' )
200+ let parlnum = s: lookForParens (' (\|{\|\[' , ' )\|}\|\]' , ' nbW' , 0 )
201+ if parlnum > 0
202+ let lnum = parlnum
206203 end
207204 end
208205 if s: Match (lnum, s: js_keywords )
@@ -219,33 +216,13 @@ function s:InMultiVarStatement(lnum, cont, prev)
219216 end
220217 endif
221218 let lnum = s: PrevNonBlankNonString (lnum - 1 )
219+ let prev = prev && lnum > 0 ? prev : 0
222220 endwhile
223221
224222 " beginning of program, not a var
225223 return 0
226224endfunction
227225
228- " Find line above with beginning of the var statement or returns 0 if it's not"{{{2
229- " this statement
230- " function s:GetVarIndent(lnum)
231- " let lvar = s:InMultiVarStatement(a:lnum, 0,0)
232- " let prev_lnum = s:PrevNonBlankNonString(a:lnum - 1)
233-
234- " if lvar
235- " let line = s:RemoveTrailingComments(getline(prev_lnum))
236-
237- " " if the previous line doesn't end in a comma, return to regular indent
238- " if (line !~ s:comma_last)
239- " return indent(prev_lnum) - s:sw()
240- " else
241- " return indent(lvar) + s:sw()
242- " endif
243- " endif
244-
245- " return -1
246- " endfunction"}}}
247-
248-
249226" Check if line 'lnum' has more opening brackets than closing ones.
250227function s: LineHasOpeningBrackets (lnum)
251228 let open_0 = 0
@@ -345,20 +322,22 @@ function GetJavascriptIndent()
345322 let line = getline (v: lnum )
346323 " previous nonblank line number
347324 let prevline = prevnonblank (v: lnum - 1 )
348-
325+ " previous line of code
326+ let lnum = s: PrevNonBlankNonString (v: lnum - 1 )
327+
349328 " to not change multiline string values
350- if synIDattr (synID (v: lnum , 1 , 1 ), ' name' ) = ~? ' string\|template' && line !~ s: line_pre . ' [ '' "`] '
351- return indent ( v: lnum )
329+ if line !~ ' ^[ '' "`] ' && synIDattr (synID (v: lnum , 1 , 1 ), ' name' ) = ~? ' string\|template'
330+ return -1
352331 endif
353332
354333 " If we are in a multi-line comment, cindent does the right thing.
355- if s: IsInMultilineComment (v: lnum , 1 ) && ! s: IsLineComment (v: lnum , 1 ) &&
356- \ s: IsInMultilineComment (v: lnum , match (line , ' \s*$' )) && line !~ ' ^\/\*'
334+ if line !~ ' ^\%(\/\*\|\s*\/\/\)' && s: IsInComment (v: lnum , 1 )
357335 return cindent (v: lnum )
358336 endif
359337
360338 " single opening bracket will assume you want a c style of indenting
361- if s: Match (v: lnum , s: line_pre . ' {' . s: line_term )
339+ if s: Match (v: lnum , s: line_pre . ' {' . s: line_term ) && ! s: Match (lnum,s: block_regex ) &&
340+ \ ! s: Match (lnum,s: comma_last )
362341 return cindent (v: lnum )
363342 endif
364343
@@ -373,47 +352,48 @@ function GetJavascriptIndent()
373352 let col = matchend (line , s: line_pre . ' []})]' )
374353 if col > 0 && ! s: IsInStringOrComment (v: lnum , col )
375354 call cursor (v: lnum , col )
376-
377-
378- let bs = strpart (' (){}[]' , stridx (' )}]' , line [col - 1 ]) * 2 , 2 )
379- if searchpair (escape (bs [0 ], ' \[' ), ' ' , bs [1 ], ' bW' , s: skip_expr ) > 0
380- let ind = s: InMultiVarStatement (line (' .' ), 0 , 0 ) ? indent (line (' .' )) : indent (s: GetMSL (line (' .' ), 0 ))
355+ let parlnum = s: lookForParens (' (\|{\|\[' , ' )\|}\|\]' , ' nbW' , 0 )
356+ if parlnum > 0
357+ let ind = s: InMultiVarStatement (parlnum, 0 , 0 ) ? indent (parlnum) : indent (s: GetMSL (parlnum, 0 ))
381358 endif
382359 return ind
383360 endif
384361
362+
385363 " If line starts with an operator...
386364 if (line = ~ s: operator_first )
387- if (s: Match (prevline , s: operator_first ))
365+ if (s: Match (lnum , s: operator_first ))
388366 " and so does previous line, don't indent
389- return indent (prevline )
367+ return indent (lnum )
390368 end
391- let counts = s: LineHasOpeningBrackets (prevline )
392- if counts[ 0 ] == ' 2 ' || counts[ 1 ] == ' 2 ' || counts[ 2 ] == ' 2'
393- call cursor (prevline , 1 )
369+ let counts = s: LineHasOpeningBrackets (lnum )
370+ if counts = ~ ' 2'
371+ call cursor (lnum , 1 )
394372 " Search for the opening tag
395- let bs = strpart (' (){}[]' , stridx (' )}]' , line [col - 1 ]) * 2 , 2 )
396- if searchpair (escape (bs [0 ], ' \[' ), ' ' , bs [1 ], ' bW' , s: skip_expr ) > 0 && s: Match (line (' .' ), s: operator_first )
397- return indent (line (' .' ))
373+ let parlnum = s: lookForParens (' (\|{\|\[' , ' )\|}\|\]' , ' nbW' , 0 )
374+ if parlnum > 0
375+ return ! s: Match (parlnum, s: operator_first ) &&
376+ \ synIDattr (synID (v: lnum , 1 , 1 ), ' name' ) !~? ' jsbracket\|jsparen\|jsobject' ?
377+ \ indent (lnum) + s: sw () : indent (parlnum)
398378 end
399- elseif counts[ 0 ] != ' 1 ' && counts[ 1 ] != ' 1 ' && counts[ 2 ] != ' 1 '
400- " otherwise, indent 1 level
401- return indent (prevline ) + s: sw ()
379+ elseif synIDattr ( synID ( v: lnum , 1 , 1 ), ' name ' ) !~? ' jsbracket\|jsparen\|jsobject '
380+ " otherwise, if not in an key/val;array item;param, indent 1 level
381+ return indent (lnum ) + s: sw ()
402382 end
403383
404384 " If previous line starts with an operator...
405- elseif (s: Match (prevline , s: operator_first ) && ! s: Match (prevline ,s: continuation_regex ))|| getline (prevline ) = ~ ' );\=' . s: line_term
406- let counts = s: LineHasOpeningBrackets (prevline )
407- if counts[0 ] == ' 2' && ! s: Match (prevline , s: operator_first )
408- call cursor (prevline , 1 )
385+ elseif (s: Match (lnum , s: operator_first ) && ! s: Match (lnum ,s: continuation_regex ))|| getline (lnum ) = ~ ' );\=' . s: line_term
386+ let counts = s: LineHasOpeningBrackets (lnum )
387+ if counts[0 ] == ' 2' && ! s: Match (lnum , s: operator_first )
388+ call cursor (lnum , 1 )
409389 " Search for the opening tag
410- let mnum = searchpair (' (' , ' ' , ' )' , ' bW ' , s: skip_expr )
390+ let mnum = s: lookForParens (' (' , ' )' , ' nbW ' , 0 )
411391 if mnum > 0 && s: Match (mnum, s: operator_first )
412392 return indent (mnum) - s: sw ()
413393 end
414- elseif s: Match (prevline , s: operator_first )
415- if counts[ 0 ] != ' 1 ' && counts[ 1 ] != ' 1 ' && counts[ 2 ] != ' 1'
416- return indent (prevline ) - s: sw ()
394+ elseif s: Match (lnum , s: operator_first )
395+ if counts !~ ' 1'
396+ return indent (lnum ) - s: sw ()
417397 end
418398 end
419399 end
@@ -424,12 +404,12 @@ function GetJavascriptIndent()
424404 " If the line is empty and the previous nonblank line was a multi-line
425405 " comment, use that comment's indent. Deduct one char to account for the
426406 " space in ' */'.
427- if line = ~ ' ^\s*$' && s: IsInMultilineComment (prevline, 1 )
407+ if line = ~ ' ^\s*$' && getline (prevline) !~ ' \%(\%(^\s*\/\/\|\/\*\).*\)\@<!\*\/' &&
408+ \ s: IsInComment (prevline, 1 )
428409 return indent (prevline) - 1
429410 endif
430411
431412 " Find a non-blank, non-multi-line string line above the current line.
432- let lnum = s: PrevNonBlankNonString (v: lnum - 1 )
433413
434414 " If the line is empty and inside a string, use the previous line.
435415 if line = ~ ' ^\s*$' && lnum != prevline
@@ -454,25 +434,14 @@ function GetJavascriptIndent()
454434 " add indent depending on the bracket type.
455435 if s: Match (lnum, ' [[({})\]]' )
456436 let counts = s: LineHasOpeningBrackets (lnum)
457- if counts[0 ] == ' 2'
458- call cursor (lnum, 1 )
459- " Search for the opening tag
460- if searchpair (' (' , ' ' , ' )' , ' bW' , s: skip_expr ) > 0
461- return indent (s: GetMSL (line (' .' ), 0 ))
462- end
463- elseif counts[1 ] == ' 2' && ! s: Match (lnum, s: line_pre . ' }' )
464- call cursor (lnum, 1 )
465- " Search for the opening tag
466- if searchpair (' {' , ' ' , ' }' , ' bW' , s: skip_expr ) > 0
467- return indent (s: GetMSL (line (' .' ), 0 ))
468- end
469- elseif counts[2 ] == ' 2' && ! s: Match (lnum, s: line_pre . ' ]' )
437+ if counts = ~ ' 2'
470438 call cursor (lnum, 1 )
471439 " Search for the opening tag
472- if searchpair (' \[' , ' ' , ' \]' , ' bW' , s: skip_expr ) > 0
473- return indent (s: GetMSL (line (' .' ), 0 ))
440+ let parlnum = s: lookForParens (' (\|{\|\[' , ' )\|}\|\]' , ' nbW' , 0 )
441+ if parlnum > 0 && ! s: InMultiVarStatement (parlnum,0 ,0 )
442+ return indent (s: GetMSL (parlnum, 0 ))
474443 end
475- elseif counts[ 1 ] == ' 1 ' || counts[ 2 ] == ' 1 ' || counts[ 0 ] == ' 1' || s: Onescope (lnum)
444+ elseif counts = ~ ' 1' || s: Onescope (lnum)
476445 return ind + s: sw ()
477446 else
478447 call cursor (v: lnum , vcol)
@@ -525,7 +494,7 @@ function! Fixedgq(lnum, count)
525494 endif
526495
527496 " This gq is only meant to do code with strings, not comments
528- if s: IsLineComment ( a: lnum , l: first_char ) || s: IsInMultilineComment (a: lnum , l: first_char )
497+ if s: IsInComment (a: lnum , l: first_char )
529498 return 1
530499 endif
531500
0 commit comments