summaryrefslogtreecommitdiffstats
path: root/indent/ruby.vim
diff options
context:
space:
mode:
authorAdam Stankiewicz <sheerun@sher.pl>2017-03-23 11:28:19 +0100
committerAdam Stankiewicz <sheerun@sher.pl>2017-03-23 11:28:28 +0100
commit0801eac01aab5940fc1e4409ba749383cc353bc2 (patch)
tree9034c9f6cd0c7592a09e6c65521c6948c3a983af /indent/ruby.vim
parent9f735b1fe77072e001a593f7f6660703bf4a8c9c (diff)
downloadvim-polyglot-0801eac01aab5940fc1e4409ba749383cc353bc2.tar.gz
vim-polyglot-0801eac01aab5940fc1e4409ba749383cc353bc2.zip
Update
Diffstat (limited to 'indent/ruby.vim')
-rw-r--r--indent/ruby.vim808
1 files changed, 510 insertions, 298 deletions
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