diff options
Diffstat (limited to 'runtime/indent')
-rw-r--r-- | runtime/indent/cs.vim | 74 | ||||
-rw-r--r-- | runtime/indent/eruby.vim | 10 | ||||
-rw-r--r-- | runtime/indent/falcon.vim | 2 | ||||
-rw-r--r-- | runtime/indent/html.vim | 19 | ||||
-rw-r--r-- | runtime/indent/matlab.vim | 187 | ||||
-rw-r--r-- | runtime/indent/raml.vim | 12 | ||||
-rw-r--r-- | runtime/indent/ruby.vim | 886 | ||||
-rw-r--r-- | runtime/indent/sh.vim | 30 | ||||
-rw-r--r-- | runtime/indent/tcl.vim | 50 | ||||
-rw-r--r-- | runtime/indent/testdir/html.in | 26 | ||||
-rw-r--r-- | runtime/indent/testdir/html.ok | 26 | ||||
-rw-r--r-- | runtime/indent/testdir/matlab.in | 80 | ||||
-rw-r--r-- | runtime/indent/testdir/matlab.ok | 80 | ||||
-rw-r--r-- | runtime/indent/testdir/tcl.in | 19 | ||||
-rw-r--r-- | runtime/indent/testdir/tcl.ok | 19 | ||||
-rw-r--r-- | runtime/indent/testdir/xml.in | 32 | ||||
-rw-r--r-- | runtime/indent/testdir/xml.ok | 32 | ||||
-rw-r--r-- | runtime/indent/xml.vim | 121 |
18 files changed, 1236 insertions, 469 deletions
diff --git a/runtime/indent/cs.vim b/runtime/indent/cs.vim index 4a040b6fe2..76c12efecf 100644 --- a/runtime/indent/cs.vim +++ b/runtime/indent/cs.vim @@ -1,15 +1,73 @@ " Vim indent file -" Language: C# -" Maintainer: Johannes Zellner <johannes@zellner.org> -" Last Change: Fri, 15 Mar 2002 07:53:54 CET +" Language: C# +" Maintainer: Nick Jensen <nickspoon@gmail.com> +" Former Maintainers: Aquila Deus +" Johannes Zellner <johannes@zellner.org> +" Last Change: 2018-11-21 +" Filenames: *.cs +" License: Vim (see :h license) +" Repository: https://github.com/nickspoons/vim-cs +" " Only load this indent file when no other was loaded. -if exists("b:did_indent") - finish +if exists('b:did_indent') + finish endif let b:did_indent = 1 -" C# is like indenting C -setlocal cindent +let s:save_cpo = &cpoptions +set cpoptions&vim -let b:undo_indent = "setl cin<" + +setlocal indentexpr=GetCSIndent(v:lnum) + +function! s:IsCompilerDirective(line) + return a:line =~? '^\s*#' +endf + +function! s:IsAttributeLine(line) + return a:line =~? '^\s*\[[A-Za-z]' && a:line =~? '\]$' +endf + +function! s:FindPreviousNonCompilerDirectiveLine(start_lnum) + for delta in range(0, a:start_lnum) + let lnum = a:start_lnum - delta + let line = getline(lnum) + let is_directive = s:IsCompilerDirective(line) + if !is_directive + return lnum + endif + endfor + return 0 +endf + +function! GetCSIndent(lnum) abort + " Hit the start of the file, use zero indent. + if a:lnum == 0 + return 0 + endif + + let this_line = getline(a:lnum) + + " Compiler directives use zero indent if so configured. + let is_first_col_macro = s:IsCompilerDirective(this_line) && stridx(&l:cinkeys, '0#') >= 0 + if is_first_col_macro + return cindent(a:lnum) + endif + + let lnum = s:FindPreviousNonCompilerDirectiveLine(a:lnum - 1) + let previous_code_line = getline(lnum) + if s:IsAttributeLine(previous_code_line) + let ind = indent(lnum) + return ind + else + return cindent(a:lnum) + endif +endfunction + +let b:undo_indent = 'setlocal indentexpr<' + +let &cpoptions = s:save_cpo +unlet s:save_cpo + +" vim:et:sw=2:sts=2 diff --git a/runtime/indent/eruby.vim b/runtime/indent/eruby.vim index 5058325495..6ff15ab958 100644 --- a/runtime/indent/eruby.vim +++ b/runtime/indent/eruby.vim @@ -3,6 +3,7 @@ " Maintainer: Tim Pope <vimNOSPAM@tpope.org> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2019 Jan 06 if exists("b:did_indent") finish @@ -12,7 +13,7 @@ runtime! indent/ruby.vim unlet! b:did_indent setlocal indentexpr= -if exists("b:eruby_subtype") +if exists("b:eruby_subtype") && b:eruby_subtype != '' && b:eruby_subtype !=# 'eruby' exe "runtime! indent/".b:eruby_subtype.".vim" else runtime! indent/html.vim @@ -47,7 +48,11 @@ set cpo&vim function! GetErubyIndent(...) " The value of a single shift-width - let sw = shiftwidth() + if exists('*shiftwidth') + let sw = shiftwidth() + else + let sw = &sw + endif if a:0 && a:1 == '.' let v:lnum = line('.') @@ -91,6 +96,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/runtime/indent/falcon.vim b/runtime/indent/falcon.vim index b34e7cfd47..664ad61aa5 100644 --- a/runtime/indent/falcon.vim +++ b/runtime/indent/falcon.vim @@ -368,7 +368,7 @@ function FalconGetIndent(...) return indent('.') endif else - call cursor(clnum, vcol) + call cursor(clnum, 0) " FIXME: column was vcol end endif diff --git a/runtime/indent/html.vim b/runtime/indent/html.vim index 6c866594c5..1a8177050a 100644 --- a/runtime/indent/html.vim +++ b/runtime/indent/html.vim @@ -216,8 +216,9 @@ endfunc "}}} " Add known tag pairs. " Self-closing tags and tags that are sometimes {{{ " self-closing (e.g., <p>) are not here (when encountering </p> we can find -" the matching <p>, but not the other way around). Known self-closing tags: -" 'p', 'img', 'source'. +" the matching <p>, but not the other way around). +" Known self-closing tags: " 'p', 'img', 'source', 'area', 'keygen', 'track', +" 'wbr'. " Old HTML tags: call s:AddITags(s:indent_tags, [ \ 'a', 'abbr', 'acronym', 'address', 'b', 'bdo', 'big', @@ -232,11 +233,11 @@ call s:AddITags(s:indent_tags, [ " New HTML5 elements: call s:AddITags(s:indent_tags, [ - \ 'area', 'article', 'aside', 'audio', 'bdi', 'canvas', - \ 'command', 'data', 'datalist', 'details', 'embed', 'figcaption', - \ 'figure', 'footer', 'header', 'keygen', 'main', 'mark', 'meter', - \ 'nav', 'output', 'picture', 'progress', 'rp', 'rt', 'ruby', 'section', - \ 'summary', 'svg', 'time', 'track', 'video', 'wbr']) + \ 'article', 'aside', 'audio', 'bdi', 'canvas', 'command', 'data', + \ 'datalist', 'details', 'dialog', 'embed', 'figcaption', 'figure', + \ 'footer', 'header', 'hgroup', 'main', 'mark', 'meter', 'nav', 'output', + \ 'picture', 'progress', 'rp', 'rt', 'ruby', 'section', 'summary', + \ 'svg', 'time', 'video']) " Tags added for web components: call s:AddITags(s:indent_tags, [ @@ -625,7 +626,7 @@ func! s:CSSIndent() return eval(b:hi_css1indent) endif - " If the current line starts with "}" align with it's match. + " If the current line starts with "}" align with its match. if curtext =~ '^\s*}' call cursor(v:lnum, 1) try @@ -934,7 +935,7 @@ func! s:InsideTag(foundHtmlString) let idx = match(text, '<' . s:tagname . '\s\+\zs\w') endif if idx == -1 - " after just <tag indent one level more + " after just "<tag" indent one level more let idx = match(text, '<' . s:tagname . '$') if idx >= 0 call cursor(lnum, idx) diff --git a/runtime/indent/matlab.vim b/runtime/indent/matlab.vim index 6a31624389..d2818a18ea 100644 --- a/runtime/indent/matlab.vim +++ b/runtime/indent/matlab.vim @@ -1,74 +1,121 @@ -" Matlab indent file -" Language: Matlab -" Maintainer: Christophe Poucet <christophe.poucet@pandora.be> -" Last Change: 6 January, 2001 - -" Only load this indent file when no other was loaded. -if exists("b:did_indent") - finish -endif +" Vim indent file +" Language: MATLAB +" Maintainer: Axel Forsman <axelsfor@gmail.com> +" Previous maintainer: Christophe Poucet <christophe.poucet@pandora.be> + +" Only load if no other indent file is loaded +if exists('b:did_indent') | finish | endif let b:did_indent = 1 -" Some preliminary setting -setlocal indentkeys=!,o,O=end,=case,=else,=elseif,=otherwise,=catch - - -setlocal indentexpr=GetMatlabIndent(v:lnum) - -" Only define the function once. -if exists("*GetMatlabIndent") - finish -endif - -function GetMatlabIndent(lnum) - " Give up if this line is explicitly joined. - if getline(a:lnum - 1) =~ '\\$' - return -1 - endif - - " Search backwards for the first non-empty line. - let plnum = a:lnum - 1 - while plnum > 0 && getline(plnum) =~ '^\s*$' - let plnum = plnum - 1 - endwhile - - if plnum == 0 - " This is the first non-empty line, use zero indent. - return 0 - endif - - let curind = indent(plnum) - - " If the current line is a stop-block statement... - if getline(v:lnum) =~ '^\s*\(end\|else\|elseif\|case\|otherwise\|catch\)\>' - " See if this line does not follow the line right after an openblock - if getline(plnum) =~ '^\s*\(for\|if\|else\|elseif\|case\|while\|switch\|try\|otherwise\|catch\)\>' - " See if the user has already dedented - elseif indent(v:lnum) > curind - shiftwidth() - " If not, recommend one dedent - let curind = curind - shiftwidth() - else - " Otherwise, trust the user - return -1 - endif -" endif - - " If the previous line opened a block - elseif getline(plnum) =~ '^\s*\(for\|if\|else\|elseif\|case\|while\|switch\|try\|otherwise\|catch\)\>' - " See if the user has already indented - if indent(v:lnum) < curind + shiftwidth() - "If not, recommend indent - let curind = curind + shiftwidth() - else - " Otherwise, trust the user - return -1 - endif - endif - - - - " If we got to here, it means that the user takes the standardversion, so we return it - return curind +setlocal indentexpr=GetMatlabIndent() +setlocal indentkeys=!,o,O,e,0=end,0=elseif,0=case,0=otherwise,0=catch,0=function,0=elsei + +" The value of the Function indenting format in +" MATLAB Editor/Debugger Language Preferences. +" The possible values are 0 for Classic, 1 for Indent nested functions +" and 2 for Indent all functions (default). +let b:MATLAB_function_indent = get(g:, 'MATLAB_function_indent', 2) +" The previous value of b:changedtick +let b:MATLAB_lasttick = -1 +" The previously indented line +let b:MATLAB_lastline = -1 +" Whether the line above was a line continuation +let b:MATLAB_waslc = 0 +let b:MATLAB_bracketlevel = 0 + +" Only define the function once +if exists("*GetMatlabIndent") | finish | endif + +let s:keepcpo = &cpo +set cpo&vim + +let s:end = '\<end\>\%([^(]*)\)\@!' " Array indexing heuristic +let s:open_pat = 'for\|if\|parfor\|spmd\|switch\|try\|while\|classdef\|properties\|methods\|events\|enumeration' +let s:dedent_pat = '\C^\s*\zs\<\%(end\|else\|elseif\|catch\|\(case\|otherwise\|function\)\)\>' +let s:start_pat = '\C\<\%(function\|' . s:open_pat . '\)\>' +let s:bracket_pair_pat = '\(\[\|{\)\|\(\]\|}\)' +let s:zflag = has('patch-7.4.984') ? 'z' : '' + +" Returns whether a comment or string envelops the specified column. +function! s:IsCommentOrString(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), "name") =~# 'matlabComment\|matlabMultilineComment\|matlabString' +endfunction + +" Returns whether the specified line continues on the next line. +function! s:IsLineContinuation(lnum) + let l = getline(a:lnum) | let c = -3 + while 1 + let c = match(l, '\.\{3}', c + 3) + if c == -1 | return 0 + elseif !s:IsCommentOrString(a:lnum, c) | return 1 | endif + endwhile +endfunction + +function! s:SubmatchCount(lnum, pattern, ...) + let endcol = a:0 >= 1 ? a:1 : 1 / 0 | let x = [0, 0, 0, 0] + call cursor(a:lnum, 1) + while 1 + let [lnum, c, submatch] = searchpos(a:pattern, 'cpe' . s:zflag, a:lnum) + if !submatch || c >= endcol | break | endif + if !s:IsCommentOrString(lnum, c) | let x[submatch - 2] += 1 | endif + if cursor(0, c + 1) == -1 || col('.') == c | break | endif + endwhile + return x +endfunction + +function! s:GetOpenCloseCount(lnum, pattern, ...) + let counts = call('s:SubmatchCount', [a:lnum, a:pattern] + a:000) + return counts[0] - counts[1] +endfunction + +function! GetMatlabIndent() + let prevlnum = prevnonblank(v:lnum - 1) + + if b:MATLAB_lasttick != b:changedtick || b:MATLAB_lastline != prevlnum + " Recalculate bracket count (only have to check same block and line above) + let b:MATLAB_bracketlevel = 0 + let previndent = indent(prevlnum) | let l = prevlnum + while 1 + let l = prevnonblank(l - 1) | let indent = indent(l) + if l <= 0 || previndent < indent | break | endif + let b:MATLAB_bracketlevel += s:GetOpenCloseCount(l, s:bracket_pair_pat) + if previndent != indent | break | endif + endwhile + + let b:MATLAB_waslc = s:IsLineContinuation(prevlnum - 1) + endif + " If line above was blank it can impossibly have been a LC + let above_lc = b:MATLAB_lasttick == b:changedtick && prevlnum != v:lnum - 1 && b:MATLAB_lastline == prevlnum ? 0 : s:IsLineContinuation(v:lnum - 1) + + let pair_pat = '\C\<\(' . s:open_pat . '\|' + \ . (b:MATLAB_function_indent == 1 ? '^\@<!' : '') + \ . (b:MATLAB_function_indent >= 1 ? 'function\|' : '') + \ . '\|\%(^\s*\)\@<=\%(else\|elseif\|case\|otherwise\|catch\)\)\>' + \ . '\|\S\s*\zs\(' . s:end . '\)' + let [open, close, b_open, b_close] = prevlnum ? s:SubmatchCount(prevlnum, + \ pair_pat . '\|' . s:bracket_pair_pat) : [0, 0, 0, 0] + let curbracketlevel = b:MATLAB_bracketlevel + b_open - b_close + + call cursor(v:lnum, 1) + let submatch = search(s:dedent_pat, 'cp' . s:zflag, v:lnum) + if submatch && !s:IsCommentOrString(v:lnum, col('.')) + " Align end, et cetera with start of block + let [lnum, col] = searchpairpos(s:start_pat, '', '\C' . s:end, 'bW', 's:IsCommentOrString(line("."), col("."))') + let result = lnum ? indent(lnum) + shiftwidth() * (s:GetOpenCloseCount(lnum, pair_pat, col) + submatch == 2) : 0 + else + " Count how many blocks the previous line opens/closes + " Line continuations/brackets indent once per statement + let result = indent(prevlnum) + shiftwidth() * (open - close + \ + (b:MATLAB_bracketlevel ? -!curbracketlevel : !!curbracketlevel) + \ + (curbracketlevel <= 0) * (above_lc - b:MATLAB_waslc)) + endif + + let b:MATLAB_waslc = above_lc + let b:MATLAB_bracketlevel = curbracketlevel + let b:MATLAB_lasttick = b:changedtick + let b:MATLAB_lastline = v:lnum + return result endfunction -" vim:sw=2 +let &cpo = s:keepcpo +unlet s:keepcpo diff --git a/runtime/indent/raml.vim b/runtime/indent/raml.vim new file mode 100644 index 0000000000..73756ae7de --- /dev/null +++ b/runtime/indent/raml.vim @@ -0,0 +1,12 @@ +" Vim indent file +" Language: RAML (RESTful API Modeling Language) +" Maintainer: mucheng <leisurelicht@gmail.com> +" License: VIM LICENSE +" Latest Revision: 2018-11-03 + +if exists("b:did_indent") + finish +endif + +" Same as yaml indenting. +runtime! indent/yaml.vim diff --git a/runtime/indent/ruby.vim b/runtime/indent/ruby.vim index d8733db305..5c420d7543 100644 --- a/runtime/indent/ruby.vim +++ b/runtime/indent/ruby.vim @@ -1,8 +1,10 @@ " Vim indent file " Language: Ruby -" Maintainer: Nikolai Weibull <now at bitwi.se> +" Maintainer: Andrew Radev <andrey.radev@gmail.com> +" Previous Maintainer: Nikolai Weibull <now at bitwi.se> " URL: https://github.com/vim-ruby/vim-ruby " Release Coordinator: Doug Kearns <dougkearns@gmail.com> +" Last Change: 2019 Jan 06 " 0. Initialization {{{1 " ================= @@ -18,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' @@ -42,28 +49,27 @@ set cpo&vim " 1. Variables {{{1 " ============ -" Regex of syntax group names that are or delimit strings/symbols or are comments. -let s:syng_strcom = '\<ruby\%(Regexp\|RegexpDelimiter\|RegexpEscape' . - \ '\|Symbol\|String\|StringDelimiter\|StringEscape\|ASCIICode' . - \ '\|Interpolation\|InterpolationDelimiter\|NoInterpolation\|Comment\|Documentation\)\>' - -" Regex of syntax group names that are strings. +" Syntax group names that are strings. let s:syng_string = - \ '\<ruby\%(String\|Interpolation\|NoInterpolation\|StringEscape\)\>' + \ ['String', 'Interpolation', 'InterpolationDelimiter', 'NoInterpolation', 'StringEscape'] + +" Syntax group names that are strings or documentation. +let s:syng_stringdoc = s:syng_string + ['Documentation'] -" Regex of syntax group names that are strings or documentation. -let s:syng_stringdoc = - \'\<ruby\%(String\|Interpolation\|NoInterpolation\|StringEscape\|Documentation\)\>' +" Syntax group names that are or delimit strings/symbols/regexes or are comments. +let s:syng_strcom = s:syng_stringdoc + + \ ['Regexp', 'RegexpDelimiter', 'RegexpEscape', + \ 'Symbol', 'StringDelimiter', 'ASCIICode', 'Comment'] " 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."'" + \ 'index(map('.string(s:syng_strcom).',"hlID(''ruby''.v:val)"), synID(line("."),col("."),1)) >= 0' " Regex used for words that, at the start of a line, add a level of indent. let s:ruby_indent_keywords = \ '^\s*\zs\<\%(module\|class\|if\|for' . \ '\|while\|until\|else\|elsif\|case\|when\|unless\|begin\|ensure\|rescue' . - \ '\|\%(public\|protected\|private\)\=\s*def\):\@!\>' . + \ '\|\%(\K\k*[!?]\?\)\=\s*def\):\@!\>' . \ '\|\%([=,*/%+-]\|<<\|>>\|:\s\)\s*\zs' . \ '\<\%(if\|for\|while\|until\|case\|unless\|begin\):\@!\>' @@ -77,7 +83,7 @@ let s:ruby_deindent_keywords = let s:end_start_regex = \ '\C\%(^\s*\|[=,*/%+\-|;{]\|<<\|>>\|:\s\)\s*\zs' . \ '\<\%(module\|class\|if\|for\|while\|until\|case\|unless\|begin' . - \ '\|\%(public\|protected\|private\)\=\s*def\):\@!\>' . + \ '\|\%(\K\k*[!?]\?\)\=\s*def\):\@!\>' . \ '\|\%(^\|[^.:@$]\)\@<=\<do:\@!\>' " Regex that defines the middle-match for the 'end' keyword. @@ -142,31 +148,562 @@ 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(...) abort + " 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 + " ------------------------------- + + " Special case: we don't need the real s:PrevNonBlankNonString for an empty + " line inside a string. And that call can be quite expensive in that + " particular situation. + let indent_callback_names = [ + \ 's:EmptyInsideString', + \ ] + + 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 + + " Previous line number + let indent_info.plnum = s:PrevNonBlankNonString(indent_info.clnum - 1) + let indent_info.pline = getline(indent_info.plnum) + + let indent_callback_names = [ + \ 's:StartOfFile', + \ 's:AfterAccessModifier', + \ 's:ContinuedLine', + \ 's:AfterBlockOpening', + \ 's:AfterHangingSplat', + \ 's:AfterUnbalancedBracket', + \ 's:AfterLeadingOperator', + \ 's:AfterEndKeyword', + \ 's:AfterIndentKeyword', + \ ] + + 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) abort + 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) abort + 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) abort + " 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) abort + 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) abort + 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) abort + 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) abort + " 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) abort + " If the line is empty and inside a string (the previous line is a string, + " too), use the previous line's indent + let info = a:pline_info + + let plnum = prevnonblank(info.clnum - 1) + let pline = getline(plnum) + + if info.cline =~ '^\s*$' + \ && s:IsInStringOrComment(plnum, 1) + \ && s:IsInStringOrComment(plnum, strlen(pline)) + return indent(plnum) + endif + return -1 +endfunction + +function! s:StartOfFile(pline_info) abort + " 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) abort + 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) abort + 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) abort + 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) abort + " 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) abort + 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) abort + 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) abort + 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) abort + 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) abort + 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) abort + 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) abort + 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 " ====================== +function! s:IsInRubyGroup(groups, lnum, col) abort + let ids = map(copy(a:groups), 'hlID("ruby".v:val)') + return index(ids, synID(a:lnum, a:col, 1)) >= 0 +endfunction + " 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:IsInStringOrComment(lnum, col) abort + return s:IsInRubyGroup(s:syng_strcom, a:lnum, a:col) 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:IsInString(lnum, col) abort + return s:IsInRubyGroup(s:syng_string, a:lnum, a:col) endfunction " Check if the character at lnum:col is inside a string or documentation. -function s:IsInStringOrDocumentation(lnum, col) - return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_stringdoc +function! s:IsInStringOrDocumentation(lnum, col) abort + return s:IsInRubyGroup(s:syng_stringdoc, a:lnum, a:col) endfunction " Check if the character at lnum:col is inside a string delimiter -function s:IsInStringDelimiter(lnum, col) - return synIDattr(synID(a:lnum, a:col, 1), 'name') == 'rubyStringDelimiter' +function! s:IsInStringDelimiter(lnum, col) abort + return s:IsInRubyGroup(['StringDelimiter'], a:lnum, a:col) +endfunction + +function! s:IsAssignment(str, pos) abort + 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) +function! s:PrevNonBlankNonString(lnum) abort let in_block = 0 let lnum = prevnonblank(a:lnum) while lnum > 0 @@ -191,10 +728,9 @@ function s:PrevNonBlankNonString(lnum) endfunction " Find line above 'lnum' that started the continuation 'lnum' may be part of. -function s:GetMSL(lnum) +function! s:GetMSL(lnum) abort " 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. @@ -291,14 +827,13 @@ function s:GetMSL(lnum) endif endif - let msl_body = getline(msl) let lnum = s:PrevNonBlankNonString(lnum - 1) endwhile return msl endfunction " Check if line 'lnum' has more opening brackets than closing ones. -function s:ExtraBrackets(lnum) +function! s:ExtraBrackets(lnum) abort let opening = {'parentheses': [], 'braces': [], 'brackets': []} let closing = {'parentheses': [], 'braces': [], 'brackets': []} @@ -360,7 +895,7 @@ function s:ExtraBrackets(lnum) return [rightmost_opening, rightmost_closing] endfunction -function s:Match(lnum, regex) +function! s:Match(lnum, regex) abort let line = getline(a:lnum) let offset = match(line, '\C'.a:regex) let col = offset + 1 @@ -380,7 +915,7 @@ endfunction " Locates the containing class/module's definition line, ignoring nested classes " along the way. " -function! s:FindContainingClass() +function! s:FindContainingClass() abort let saved_position = getpos('.') while searchpair(s:end_start_regex, s:end_middle_regex, s:end_end_regex, 'bW', @@ -396,297 +931,6 @@ function! s:FindContainingClass() return 0 endfunction -" 3. GetRubyIndent Function {{{1 -" ========================= - -function GetRubyIndent(...) - " 3.1. Setup {{{2 - " ---------- - - " The value of a single shift-width - let sw = shiftwidth() - - " 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/runtime/indent/sh.vim b/runtime/indent/sh.vim index 32bc9f35bb..c93be31958 100644 --- a/runtime/indent/sh.vim +++ b/runtime/indent/sh.vim @@ -3,10 +3,11 @@ " Maintainer: Christian Brabandt <cb@256bit.org> " Original Author: Nikolai Weibull <now@bitwi.se> " Previous Maintainer: Peter Aronoff <telemachus@arpinum.org> -" Latest Revision: 2018-03-26 +" Latest Revision: 2019-02-02 " License: Vim (see :h license) " Repository: https://github.com/chrisbra/vim-sh-indent " Changelog: +" 20190201 - Better check for closing if sections " 20180724 - make check for zsh syntax more rigid (needs word-boundaries) " 20180326 - better support for line continuation " 20180325 - better detection of function definitions @@ -59,6 +60,7 @@ function! s:indent_value(option) endfunction function! GetShIndent() + let curline = getline(v:lnum) let lnum = prevnonblank(v:lnum - 1) if lnum == 0 return 0 @@ -72,7 +74,7 @@ function! GetShIndent() " Check contents of previous lines if line =~ '^\s*\%(if\|then\|do\|else\|elif\|case\|while\|until\|for\|select\|foreach\)\>' || \ (&ft is# 'zsh' && line =~ '\<\%(if\|then\|do\|else\|elif\|case\|while\|until\|for\|select\|foreach\)\>') - if line !~ '\<\%(fi\|esac\|done\|end\)\>\s*\%(#.*\)\=$' + if !s:is_end_expression(line) let ind += s:indent_value('default') endif elseif s:is_case_label(line, pnum) @@ -90,7 +92,10 @@ function! GetShIndent() endif elseif s:end_block(line) && !s:start_block(line) let ind -= s:indent_value('default') - elseif pnum != 0 && s:is_continuation_line(pline) && !s:end_block(getline(v:lnum)) + elseif pnum != 0 && + \ s:is_continuation_line(pline) && + \ !s:end_block(curline) && + \ !s:is_end_expression(curline) " only add indent, if line and pline is in the same block let i = v:lnum let ind2 = indent(s:find_continued_lnum(pnum)) @@ -106,8 +111,15 @@ function! GetShIndent() let pine = line " Check content of current line - let line = getline(v:lnum) - if line =~ '^\s*\%(then\|do\|else\|elif\|fi\|done\|end\)\>' || s:end_block(line) + let line = curline + " Current line is a endif line, so get indent from start of "if condition" line + " TODO: should we do the same for other "end" lines? + if curline =~ '^\s*\%(fi\)\s*\%(#.*\)\=$' + let previous_line = search('if.\{-\};\s*then\s*\%(#.*\)\=$', 'bnW') + if previous_line > 0 + let ind = indent(previous_line) + endif + elseif line =~ '^\s*\%(then\|do\|else\|elif\|done\|end\)\>' || s:end_block(line) let ind -= s:indent_value('default') elseif line =~ '^\s*esac\>' && s:is_case_empty(getline(v:lnum - 1)) let ind -= s:indent_value('default') @@ -210,8 +222,8 @@ endfunction function! s:is_here_doc(line) if a:line =~ '^\w\+$' - let here_pat = '<<-\?'. s:escape(a:line). '\$' - return search(here_pat, 'bnW') > 0 + let here_pat = '<<-\?'. s:escape(a:line). '\$' + return search(here_pat, 'bnW') > 0 endif return 0 endfunction @@ -256,5 +268,9 @@ function! s:is_comment(line) return a:line =~ '^\s*#' endfunction +function! s:is_end_expression(line) + return a:line =~ '\<\%(fi\|esac\|done\|end\)\>\s*\%(#.*\)\=$' +endfunction + let &cpo = s:cpo_save unlet s:cpo_save diff --git a/runtime/indent/tcl.vim b/runtime/indent/tcl.vim index e9d61e4366..d77081841d 100644 --- a/runtime/indent/tcl.vim +++ b/runtime/indent/tcl.vim @@ -1,7 +1,8 @@ " Vim indent file -" Language: Tcl -" Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2006-12-20 +" Language: Tcl +" Previous Maintainer: Nikolai Weibull <now@bitwi.se> +" Latest Update: Chris Heithoff <chrisheithoff@gmail.com> +" Latest Revision: 2018-12-05 if exists("b:did_indent") finish @@ -28,6 +29,15 @@ function s:prevnonblanknoncomment(lnum) return lnum endfunction +function s:ends_with_backslash(lnum) + let line = getline(a:lnum) + if line =~ '\\\s*$' + return 1 + else + return 0 + endif +endfunction + function s:count_braces(lnum, count_open) let n_open = 0 let n_close = 0 @@ -53,23 +63,39 @@ endfunction function GetTclIndent() let line = getline(v:lnum) - if line =~ '^\s*\*' - return cindent(v:lnum) - elseif line =~ '^\s*}' - return indent(v:lnum) - shiftwidth() - endif + " Get the line number of the previous non-blank or non-comment line. let pnum = s:prevnonblanknoncomment(v:lnum - 1) if pnum == 0 return 0 endif - let ind = indent(pnum) + s:count_braces(pnum, 1) * shiftwidth() + " ..and the previous line before the previous line. + let pnum2 = s:prevnonblanknoncomment(pnum-1) - let pline = getline(pnum) - if pline =~ '}\s*$' - let ind -= (s:count_braces(pnum, 0) - (pline =~ '^\s*}' ? 1 : 0)) * shiftwidth() + " Default indentation is to preserve the previous indentation. + let ind = indent(pnum) + + " ...but if previous line introduces an open brace, then increase current line's indentation + if s:count_braces(pnum, 1) > 0 + let ind += shiftwidth() + else + " Look for backslash line continuation on the previous two lines. + let slash1 = s:ends_with_backslash(pnum) + let slash2 = s:ends_with_backslash(pnum2) + if slash1 && !slash2 + " If the previous line begins a line continuation. + let ind += shiftwidth() + elseif !slash1 && slash2 + " If two lines ago was the end of a line continuation group of lines. + let ind -= shiftwidth() + endif endif + " If the current line begins with a closed brace, then decrease the indentation by one. + if line =~ '^\s*}' + let ind -= shiftwidth() + endif + return ind endfunction diff --git a/runtime/indent/testdir/html.in b/runtime/indent/testdir/html.in new file mode 100644 index 0000000000..9c776d61c6 --- /dev/null +++ b/runtime/indent/testdir/html.in @@ -0,0 +1,26 @@ +" vim: set ft=html sw=4 : + + +" START_INDENT +<div> +<div> +text +</div> +</div> + +<div +class="foo bar"> +text +</div> + +<div class="foo bar" +data="something"> +text +</div> + +<div class="foo +bar"> +text +</div> + +" END_INDENT diff --git a/runtime/indent/testdir/html.ok b/runtime/indent/testdir/html.ok new file mode 100644 index 0000000000..524d57bb6c --- /dev/null +++ b/runtime/indent/testdir/html.ok @@ -0,0 +1,26 @@ +" vim: set ft=html sw=4 : + + +" START_INDENT +<div> + <div> + text + </div> +</div> + +<div + class="foo bar"> + text +</div> + +<div class="foo bar" + data="something"> + text +</div> + +<div class="foo + bar"> + text +</div> + +" END_INDENT diff --git a/runtime/indent/testdir/matlab.in b/runtime/indent/testdir/matlab.in new file mode 100644 index 0000000000..5bba1a56dd --- /dev/null +++ b/runtime/indent/testdir/matlab.in @@ -0,0 +1,80 @@ +% vim: set ft=matlab sw=4 : + +% START_INDENT +if true +disp foo +elseif false +disp bar +end +% END_INDENT + +% START_INDENT +try +statements +catch exception +statements +end +% END_INDENT + +% START_INDENT +if true, ... +if true +disp hello +end +end +% END_INDENT + +% START_INDENT +switch a +case expr +if true, foo; end +disp hello +otherwise +disp bar +end +% END_INDENT + +% START_INDENT +if true +A(1:end - 1) +disp foo +end +% END_INDENT + +% START_INDENT +A = [{ +} +] ... +disp foo +disp bar +% END_INDENT + +% START_INDENT +% INDENT_EXE let b:MATLAB_function_indent = 0 +function foo +disp foo +function nested +disp bar +end +end +% END_INDENT + +% START_INDENT +% INDENT_EXE let b:MATLAB_function_indent = 1 +function foo +disp foo +function nested +disp bar +end +end +% END_INDENT + +% START_INDENT +% INDENT_EXE let b:MATLAB_function_indent = 2 +function foo +disp foo +function nested +disp bar +end +end +% END_INDENT diff --git a/runtime/indent/testdir/matlab.ok b/runtime/indent/testdir/matlab.ok new file mode 100644 index 0000000000..b1112263b2 --- /dev/null +++ b/runtime/indent/testdir/matlab.ok @@ -0,0 +1,80 @@ +% vim: set ft=matlab sw=4 : + +% START_INDENT +if true + disp foo +elseif false + disp bar +end +% END_INDENT + +% START_INDENT +try + statements +catch exception + statements +end +% END_INDENT + +% START_INDENT +if true, ... + if true + disp hello + end +end +% END_INDENT + +% START_INDENT +switch a + case expr + if true, foo; end + disp hello + otherwise + disp bar +end +% END_INDENT + +% START_INDENT +if true + A(1:end - 1) + disp foo +end +% END_INDENT + +% START_INDENT +A = [{ + } + ] ... + disp foo +disp bar +% END_INDENT + +% START_INDENT +% INDENT_EXE let b:MATLAB_function_indent = 0 +function foo +disp foo + function nested + disp bar + end +end +% END_INDENT + +% START_INDENT +% INDENT_EXE let b:MATLAB_function_indent = 1 +function foo +disp foo + function nested + disp bar + end +end +% END_INDENT + +% START_INDENT +% INDENT_EXE let b:MATLAB_function_indent = 2 +function foo + disp foo + function nested + disp bar + end +end +% END_INDENT diff --git a/runtime/indent/testdir/tcl.in b/runtime/indent/testdir/tcl.in new file mode 100644 index 0000000000..c769d5bf5e --- /dev/null +++ b/runtime/indent/testdir/tcl.in @@ -0,0 +1,19 @@ +# vim: set filetype=tcl shiftwidth=4 tabstop=8 expandtab : + +# START_INDENT +proc abc {} { +set a 5 +if {[some_cmd]==1} { +foreach i [list {1 2 3}] { +# Does this comment affect anything? +puts $i +} +} +} + +command_with_a_long_time -arg1 "First" \ +-arg2 "Second" \ +-arg3 "Third" + +puts "Move indent back after line continuation is complete" +# END_INDENT
\ No newline at end of file diff --git a/runtime/indent/testdir/tcl.ok b/runtime/indent/testdir/tcl.ok new file mode 100644 index 0000000000..77f24e9044 --- /dev/null +++ b/runtime/indent/testdir/tcl.ok @@ -0,0 +1,19 @@ +# vim: set filetype=tcl shiftwidth=4 tabstop=8 expandtab : + +# START_INDENT +proc abc {} { + set a 5 + if {[some_cmd]==1} { + foreach i [list {1 2 3}] { + # Does this comment affect anything? + puts $i + } + } +} + +command_with_a_long_time -arg1 "First" \ + -arg2 "Second" \ + -arg3 "Third" + +puts "Move indent back after line continuation is complete" +# END_INDENT diff --git a/runtime/indent/testdir/xml.in b/runtime/indent/testdir/xml.in new file mode 100644 index 0000000000..b6333340e2 --- /dev/null +++ b/runtime/indent/testdir/xml.in @@ -0,0 +1,32 @@ +<!-- vim: set ft=xml ts=8 sw=0 sts=-1 et : --> +<!-- START_INDENT --> +<?xml version="1.0" encoding="utf-8"?> +<tag0> + <tag1> +<!-- comment --> +<tag2> + <tag3/> +</tag2> +<!-- text comment --> + +<!-- +text comment +--> +</tag1> +<!-- +text comment +end coment --> +</tag0> +<!-- END_INDENT --> + +<!-- START_INDENT --> +<?xml version="1.0" encoding="utf-8"?> +<tag0> + <tag1> +<!-- comment --> +<tag2> + <tag3/> +</tag2> +</tag1> +</tag0> +<!-- END_INDENT --> diff --git a/runtime/indent/testdir/xml.ok b/runtime/indent/testdir/xml.ok new file mode 100644 index 0000000000..529198572a --- /dev/null +++ b/runtime/indent/testdir/xml.ok @@ -0,0 +1,32 @@ +<!-- vim: set ft=xml ts=8 sw=0 sts=-1 et : --> +<!-- START_INDENT --> +<?xml version="1.0" encoding="utf-8"?> +<tag0> + <tag1> + <!-- comment --> + <tag2> + <tag3/> + </tag2> + <!-- text comment --> + + <!-- + text comment + --> + </tag1> + <!-- + text comment + end coment --> +</tag0> +<!-- END_INDENT --> + +<!-- START_INDENT --> +<?xml version="1.0" encoding="utf-8"?> +<tag0> + <tag1> + <!-- comment --> + <tag2> + <tag3/> + </tag2> + </tag1> +</tag0> +<!-- END_INDENT --> diff --git a/runtime/indent/xml.vim b/runtime/indent/xml.vim index dcafb467a6..29069bab84 100644 --- a/runtime/indent/xml.vim +++ b/runtime/indent/xml.vim @@ -1,13 +1,23 @@ -" Language: xml -" Repository: https://github.com/chrisbra/vim-xml-ftplugin -" Maintainer: Christian Brabandt <cb@256bit.org> -" Previous Maintainer: Johannes Zellner <johannes@zellner.org> -" Last Change: 20180724 - Correctly indent xml comments https://github.com/vim/vim/issues/3200 -" Notes: 1) does not indent pure non-xml code (e.g. embedded scripts) -" 2) will be confused by unbalanced tags in comments -" or CDATA sections. -" 2009-05-26 patch by Nikolai Weibull -" TODO: implement pre-like tags, see xml_indent_open / xml_indent_close +" Language: xml +" Repository: https://github.com/chrisbra/vim-xml-ftplugin +" Last Changed: Jan 28, 2019 +" Maintainer: Christian Brabandt <cb@256bit.org> +" Previous Maintainer: Johannes Zellner <johannes@zellner.org> +" Last Change: +" 20190128 - Make sure to find previous tag +" https://github.com/chrisbra/vim-xml-ftplugin/issues/4 +" 20181116 - Fix indentation when tags start with a colon or an underscore +" https://github.com/vim/vim/pull/926 +" 20181022 - Do not overwrite indentkeys setting +" https://github.com/chrisbra/vim-xml-ftplugin/issues/1 +" 20180724 - Correctly indent xml comments https://github.com/vim/vim/issues/3200 +" +" Notes: +" 1) does not indent pure non-xml code (e.g. embedded scripts) +" 2) will be confused by unbalanced tags in comments +" or CDATA sections. +" 2009-05-26 patch by Nikolai Weibull +" TODO: implement pre-like tags, see xml_indent_open / xml_indent_close " Only load this indent file when no other was loaded. if exists("b:did_indent") @@ -18,11 +28,12 @@ let s:keepcpo= &cpo set cpo&vim " [-- local settings (must come before aborting the script) --] +" Attention: Parameter use_syntax_check is used by the docbk.vim indent script setlocal indentexpr=XmlIndentGet(v:lnum,1) -setlocal indentkeys=o,O,*<Return>,<>>,<<>,/,{,} +setlocal indentkeys=o,O,*<Return>,<>>,<<>,/,{,},!^F if !exists('b:xml_indent_open') - let b:xml_indent_open = '.\{-}<\a' + let b:xml_indent_open = '.\{-}<[:A-Z_a-z]' " pre tag, e.g. <address> " let b:xml_indent_open = '.\{-}<[/]\@!\(address\)\@!' endif @@ -38,7 +49,7 @@ unlet s:keepcpo " [-- finish, if the function already exists --] if exists('*XmlIndentGet') - finish + finish endif let s:keepcpo= &cpo @@ -51,13 +62,13 @@ endfun " [-- check if it's xml --] fun! <SID>XmlIndentSynCheck(lnum) - if '' != &syntax - let syn1 = synIDattr(synID(a:lnum, 1, 1), 'name') - let syn2 = synIDattr(synID(a:lnum, strlen(getline(a:lnum)) - 1, 1), 'name') - if '' != syn1 && syn1 !~ 'xml' && '' != syn2 && syn2 !~ 'xml' - " don't indent pure non-xml code - return 0 - endif + if &syntax != '' + let syn1 = synIDattr(synID(a:lnum, 1, 1), 'name') + let syn2 = synIDattr(synID(a:lnum, strlen(getline(a:lnum)) - 1, 1), 'name') + if syn1 != '' && syn1 !~ 'xml' && syn2 != '' && syn2 !~ 'xml' + " don't indent pure non-xml code + return 0 + endif endif return 1 endfun @@ -66,41 +77,73 @@ endfun fun! <SID>XmlIndentSum(lnum, style, add) let line = getline(a:lnum) if a:style == match(line, '^\s*</') - return (shiftwidth() * - \ (<SID>XmlIndentWithPattern(line, b:xml_indent_open) - \ - <SID>XmlIndentWithPattern(line, b:xml_indent_close) - \ - <SID>XmlIndentWithPattern(line, '.\{-}/>'))) + a:add + return (shiftwidth() * + \ (<SID>XmlIndentWithPattern(line, b:xml_indent_open) + \ - <SID>XmlIndentWithPattern(line, b:xml_indent_close) + \ - <SID>XmlIndentWithPattern(line, '.\{-}/>'))) + a:add else - return a:add + return a:add endif endfun +" Main indent function fun! XmlIndentGet(lnum, use_syntax_check) " Find a non-empty line above the current line. - let lnum = prevnonblank(a:lnum - 1) - + let plnum = prevnonblank(a:lnum - 1) " Hit the start of the file, use zero indent. - if lnum == 0 - return 0 + if plnum == 0 + return 0 endif + " Find previous line with a tag (regardless whether open or closed, + " but always start restrict the match to a line before the current one + let ptag_pattern = '\%(.\{-}<[/:A-Z_a-z]\)'. '\%(\&\%<'. line('.').'l\)' + let ptag = search(ptag_pattern, 'bnw') + let syn_name = '' if a:use_syntax_check - let check_lnum = <SID>XmlIndentSynCheck(lnum) - let check_alnum = <SID>XmlIndentSynCheck(a:lnum) - if 0 == check_lnum || 0 == check_alnum - return indent(a:lnum) - elseif -1 == check_lnum || -1 == check_alnum - return -1 - endif + let check_lnum = <SID>XmlIndentSynCheck(plnum) + let check_alnum = <SID>XmlIndentSynCheck(a:lnum) + if check_lnum == 0 || check_alnum == 0 + return indent(a:lnum) + endif + let syn_name = synIDattr(synID(a:lnum, strlen(getline(a:lnum)) - 1, 1), 'name') endif - let ind = <SID>XmlIndentSum(lnum, -1, indent(lnum)) - let ind = <SID>XmlIndentSum(a:lnum, 0, ind) + if syn_name =~ 'Comment' + return <SID>XmlIndentComment(a:lnum) + endif + " Get indent from previous tag line + let ind = <SID>XmlIndentSum(ptag, -1, indent(ptag)) + " Determine indent from current line + let ind = <SID>XmlIndentSum(a:lnum, 0, ind) return ind endfun +" return indent for a commented line, +" the middle part might be indented on additional level +func! <SID>XmlIndentComment(lnum) + let ptagopen = search(b:xml_indent_open, 'bnw') + let ptagclose = search(b:xml_indent_close, 'bnw') + if getline(a:lnum) =~ '<!--' + " if previous tag was a closing tag, do not add + " one additional level of indent + if ptagclose > ptagopen && a:lnum > ptagclose + return indent(ptagclose) + else + " start of comment, add one indentation level + return indent(ptagopen) + shiftwidth() + endif + elseif getline(a:lnum) =~ '-->' + " end of comment, same as start of comment + return indent(search('<!--', 'bnw')) + else + " middle part of comment, add one additional level + return indent(search('<!--', 'bnw')) + shiftwidth() + endif +endfunc + let &cpo = s:keepcpo unlet s:keepcpo -" vim:ts=8 +" vim:ts=4 et sts=-1 sw=0 |