Matthias Kopfermann (kopfermann / trio-hittfeld.de) wrote:
> hi fellows.
> As a new user of ruby which is really wonderful i still find these
> `while ... end', `if ... end',`def ... end' things ugly ( perhaps that's the
> only thing I really don't like at all). I would much more like to
> have `endwhile', `endif' `enddef' `endclass'... as it seems to
> me, that this makes searching much easier.
[...]
> ps: i didn't like the bracing of C ( the same problem or even
>       worse ) as the closed brace means many different things but
>       at least i can jump to the beginning with `%' in my vi.

If you're using vim, you may want to try the following (warning: it
doesn't exactly follow good coding standards for vim, being a quick
hack, and it was written using version 6.0, which is still in beta, but
may work with version 5.x). It does require the ruby interface to be
compiled in. Use at your own risk.

The code will remap the '%' command to not only match the characters
in 'matchpairs', but also just about any piece of code that has a
comb-like structure where the last line is terminated with an 'end'
or a right brace. It can be used with visual modes and operators,
and should also work for other languages (if you adjust the "au"
lines).

			Reimer Behrends

--- ruby.vim
if has("ruby")
  function RubyEval(...)
    ruby dispatcher
    return l:result
  endfunction
  rubyf $HOME/.vim/ruby.rb
  au filetype ruby noremap % @=RubyEval("blockmatch")<CR>
  au filetype ruby noremap \% %
endif

-- ruby.rb
include VIM

def clear_message
  message ""
end

def dispatcher
  count = VIM::evaluate("a:0").to_i
  args = []
  for i in 1..count do
    args << VIM::evaluate("a:"+i.to_s)
  end
  fname = args.shift
  result = send(fname, *args).to_s
  result = result.gsub("[\\\\\"]", "\\\\\\&")
  VIM::command "let l:result = \"#{result}\""
end

def compute_indent(s)
  indent = 0
  ts = 8 # Tab stops
  for i in 0..s.length-1 do
    case s[i]
    when ?\s then
      indent += 1
    when ?\t then
      if indent % ts == 0 then
	indent += ts
      else
	indent = indent/ts * ts + ts
      end
    else
      return indent
    end
  end
  return indent
end

def blockmatch
  window = Window.current
  buffer = window.buffer
  row, col = window.cursor
  line = buffer[row]
  if evaluate("&matchpairs").split(/[:,]/).index(line[col..col]) then
    clear_message
    return "\\%"
  end
  lines = buffer.length
  indent = compute_indent(line)
  if /^[ \t]*(end\b|\})/.match(line) then
    row -= 1
    lastrow = 0
    lastindent = -1
    while row > 1 do
      line = buffer[row]
      unless line =~ /^[ \t]*$/ then
	newindent = compute_indent(line)
	if newindent < indent then
	  row = lastrow
	  break
	elsif newindent == indent then
	  if line =~ /^[ \t]*(end\b|\})/ then
	    break
	  end
	  if newindent != lastindent then
	    lastrow = row
	  end
	end
	lastindent = newindent
      end
      row -= 1
    end
    if lastrow >= 1 then
      row = lastrow
    end
  else
    row += 1
    while row <= buffer.length do
      line = buffer[row]
      unless line =~ /^[ \t]*$/ then
	newindent = compute_indent(line)
	if newindent == indent then
	  break
	end
	if newindent < indent then
	  row = 0
	  break
	end
      end
      row += 1
    end
  end
  if row < 1 || row > buffer.length then
    message 'No match found.'
  else
    # col = /^[ \t]*/.match(buffer[row]).to_s.length
    # window.cursor = [ row, col ]
    clear_message
    return row.to_s+"gg"
  end
end