diff options
Diffstat (limited to 'indent/typescript.vim')
-rw-r--r-- | indent/typescript.vim | 706 |
1 files changed, 282 insertions, 424 deletions
diff --git a/indent/typescript.vim b/indent/typescript.vim index ed2e6c04..6dead67a 100644 --- a/indent/typescript.vim +++ b/indent/typescript.vim @@ -2,504 +2,362 @@ if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'typescript') == " Vim indent file " Language: Typescript -" Acknowledgement: Based off of vim-ruby maintained by Nikolai Weibull http://vim-ruby.rubyforge.org - -" 0. Initialization {{{1 -" ================= +" Acknowledgement: Almost direct copy from https://github.com/pangloss/vim-javascript " Only load this indent file when no other was loaded. -if exists("b:did_indent") +if exists('b:did_indent') || get(g:, 'typescript_indent_disable', 0) finish endif let b:did_indent = 1 -setlocal nosmartindent - " Now, set up our indentation expression and keys that trigger it. setlocal indentexpr=GetTypescriptIndent() -setlocal formatexpr=Fixedgq(v:lnum,v:count) -setlocal indentkeys=0{,0},0),0],0\,,!^F,o,O,e +setlocal autoindent nolisp nosmartindent +setlocal indentkeys+=0],0) + +let b:undo_indent = 'setlocal indentexpr< smartindent< autoindent< indentkeys<' " Only define the function once. -if exists("*GetTypescriptIndent") +if exists('*GetTypescriptIndent') finish endif let s:cpo_save = &cpo set cpo&vim -" 1. Variables {{{1 -" ============ +" Get shiftwidth value +if exists('*shiftwidth') + function s:sw() + return shiftwidth() + endfunction +else + function s:sw() + return &sw + endfunction +endif -let s:ts_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\)' +" 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 " Regex of syntax group names that are or delimit string or are comments. -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' - +let s:syng_strcom = 'string\|comment\|regex\|special\|doc\|template\%(braces\)\@!' +let s:syng_str = 'string\|template\|special' +let s:syng_com = 'comment\|doc' " 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."'" - -let s:line_term = '\s*\%(\%(\/\/\).*\)\=$' - -" 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 - -let s:var_stmt = '^\s*var' - -let s:comma_first = '^\s*,' -let s:comma_last = ',\s*$' +let s:skip_expr = "synIDattr(synID(line('.'),col('.'),0),'name') =~? '".s:syng_strcom."'" -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 +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 -" 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 +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 -" 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 +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 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:syn_at(l,c) + return synIDattr(synID(a:l,a:c,0),'name') 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 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 +function s:looking_at() + return getline('.')[col('.')-1] endfunction -" 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 +function s:token() + return s:looking_at() =~ '\k' ? expand('<cword>') : s:looking_at() endfunction -function s:RemoveTrailingComments(content) - let single = '\/\/\(.*\)\s*$' - let multi = '\/\*\(.*\)\*\/\s*$' - return substitute(substitute(a:content, single, '', ''), multi, '', '') +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 '' endfunction -" 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 ts keyword - if (line =~ s:ts_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 ts keywords, not a var - return 0 - endif +function s:others(p) + return "((line2byte(line('.')) + col('.')) <= ".(line2byte(a:p[0]) + a:p[1]).") || ".s:skip_expr +endfunction - let lnum = s:PrevNonBlankNonString(lnum - 1) - endwhile +function s:tern_skip(p) + return s:GetPair('{','}','nbW',s:others(a:p),200,a:p[0]) > 0 +endfunction - " beginning of program, not a var - return 0 +function s:tern_col(p) + return s:GetPair('?',':\@<!::\@!','nbW',s:others(a:p) + \ .' || s:tern_skip('.string(a:p).')',200,a:p[0]) > 0 endfunction -" 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) - &sw - else - return indent(lvar) + &sw - endif +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]) endif +endfunction - return -1 +" 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())]) 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 -" 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) +" 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 endwhile - return (open_0 > 0) . (open_2 > 0) . (open_4 > 0) endfunction -function s:Match(lnum, regex) - let col = match(getline(a:lnum), a:regex) + 1 - return col > 0 && !s:IsInStringOrComment(a:lnum, col) ? col : 0 +" 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 endfunction -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 +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 endif - - return a:ind + return pline[-2:] == '=>' || index(split(kw),s:token()) + 1 && + \ s:save_pos('s:previous_token') != '.' endfunction -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 +" 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 endfunction -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 +" 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) endif - return 0 endfunction -" 3. GetTypescriptIndent Function {{{1 -" ========================= - function GetTypescriptIndent() - " 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 + let b:js_cache = get(b:,'js_cache',[0,0,0]) " Get the current line. - 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) + &sw - 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 + 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 endif - - " If the line is comma first, dedent 1 level - if (getline(prevline) =~ s:comma_first) - return indent(prevline) - &sw + let l:lnum = s:PrevCodeLine(v:lnum - 1) + if !l:lnum + return endif - if (line =~ s:ternary) - if (getline(prevline) =~ s:ternary_q) - return indent(prevline) - else - return indent(prevline) + &sw - endif + let l:line = substitute(l:line,'^\s*','','') + if l:line[:1] == '/*' + let l:line = substitute(l:line,'^\%(\/\*.\{-}\*\/\s*\)*','','') 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) - return cindent(v:lnum) + if l:line =~ '^\/[/*]' + let l:line = '' 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 - - " At the start of the file use zero indent. - if lnum == 0 - return 0 - endif - - " 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)) + &sw + " 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 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 + &sw - else - return virtcol('.') - endif - elseif counts[1] == '1' || counts[2] == '1' - return ind + &sw - else - call cursor(v:lnum, vcol) - end + 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 endif - " 3.4. Work on the MSL line. {{{2 - " -------------------------- - - let ind_con = ind - let ind = s:IndentWithContinuation(lnum, ind_con, &sw) - - " }}}2 - " - " - let ols = s:InOneLineScope(lnum) - if ols > 0 - let ind = ind + &sw - else - let ols = s:ExitingOneLineScope(lnum) - while ols > 0 && ind > 0 - let ind = ind - &sw - let ols = s:InOneLineScope(ols - 1) - endwhile + " main return + if idx + 1 || l:line[:1] == '|}' + return indent(num) + elseif num + return indent(num) + s:W + switch_offset + bL + isOp endif - - return ind + return bL + isOp 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 endif |