4343let s: js_keywords = ' ^\s*\(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\)'
4444let s: expr_case = ' ^\s*\(case\s\+[^\:]*\|default\)\s*:\s*'
4545" Regex of syntax group names that are or delimit string or are comments.
46- let s: syng_strcom = ' string\|regex\|comment\c'
46+ let s: syng_strcom = ' \%(\%(template\)\@<!string\|regex\|comment\)\c'
47+
48+ " Regex of syntax group names that are or delimit template strings
49+ let s: syng_template = ' template\c'
4750
4851" Regex of syntax group names that are strings.
4952let s: syng_string = ' regex\c'
@@ -60,25 +63,24 @@ let s:skip_expr = "synIDattr(synID(line('.'),col('.'),1),'name') =~ '".s:syng_st
6063let s: line_term = ' \s*\%(\%(\/\/\).*\)\=$'
6164
6265" Regex that defines continuation lines, not including (, {, or [.
63- let s: continuation_regex = ' \%([\\*+ /.:]\|\ %(<%\)\@<![=-] \|\W[|&?]\|||\|&&\|[^=]=[^=].*,\)' . s: line_term
66+ let s: continuation_regex = ' \%([\\*/.:]\|+\@<!+\|-\@<!-\|\ %(<%\)\@<!= \|\W[|&?]\|||\|&&\|[^=]=[^=> ].*,\)' . s: line_term
6467
6568" Regex that defines continuation lines.
6669" TODO: this needs to deal with if ...: and so on
67- let s: msl_regex = s: continuation_regex .' |' .s: expr_case
70+ let s: msl_regex = s: continuation_regex .' \ |' .s: expr_case
6871
69- let s: one_line_scope_regex = ' \%(\<else\>\|\<\%(if\|for\|while\)\>\s*(.*) \)' . s: line_term
72+ let s: one_line_scope_regex = ' \%(\%(\ <else\>\|\<\%(if\|for\|while\)\>\s*(\%([^()]*\|[^()]*(\%([^()]*\|[^()]*(\%([^()]*\|[^()]*([^()]*)[^()]*\))[^()]*\))[^()]*\))\)\|=> \)' . s: line_term
7073
7174" Regex that defines blocks.
72- let s: block_regex = ' \%([{[]\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s: line_term
75+ let s: block_regex = ' \%([{([]\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s: line_term
76+
77+ let s: operator_first = ' ^\s*\%([*/.:?]\|\([-+]\)\1\@!\|||\|&&\)'
7378
74- let s: var_stmt = ' ^\s*(const\|let\|var)'
79+ let s: var_stmt = ' ^\s*\% (const\|let\|var\ )'
7580
7681let s: comma_first = ' ^\s*,'
7782let s: comma_last = ' ,\s*$'
7883
79- let s: ternary = ' ^\s\+[?|:]'
80- let s: ternary_q = ' ^\s\+?'
81-
8284let s: case_indent = s: sw ()
8385let s: case_indent_after = s: sw ()
8486let s: m = matchlist (&cinoptions , ' :\(.\)' )
@@ -102,6 +104,11 @@ function s:IsInString(lnum, col)
102104 return synIDattr (synID (a: lnum , a: col , 1 ), ' name' ) = ~ s: syng_string
103105endfunction
104106
107+ " Check if the character at lnum:col is inside a template string.
108+ function s: IsInTempl (lnum, col )
109+ return synIDattr (synID (a: lnum , a: col , 1 ), ' name' ) = ~ s: syng_template
110+ endfunction
111+
105112" Check if the character at lnum:col is inside a multi-line comment.
106113function s: IsInMultilineComment (lnum, col )
107114 return ! s: IsLineComment (a: lnum , a: col ) && synIDattr (synID (a: lnum , a: col , 1 ), ' name' ) = ~ s: syng_multiline
@@ -120,13 +127,13 @@ function s:PrevNonBlankNonString(lnum)
120127 " Go in and out of blocks comments as necessary.
121128 " If the line isn't empty (with opt. comment) or in a string, end search.
122129 let line = getline (lnum)
123- if line = ~ ' /\*'
130+ if s: IsInMultilineComment (lnum, matchend ( line , ' /\*' ) - 1 )
124131 if in_block
125132 let in_block = 0
126133 else
127134 break
128135 endif
129- elseif ! in_block && line = ~ ' \*/'
136+ elseif ! in_block && s: IsInMultilineComment (lnum, matchend ( line , ' \*/' ) - 1 )
130137 let in_block = 1
131138 elseif ! in_block && line !~ ' ^\s*\%(//\).*$' && ! (s: IsInStringOrComment (lnum, 1 ) && s: IsInStringOrComment (lnum, strlen (line )))
132139 break
@@ -146,9 +153,21 @@ function s:GetMSL(lnum, in_one_line_scope)
146153 " Otherwise, terminate search as we have found our MSL already.
147154 let line = getline (lnum)
148155 let col = match (line , s: msl_regex ) + 1
156+ let line2 = getline (msl)
157+ let col2 = matchend (line2, ' )' )
149158 if (col > 0 && ! s: IsInStringOrComment (lnum, col )) || s: IsInString (lnum, strlen (line ))
150159 let msl = lnum
160+
161+ " if there are more closing brackets, continue from the line which has the matching opening bracket
162+ elseif col2 > 0 && ! s: IsInStringOrComment (msl, col2) && s: LineHasOpeningBrackets (msl)[0 ] == ' 2' && ! a: in_one_line_scope
163+ call cursor (msl, 1 )
164+ if searchpair (' (' , ' ' , ' )' , ' bW' , s: skip_expr ) > 0
165+ let lnum = line (' .' )
166+ let msl = lnum
167+ endif
168+
151169 else
170+
152171 " Don't use lines that are part of a one line scope as msl unless the
153172 " flag in_one_line_scope is set to 1
154173 "
@@ -159,7 +178,7 @@ function s:GetMSL(lnum, in_one_line_scope)
159178 if msl_one_line == 0
160179 break
161180 endif
162- endif
181+ end
163182 let lnum = s: PrevNonBlankNonString (lnum - 1 )
164183 endwhile
165184 return msl
@@ -240,7 +259,7 @@ function s:LineHasOpeningBrackets(lnum)
240259 endif
241260 let pos = match (line , ' [][(){}]' , pos + 1 )
242261 endwhile
243- return (open_0 > 0 ) . (open_2 > 0 ) . (open_4 > 0 )
262+ return (open_0 > 0 ? 1 : (open_0 == 0 ? 0 : 2 ) ) . (open_2 > 0 ) . (open_4 > 0 )
244263endfunction
245264
246265function s: Match (lnum, regex)
@@ -378,12 +397,45 @@ function GetJavascriptIndent()
378397 return indent (prevline) + s: case_indent_after
379398 endif
380399
381- if (line = ~ s: ternary )
382- if (getline (prevline) = ~ s: ternary_q )
400+ " If line starts with an operator...
401+ if (s: Match (v: lnum , s: operator_first ))
402+ if (s: Match (prevline, s: operator_first ))
403+ " and so does previous line, don't indent
383404 return indent (prevline)
384- else
405+ end
406+ let counts = s: LineHasOpeningBrackets (prevline)
407+ if counts[0 ] == ' 2'
408+ call cursor (prevline, 1 )
409+ " Search for the opening tag
410+ let mnum = searchpair (' (' , ' ' , ' )' , ' bW' , s: skip_expr )
411+ if mnum > 0 && s: Match (mnum, s: operator_first )
412+ return indent (mnum)
413+ end
414+ elseif counts[0 ] != ' 1' && counts[1 ] != ' 1' && counts[2 ] != ' 1'
415+ " otherwise, indent 1 level
416+ return indent (prevline) + s: sw ()
417+ end
418+ " If previous line starts with an operator...
419+ elseif s: Match (prevline, s: operator_first ) && ! s: Match (prevline, s: comma_last ) && ! s: Match (prevline, ' };\=' . s: line_term )
420+ let counts = s: LineHasOpeningBrackets (prevline)
421+ if counts[0 ] == ' 2' && counts[1 ] == ' 1'
422+ call cursor (prevline, 1 )
423+ " Search for the opening tag
424+ let mnum = searchpair (' (' , ' ' , ' )' , ' bW' , s: skip_expr )
425+ if mnum > 0 && ! s: Match (mnum, s: operator_first )
426+ return indent (mnum) + s: sw ()
427+ end
428+ elseif counts[0 ] != ' 1' && counts[1 ] != ' 1' && counts[2 ] != ' 1'
429+ return indent (prevline) - s: sw ()
430+ end
431+ end
432+
433+ if getline (prevline) = ~ ' ^\s*`$' && s: IsInTempl (v: lnum , 1 )
434+ if line !~ ' ^\s*`$'
385435 return indent (prevline) + s: sw ()
386436 endif
437+ elseif line = ~ ' ^\s*`$' && s: IsInTempl (prevline, 1 )
438+ return indent (prevline) - s: sw ()
387439 endif
388440
389441 " If we are in a multi-line comment, cindent does the right thing.
@@ -438,17 +490,36 @@ function GetJavascriptIndent()
438490 if line = ~ ' [[({]'
439491 let counts = s: LineHasOpeningBrackets (lnum)
440492 if counts[0 ] == ' 1' && searchpair (' (' , ' ' , ' )' , ' bW' , s: skip_expr ) > 0
441- if col (' .' ) + 1 == col (' $' )
493+ if col (' .' ) + 1 == col (' $' ) || line = ~ s: one_line_scope_regex
442494 return ind + s: sw ()
443495 else
444496 return virtcol (' .' )
445497 endif
446- elseif counts[1 ] == ' 1' || counts[2 ] == ' 1'
498+ elseif counts[1 ] == ' 1' || counts[2 ] == ' 1' && counts[ 0 ] != ' 2 '
447499 return ind + s: sw ()
448500 else
449501 call cursor (v: lnum , vcol)
450502 end
451- endif
503+ elseif line = ~ ' .\+};\=' . s: line_term
504+ call cursor (lnum, 1 )
505+ " Search for the opening tag
506+ let mnum = searchpair (' {' , ' ' , ' }' , ' bW' , s: skip_expr )
507+ if mnum > 0
508+ return indent (s: GetMSL (mnum, 0 ))
509+ end
510+ elseif line = ~ ' .\+);\=' || line = ~ s: comma_last
511+ let counts = s: LineHasOpeningBrackets (lnum)
512+ if counts[0 ] == ' 2'
513+ call cursor (lnum, 1 )
514+ " Search for the opening tag
515+ let mnum = searchpair (' (' , ' ' , ' )' , ' bW' , s: skip_expr )
516+ if mnum > 0
517+ return indent (s: GetMSL (mnum, 0 ))
518+ end
519+ elseif line !~ s: var_stmt
520+ return indent (prevline)
521+ end
522+ end
452523
453524 " 3.4. Work on the MSL line. {{{2
454525 " --------------------------
0 commit comments