diff options
| author | Adam Stankiewicz <sheerun@sher.pl> | 2019-09-06 14:32:07 +0200 | 
|---|---|---|
| committer | Adam Stankiewicz <sheerun@sher.pl> | 2019-09-06 14:32:07 +0200 | 
| commit | 84ec4eedcdd2892249b5369f91a6dd1d12fef2fc (patch) | |
| tree | 6c9806851123656af2b71f6c6f5d89649442909c /indent | |
| parent | 66b769328c4511b2273f01c70de971c41f6964dd (diff) | |
| download | vim-polyglot-84ec4eedcdd2892249b5369f91a6dd1d12fef2fc.tar.gz vim-polyglot-84ec4eedcdd2892249b5369f91a6dd1d12fef2fc.zip | |
Switch typescript provider, closes #428v4.0.0
Diffstat (limited to 'indent')
| -rw-r--r-- | indent/tsx.vim | 114 | ||||
| -rw-r--r-- | indent/typescript.vim | 708 | 
2 files changed, 539 insertions, 283 deletions
| diff --git a/indent/tsx.vim b/indent/tsx.vim new file mode 100644 index 00000000..906ee222 --- /dev/null +++ b/indent/tsx.vim @@ -0,0 +1,114 @@ +if exists('g:polyglot_disabled') && index(g:polyglot_disabled, 'typescript') != -1 +  finish +endif + +" Save the current JavaScript indentexpr. +let b:tsx_ts_indentexpr = &indentexpr + +" Prologue; load in XML indentation. +if exists('b:did_indent') +  let s:did_indent=b:did_indent +  unlet b:did_indent +endif +exe 'runtime! indent/xml.vim' +if exists('s:did_indent') +  let b:did_indent=s:did_indent +endif + +setlocal indentexpr=GetTsxIndent() + +" JS indentkeys +setlocal indentkeys=0{,0},0),0],0\,,!^F,o,O,e +" XML indentkeys +setlocal indentkeys+=*<Return>,<>>,<<>,/ + +" Multiline end tag regex (line beginning with '>' or '/>') +let s:endtag = '^\s*\/\?>\s*;\=' +let s:startexp = '[\{\(]\s*$' + +" Get all syntax types at the beginning of a given line. +fu! SynSOL(lnum) +  return map(synstack(a:lnum, 1), 'synIDattr(v:val, "name")') +endfu + +" Get all syntax types at the end of a given line. +fu! SynEOL(lnum) +  let lnum = prevnonblank(a:lnum) +  let col = strlen(getline(lnum)) +  return map(synstack(lnum, col), 'synIDattr(v:val, "name")') +endfu + +" Check if a syntax attribute is XMLish. +fu! SynAttrXMLish(synattr) +  return a:synattr =~ "^xml" || a:synattr =~ "^tsx" +endfu + +" Check if a synstack is XMLish (i.e., has an XMLish last attribute). +fu! SynXMLish(syns) +  return SynAttrXMLish(get(a:syns, -1)) +endfu + +" Check if a synstack denotes the end of a TSX block. +fu! SynTSXBlockEnd(syns) +  return get(a:syns, -1) =~ '\%(ts\|typescript\)Braces' && +       \ SynAttrXMLish(get(a:syns, -2)) +endfu + +" Determine how many tsxRegions deep a synstack is. +fu! SynTSXDepth(syns) +  return len(filter(copy(a:syns), 'v:val ==# "tsxRegion"')) +endfu + +" Check whether `cursyn' continues the same tsxRegion as `prevsyn'. +fu! SynTSXContinues(cursyn, prevsyn) +  let curdepth = SynTSXDepth(a:cursyn) +  let prevdepth = SynTSXDepth(a:prevsyn) + +  " In most places, we expect the nesting depths to be the same between any +  " two consecutive positions within a tsxRegion (e.g., between a parent and +  " child node, between two TSX attributes, etc.).  The exception is between +  " sibling nodes, where after a completed element (with depth N), we return +  " to the parent's nesting (depth N - 1).  This case is easily detected, +  " since it is the only time when the top syntax element in the synstack is +  " tsxRegion---specifically, the tsxRegion corresponding to the parent. +  return prevdepth == curdepth || +      \ (prevdepth == curdepth + 1 && get(a:cursyn, -1) ==# 'tsxRegion') +endfu + +" Cleverly mix JS and XML indentation. +fu! GetTsxIndent() +  let cursyn  = SynSOL(v:lnum) +  let prevsyn = SynEOL(v:lnum - 1) + +  " Use XML indenting iff: +  "   - the syntax at the end of the previous line was either TSX or was the +  "     closing brace of a jsBlock whose parent syntax was TSX; and +  "   - the current line continues the same tsxRegion as the previous line. +  if (SynXMLish(prevsyn) || SynTSXBlockEnd(prevsyn)) && +        \ SynTSXContinues(cursyn, prevsyn) +    let ind = XmlIndentGet(v:lnum, 0) +    let l:line = getline(v:lnum) +    let l:pline = getline(v:lnum - 1) + +    " Align '/>' and '>' with '<' for multiline tags. +    " Align end of expression ')' or '}'. +    if l:line =~? s:endtag +      let ind = ind - shiftwidth() +    endif + +    " Then correct the indentation of any TSX following '/>' or '>'. +    " Align start of expression '(' or '{' +    if l:pline =~? s:endtag || l:pline =~? s:startexp +      let ind = ind + shiftwidth() +    endif +  else +    if len(b:tsx_ts_indentexpr) +      " Invoke the base TS package's custom indenter +      let ind = eval(b:tsx_ts_indentexpr) +    else +      let ind = cindent(v:lnum) +    endif +  endif + +  return ind +endfu diff --git a/indent/typescript.vim b/indent/typescript.vim index 04f7e876..deb4f18c 100644 --- a/indent/typescript.vim +++ b/indent/typescript.vim @@ -3,361 +3,503 @@ if exists('g:polyglot_disabled') && index(g:polyglot_disabled, 'typescript') !=  endif  " Vim indent file -" Language: Typescript -" Acknowledgement: Almost direct copy from https://github.com/pangloss/vim-javascript +" Language: TypeScript +" Acknowledgement: Based off of vim-ruby maintained by Nikolai Weibull http://vim-ruby.rubyforge.org + +" 0. Initialization {{{1 +" =================  " Only load this indent file when no other was loaded. -if exists('b:did_indent') || get(g:, 'typescript_indent_disable', 0) +if exists("b:did_indent")    finish  endif  let b:did_indent = 1 +setlocal nosmartindent +  " Now, set up our indentation expression and keys that trigger it.  setlocal indentexpr=GetTypescriptIndent() -setlocal autoindent nolisp nosmartindent -setlocal indentkeys+=0],0) - -let b:undo_indent = 'setlocal indentexpr< smartindent< autoindent< indentkeys<' +setlocal formatexpr=Fixedgq(v:lnum,v:count) +setlocal indentkeys=0{,0},0),0],0\,,!^F,o,O,e  " Only define the function once. -if exists('*GetTypescriptIndent') +if exists("*GetTypescriptIndent")    finish  endif  let s:cpo_save = &cpo  set cpo&vim -" Get shiftwidth value -if exists('*shiftwidth') -  function s:sw() -	return shiftwidth() -  endfunction -else -  function s:sw() -	return &sw -  endfunction -endif +" 1. Variables {{{1 +" ============ -" searchpair() wrapper -if has('reltime') -  function s:GetPair(start,end,flags,skip,time,...) -	return searchpair('\m'.a:start,'','\m'.a:end,a:flags,a:skip,max([prevnonblank(v:lnum) - 2000,0] + a:000),a:time) -  endfunction -else -  function s:GetPair(start,end,flags,skip,...) -	return searchpair('\m'.a:start,'','\m'.a:end,a:flags,a:skip,max([prevnonblank(v:lnum) - 1000,get(a:000,1)])) -  endfunction -endif +let s:js_keywords = '^\s*\(break\|case\|catch\|continue\|debugger\|default\|delete\|do\|else\|finally\|for\|function\|if\|in\|instanceof\|new\|return\|switch\|this\|throw\|try\|typeof\|var\|void\|while\|with\)'  " Regex of syntax group names that are or delimit string or are comments. -let s:syng_strcom = 'string\|comment\|regex\|special\|doc\|template\%(braces\)\@!' -let s:syng_str = 'string\|template\|special' -let s:syng_com = 'comment\|doc' +let s:syng_strcom = 'string\|regex\|comment\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\c' + +" Regex of syntax group names that are line comment. +let s:syng_linecom = 'linecomment\c' +  " Expression used to check whether we should skip a match with searchpair(). -let s:skip_expr = "synIDattr(synID(line('.'),col('.'),0),'name') =~? '".s:syng_strcom."'" +let s:skip_expr = "synIDattr(synID(line('.'),col('.'),1),'name') =~ '".s:syng_strcom."'" -function s:skip_func() -  if !s:free || search('\m`\|\${\|\*\/','nW',s:looksyn) -	let s:free = !eval(s:skip_expr) -	let s:looksyn = line('.') -	return !s:free -  endif -  let s:looksyn = line('.') -  return getline('.') =~ '\%<'.col('.').'c\/.\{-}\/\|\%>'.col('.').'c[''"]\|\\$' && -		\ eval(s:skip_expr) -endfunction +let s:line_term = '\s*\%(\%(\/\/\).*\)\=$' -function s:alternatePair(stop) -  let pos = getpos('.')[1:2] -  while search('\m[][(){}]','bW',a:stop) -	if !s:skip_func() -	  let idx = stridx('])}',s:looking_at()) -	  if idx + 1 -		if s:GetPair(['\[','(','{'][idx], '])}'[idx],'bW','s:skip_func()',2000,a:stop) <= 0 -		  break -		endif -	  else -		return -	  endif -	endif -  endwhile -  call call('cursor',pos) -endfunction +" Regex that defines continuation lines, not including (, {, or [. +let s:continuation_regex = '\%([\\*+/.:]\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\|[^=]=[^=].*,\)' . s:line_term + +" Regex that defines continuation lines. +" TODO: this needs to deal with if ...: and so on +let s:msl_regex = s:continuation_regex + +let s:one_line_scope_regex = '\<\%(if\|else\|for\|while\)\>[^{;]*' . s:line_term + +" Regex that defines blocks. +let s:block_regex = '\%([{[]\)\s*\%(|\%([*@]\=\h\w*,\=\s*\)\%(,\s*[*@]\=\h\w*\)*|\)\=' . s:line_term -function s:save_pos(f,...) -  let l:pos = getpos('.')[1:2] -  let ret = call(a:f,a:000) -  call call('cursor',l:pos) -  return ret +let s:var_stmt = '^\s*var' + +let s:comma_first = '^\s*,' +let s:comma_last = ',\s*$' + +let s:ternary = '^\s\+[?|:]' +let s:ternary_q = '^\s\+?' + +" 2. Auxiliary Functions {{{1 +" ====================== + +" Check if the character at lnum:col is inside a string, comment, or is ascii. +function s:IsInStringOrComment(lnum, col) +  return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_strcom  endfunction -function s:syn_at(l,c) -  return synIDattr(synID(a:l,a:c,0),'name') +" Check if the character at lnum:col is inside a string. +function s:IsInString(lnum, col) +  return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_string  endfunction -function s:looking_at() -  return getline('.')[col('.')-1] +" 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 -function s:token() -  return s:looking_at() =~ '\k' ? expand('<cword>') : s:looking_at() +" 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  endfunction -function s:previous_token() -  let l:n = line('.') -  if (s:looking_at() !~ '\k' || search('\m\<','cbW')) && search('\m\S','bW') -	if (getline('.')[col('.')-2:col('.')-1] == '*/' || line('.') != l:n && -		  \ getline('.') =~ '\%<'.col('.').'c\/\/') && s:syn_at(line('.'),col('.')) =~? s:syng_com -	  while search('\m\/\ze[/*]','cbW') -		if !search('\m\S','bW') -		  break -		elseif s:syn_at(line('.'),col('.')) !~? s:syng_com -		  return s:token() -		endif -	  endwhile -	else -	  return s:token() -	endif -  endif -  return '' +" 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 line =~ '/\*' +      if in_block +        let in_block = 0 +      else +        break +      endif +    elseif !in_block && line =~ '\*/' +      let in_block = 1 +    elseif !in_block && line !~ '^\s*\%(//\).*$' && !(s:IsInStringOrComment(lnum, 1) && s:IsInStringOrComment(lnum, strlen(line))) +      break +    endif +    let lnum = prevnonblank(lnum - 1) +  endwhile +  return lnum  endfunction -function s:others(p) -  return "((line2byte(line('.')) + col('.')) <= ".(line2byte(a:p[0]) + a:p[1]).") || ".s:skip_expr +" Find line above 'lnum' that started the continuation 'lnum' may be part of. +function s:GetMSL(lnum, in_one_line_scope) +  " Start on the line we're at and use its indent. +  let msl = a:lnum +  let lnum = s:PrevNonBlankNonString(a:lnum - 1) +  while lnum > 0 +    " 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:msl_regex) + 1 +    if (col > 0 && !s:IsInStringOrComment(lnum, col)) || s:IsInString(lnum, strlen(line)) +      let msl = lnum +    else +      " Don't use lines that are part of a one line scope as msl unless the +      " flag in_one_line_scope is set to 1 +      " +      if a:in_one_line_scope +        break +      end +      let msl_one_line = s:Match(lnum, s:one_line_scope_regex) +      if msl_one_line == 0 +        break +      endif +    endif +    let lnum = s:PrevNonBlankNonString(lnum - 1) +  endwhile +  return msl  endfunction -function s:tern_skip(p) -  return s:GetPair('{','}','nbW',s:others(a:p),200,a:p[0]) > 0 +function s:RemoveTrailingComments(content) +  let single = '\/\/\(.*\)\s*$' +  let multi = '\/\*\(.*\)\*\/\s*$' +  return substitute(substitute(a:content, single, '', ''), multi, '', '')  endfunction -function s:tern_col(p) -  return s:GetPair('?',':\@<!::\@!','nbW',s:others(a:p) -		\ .' || s:tern_skip('.string(a:p).')',200,a:p[0]) > 0 +" Find if the string is inside var statement (but not the first string) +function s:InMultiVarStatement(lnum) +  let lnum = s:PrevNonBlankNonString(a:lnum - 1) + +"  let type = synIDattr(synID(lnum, indent(lnum) + 1, 0), 'name') + +  " loop through previous expressions to find a var statement +  while lnum > 0 +    let line = getline(lnum) + +    " if the line is a js keyword +    if (line =~ s:js_keywords) +      " check if the line is a var stmt +      " if the line has a comma first or comma last then we can assume that we +      " are in a multiple var statement +      if (line =~ s:var_stmt) +        return lnum +      endif + +      " other js keywords, not a var +      return 0 +    endif + +    let lnum = s:PrevNonBlankNonString(lnum - 1) +  endwhile + +  " beginning of program, not a var +  return 0  endfunction -function s:label_col() -  let pos = getpos('.')[1:2] -  let [s:looksyn,s:free] = pos -  call s:alternatePair(0) -  if s:save_pos('s:IsBlock') -	let poss = getpos('.')[1:2] -	return call('cursor',pos) || !s:tern_col(poss) -  elseif s:looking_at() == ':' -	return !s:tern_col([0,0]) +" Find line above with beginning of the var statement or returns 0 if it's not +" this statement +function s:GetVarIndent(lnum) +  let lvar = s:InMultiVarStatement(a:lnum) +  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) - shiftwidth() +    else +      return indent(lvar) + shiftwidth() +    endif    endif -endfunction -" configurable regexes that define continuation lines, not including (, {, or [. -let s:opfirst = '^' . get(g:,'typescript_opfirst', -	  \ '\%([<>=,?^%|*/&]\|\([-.:+]\)\1\@!\|!=\|in\%(stanceof\)\=\>\)') -let s:continuation = get(g:,'typescript_continuation', -	  \ '\%([-+<>=,.~!?/*^%|&:]\|\<\%(typeof\|delete\|void\|in\|instanceof\)\)') . '$' - -function s:continues(ln,con) -  return !cursor(a:ln, match(' '.a:con,s:continuation)) && -		\ eval( (['s:syn_at(line("."),col(".")) !~? "regex"'] + -		\ repeat(['getline(".")[col(".")-2] != tr(s:looking_at(),">","=")'],3) + -		\ repeat(['s:previous_token() != "."'],5) + [1])[ -		\ index(split('/ > - + typeof in instanceof void delete'),s:token())]) +  return -1  endfunction -" get the line of code stripped of comments and move cursor to the last -" non-comment char. -function s:Trim(ln) -  call cursor(a:ln+1,1) -  call s:previous_token() -  return strpart(getline('.'),0,col('.')) -endfunction -" Find line above 'lnum' that isn't empty or in a comment -function s:PrevCodeLine(lnum) -  let l:n = prevnonblank(a:lnum) -  while l:n -	if getline(l:n) =~ '^\s*\/[/*]'  -	  if (stridx(getline(l:n),'`') > 0 || getline(l:n-1)[-1:] == '\') && -			\ s:syn_at(l:n,1) =~? s:syng_str -		return l:n -	  endif -	  let l:n = prevnonblank(l:n-1) -	elseif getline(l:n) =~ '\([/*]\)\1\@![/*]' && s:syn_at(l:n,1) =~? s:syng_com -	  let l:n = s:save_pos('eval', -			\ 'cursor('.l:n.',1) + search(''\m\/\*'',"bW")') -	else -	  return l:n -	endif +" Check if line 'lnum' has more opening brackets than closing ones. +function s:LineHasOpeningBrackets(lnum) +  let open_0 = 0 +  let open_2 = 0 +  let open_4 = 0 +  let line = getline(a:lnum) +  let pos = match(line, '[][(){}]', 0) +  while pos != -1 +    if !s:IsInStringOrComment(a:lnum, pos + 1) +      let idx = stridx('(){}[]', line[pos]) +      if idx % 2 == 0 +        let open_{idx} = open_{idx} + 1 +      else +        let open_{idx - 1} = open_{idx - 1} - 1 +      endif +    endif +    let pos = match(line, '[][(){}]', pos + 1)    endwhile +  return (open_0 > 0) . (open_2 > 0) . (open_4 > 0)  endfunction -" Check if line 'lnum' has a balanced amount of parentheses. -function s:Balanced(lnum) -  let l:open = 0 -  let l:line = getline(a:lnum) -  let pos = match(l:line, '[][(){}]', 0) -  while pos != -1 -	if s:syn_at(a:lnum,pos + 1) !~? s:syng_strcom -	  let l:open += match(' ' . l:line[pos],'[[({]') -	  if l:open < 0 -		return -	  endif -	endif -	let pos = match(l:line, '[][(){}]', pos + 1) -  endwhile -  return !l:open +function s:Match(lnum, regex) +  let col = match(getline(a:lnum), a:regex) + 1 +  return col > 0 && !s:IsInStringOrComment(a:lnum, col) ? col : 0  endfunction -function s:OneScope(lnum) -  let pline = s:Trim(a:lnum) -  let kw = 'else do' -  if pline[-1:] == ')' && s:GetPair('(', ')', 'bW', s:skip_expr, 100) > 0 -	call s:previous_token() -	let kw = 'for if let while with' -	if index(split('await each'),s:token()) + 1 -	  call s:previous_token() -	  let kw = 'for' -	endif +function s:IndentWithContinuation(lnum, ind, width) +  " Set up variables to use and search for MSL to the previous line. +  let p_lnum = a:lnum +  let lnum = s:GetMSL(a:lnum, 1) +  let line = getline(lnum) + +  " If the previous line wasn't a MSL and is continuation return its indent. +  " TODO: the || s:IsInString() thing worries me a bit. +  if p_lnum != lnum +    if s:Match(p_lnum,s:continuation_regex)||s:IsInString(p_lnum,strlen(line)) +      return a:ind +    endif +  endif + +  " Set up more variables now that we know we aren't continuation bound. +  let msl_ind = indent(lnum) + +  " If the previous line ended with [*+/.-=], start a continuation that +  " indents an extra level. +  if s:Match(lnum, s:continuation_regex) +    if lnum == p_lnum +      return msl_ind + a:width +    else +      return msl_ind +    endif    endif -  return pline[-2:] == '=>' || index(split(kw),s:token()) + 1 && -		\ s:save_pos('s:previous_token') != '.' + +  return a:ind  endfunction -" returns braceless levels started by 'i' and above lines * &sw. 'num' is the -" lineNr which encloses the entire context, 'cont' if whether line 'i' + 1 is -" a continued expression, which could have started in a braceless context -function s:iscontOne(i,num,cont) -  let [l:i, l:num, bL] = [a:i, a:num + !a:num, 0] -  let pind = a:num ? indent(l:num) + s:W : 0 -  let ind = indent(l:i) + (a:cont ? 0 : s:W) -  while l:i >= l:num && (ind > pind || l:i == l:num) -	if indent(l:i) < ind && s:OneScope(l:i) -	  let bL += s:W -	  let l:i = line('.') -	elseif !a:cont || bL || ind < indent(a:i) -	  break -	endif -	let ind = min([ind, indent(l:i)]) -	let l:i = s:PrevCodeLine(l:i - 1) -  endwhile -  return bL +function s:InOneLineScope(lnum) +  let msl = s:GetMSL(a:lnum, 1) +  if msl > 0 && s:Match(msl, s:one_line_scope_regex) +    return msl +  endif +  return 0  endfunction -" https://github.com/sweet-js/sweet.js/wiki/design#give-lookbehind-to-the-reader -function s:IsBlock() -  if s:looking_at() == '{' -	let l:n = line('.') -	let char = s:previous_token() -	if match(s:stack,'xml\|jsx') + 1 && s:syn_at(line('.'),col('.')-1) =~? 'xml\|jsx' -	  return char != '{' -	elseif char =~ '\k' -	  return index(split('return const let import export yield default delete var await void typeof throw case new in instanceof') -			\ ,char) < (line('.') != l:n) || s:previous_token() == '.' -	elseif char == '>' -	  return getline('.')[col('.')-2] == '=' || s:syn_at(line('.'),col('.')) =~? '^jsflow' -	elseif char == ':' -	  return getline('.')[col('.')-2] != ':' && s:label_col() -	elseif char == '/' -	  return s:syn_at(line('.'),col('.')) =~? 'regex' -	endif -	return char !~ '[=~!<*,?^%|&([]' && -		  \ (char !~ '[-+]' || l:n != line('.') && getline('.')[col('.')-2] == char) +function s:ExitingOneLineScope(lnum) +  let msl = s:GetMSL(a:lnum, 1) +  if msl > 0 +    " if the current line is in a one line scope .. +    if s:Match(msl, s:one_line_scope_regex) +      return 0 +    else +      let prev_msl = s:GetMSL(msl - 1, 1) +      if s:Match(prev_msl, s:one_line_scope_regex) +        return prev_msl +      endif +    endif    endif +  return 0  endfunction +" 3. GetTypescriptIndent Function {{{1 +" ========================= +  function GetTypescriptIndent() -  let b:js_cache = get(b:,'js_cache',[0,0,0]) +  " 3.1. Setup {{{2 +  " ---------- + +  " Set up variables for restoring position in file.  Could use v:lnum here. +  let vcol = col('.') + +  " 3.2. Work on the current line {{{2 +  " ----------------------------- + +  let ind = -1    " Get the current line. -  call cursor(v:lnum,1) -  let l:line = getline('.') -  " use synstack as it validates syn state and works in an empty line -  let s:stack = synstack(v:lnum,1) -  let syns = synIDattr(get(s:stack,-1),'name') - -  " start with strings,comments,etc. -  if syns =~? s:syng_com -	if l:line =~ '^\s*\*' -	  return cindent(v:lnum) -	elseif l:line !~ '^\s*\/[/*]' -	  return -1 -	endif -  elseif syns =~? s:syng_str && l:line !~ '^[''"]' -	if b:js_cache[0] == v:lnum - 1 && s:Balanced(v:lnum-1) -	  let b:js_cache[0] = v:lnum -	endif -	return -1 +  let line = getline(v:lnum) +  " previous nonblank line number +  let prevline = prevnonblank(v:lnum - 1) + +  " 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*[],})]') +  if col > 0 && !s:IsInStringOrComment(v:lnum, col) +    call cursor(v:lnum, col) + +    let lvar = s:InMultiVarStatement(v:lnum) +    if lvar +      let prevline_contents = s:RemoveTrailingComments(getline(prevline)) + +      " check for comma first +      if (line[col - 1] =~ ',') +        " if the previous line ends in comma or semicolon don't indent +        if (prevline_contents =~ '[;,]\s*$') +          return indent(s:GetMSL(line('.'), 0)) +        " get previous line indent, if it's comma first return prevline indent +        elseif (prevline_contents =~ s:comma_first) +          return indent(prevline) +        " otherwise we indent 1 level +        else +          return indent(lvar) + shiftwidth() +        endif +      endif +    endif + + +    let bs = strpart('(){}[]', stridx(')}]', line[col - 1]) * 2, 2) +    if searchpair(escape(bs[0], '\['), '', bs[1], 'bW', s:skip_expr) > 0 +      if line[col-1]==')' && col('.') != col('$') - 1 +        let ind = virtcol('.')-1 +      else +        let ind = indent(s:GetMSL(line('.'), 0)) +      endif +    endif +    return ind    endif -  let l:lnum = s:PrevCodeLine(v:lnum - 1) -  if !l:lnum -	return + +  " If the line is comma first, dedent 1 level +  if (getline(prevline) =~ s:comma_first) +    return indent(prevline) - shiftwidth()    endif -  let l:line = substitute(l:line,'^\s*','','') -  if l:line[:1] == '/*' -	let l:line = substitute(l:line,'^\%(\/\*.\{-}\*\/\s*\)*','','') +  if (line =~ s:ternary) +    if (getline(prevline) =~ s:ternary_q) +      return indent(prevline) +    else +      return indent(prevline) + shiftwidth() +    endif    endif -  if l:line =~ '^\/[/*]' -	let l:line = '' + +  " If we are in a multi-line comment, cindent does the right thing. +  if s:IsInMultilineComment(v:lnum, 1) && !s:IsLineComment(v:lnum, 1) +    return cindent(v:lnum)    endif -  " the containing paren, bracket, or curly. Many hacks for performance -  let idx = index([']',')','}'],l:line[0]) -  if b:js_cache[0] >= l:lnum && b:js_cache[0] < v:lnum && -		\ (b:js_cache[0] > l:lnum || s:Balanced(l:lnum)) -	call call('cursor',b:js_cache[1:]) -  else -	let [s:looksyn, s:free, top] = [v:lnum - 1, 1, (!indent(l:lnum) && -		  \ s:syn_at(l:lnum,1) !~? s:syng_str) * l:lnum] -	if idx + 1 -	  call s:GetPair(['\[','(','{'][idx],'])}'[idx],'bW','s:skip_func()',2000,top) -	elseif getline(v:lnum) !~ '^\S' && syns =~? 'block' -	  call s:GetPair('{','}','bW','s:skip_func()',2000,top) -	else -	  call s:alternatePair(top) -	endif +  " Check for multiple var assignments +"  let var_indent = s:GetVarIndent(v:lnum) +"  if var_indent >= 0 +"    return var_indent +"  endif + +  " 3.3. Work on the previous line. {{{2 +  " ------------------------------- + +  " 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) +    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 +    return indent(prevnonblank(v:lnum))    endif -  let b:js_cache = [v:lnum] + (line('.') == v:lnum ? [0,0] : getpos('.')[1:2]) -  let num = b:js_cache[1] - -  let [s:W, isOp, bL, switch_offset] = [s:sw(),0,0,0] -  if !num || s:IsBlock() -	let ilnum = line('.') -	let pline = s:save_pos('s:Trim',l:lnum) -	if num && s:looking_at() == ')' && s:GetPair('(', ')', 'bW', s:skip_expr, 100) > 0 -	  let num = ilnum == num ? line('.') : num -	  if idx < 0 && s:previous_token() ==# 'switch' && s:previous_token() != '.' -		if &cino !~ ':' -		  let switch_offset = s:W -		else -		  let cinc = matchlist(&cino,'.*:\zs\(-\)\=\(\d*\)\(\.\d\+\)\=\(s\)\=\C') -		  let switch_offset = max([cinc[0] is '' ? 0 : (cinc[1].1) * -				\ ((strlen(cinc[2].cinc[3]) ? str2nr(cinc[2].str2nr(cinc[3][1])) : 10) * -				\ (cinc[4] is '' ? 1 : s:W)) / 10, -indent(num)]) -		endif -		if pline[-1:] != '.' && l:line =~# '^\%(default\|case\)\>' -		  return indent(num) + switch_offset -		endif -	  endif -	endif -	if idx < 0 && pline !~ '[{;]$' -	  if pline =~# ':\@<!:$' -		call cursor(l:lnum,strlen(pline)) -		let isOp = s:tern_col(b:js_cache[1:2]) * s:W -	  else -		let isOp = (l:line =~# s:opfirst || s:continues(l:lnum,pline)) * s:W -	  endif -	  let bL = s:iscontOne(l:lnum,b:js_cache[1],isOp) -	  let bL -= (bL && l:line[0] == '{') * s:W -	endif +  " At the start of the file use zero indent. +  if lnum == 0 +    return 0    endif -  " main return -  if idx + 1 || l:line[:1] == '|}' -	return indent(num) -  elseif num -	return indent(num) + s:W + switch_offset + bL + isOp +  " Set up variables for current line. +  let line = getline(lnum) +  let ind = indent(lnum) + +  " If the previous line ended with a block opening, add a level of indent. +  if s:Match(lnum, s:block_regex) +    return indent(s:GetMSL(lnum, 0)) + shiftwidth() +  endif + +  " If the previous line contained an opening bracket, and we are still in it, +  " add indent depending on the bracket type. +  if line =~ '[[({]' +    let counts = s:LineHasOpeningBrackets(lnum) +    if counts[0] == '1' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0 +      if col('.') + 1 == col('$') +        return ind + shiftwidth() +      else +        return virtcol('.') +      endif +    elseif counts[1] == '1' || counts[2] == '1' +      return ind + shiftwidth() +    else +      call cursor(v:lnum, vcol) +    end    endif -  return bL + isOp + +  " 3.4. Work on the MSL line. {{{2 +  " -------------------------- + +  let ind_con = ind +  let ind = s:IndentWithContinuation(lnum, ind_con, shiftwidth()) + +  " }}}2 +  " +  " +  let ols = s:InOneLineScope(lnum) +  if ols > 0 +    let ind = ind + shiftwidth() +  else +    let ols = s:ExitingOneLineScope(lnum) +    while ols > 0 && ind > 0 +      let ind = ind - shiftwidth() +      let ols = s:InOneLineScope(ols - 1) +    endwhile +  endif + +  return ind  endfunction +" }}}1 +  let &cpo = s:cpo_save  unlet s:cpo_save +function! Fixedgq(lnum, count) +    let l:tw = &tw ? &tw : 80; + +    let l:count = a:count +    let l:first_char = indent(a:lnum) + 1 + +    if mode() == 'i' " gq was not pressed, but tw was set +        return 1 +    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) +        return 1 +    endif + +    if len(getline(a:lnum)) < l:tw && l:count == 1 " No need for gq +        return 1 +    endif + +    " Put all the lines on one line and do normal spliting after that +    if l:count > 1 +        while l:count > 1 +            let l:count -= 1 +            normal! J +        endwhile +    endif + +    let l:winview = winsaveview() + +    call cursor(a:lnum, l:tw + 1) +    let orig_breakpoint = searchpairpos(' ', '', '\.', 'bcW', '', a:lnum) +    call cursor(a:lnum, l:tw + 1) +    let breakpoint = searchpairpos(' ', '', '\.', 'bcW', s:skip_expr, a:lnum) + +    " No need for special treatment, normal gq handles edgecases better +    if breakpoint[1] == orig_breakpoint[1] +        call winrestview(l:winview) +        return 1 +    endif + +    " Try breaking after string +    if breakpoint[1] <= indent(a:lnum) +        call cursor(a:lnum, l:tw + 1) +        let breakpoint = searchpairpos('\.', '', ' ', 'cW', s:skip_expr, a:lnum) +    endif + + +    if breakpoint[1] != 0 +        call feedkeys("r\<CR>") +    else +        let l:count = l:count - 1 +    endif + +    " run gq on new lines +    if l:count == 1 +        call feedkeys("gqq") +    endif + +    return 0 +endfunction | 
