Search code examples
rubyvimfolding

Automatic ruby folding in vim


Is there a way that I can setup vim to automatically fold ruby source files, but only fold at the method level regardless of the level that they are defined?

So it will fold when I have:

class MyClass
  def method
    ...
  end
end

but also when I have:

module FirstModule
  module SecondModule
    class MyClass
      def method
        ...
      end
    end
  end
end

I've experimented with foldmethod=syntax and various fold levels but it doesn't take into account the depth where the method is defined.

Also I don't want nothing inside the method to get folded (if blocks, each blocks, etc).

I think foldmethod=expr would be my best bet, but I haven't manage to figure out how fold expressions work, and the help in vim hasn't been very enlightening.


Solution

  • Your hunch about using the expr method, I believe, was correct!

    You can use the syntax structure of the file to jury-rig your own syntax-style folding. The following in my .vimrc produced expected behavior:

    function! RubyMethodFold(line)
      let line_is_method_or_end = synIDattr(synID(a:line,1,0), 'name') == 'rubyMethodBlock'
      let line_is_def = getline(a:line) =~ '\s*def '
      return line_is_method_or_end || line_is_def
    endfunction
    
    set foldexpr=RubyMethodFold(v:lnum)
    

    Some caveats:

    I'm not sure if the final argument to synID should be 0 or 1. It's the argument that determines whether you get the syntax information for the topmost transparent or non-transparent element at the provided location. When there's no transparent element, the argument has no effect. In the trivial example I tried, it didn't cause any issues, but it might.

    It's also worth noting that the line_is_def regex is probably too lenient. It might be better to return -1 in this situation, so a line matching the regex is only folded when it's right next to the folded method block. A more restrictive regex could also work.

    If you're feeling squirrely, you could expand on this and return separate foldlevels for rubyClass and rubyModule elements as well.

    If you decide to go down that route, there are some useful custom functions in my .vimrc for introspecting into syntax elements and hilighting. They're handiest when mapped to a keybinding for quick use like so:

    nnoremap g<C-h> :echo GetSynInfo()<CR>
    

    Please do let me know if this works out for you! It was fun to figure out. Also, for what it's worth, while :help 'foldexpr' is light on details, :help 'fold-expr' is much more helpful, and even has some examples at the top.