diff options
Diffstat (limited to 'indent')
| -rw-r--r-- | indent/blade.vim | 94 | ||||
| -rw-r--r-- | indent/erlang.vim | 40 | ||||
| -rw-r--r-- | indent/eruby.vim | 1 | ||||
| -rw-r--r-- | indent/haskell.vim | 58 | ||||
| -rw-r--r-- | indent/javascript.vim | 212 | ||||
| -rw-r--r-- | indent/litcoffee.vim | 26 | ||||
| -rw-r--r-- | indent/ruby.vim | 808 | ||||
| -rw-r--r-- | indent/typescript.vim | 706 | 
8 files changed, 1035 insertions, 910 deletions
| diff --git a/indent/blade.vim b/indent/blade.vim index 97f7f293..95cc00a4 100644 --- a/indent/blade.vim +++ b/indent/blade.vim @@ -4,22 +4,22 @@ if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'blade') == -1  " Language:     Blade (Laravel)  " Maintainer:   Jason Walton <jwalton512@gmail.com> -if exists("b:did_indent") +if exists('b:did_indent')      finish  endif  runtime! indent/html.vim  let s:htmlindent = &indentexpr -unlet! b:did_indent +unlet! b:did_indent  runtime! indent/php.vim  let s:phpindent = &indentexpr -unlet! b:did_indent  let b:did_indent = 1  " Doesn't include 'foreach' and 'forelse' because these already get matched by 'for'. -let s:directives_start = 'if\|else\|unless\|for\|while\|empty\|push\|section\|can\|hasSection\|verbatim' +let s:directives_start = 'if\|else\|unless\|for\|while\|empty\|push\|section\|can\|hasSection\|verbatim\|php\|' . +            \ 'component\|slot\|prepend'  let s:directives_end = 'else\|end\|empty\|show\|stop\|append\|overwrite'  if exists('g:blade_custom_directives_pairs') @@ -29,55 +29,71 @@ endif  setlocal autoindent  setlocal indentexpr=GetBladeIndent() -exe "setlocal indentkeys=o,O,<>>,!^F,0=}},0=!!},=@" . substitute(s:directives_end, '\\|', ',=@', 'g') +exe 'setlocal indentkeys=o,O,<>>,!^F,0=}},0=!!},=@' . substitute(s:directives_end, '\\|', ',=@', 'g')  " Only define the function once. -if exists("*GetBladeIndent") +if exists('*GetBladeIndent')      finish  endif +function! s:IsStartingDelimiter(lnum) +    let line = getline(a:lnum) +    return line =~# '\%(\w\|@\)\@<!@\%(' . s:directives_start . '\)\%(.*@end\|.*@stop\)\@!' +                \ || line =~# '{{\%(.*}}\)\@!' +                \ || line =~# '{!!\%(.*!!}\)\@!' +                \ || line =~# '<?\%(.*?>\)\@!' +endfunction +  function! GetBladeIndent() -    let lnum = prevnonblank(v:lnum-1) +    let lnum = prevnonblank(v:lnum - 1)      if lnum == 0          return 0      endif -    let line = substitute(substitute(getline(lnum), '\s\+$', '', ''), '^\s\+', '', '') -    let cline = substitute(substitute(getline(v:lnum), '\s\+$', '', ''), '^\s\+', '', '') +    let line = getline(lnum) +    let cline = getline(v:lnum)      let indent = indent(lnum) -    if cline =~# '@\%(' . s:directives_end . '\)' || -                \ cline =~# '\%(<?.*\)\@<!?>\|\%({{.*\)\@<!}}\|\%({!!.*\)\@<!!!}' -        let indent = indent - &sw -    elseif line =~# '<?\%(.*?>\)\@!\|@php\%(\s*(\)\@!' -        let indent = indent + &sw -    else -        if exists("*GetBladeIndentCustom") -            let hindent = GetBladeIndentCustom() -        " Don't use PHP indentation if line is a comment -        elseif line !~# '^\s*\%(#\|//\)\|\*/\s*$' && ( -                    \ searchpair('@include\%(If\)\?\s*(', '', ')', 'bWr') || -                    \ searchpair('{!!', '', '!!}', 'bWr') || -                    \ searchpair('{{', '', '}}', 'bWr') || -                    \ searchpair('<?', '', '?>', 'bWr') || -                    \ searchpair('@php\%(\s*(\)\@!', '', '@endphp', 'bWr') ) -            execute 'let hindent = ' . s:phpindent -        else -            execute 'let hindent = ' . s:htmlindent -        endif -        if hindent > -1 -            let indent = hindent -        endif -    endif -    let increase = indent + &sw -    if line =~# '@\%(section\)\%(.*@end\)\@!' && line !~# '@\%(section\)\s*([^,]*)' -        return indent -    elseif line =~# '@\%(' . s:directives_start . '\)\%(.*@end\|.*@stop\)\@!' || -                \ line =~# '{{\%(.*}}\)\@!' || line =~# '{!!\%(.*!!}\)\@!' -        return increase -    else +    " 1. Check for special directives +    " @section is a single-line directive if it has a second argument. +    " @php is a single-line directive if it is followed by parentheses. +    if (line =~# '@section\%(.*@end\)\@!' && line !~# '@section\s*([^,]*)') +                \ || line =~# '@php\s*('          return indent      endif + +    " 2. When the current line is an ending delimiter: decrease indentation +    "    if the previous line wasn't a starting delimiter. +    if cline =~# '^\s*@\%(' . s:directives_end . '\)' +                \ || cline =~# '\%(<?.*\)\@<!?>' +                \ || cline =~# '\%({{.*\)\@<!}}' +                \ || cline =~# '\%({!!.*\)\@<!!!}' +        return s:IsStartingDelimiter(lnum) ? indent : indent - &sw +    endif + +    " 3. Increase indentation if the line contains a starting delimiter. +    if s:IsStartingDelimiter(lnum) +        return indent + &sw +    endif + +    " 4. External indent scripts (PHP and HTML) +    execute 'let indent = ' . s:htmlindent + +    if exists('*GetBladeIndentCustom') +        let indent = GetBladeIndentCustom() +    elseif line !~# '^\s*\%(#\|//\)\|\*/\s*$' && ( +                \ searchpair('@include\%(If\)\?\s*(', '', ')', 'bWr') || +                \ searchpair('{!!', '', '!!}', 'bWr') || +                \ searchpair('{{', '', '}}', 'bWr') || +                \ searchpair('<?', '', '?>', 'bWr') || +                \ searchpair('@php\s*(\@!', '', '@endphp', 'bWr') ) +        " Only use PHP's indent if the region spans multiple lines +        if !s:IsStartingDelimiter(v:lnum) +            execute 'let indent = ' . s:phpindent +        endif +    endif + +    return indent  endfunction  endif diff --git a/indent/erlang.vim b/indent/erlang.vim index b1186ca8..64525bbd 100644 --- a/indent/erlang.vim +++ b/indent/erlang.vim @@ -6,9 +6,9 @@ if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'erlang') == -1  " Contributors: Edwin Fine <efine145_nospam01 at usa dot net>  "               Pawel 'kTT' Salata <rockplayer.pl@gmail.com>  "               Ricardo Catalinas Jiménez <jimenezrick@gmail.com> -" Last Update:  2013-Jul-21 +" Last Update:  2017-Feb-28  " License:      Vim license -" URL:          https://github.com/hcs42/vim-erlang +" URL:          https://github.com/vim-erlang/vim-erlang-runtime  " Note About Usage:  "   This indentation script works best with the Erlang syntax file created by @@ -528,7 +528,9 @@ endfunction  "       ok.          % IsLineAtomContinuation = false  function! s:IsLineAtomContinuation(lnum)    if has('syntax_items') -    return synIDattr(synID(a:lnum, 1, 0), 'name') =~# '^erlangQuotedAtom' +    let syn_name = synIDattr(synID(a:lnum, 1, 0), 'name') +    return syn_name =~# '^erlangQuotedAtom' || +         \ syn_name =~# '^erlangQuotedRecord'    else      return 0    endif @@ -682,7 +684,7 @@ function! s:BeginningOfClauseFound(stack, token, stored_vcol, lnum, i)      call s:Pop(a:stack)      if empty(a:stack)        call s:Log('    Stack is ["when"], so LTI is in a guard -> return') -      return [1, a:stored_vcol + &sw + 2] +      return [1, a:stored_vcol + shiftwidth() + 2]      else        return [1, s:UnexpectedToken(a:token, a:stack)]      endif @@ -691,7 +693,7 @@ function! s:BeginningOfClauseFound(stack, token, stored_vcol, lnum, i)      call s:Pop(a:stack)      if empty(a:stack)        call s:Log('    Stack is ["->"], so LTI is in function body -> return') -      return [1, a:stored_vcol + &sw] +      return [1, a:stored_vcol + shiftwidth()]      elseif a:stack[0] ==# ';'        call s:Pop(a:stack) @@ -843,7 +845,7 @@ function! s:ErlangCalcIndent2(lnum, stack)        elseif token ==# 'begin'          let [ret, res] = s:BeginElementFound(stack, token, curr_vcol, -                                            \stored_vcol, 'end', &sw) +                                            \stored_vcol, 'end', shiftwidth())          if ret | return res | endif        " case EXPR of BRANCHES end @@ -894,11 +896,11 @@ function! s:ErlangCalcIndent2(lnum, stack)          elseif stack == ['->']            call s:Log('    LTI is in a branch after ' .                      \'"of/receive/after/if/catch" -> return') -          return stored_vcol + &sw +          return stored_vcol + shiftwidth()          elseif stack == ['when']            call s:Log('    LTI is in a guard after ' .                      \'"of/receive/after/if/catch" -> return') -          return stored_vcol + &sw +          return stored_vcol + shiftwidth()          else            return s:UnexpectedToken(token, stack)          endif @@ -934,7 +936,7 @@ function! s:ErlangCalcIndent2(lnum, stack)            if empty(stack)              call s:Log('    LTI is in a condition; matching ' .                        \'"case/if/try/receive" found') -            let stored_vcol = curr_vcol + &sw +            let stored_vcol = curr_vcol + shiftwidth()            elseif stack[0] ==# 'align_to_begin_element'              call s:Pop(stack)              let stored_vcol = curr_vcol @@ -943,23 +945,23 @@ function! s:ErlangCalcIndent2(lnum, stack)                        \'"case/if/try/receive" found')              call s:Pop(stack)              call s:Pop(stack) -            let stored_vcol = curr_vcol + &sw +            let stored_vcol = curr_vcol + shiftwidth()            elseif stack[0] ==# '->'              call s:Log('    LTI is in a branch; matching ' .                        \'"case/if/try/receive" found')              call s:Pop(stack) -            let stored_vcol = curr_vcol + 2 * &sw +            let stored_vcol = curr_vcol + 2 * shiftwidth()            elseif stack[0] ==# 'when'              call s:Log('    LTI is in a guard; matching ' .                        \'"case/if/try/receive" found')              call s:Pop(stack) -            let stored_vcol = curr_vcol + 2 * &sw + 2 +            let stored_vcol = curr_vcol + 2 * shiftwidth() + 2            endif          endif          let [ret, res] = s:BeginElementFound(stack, token, curr_vcol, -                                            \stored_vcol, 'end', &sw) +                                            \stored_vcol, 'end', shiftwidth())          if ret | return res | endif        elseif token ==# 'fun' @@ -985,7 +987,7 @@ function! s:ErlangCalcIndent2(lnum, stack)            " stack = ['when']  =>  LTI is in a guard            if empty(stack)              call s:Log('    LTI is in a condition; matching "fun" found') -            let stored_vcol = curr_vcol + &sw +            let stored_vcol = curr_vcol + shiftwidth()            elseif len(stack) > 1 && stack[0] ==# '->' && stack[1] ==# ';'              call s:Log('    LTI is in a condition; matching "fun" found')              call s:Pop(stack) @@ -993,15 +995,15 @@ function! s:ErlangCalcIndent2(lnum, stack)            elseif stack[0] ==# '->'              call s:Log('    LTI is in a branch; matching "fun" found')              call s:Pop(stack) -            let stored_vcol = curr_vcol + 2 * &sw +            let stored_vcol = curr_vcol + 2 * shiftwidth()            elseif stack[0] ==# 'when'              call s:Log('    LTI is in a guard; matching "fun" found')              call s:Pop(stack) -            let stored_vcol = curr_vcol + 2 * &sw + 2 +            let stored_vcol = curr_vcol + 2 * shiftwidth() + 2            endif            let [ret, res] = s:BeginElementFound(stack, token, curr_vcol, -                                              \stored_vcol, 'end', &sw) +                                              \stored_vcol, 'end', shiftwidth())            if ret | return res | endif          else            " Pass: we have a function reference (e.g. "fun f/0") @@ -1275,7 +1277,7 @@ function! s:ErlangCalcIndent2(lnum, stack)              "   when A,              "        LTI              let [ret, res] = s:BeginElementFoundIfEmpty(stack, token, curr_vcol, -                                                       \stored_vcol, &sw) +                                                       \stored_vcol, shiftwidth())              if ret | return res | endif            else              " Example: @@ -1307,7 +1309,7 @@ function! s:ErlangCalcIndent2(lnum, stack)            " If LTI is between an 'after' and the corresponding            " 'end', then let's return            let [ret, res] = s:BeginElementFoundIfEmpty(stack, token, curr_vcol, -                                                     \stored_vcol, &sw) +                                                     \stored_vcol, shiftwidth())            if ret | return res | endif          endif diff --git a/indent/eruby.vim b/indent/eruby.vim index 3736f529..5b21ab91 100644 --- a/indent/eruby.vim +++ b/indent/eruby.vim @@ -97,6 +97,7 @@ function! GetErubyIndent(...)      let ind = ind + sw    endif    if line !~# '^\s*<%' && line =~# '%>\s*$' && line !~# '^\s*end\>' +	\ && synID(v:lnum, match(cline, '\S') + 1, 1) != hlID('htmlEndTag')      let ind = ind - sw    endif    if cline =~# '^\s*[-=]\=%>\s*$' diff --git a/indent/haskell.vim b/indent/haskell.vim index b1e1bdc4..43dc97a6 100644 --- a/indent/haskell.vim +++ b/indent/haskell.vim @@ -13,12 +13,16 @@ if exists('b:did_indent')    finish  endif -let b:did_indent = 1 -  if !exists('g:haskell_indent_disable')      let g:haskell_indent_disable = 0  endif +if g:haskell_indent_disable != 0 +  finish +endif + +let b:did_indent = 1 +  if !exists('g:haskell_indent_if')    " if x    " >>>then ... @@ -63,22 +67,16 @@ if !exists('g:haskell_indent_guard')    let g:haskell_indent_guard = 2  endif -if exists("g:haskell_indent_disable") && g:haskell_indent_disable == 0 -    setlocal indentexpr=GetHaskellIndent() -    setlocal indentkeys=0{,0},0(,0),0[,0],!^F,o,O,0\=,0=where,0=let,0=deriving,<space> -else -    setlocal nocindent -    setlocal nosmartindent -    setlocal autoindent -endif +setlocal indentexpr=GetHaskellIndent() +setlocal indentkeys=0},0),0],!^F,o,O,0\=,0=where,0=let,0=deriving,<space>  function! s:isInBlock(hlstack) -  return index(a:hlstack, 'haskellParens') > -1 || index(a:hlstack, 'haskellBrackets') > -1 || index(a:hlstack, 'haskellBlock') > -1 || index(a:hlstack, 'haskellBlockComment') > -1 || index(a:hlstack, 'haskellPragma') > -1 +  return index(a:hlstack, 'haskellDelimiter') > -1 || index(a:hlstack, 'haskellParens') > -1 || index(a:hlstack, 'haskellBrackets') > -1 || index(a:hlstack, 'haskellBlock') > -1 || index(a:hlstack, 'haskellBlockComment') > -1 || index(a:hlstack, 'haskellPragma') > -1  endfunction -function! s:stripTrailingComment(line) -  if a:line =~ '^\s*--\(-\+\|\s\+\)' || a:line =~ '^\s*{-' -    return a:line +function! s:stripComment(line) +  if a:line =~ '^\s*--\(-*\s\+\|$\)' +    return ''    else      let l:stripped = split(a:line, '-- ')      if len(l:stripped) > 1 @@ -144,7 +142,7 @@ function! GetHaskellIndent()      return -1    endif -  let l:prevline = s:stripTrailingComment(getline(v:lnum - 1)) +  let l:prevline = s:stripComment(getline(v:lnum - 1))    let l:line     = getline(v:lnum)    " indent multiline strings @@ -161,13 +159,14 @@ function! GetHaskellIndent()      return 0    endif -  " comment indentation -  if l:line =~ '^\s*--' -    return match(l:prevline, '-- ') -  endif -  if l:prevline =~ '^\s*--' -    return match(l:prevline, '\S') -  endif +  " " comment indentation +  " if l:line =~ '^\s*--' +  "   let l:s = match(l:prevline, '-- ') +  "   if l:s > -1 +  " endif +  " " if l:prevline =~ '^\s*--' +  " "   return match(l:prevline, '\S') +  " " endif    "   { foo :: Int    " >>, @@ -176,7 +175,7 @@ function! GetHaskellIndent()    "   ...    " >>,    if l:line =~ '^\s*,' -    if s:isInBlock(l:hlstack) +    if s:isInBlock(s:getHLStack(line('.'), col('.')))        normal! 0        call search(',', 'cW')        let l:n = s:getNesting(s:getHLStack(line('.'), col('.'))) @@ -395,7 +394,7 @@ function! GetHaskellIndent()    " foo    " >>{ -  if l:line =~ '^\s*{' && l:prevline !~ '^{' +  if l:line =~ '^\s*{ '      let l:s = match(l:prevline, '\S')      if l:s >= 0        return l:s + &shiftwidth @@ -477,16 +476,7 @@ function! GetHaskellIndent()      return s:indentMatching(']')    endif -  " do not reindent indented lines -  if match(l:prevline, '\S') < match(l:line, '\S') -    return -1 -  endif - -  if l:line !~ '^\s*[=-]>\s' && l:line =~ '^\s*[!#$%&*+./<>?@\\^|~-]\+' -    return -1 -  endif - -  return match(l:prevline, '\S') +  return -1  endfunction  endif diff --git a/indent/javascript.vim b/indent/javascript.vim index e5fc7c9f..2237b4b9 100644 --- a/indent/javascript.vim +++ b/indent/javascript.vim @@ -4,7 +4,7 @@ if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'javascript') ==  " Language: Javascript  " Maintainer: Chris Paul ( https://github.com/bounceme )  " URL: https://github.com/pangloss/vim-javascript -" Last Change: January 24, 2017 +" Last Change: March 9, 2017  " Only load this indent file when no other was loaded.  if exists('b:did_indent') @@ -16,6 +16,10 @@ let b:did_indent = 1  setlocal indentexpr=GetJavascriptIndent()  setlocal autoindent nolisp nosmartindent  setlocal indentkeys+=0],0) +" Testable with something like: +" vim  -eNs "+filetype plugin indent on" "+syntax on" "+set ft=javascript" \ +"       "+norm! gg=G" '+%print' '+:q!' testfile.js \ +"       | diff -uBZ testfile.js -  let b:undo_indent = 'setlocal indentexpr< smartindent< autoindent< indentkeys<' @@ -34,10 +38,14 @@ if exists('*shiftwidth')    endfunction  else    function s:sw() -    return &sw +    return &l:shiftwidth == 0 ? &l:tabstop : &l:shiftwidth    endfunction  endif +" Performance for forwards search(): start search at pos rather than masking +" matches before pos. +let s:z = has('patch-7.4.984') ? 'z' : '' +  " searchpair() wrapper  if has('reltime')    function s:GetPair(start,end,flags,skip,time,...) @@ -51,34 +59,40 @@ endif  " 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' +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('.'),0),'name') =~? '".s:syng_strcom."'" +function s:parse_cino(f) abort +  return float2nr(eval(substitute(substitute(join(split( +        \ matchstr(&cino,'\C.*'.a:f.'\zs[^,]*'), 's',1), '*'.s:W) +        \ , '^-\=\zs\*','',''), '^-\=\zs\.','0.',''))) +endfunction +  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 +  if getline('.') =~ '\%<'.col('.').'c\/.\{-}\/\|\%>'.col('.').'c[''"]\|\\$' +    return eval(s:skip_expr) +  elseif s:checkIn || search('\m`\|\${\|\*\/','nW'.s:z,s:looksyn) +    let s:checkIn = eval(s:skip_expr)    endif    let s:looksyn = line('.') -  return getline('.') =~ '\%<'.col('.').'c\/.\{-}\/\|\%>'.col('.').'c[''"]\|\\$' && -        \ eval(s:skip_expr) +  return s:checkIn  endfunction  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 +  let pat = '[][(){};]' +  while search('\m'.pat,'bW',a:stop) +    if s:skip_func() | continue | endif +    let idx = stridx('])};',s:looking_at()) +    if idx is 3 | let pat = '[{}()]' | continue | endif +    if idx + 1 +      if s:GetPair(['\[','(','{'][idx], '])}'[idx],'bW','s:skip_func()',2000,a:stop) <= 0 +        break        endif +    else +      return      endif    endwhile    call call('cursor',pos) @@ -104,14 +118,12 @@ function s:token()  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 && +  let l:pos = getpos('.')[1:2] +  if search('\m\k\{1,}\|\S','ebW') +    if (getline('.')[col('.')-2:col('.')-1] == '*/' || line('.') != l:pos[0] &&            \ 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 +      while search('\m\S\ze\_s*\/[/*]','bW') +        if s:syn_at(line('.'),col('.')) !~? s:syng_com            return s:token()          endif        endwhile @@ -119,73 +131,77 @@ function s:previous_token()        return s:token()      endif    endif +  call call('cursor',l:pos)    return ''  endfunction -function s:others(p) -  return "((line2byte(line('.')) + col('.')) <= ".(line2byte(a:p[0]) + a:p[1]).") || ".s:skip_expr -endfunction - -function s:tern_skip(p) -  return s:GetPair('{','}','nbW',s:others(a:p),200,a:p[0]) > 0 -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 -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]) +function s:expr_col() +  if getline('.')[col('.')-2] == ':' +    return 1    endif +  let bal = 0 +  while search('\m[{}?:;]','bW') +    if eval(s:skip_expr) | continue | endif +    " switch (looking_at()) +    exe {   '}': "if s:GetPair('{','}','bW',s:skip_expr,200) <= 0 | return | endif", +          \ ';': "return", +          \ '{': "return getpos('.')[1:2] != b:js_cache[1:] && !s:IsBlock()", +          \ ':': "let bal -= getline('.')[max([col('.')-2,0]):col('.')] !~ '::'", +          \ '?': "let bal += 1 | if bal > 0 | return 1 | endif" }[s:looking_at()] +  endwhile  endfunction  " configurable regexes that define continuation lines, not including (, {, or [.  let s:opfirst = '^' . get(g:,'javascript_opfirst', -      \ '\%([<>=,?^%|*/&]\|\([-.:+]\)\1\@!\|!=\|in\%(stanceof\)\=\>\)') +      \ '\C\%([<>=,?^%|*/&]\|\([-.:+]\)\1\@!\|!=\|in\%(stanceof\)\=\>\)')  let s:continuation = get(g:,'javascript_continuation', -      \ '\%([-+<>=,.~!?/*^%|&:]\|\<\%(typeof\|delete\|void\|in\|instanceof\)\)') . '$' +      \ '\C\%([-+<>=,.~!?/*^%|&:]\|\<\%(typeof\|new\|delete\|void\|in\|instanceof\|await\)\)') . '$'  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())]) +  if !cursor(a:ln, match(' '.a:con,s:continuation)) +    let teol = s:looking_at() +    if teol == '/' +      return s:syn_at(line('.'),col('.')) !~? 'regex' +    elseif teol =~ '[-+>]' +      return getline('.')[col('.')-2] != tr(teol,'>','=') +    elseif teol =~ '\l' +      return s:previous_token() != '.' +    elseif teol == ':' +      return s:expr_col() +    endif +    return 1 +  endif  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('.')) +  let pline = substitute(getline(a:ln),'\s*$','','') +  let l:max = max([strridx(pline,'//'), strridx(pline,'/*')]) +  while l:max != -1 && s:syn_at(a:ln, strlen(pline)) =~? s:syng_com +    let pline = pline[: l:max] +    let l:max = max([strridx(pline,'//'), strridx(pline,'/*')]) +    let pline = substitute(pline[:-2],'\s*$','','') +  endwhile +  return pline is '' || cursor(a:ln,strlen(pline)) ? pline : pline  endfunction  " Find line above 'lnum' that isn't empty or in a comment  function s:PrevCodeLine(lnum) -  let l:n = prevnonblank(a:lnum) +  let [l:pos, l:n] = [getpos('.')[1:2], 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 +    if getline(l:n) =~ '^\s*\/[/*]'        let l:n = prevnonblank(l:n-1) -    elseif s:syn_at(l:n,1) =~? s:syng_com -      let l:n = s:save_pos('eval', -            \ 'cursor('.l:n.',1) + search(''\m\/\*'',"bW")') +    elseif stridx(getline(l:n), '*/') + 1 && s:syn_at(l:n,1) =~? s:syng_com +      call cursor(l:n,1) +      keepjumps norm! [* +      let l:n = search('\m\S','nbW')      else -      return l:n +      break      endif    endwhile +  call call('cursor',l:pos) +  return l:n  endfunction  " Check if line 'lnum' has a balanced amount of parentheses. @@ -200,7 +216,9 @@ function s:Balanced(lnum)          return        endif      endif -    let pos = match(l:line, '[][(){}]', pos + 1) +    let pos = match(l:line, (l:open ? +          \ '['.escape(tr(l:line[pos],'({[]})',')}][{(').l:line[pos],']').']' : +          \ '[][(){}]'), pos + 1)    endwhile    return !l:open  endfunction @@ -209,11 +227,11 @@ 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 +    if s:previous_token() =~# '^\%(await\|each\)$'        call s:previous_token()        let kw = 'for' +    else +      let kw = 'for if let while with'      endif    endif    return pline[-2:] == '=>' || index(split(kw),s:token()) + 1 && @@ -245,18 +263,22 @@ function s:IsBlock()    if s:looking_at() == '{'      let l:n = line('.')      let char = s:previous_token() -    let syn = char =~ '[{>/]' ? s:syn_at(line('.'),col('.')-(char == '{')) : '' -    if syn =~? 'xml\|jsx' +    if match(s:stack,'\cxml\|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() == '.' +      if char ==# 'type' +        return s:previous_token() !~# '^\%(im\|ex\)port$' +      endif +      return index(split('return const let import export extends yield default delete var await void typeof throw case new of in instanceof') +            \ ,char) < (line('.') != l:n) || s:save_pos('s:previous_token') == '.'      elseif char == '>' -      return getline('.')[col('.')-2] == '=' || syn =~? '^jsflow' +      return getline('.')[col('.')-2] == '=' || s:syn_at(line('.'),col('.')) =~? '^jsflow'      elseif char == ':' -      return getline('.')[col('.')-2] != ':' && s:label_col() +      return !s:save_pos('s:expr_col') +    elseif char == '/' +      return s:syn_at(line('.'),col('.')) =~? 'regex'      endif -    return syn =~? 'regex' || char !~ '[=~!<*,/?^%|&([]' && +    return char !~ '[=~!<*,?^%|&([]' &&            \ (char !~ '[-+]' || l:n != line('.') && getline('.')[col('.')-2] == char)    endif  endfunction @@ -266,7 +288,9 @@ function GetJavascriptIndent()    " Get the current line.    call cursor(v:lnum,1)    let l:line = getline('.') -  let syns = s:syn_at(v:lnum, 1) +  " use synstack as it validates syn state and works in an empty line +  let s:stack = map(synstack(v:lnum,1),"synIDattr(v:val,'name')") +  let syns = get(s:stack,-1,'')    " start with strings,comments,etc.    if syns =~? s:syng_com @@ -275,7 +299,7 @@ function GetJavascriptIndent()      elseif l:line !~ '^\s*\/[/*]'        return -1      endif -  elseif syns =~? s:syng_str && l:line !~ '^[''"]' +  elseif syns =~? s:syng_str      if b:js_cache[0] == v:lnum - 1 && s:Balanced(v:lnum-1)        let b:js_cache[0] = v:lnum      endif @@ -300,11 +324,11 @@ function GetJavascriptIndent()          \ (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) && +    let [s:looksyn, s:checkIn, top] = [v:lnum - 1, 0, (!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 indent(v:lnum) && syns =~? 'block' +      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) @@ -324,31 +348,27 @@ function GetJavascriptIndent()          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)]) +          let switch_offset = max([-indent(num),s:parse_cino(':')])          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 +    if idx < 0 && pline[-1:] !~ '[{;]' +      let isOp = (l:line =~# s:opfirst || s:continues(l:lnum,pline)) * s:W        let bL = s:iscontOne(l:lnum,b:js_cache[1],isOp)        let bL -= (bL && l:line[0] == '{') * s:W      endif +  elseif idx < 0 && getline(b:js_cache[1])[b:js_cache[2]-1] == '(' && &cino =~ '(' +    let pval = s:parse_cino('(') +    return !pval ? (s:parse_cino('w') ? 0 : -(!!search('\m\S','W'.s:z,num))) + virtcol('.') : +          \ max([indent('.') + pval + (s:GetPair('(',')','nbrmW',s:skip_expr,100,num) * s:W),0])    endif    " main return -  if idx + 1 || l:line[:1] == '|}' -    return indent(num) +  if l:line =~ '^\%([])}]\||}\)' +    return max([indent(num),0])    elseif num      return indent(num) + s:W + switch_offset + bL + isOp    endif diff --git a/indent/litcoffee.vim b/indent/litcoffee.vim new file mode 100644 index 00000000..55398462 --- /dev/null +++ b/indent/litcoffee.vim @@ -0,0 +1,26 @@ +if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'coffee-script') == -1 +   +if exists('b:did_indent') +  finish +endif + +runtime! indent/coffee.vim + +let b:did_indent = 1 + +setlocal indentexpr=GetLitCoffeeIndent() + +if exists('*GetLitCoffeeIndent') +  finish +endif + +function GetLitCoffeeIndent() +  if searchpair('^    \|\t', '', '$', 'bWnm') > 0 +    return GetCoffeeIndent(v:lnum) +  else +    return -1 +  endif +endfunc + + +endif diff --git a/indent/ruby.vim b/indent/ruby.vim index 84067fe9..8fa929be 100644 --- a/indent/ruby.vim +++ b/indent/ruby.vim @@ -20,6 +20,11 @@ if !exists('g:ruby_indent_access_modifier_style')    let g:ruby_indent_access_modifier_style = 'normal'  endif +if !exists('g:ruby_indent_assignment_style') +  " Possible values: "variable", "hanging" +  let g:ruby_indent_assignment_style = 'hanging' +endif +  if !exists('g:ruby_indent_block_style')    " Possible values: "expression", "do"    let g:ruby_indent_block_style = 'expression' @@ -144,7 +149,507 @@ let s:block_continuation_regex = '^\s*[^])}\t ].*'.s:block_regex  " Regex that describes a leading operator (only a method call's dot for now)  let s:leading_operator_regex = '^\s*[.]' -" 2. Auxiliary Functions {{{1 +" 2. GetRubyIndent Function {{{1 +" ========================= + +function GetRubyIndent(...) +  " 2.1. Setup {{{2 +  " ---------- + +  let indent_info = {} + +  " The value of a single shift-width +  if exists('*shiftwidth') +    let indent_info.sw = shiftwidth() +  else +    let indent_info.sw = &sw +  endif + +  " For the current line, use the first argument if given, else v:lnum +  let indent_info.clnum = a:0 ? a:1 : v:lnum +  let indent_info.cline = getline(indent_info.clnum) + +  " Set up variables for restoring position in file.  Could use clnum here. +  let indent_info.col = col('.') + +  " 2.2. Work on the current line {{{2 +  " ----------------------------- +  let indent_callback_names = [ +        \ 's:AccessModifier', +        \ 's:ClosingBracketOnEmptyLine', +        \ 's:BlockComment', +        \ 's:DeindentingKeyword', +        \ 's:MultilineStringOrLineComment', +        \ 's:ClosingHeredocDelimiter', +        \ 's:LeadingOperator', +        \ ] + +  for callback_name in indent_callback_names +"    Decho "Running: ".callback_name +    let indent = call(function(callback_name), [indent_info]) + +    if indent >= 0 +"      Decho "Match: ".callback_name." indent=".indent." info=".string(indent_info) +      return indent +    endif +  endfor + +  " 2.3. Work on the previous line. {{{2 +  " ------------------------------- +  let indent_callback_names = [ +        \ 's:EmptyInsideString', +        \ 's:StartOfFile', +        \ 's:AfterAccessModifier', +        \ 's:ContinuedLine', +        \ 's:AfterBlockOpening', +        \ 's:AfterHangingSplat', +        \ 's:AfterUnbalancedBracket', +        \ 's:AfterLeadingOperator', +        \ 's:AfterEndKeyword', +        \ 's:AfterIndentKeyword', +        \ ] + +  " Previous line number +  let indent_info.plnum = s:PrevNonBlankNonString(indent_info.clnum - 1) +  let indent_info.pline = getline(indent_info.plnum) + +  for callback_name in indent_callback_names +"    Decho "Running: ".callback_name +    let indent = call(function(callback_name), [indent_info]) + +    if indent >= 0 +"      Decho "Match: ".callback_name." indent=".indent." info=".string(indent_info) +      return indent +    endif +  endfor + +  " 2.4. Work on the MSL line. {{{2 +  " -------------------------- +  let indent_callback_names = [ +        \ 's:PreviousNotMSL', +        \ 's:IndentingKeywordInMSL', +        \ 's:ContinuedHangingOperator', +        \ ] + +  " Most Significant line based on the previous one -- in case it's a +  " contination of something above +  let indent_info.plnum_msl = s:GetMSL(indent_info.plnum) + +  for callback_name in indent_callback_names +"    Decho "Running: ".callback_name +    let indent = call(function(callback_name), [indent_info]) + +    if indent >= 0 +"      Decho "Match: ".callback_name." indent=".indent." info=".string(indent_info) +      return indent +    endif +  endfor + +  " }}}2 + +  " By default, just return the previous line's indent +"  Decho "Default case matched" +  return indent(indent_info.plnum) +endfunction + +" 3. Indenting Logic Callbacks {{{1 +" ============================ + +function! s:AccessModifier(cline_info) +  let info = a:cline_info + +  " If this line is an access modifier keyword, align according to the closest +  " class declaration. +  if g:ruby_indent_access_modifier_style == 'indent' +    if s:Match(info.clnum, s:access_modifier_regex) +      let class_lnum = s:FindContainingClass() +      if class_lnum > 0 +        return indent(class_lnum) + info.sw +      endif +    endif +  elseif g:ruby_indent_access_modifier_style == 'outdent' +    if s:Match(info.clnum, s:access_modifier_regex) +      let class_lnum = s:FindContainingClass() +      if class_lnum > 0 +        return indent(class_lnum) +      endif +    endif +  endif + +  return -1 +endfunction + +function! s:ClosingBracketOnEmptyLine(cline_info) +  let info = a:cline_info + +  " 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(info.cline, '^\s*[]})]') + +  if col > 0 && !s:IsInStringOrComment(info.clnum, col) +    call cursor(info.clnum, col) +    let closing_bracket = info.cline[col - 1] +    let bracket_pair = strpart('(){}[]', stridx(')}]', closing_bracket) * 2, 2) + +    if searchpair(escape(bracket_pair[0], '\['), '', bracket_pair[1], 'bW', s:skip_expr) > 0 +      if closing_bracket == ')' && col('.') != col('$') - 1 +        let ind = virtcol('.') - 1 +      elseif g:ruby_indent_block_style == 'do' +        let ind = indent(line('.')) +      else " g:ruby_indent_block_style == 'expression' +        let ind = indent(s:GetMSL(line('.'))) +      endif +    endif + +    return ind +  endif + +  return -1 +endfunction + +function! s:BlockComment(cline_info) +  " If we have a =begin or =end set indent to first column. +  if match(a:cline_info.cline, '^\s*\%(=begin\|=end\)$') != -1 +    return 0 +  endif +  return -1 +endfunction + +function! s:DeindentingKeyword(cline_info) +  let info = a:cline_info + +  " If we have a deindenting keyword, find its match and indent to its level. +  " TODO: this is messy +  if s:Match(info.clnum, s:ruby_deindent_keywords) +    call cursor(info.clnum, 1) + +    if searchpair(s:end_start_regex, s:end_middle_regex, s:end_end_regex, 'bW', +          \ s:end_skip_expr) > 0 +      let msl  = s:GetMSL(line('.')) +      let line = getline(line('.')) + +      if s:IsAssignment(line, col('.')) && +            \ strpart(line, col('.') - 1, 2) !~ 'do' +        " assignment to case/begin/etc, on the same line +        if g:ruby_indent_assignment_style == 'hanging' +          " hanging indent +          let ind = virtcol('.') - 1 +        else +          " align with variable +          let ind = indent(line('.')) +        endif +      elseif g:ruby_indent_block_style == 'do' +        " align to line of the "do", not to the MSL +        let ind = indent(line('.')) +      elseif getline(msl) =~ '=\s*\(#.*\)\=$' +        " in the case of assignment to the MSL, align to the starting line, +        " not to the MSL +        let ind = indent(line('.')) +      else +        " align to the MSL +        let ind = indent(msl) +      endif +    endif +    return ind +  endif + +  return -1 +endfunction + +function! s:MultilineStringOrLineComment(cline_info) +  let info = a:cline_info + +  " If we are in a multi-line string or line-comment, don't do anything to it. +  if s:IsInStringOrDocumentation(info.clnum, matchend(info.cline, '^\s*') + 1) +    return indent(info.clnum) +  endif +  return -1 +endfunction + +function! s:ClosingHeredocDelimiter(cline_info) +  let info = a:cline_info + +  " If we are at the closing delimiter of a "<<" heredoc-style string, set the +  " indent to 0. +  if info.cline =~ '^\k\+\s*$' +        \ && s:IsInStringDelimiter(info.clnum, 1) +        \ && search('\V<<'.info.cline, 'nbW') > 0 +    return 0 +  endif + +  return -1 +endfunction + +function! s:LeadingOperator(cline_info) +  " If the current line starts with a leading operator, add a level of indent. +  if s:Match(a:cline_info.clnum, s:leading_operator_regex) +    return indent(s:GetMSL(a:cline_info.clnum)) + a:cline_info.sw +  endif +  return -1 +endfunction + +function! s:EmptyInsideString(pline_info) +  " If the line is empty and inside a string (plnum would not be the real +  " prevnonblank in that case), use the previous line's indent +  let info = a:pline_info + +  if info.cline =~ '^\s*$' && info.plnum != prevnonblank(info.clnum - 1) +    return indent(prevnonblank(info.clnum)) +  endif +  return -1 +endfunction + +function! s:StartOfFile(pline_info) +  " At the start of the file use zero indent. +  if a:pline_info.plnum == 0 +    return 0 +  endif +  return -1 +endfunction + +function! s:AfterAccessModifier(pline_info) +  let info = a:pline_info + +  if g:ruby_indent_access_modifier_style == 'indent' +    " If the previous line was a private/protected keyword, add a +    " level of indent. +    if s:Match(info.plnum, s:indent_access_modifier_regex) +      return indent(info.plnum) + info.sw +    endif +  elseif g:ruby_indent_access_modifier_style == 'outdent' +    " If the previous line was a private/protected/public keyword, add +    " a level of indent, since the keyword has been out-dented. +    if s:Match(info.plnum, s:access_modifier_regex) +      return indent(info.plnum) + info.sw +    endif +  endif +  return -1 +endfunction + +" Example: +" +"   if foo || bar || +"       baz || bing +"     puts "foo" +"   end +" +function! s:ContinuedLine(pline_info) +  let info = a:pline_info + +  let col = s:Match(info.plnum, s:ruby_indent_keywords) +  if s:Match(info.plnum, s:continuable_regex) && +        \ s:Match(info.plnum, s:continuation_regex) +    if col > 0 && s:IsAssignment(info.pline, col) +      if g:ruby_indent_assignment_style == 'hanging' +        " hanging indent +        let ind = col - 1 +      else +        " align with variable +        let ind = indent(info.plnum) +      endif +    else +      let ind = indent(s:GetMSL(info.plnum)) +    endif +    return ind + info.sw + info.sw +  endif +  return -1 +endfunction + +function! s:AfterBlockOpening(pline_info) +  let info = a:pline_info + +  " If the previous line ended with a block opening, add a level of indent. +  if s:Match(info.plnum, s:block_regex) +    if g:ruby_indent_block_style == 'do' +      " don't align to the msl, align to the "do" +      let ind = indent(info.plnum) + info.sw +    else +      let plnum_msl = s:GetMSL(info.plnum) + +      if getline(plnum_msl) =~ '=\s*\(#.*\)\=$' +        " in the case of assignment to the msl, align to the starting line, +        " not to the msl +        let ind = indent(info.plnum) + info.sw +      else +        let ind = indent(plnum_msl) + info.sw +      endif +    endif + +    return ind +  endif + +  return -1 +endfunction + +function! s:AfterLeadingOperator(pline_info) +  " If the previous line started with a leading operator, use its MSL's level +  " of indent +  if s:Match(a:pline_info.plnum, s:leading_operator_regex) +    return indent(s:GetMSL(a:pline_info.plnum)) +  endif +  return -1 +endfunction + +function! s:AfterHangingSplat(pline_info) +  let info = a:pline_info + +  " If the previous line ended with the "*" of a splat, add a level of indent +  if info.pline =~ s:splat_regex +    return indent(info.plnum) + info.sw +  endif +  return -1 +endfunction + +function! s:AfterUnbalancedBracket(pline_info) +  let info = a:pline_info + +  " If the previous line contained unclosed opening brackets and we are still +  " in them, find the rightmost one and add indent depending on the bracket +  " type. +  " +  " If it contained hanging closing brackets, find the rightmost one, find its +  " match and indent according to that. +  if info.pline =~ '[[({]' || info.pline =~ '[])}]\s*\%(#.*\)\=$' +    let [opening, closing] = s:ExtraBrackets(info.plnum) + +    if opening.pos != -1 +      if opening.type == '(' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0 +        if col('.') + 1 == col('$') +          return indent(info.plnum) + info.sw +        else +          return virtcol('.') +        endif +      else +        let nonspace = matchend(info.pline, '\S', opening.pos + 1) - 1 +        return nonspace > 0 ? nonspace : indent(info.plnum) + info.sw +      endif +    elseif closing.pos != -1 +      call cursor(info.plnum, closing.pos + 1) +      normal! % + +      if s:Match(line('.'), s:ruby_indent_keywords) +        return indent('.') + info.sw +      else +        return indent(s:GetMSL(line('.'))) +      endif +    else +      call cursor(info.clnum, info.col) +    end +  endif + +  return -1 +endfunction + +function! s:AfterEndKeyword(pline_info) +  let info = a:pline_info +  " If the previous line ended with an "end", match that "end"s beginning's +  " indent. +  let col = s:Match(info.plnum, '\%(^\|[^.:@$]\)\<end\>\s*\%(#.*\)\=$') +  if col > 0 +    call cursor(info.plnum, col) +    if searchpair(s:end_start_regex, '', s:end_end_regex, 'bW', +          \ s:end_skip_expr) > 0 +      let n = line('.') +      let ind = indent('.') +      let msl = s:GetMSL(n) +      if msl != n +        let ind = indent(msl) +      end +      return ind +    endif +  end +  return -1 +endfunction + +function! s:AfterIndentKeyword(pline_info) +  let info = a:pline_info +  let col = s:Match(info.plnum, s:ruby_indent_keywords) + +  if col > 0 +    call cursor(info.plnum, col) +    let ind = virtcol('.') - 1 + info.sw +    " TODO: make this better (we need to count them) (or, if a searchpair +    " fails, we know that something is lacking an end and thus we indent a +    " level +    if s:Match(info.plnum, s:end_end_regex) +      let ind = indent('.') +    elseif s:IsAssignment(info.pline, col) +      if g:ruby_indent_assignment_style == 'hanging' +        " hanging indent +        let ind = col + info.sw - 1 +      else +        " align with variable +        let ind = indent(info.plnum) + info.sw +      endif +    endif +    return ind +  endif + +  return -1 +endfunction + +function! s:PreviousNotMSL(msl_info) +  let info = a:msl_info + +  " If the previous line wasn't a MSL +  if info.plnum != info.plnum_msl +    " If previous line ends bracket and begins non-bracket continuation decrease indent by 1. +    if s:Match(info.plnum, s:bracket_switch_continuation_regex) +      " TODO (2016-10-07) Wrong/unused? How could it be "1"? +      return indent(info.plnum) - 1 +      " If previous line is a continuation return its indent. +      " TODO: the || s:IsInString() thing worries me a bit. +    elseif s:Match(info.plnum, s:non_bracket_continuation_regex) || s:IsInString(info.plnum, strlen(line)) +      return indent(info.plnum) +    endif +  endif + +  return -1 +endfunction + +function! s:IndentingKeywordInMSL(msl_info) +  let info = a:msl_info +  " If the MSL line had an indenting keyword in it, add a level of indent. +  " TODO: this does not take into account contrived things such as +  " module Foo; class Bar; end +  let col = s:Match(info.plnum_msl, s:ruby_indent_keywords) +  if col > 0 +    let ind = indent(info.plnum_msl) + info.sw +    if s:Match(info.plnum_msl, s:end_end_regex) +      let ind = ind - info.sw +    elseif s:IsAssignment(getline(info.plnum_msl), col) +      if g:ruby_indent_assignment_style == 'hanging' +        " hanging indent +        let ind = col + info.sw - 1 +      else +        " align with variable +        let ind = indent(info.plnum_msl) + info.sw +      endif +    endif +    return ind +  endif +  return -1 +endfunction + +function! s:ContinuedHangingOperator(msl_info) +  let info = a:msl_info + +  " If the previous line ended with [*+/.,-=], but wasn't a block ending or a +  " closing bracket, indent one extra level. +  if s:Match(info.plnum_msl, s:non_bracket_continuation_regex) && !s:Match(info.plnum_msl, '^\s*\([\])}]\|end\)') +    if info.plnum_msl == info.plnum +      let ind = indent(info.plnum_msl) + info.sw +    else +      let ind = indent(info.plnum_msl) +    endif +    return ind +  endif + +  return -1 +endfunction + +" 4. Auxiliary Functions {{{1  " ======================  " Check if the character at lnum:col is inside a string, comment, or is ascii. @@ -167,6 +672,10 @@ function s:IsInStringDelimiter(lnum, col)    return synIDattr(synID(a:lnum, a:col, 1), 'name') == 'rubyStringDelimiter'  endfunction +function s:IsAssignment(str, pos) +  return strpart(a:str, 0, a:pos - 1) =~ '=\s*$' +endfunction +  " Find line above 'lnum' that isn't empty, in a comment, or in a string.  function s:PrevNonBlankNonString(lnum)    let in_block = 0 @@ -196,7 +705,6 @@ endfunction  function s:GetMSL(lnum)    " Start on the line we're at and use its indent.    let msl = a:lnum -  let msl_body = getline(msl)    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. @@ -293,7 +801,6 @@ function s:GetMSL(lnum)        endif      endif -    let msl_body = getline(msl)      let lnum = s:PrevNonBlankNonString(lnum - 1)    endwhile    return msl @@ -398,301 +905,6 @@ function! s:FindContainingClass()    return 0  endfunction -" 3. GetRubyIndent Function {{{1 -" ========================= - -function GetRubyIndent(...) -  " 3.1. Setup {{{2 -  " ---------- - -  " The value of a single shift-width -  if exists('*shiftwidth') -    let sw = shiftwidth() -  else -    let sw = &sw -  endif - -  " For the current line, use the first argument if given, else v:lnum -  let clnum = a:0 ? a:1 : v:lnum - -  " Set up variables for restoring position in file.  Could use clnum here. -  let vcol = col('.') - -  " 3.2. Work on the current line {{{2 -  " ----------------------------- - -  " Get the current line. -  let line = getline(clnum) -  let ind = -1 - -  " If this line is an access modifier keyword, align according to the closest -  " class declaration. -  if g:ruby_indent_access_modifier_style == 'indent' -    if s:Match(clnum, s:access_modifier_regex) -      let class_line = s:FindContainingClass() -      if class_line > 0 -        return indent(class_line) + sw -      endif -    endif -  elseif g:ruby_indent_access_modifier_style == 'outdent' -    if s:Match(clnum, s:access_modifier_regex) -      let class_line = s:FindContainingClass() -      if class_line > 0 -        return indent(class_line) -      endif -    endif -  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*[]})]') -  if col > 0 && !s:IsInStringOrComment(clnum, col) -    call cursor(clnum, col) -    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 -      elseif g:ruby_indent_block_style == 'do' -        let ind = indent(line('.')) -      else " g:ruby_indent_block_style == 'expression' -        let ind = indent(s:GetMSL(line('.'))) -      endif -    endif -    return ind -  endif - -  " If we have a =begin or =end set indent to first column. -  if match(line, '^\s*\%(=begin\|=end\)$') != -1 -    return 0 -  endif - -  " If we have a deindenting keyword, find its match and indent to its level. -  " TODO: this is messy -  if s:Match(clnum, s:ruby_deindent_keywords) -    call cursor(clnum, 1) -    if searchpair(s:end_start_regex, s:end_middle_regex, s:end_end_regex, 'bW', -          \ s:end_skip_expr) > 0 -      let msl  = s:GetMSL(line('.')) -      let line = getline(line('.')) - -      if strpart(line, 0, col('.') - 1) =~ '=\s*$' && -            \ strpart(line, col('.') - 1, 2) !~ 'do' -        " assignment to case/begin/etc, on the same line, hanging indent -        let ind = virtcol('.') - 1 -      elseif g:ruby_indent_block_style == 'do' -        " align to line of the "do", not to the MSL -        let ind = indent(line('.')) -      elseif getline(msl) =~ '=\s*\(#.*\)\=$' -        " in the case of assignment to the MSL, align to the starting line, -        " not to the MSL -        let ind = indent(line('.')) -      else -        " align to the MSL -        let ind = indent(msl) -      endif -    endif -    return ind -  endif - -  " If we are in a multi-line string or line-comment, don't do anything to it. -  if s:IsInStringOrDocumentation(clnum, matchend(line, '^\s*') + 1) -    return indent('.') -  endif - -  " If we are at the closing delimiter of a "<<" heredoc-style string, set the -  " indent to 0. -  if line =~ '^\k\+\s*$' -        \ && s:IsInStringDelimiter(clnum, 1) -        \ && search('\V<<'.line, 'nbW') > 0 -    return 0 -  endif - -  " If the current line starts with a leading operator, add a level of indent. -  if s:Match(clnum, s:leading_operator_regex) -    return indent(s:GetMSL(clnum)) + sw -  endif - -  " 3.3. Work on the previous line. {{{2 -  " ------------------------------- - -  " Find a non-blank, non-multi-line string line above the current line. -  let lnum = s:PrevNonBlankNonString(clnum - 1) - -  " If the line is empty and inside a string, use the previous line. -  if line =~ '^\s*$' && lnum != prevnonblank(clnum - 1) -    return indent(prevnonblank(clnum)) -  endif - -  " At the start of the file use zero indent. -  if lnum == 0 -    return 0 -  endif - -  " Set up variables for the previous line. -  let line = getline(lnum) -  let ind = indent(lnum) - -  if g:ruby_indent_access_modifier_style == 'indent' -    " If the previous line was a private/protected keyword, add a -    " level of indent. -    if s:Match(lnum, s:indent_access_modifier_regex) -      return indent(lnum) + sw -    endif -  elseif g:ruby_indent_access_modifier_style == 'outdent' -    " If the previous line was a private/protected/public keyword, add -    " a level of indent, since the keyword has been out-dented. -    if s:Match(lnum, s:access_modifier_regex) -      return indent(lnum) + sw -    endif -  endif - -  if s:Match(lnum, s:continuable_regex) && s:Match(lnum, s:continuation_regex) -    return indent(s:GetMSL(lnum)) + sw + sw -  endif - -  " If the previous line ended with a block opening, add a level of indent. -  if s:Match(lnum, s:block_regex) -    let msl = s:GetMSL(lnum) - -    if g:ruby_indent_block_style == 'do' -      " don't align to the msl, align to the "do" -      let ind = indent(lnum) + sw -    elseif getline(msl) =~ '=\s*\(#.*\)\=$' -      " in the case of assignment to the msl, align to the starting line, -      " not to the msl -      let ind = indent(lnum) + sw -    else -      let ind = indent(msl) + sw -    endif -    return ind -  endif - -  " If the previous line started with a leading operator, use its MSL's level -  " of indent -  if s:Match(lnum, s:leading_operator_regex) -    return indent(s:GetMSL(lnum)) -  endif - -  " If the previous line ended with the "*" of a splat, add a level of indent -  if line =~ s:splat_regex -    return indent(lnum) + sw -  endif - -  " If the previous line contained unclosed opening brackets and we are still -  " in them, find the rightmost one and add indent depending on the bracket -  " type. -  " -  " If it contained hanging closing brackets, find the rightmost one, find its -  " match and indent according to that. -  if line =~ '[[({]' || line =~ '[])}]\s*\%(#.*\)\=$' -    let [opening, closing] = s:ExtraBrackets(lnum) - -    if opening.pos != -1 -      if opening.type == '(' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0 -        if col('.') + 1 == col('$') -          return ind + sw -        else -          return virtcol('.') -        endif -      else -        let nonspace = matchend(line, '\S', opening.pos + 1) - 1 -        return nonspace > 0 ? nonspace : ind + sw -      endif -    elseif closing.pos != -1 -      call cursor(lnum, closing.pos + 1) -      normal! % - -      if s:Match(line('.'), s:ruby_indent_keywords) -        return indent('.') + sw -      else -        return indent(s:GetMSL(line('.'))) -      endif -    else -      call cursor(clnum, vcol) -    end -  endif - -  " If the previous line ended with an "end", match that "end"s beginning's -  " indent. -  let col = s:Match(lnum, '\%(^\|[^.:@$]\)\<end\>\s*\%(#.*\)\=$') -  if col > 0 -    call cursor(lnum, col) -    if searchpair(s:end_start_regex, '', s:end_end_regex, 'bW', -          \ s:end_skip_expr) > 0 -      let n = line('.') -      let ind = indent('.') -      let msl = s:GetMSL(n) -      if msl != n -        let ind = indent(msl) -      end -      return ind -    endif -  end - -  let col = s:Match(lnum, s:ruby_indent_keywords) -  if col > 0 -    call cursor(lnum, col) -    let ind = virtcol('.') - 1 + sw -    " TODO: make this better (we need to count them) (or, if a searchpair -    " fails, we know that something is lacking an end and thus we indent a -    " level -    if s:Match(lnum, s:end_end_regex) -      let ind = indent('.') -    endif -    return ind -  endif - -  " 3.4. Work on the MSL line. {{{2 -  " -------------------------- - -  " Set up variables to use and search for MSL to the previous line. -  let p_lnum = lnum -  let lnum = s:GetMSL(lnum) - -  " If the previous line wasn't a MSL. -  if p_lnum != lnum -    " If previous line ends bracket and begins non-bracket continuation decrease indent by 1. -    if s:Match(p_lnum, s:bracket_switch_continuation_regex) -      return ind - 1 -    " If previous line is a continuation return its indent. -    " TODO: the || s:IsInString() thing worries me a bit. -    elseif s:Match(p_lnum, s:non_bracket_continuation_regex) || s:IsInString(p_lnum,strlen(line)) -      return ind -    endif -  endif - -  " Set up more variables, now that we know we wasn't continuation bound. -  let line = getline(lnum) -  let msl_ind = indent(lnum) - -  " If the MSL line had an indenting keyword in it, add a level of indent. -  " TODO: this does not take into account contrived things such as -  " module Foo; class Bar; end -  if s:Match(lnum, s:ruby_indent_keywords) -    let ind = msl_ind + sw -    if s:Match(lnum, s:end_end_regex) -      let ind = ind - sw -    endif -    return ind -  endif - -  " If the previous line ended with [*+/.,-=], but wasn't a block ending or a -  " closing bracket, indent one extra level. -  if s:Match(lnum, s:non_bracket_continuation_regex) && !s:Match(lnum, '^\s*\([\])}]\|end\)') -    if lnum == p_lnum -      let ind = msl_ind + sw -    else -      let ind = msl_ind -    endif -    return ind -  endif - -  " }}}2 - -  return ind -endfunction -  " }}}1  let &cpo = s:cpo_save 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 | 
