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.
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.
You may have better luck with this, especially if you already have syntax highlighting:
This would go into your ~/.vimrc
"" Enable folding based on syntax rules set foldmethod=syntax "" Adjust the highlighting highlight Folded guibg=grey guifg=blue "" Map folding to Spacebar nnoremap za
This is also a great VIMcast on folding: http://vimcasts.org/episodes/how-to-fold/
Made a change to Max Cantor's solution, mine works perfectly, it will fold def methods (including the end keyword) and documentations (=begin =end) blocks regardless of where it is (in a class, or indented)
function! RubyMethodFold(line)
let stack = synstack(a:line, (match(getline(a:line), '^\s*\zs'))+1)
for synid in stack
if GetSynString(GetSynDict(synid)) ==? "rubyMethodBlock" || GetSynString(GetSynDict(synid)) ==? "rubyDefine" || GetSynString(GetSynDict(synid)) ==? "rubyDocumentation"
return 1
endif
endfor
return 0
endfunction
set foldexpr=RubyMethodFold(v:lnum)
set foldmethod=expr
Thanks to Max Cantor for his helper methods to print out syntax info.
Let me explain this line by line:
The most important line would be the second line where it gets the stacks of verious level of syntax element in a line.
So if you have a line like def methodname
with whitespace or tabs before it, it will always get the stack at the first non blank character. The problem with Max Cantor's solution is that his code will only print out the lowest syntax element found at the first column of the line, so it will always return something random, elements like "rubyContant" which we don't care about.
So by using match(getline(a:line), '^\s*\zs')
we can move our cursor to the column of the first non blank character so we can get a more accurate picture of the stacks. +1 because match is zero-indexed.
And then the rest is similar to the GetSynInfo method where we loop through the stack and match the elements we want, and return 1 so they are all on the same fold level and 0 for the rest.
And that's it, a working folding expr function that will only fold ruby define methods :)
Enjoy and have a nice day!
I made vim-ruby-fold plugin for simple method folding in ruby. I hope it helps people.
You might be able to get away with a solution which folds inside a method as well, by using the zO
command. zO
will recursively open a fold, so if you use it instead of zo
, it will be as if nothing inside the method was folded. You could also remap zo
to zO
if you are feeling squirrely.
精彩评论