A new, and very improved indent/ruby.vim

Nikolai Weibull lone-star at home.se
Tue Jul 29 03:10:22 EDT 2003


OK.  About 8 hours worth of work (Vim scripting is a pain), here it
is.  A goddam smashing update to the indent/ruby.vim file.  It seems to be
working rather flawlessly.  I can think of some issues i would like to
solve (indenting bad syntax correctly a bit better), but otherwise
everything works like a charm.  Many of the niceties about Vim's built-in C
indenter exist, and I can't personally think of anything it currently
lacks.

Well, not quite true...Two thinks are missing...
continuation of [ lists on multiple lines:
list = [ 1, 2,
3, 4 ]
should be indented like
list = [ 1, 2,
	3, 4 ]
or similar (add one &sw, or perhaps line up)
Number two is that =begin and =end's aren't handled.  these should be
put in column 1.  The biggest problem (well, not really a problem) is to
find the indent of lines that follow.

Gavin: Can you perhaps add me (pcp) to the developers (so i can CVS
changes)?

enjoy,
	nikolai

--
::: name: Nikolai Weibull    :: aliases: pcp / lone-star       :::
::: born: Chicago, IL USA    :: loc atm: Gothenburg, Sweden    :::
::: page: www.pcppopper.org  :: fun atm: gf,lps,ruby,php,war3  :::
main(){printf(&linux["\021%six\012\0"],(linux)["have"]+"fun"-97);}
-------------- next part --------------
" Vim indent file
" Language:	Ruby
" Maintainer:	Gavin Sinclair <gsinclair at soyabean.com.au>
" Last Change:	2003 May 11
" URL: www.soyabean.com.au/gavin/vim/index.html
" Changes: (since vim 6.1)
"  - indentation after a line ending in comma, etc, (even in a comment) was
"    broken, now fixed (2002/08/14)
" TODO: need to indent one level on [

" Only load this indent file when no other was loaded.
if exists("b:did_indent")
  finish
endif
let b:did_indent = 1

setlocal indentexpr=GetRubyIndent()
setlocal nolisp
setlocal nosmartindent
setlocal autoindent
setlocal indentkeys+==end,=else,=elsif,=when,=ensure,=rescue,0),==begin,==end

" Only define the function once.
if exists("*GetRubyIndent")
  finish
endif

function s:IsInStringOrComment(line, col)
  return synIDattr(synID(a:line, a:col, 0), 'name') =~? 'string\|comment'
endfunction

let g:ruby_indent_keywords = 'module,class,def,if,for,while,until,else,elsif,'.
      \'case,when,unless,begin,ensure,rescue'

function s:BuildIndentKeywords()
  let idx = stridx(g:ruby_indent_keywords, ',')
  while idx > -1
    let keyword = strpart(g:ruby_indent_keywords, 0, idx).'\>'
    if exists('s:ruby_indent_keywords')
      let s:ruby_indent_keywords = s:ruby_indent_keywords.'\|'.keyword
    else
      let s:ruby_indent_keywords = "\\(".keyword
    endif
    let g:ruby_indent_keywords = strpart(g:ruby_indent_keywords, idx + 1)
    let idx = stridx(g:ruby_indent_keywords, ',')
  endwhile

  if exists('s:ruby_indent_keywords')
    let s:ruby_indent_keywords = s:ruby_indent_keywords.'\|'.
	  \g:ruby_indent_keywords.'\>\)'
  else
    let s:ruby_indent_keywords = g:ruby_indent_keywords.'\>'
  endif
endfunction
call s:BuildIndentKeywords()

let s:continuation_regexp = '[*+/.-]\s*\(#.*\)\=$'
let s:block_regexp = '\({\|\<do\>\)\s*\(|\(\h\w*,\=\)\+|\s*\)\=\(#.*\)\=$'
let s:skip_expr = 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\\|comment"'


" FIXME: with a little reorginization (using returns instead of let ind =
" ...), we could speed things up a bit
function GetRubyIndent()
  " Set up variables for restoring position in file.  Could use v:lnum here.
  let clnum = line('.')
  let ccol = col('.')

  " Find a non-blank line above the current line.
  let lnum = prevnonblank(v:lnum - 1)

  " At the start of the file use zero indent.
  if lnum == 0
    return 0
  endif

  let line = getline(lnum)
  let ind = indent(lnum)
  let did_indent = 0
  let did_end_indent = 0

  " If the previous line ended with [*+/.-], indent one extra level.
  let col = match(line, s:continuation_regexp) + 1
  if col > 0 && !s:IsInStringOrComment(lnum, col)
    let ind + &sw
    let did_indent = 1
  endif

  " If the previous line ended in a parentheses, get the indent of the line
  " that opened it.
  let col = matchend(line, '^\s*)') + 1
  if !did_indent && col > 0
    execute 'normal '.lnum.'G'.col.'|'
    if searchpair('(', '', ')', 'bW', s:skip_expr) > 0
      let ind = indent('.')
      let did_indent = 1
    endif
    execute 'normal '.clnum.'G'.ccol.'|'
  endif

  " If the previous line ended with an indenting keyword, add one level.
  let col = match(line,  '^\s*'.s:ruby_indent_keywords) + 1
  if !did_indent && col > 0 || line =~ s:block_regexp
    let ind = ind + &sw
    let did_indent = 1
    let did_end_indent = 1
  endif

  " Otherwise, check if the previous line was a continuation line.
  if !did_indent
    let my_lnum = prevnonblank(lnum - 1)
    if my_lnum > 0
      let my_line = getline(my_lnum)
      let my_ind = indent(my_lnum)

      if my_line =~ s:continuation_regexp
	let col = match(my_line, s:continuation_regexp) + 1
	if col > 0 && !s:IsInStringOrComment(my_lnum, col)
	  let ind = my_ind
	  let did_indent = 1
	endif
      endif
    endif
  endif

  " If we indented and the line ended with an 'end', decrese indent.
  " TODO: make this more intelligent (check with searchpair())
  if !did_indent && line =~ '\<end\>\s*\(#.*\)\=$'
    let ind = ind - &sw
  endif

  " Get the current line.
  let line = getline(v:lnum)

  " If we are inside a pair of braces, well at least after an opening one.
  if 0 < searchpair('(', '', ')', 'bW', s:skip_expr)
    let ind = virtcol('.')
    execute 'normal '.clnum.'G'.ccol.'|'
  endif

  " Deindent on a closing ) on an empty line.
  let col = matchend(line, '^\s*)') + 1
  if col > 0
    execute 'normal '.col.'|'
    if searchpair('(', '', ')', 'bW', s:skip_expr) > 0
      let ind = virtcol('.') - 1
    endif
    execute 'normal '.ccol.'|'
  endif


  " If we got a brace on an empty line, find match and indent to its level.
  let col = matchend(line, '^\s*}') + 1
  if col > 0
    execute 'normal '.col.'|'
    if searchpair('{', '', '}', 'bW', s:skip_expr) > 0
      let ind = indent('.')
    endif
    execute 'normal '.ccol.'|'
  endif

  " If we got an 'end' on an empty line, find match and indent to its level.
  let col = matchend(line,
	\'^\s*\(rescue\>\|else\>\|ensure\>\|end\>\|when\>\)') + 1
  if col > 0
    execute 'normal '.col.'|'
    if searchpair('\<def\>\|\<do\>\|\<if\>\|\<unless\>\|\<case\>\|\<when\>\|\<begin\>\|\<until\>\|\<for\>\|\<while\>\|\<class\>\|\<module\>', 
	  \'\<ensure\>\|\<else\>\|\<rescue\>\|\<elsif\>\|\<when\>', '\<end\>',
	  \'bW', '') > 0
      let ind = indent('.')
    endif
    execute 'normal '.clnum.'G'.ccol.'|'
  endif

  return ind
endfunction

" vim:sw=2


More information about the vim-ruby-devel mailing list