Usable Ruby folding for Vim
The folding methods you get with a default vim install (even with updated Ruby support) are unsatisfying for a number of reasons:
- fold_syntax and fold_indent create far too many folds (e.g. for syntactical elements like if or while). On top of that, they’re nested, so you have to open folds recursively all the time. foldnestmax isn’t an option because you never know how many class/module levels you’re going to get: a value that works fine for code wrapped in a module would show too many folds in one consisting of bare (private Object) method definitions.
- fold_marker requires too much work to mark all methods and classes
- fold_expr seems a bit restrictive
As happens often with vim, spotting what I dislike is easier than finding right away what I want. I now know I want code folding in Ruby to work as follows:
- no need for manual markers in general, but I want to have the option to use them.
- folds for modules, classes, methods and constant definitions, and nothing else. I specifically want no intra-method folding (on if and such): if the method is large enough to warrant folds, you’re screwed anyway.
- no nested folds: I don’t want to have to open a class fold to look at the folded methods.
To sum up, this is how I’d like folds to behave:
I can glance through the class without having to open nested folds, and can locate visually constant definitions, methods and attributes very quickly. I actually prefer this to tagexplorer.
Usage
After adding the following snippet to .vimrc, vim will fold on :R (that won’t set foldenable though, so you might have to zi too).
The default fold expression will work with Ruby code, but the b:foldsearchexpr buffer-local variable can be set before folding to suit it to other languages.
" FoldSearch-based folding. " Copyright (C) 2005 Mauricio Fernandez <[email protected]> " Current version: https://eigenclass.org/hiki.rb?Usable+Ruby+folding+for+Vim " " Add this to your .vimrc and fold with :R. The default fold expression will " work with Ruby scripts; you can specify where folds start with " let b:foldsearchexpr = 'myexpression' " e.g. " let b:foldsearchexpr='\(^\s*\(\(private\|public\|protected\|class\)\s\)\)' " or so for Java. " One way to have this buffer-local variable set is " au Filetype java let b:foldsearchexpr='\(^\s*\(\(private\|public\|protected\|class\)\s\)\)' " " It is possible to have comments above a method/class/etc be included in the " fold, by setting b:foldsearchprefix. All the lines above the detected fold " matching b:foldsearchprefix will be included in said fold. " For instance, for Ruby code: " let b:foldsearchprefix = '\v^\s*(#.*)?$' " which can be automated with " au Filetype ruby let b:foldsearchprefix='\v^\s*(#.*)?$' " " Changelog: " 2005-12-12 1.1 use b:foldsearchprefix to prepend comments to a fold. "{{{ set s:sid map <SID>xx <SID>xx let s:sid = maparg("<SID>xx") unmap <SID>xx let s:sid = substitute(s:sid, 'xx', '', '') "{{{ FoldText function! s:Num2S(num, len) let filler = " " let text = '' . a:num return strpart(filler, 1, a:len - strlen(text)) . text endfunction execute 'set foldtext=' . s:sid . 'MyNewFoldText()' function! <SID>MyNewFoldText() let linenum = v:foldstart while linenum <= v:foldend let line = getline(linenum) if !exists("b:foldsearchprefix") || match(line, b:foldsearchprefix) == -1 break else let linenum = linenum + 1 endif endwhile if exists("b:foldsearchprefix") && match(line, b:foldsearchprefix) != -1 " all lines matched the prefix regexp let line = getline(v:foldstart) endif let sub = substitute(line, '/\*\|\*/\|{{{\d\=', '', 'g') let diff = v:foldend - v:foldstart + 1 return '+ [' . s:Num2S(diff,4) . ']' . sub endfunction "{{{~foldsearch adapted from t77: Fold on search result (Fs <pattern>) "Fs pattern Fold search "Vimtip put to good use by Ralph Amissah [email protected] "Modified by Mauricio Fernandez <[email protected]> function! Foldsearch(search) setlocal fdm=manual let origlineno = line(".") normal zE normal G$ let folded = 0 "flag to set when a fold is found let flags = "w" "allow wrapping in the search let line1 = 0 "set marker for beginning of fold if a:search == "" if exists("b:foldsearchexpr") let searchre = b:foldsearchexpr else "Default value, suitable for Ruby scripts "\(^\s*\(\(def\|class\|module\)\s\)\)\|^\s*[#%"0-9]\{0,4\}\s*{\({{\|!!\) let searchre = '\v(^\s*(def|class|module|attr_reader|attr_accessor|alias_method)\s' . \ '|^\s*\w+attr_(reader|accessor)\s|^\s*[#%"0-9]{0,4}\s*\{(\{\{|!!))' . \ '|^\s*[A-Z]\w+\s*\=' let b:foldsearchexpr = searchre endif else let searchre = a:search endif while search(searchre, flags) > 0 let line2 = line(".") while line2 - 1 >= line1 && line2 - 1 > 0 "sanity check let prevline = getline(line2 - 1) if exists("b:foldsearchprefix") && (match(prevline, b:foldsearchprefix) != -1) let line2 = line2 - 1 else break endif endwhile if (line2 -1 >= line1) execute ":" . line1 . "," . (line2-1) . "fold" let folded = 1 "at least one fold has been found endif let line1 = line2 "update marker let flags = "W" "turn off wrapping endwhile normal $G let line2 = line(".") if (line2 >= line1 && folded == 1) execute ":". line1 . "," . line2 . "fold" endif execute "normal " . origlineno . "G" endfunction "{{{~folds Fold Patterns " Command is executed as ':Fs pattern'" command! -nargs=? -complete=command Fs call Foldsearch(<q-args>) command! -nargs=? -complete=command Fold call Foldsearch(<q-args>) "command! R Fs \(^\s*\(\(def\|class\|module\)\s\)\)\|^\s*[#%"0-9]\{0,4\}\s*{\({{\|!!\) command! R Fs
Sorry for my ignorance – Josiah Ritchie (2005-11-04 (Fri) 08:01:05)
Is this intended to be added http://vim-ruby.rubyforge.org/ or standalone?
Maybe a better question? Do you assume familiarity with vim-ruby before using what you have here?
mfp 2005-11-04 (Fri) 15:25:44
The described folding mechanism doesn’t require vim-ruby; it will work fine without it. Besides, the Foldsearch function was derived from vimtip #77 and is fairly generic: it can be used for non-Ruby stuff too. For instance, when editing markup for this site, I use :Fs ^! (!, !!… correspond to headings in HikiDoc markup).

Keyword(s):[ruby] [vim] [code] [fold] [folding]
References:[Usable Ruby folding for Vim, second update] [Ruby support for Vim]