" Vim global plugin -- create folds based on last search pattern " General: {{{1 " File: searchfold.vim " Created: 2008 Jan 19 " Last Change: 2011 May 24 " Rev Days: 18 " Author: Andy Wokula " Credits: Antonio Colombo's f.vim (Vimscript #318, 2005 May 10) " Vim Version: Vim 7.0 " Version: 0.9 " Description: " Provide mappings to fold away lines not matching the last search pattern " and to restore old fold settings afterwards. Uses manual fold method, " which allows for nested folds to hide or show more context. Doesn't " preserve the user's manual folds. " Usage: " z fold away lines not matching the last search pattern. " " With [count], change the initial foldlevel to ([count] minus " one). The setting will be stored in g:searchfold_foldlevel " and will be reused when [count] is omitted. " " iz fold away lines that do match the last search pattern " (inverse folding), also with [count]. " " Z restore the previous fold settings. " " Minor Extra: If already in restored state, show a dialog to " revert all involved local fold options to the global " defaults. The "(s)how" just prints info. " Customization: " :let g:searchfold_maxdepth = 7 " (number) " maximum fold depth " " :let g:searchfold_usestep = 1 " (boolean) " Controls how folds are organized: If 1 (default), each "zr" " (after "\z") unfolds the same amount of lines above and " below a match. If 0, only one more line is unfolded above a " match. This applies for next "\z" or "\iz". " " :let g:searchfold_postZ_do_zv = 1 " (boolean) " If 1, execute "zv" (view cursor line) after Z. " " :let g:searchfold_foldlevel = 0 " (number) " Initial 'foldlevel' to set for z and iz. " " :let g:searchfold_do_maps = 1 " (boolean) " Whether to map the default keys or not. " " Hint: For boolean options, 1 actually means any non-zero number. " Related: Vimscript #158 (foldutil.vim) ... still to be checked out " http://www.noah.org/wiki/Vim#Folding " Vimscript #2302 (foldsearch.vim) " Vimscript #578 (allfold.tar.gz) " " Changes: " v0.9 redraw removed, plug map renamed, comments (usestep ...) " v0.8 added inverse folding (iz), g:searchfold_foldlevel, " count for z, mappings, disabled F(), (fixes) " v0.7 b:searchfold fallback, s:foldtext check " v0.6 (after v0.4) added customization vars (usestep, maxdepth, Zpost) " reverting global fold settings adds to cmd-history " v0.4 decreasing fold step always 1 " maxdepth 7 (before: 6) " v0.3 (after v0.1) added a modified F() from f.vim " functions now with return values " v0.2 (skipped) " Init Folklore: {{{1 if exists("loaded_searchfold") finish endif let loaded_searchfold = 1 if v:version<700 echo "Searchfold: you need at least Vim 7.0" finish endif " Customization: {{{1 if !exists("g:searchfold_maxdepth") let g:searchfold_maxdepth = 7 endif if !exists("g:searchfold_usestep") let g:searchfold_usestep = 1 endif if !exists("g:searchfold_postZ_do_zv") let g:searchfold_postZ_do_zv = 1 endif if !exists("g:searchfold_foldlevel") let g:searchfold_foldlevel = 0 endif if !exists("g:searchfold_do_maps") let g:searchfold_do_maps = 1 endif " s:variables {{{1 let s:foldtext = "(v:folddashes.'').((v:foldend)-(v:foldstart)+(1))" " use unique notation of 'foldtext' to identify active searchfold in a " window func! s:FoldNested(from, to) " {{{1 " create one fold from line a:from to line a:to, with more nested folds " return 1 if folds were created " return 0 if from > to let nlines = a:to - a:from if nlines < 0 return 0 elseif nlines < 3 " range of 1 line possible exec a:from.",".a:to. "fold" return 1 endif " calc folds, start with most outer fold " - range of inner folds at least 2 lines (from0), don't create folds with few " lines only (check to-from>step) if g:searchfold_maxdepth < 1 || g:searchfold_maxdepth > 12 let g:searchfold_maxdepth = 7 endif let depth = g:searchfold_maxdepth let step = 1 " decstep:'' let step1 = 1 " (const) decstep:'1' let from = a:from let to = a:to " let decstep = exists("g:searchfold_usestep") && g:searchfold_usestep ? "" : "1" let decstep = g:searchfold_usestep ? "" : "1" let foldranges = [] let lined = line("$") while depth>0 && fromstep call insert(foldranges, from.",".to) let from += from>1 ? step : 0 " let to -= to 0 call s:FoldNested(1, matches[0]-1) let imax = nmatches - 1 let i = 0 while i < imax if matches[i]+1 < matches[i+1] call s:FoldNested(matches[i]+1, matches[i+1]-1) endif let i += 1 endwhile call s:FoldNested(matches[imax]+1, line("$")) endif let &l:foldlevel = g:searchfold_foldlevel call cursor(sav_cur[1:]) return nmatches endfunc func! SearchFoldEnable(inverse) "{{{1 " return number of matches if !search("", "n") " last search pattern not found, do nothing return 0 endif if (!exists("w:searchfold") || w:searchfold.bufnr != bufnr("")) \ && &fdt != s:foldtext " remember settings let w:searchfold = { "bufnr": bufnr(""), \ "fdm": &fdm, \ "fdl": &fdl, \ "fdt": &fdt, \ "fen": &fen, \ "fml": &fml } " else: do not remember settings if already enabled endif setlocal foldmethod=manual setlocal foldlevel=0 let &l:foldtext=s:foldtext setlocal foldenable setlocal foldminlines=0 normal! zE if exists("w:searchfold") let b:searchfold = w:searchfold endif return s:CreateFolds(a:inverse) endfunc func! SearchFoldRestore() "{{{1 " turn off if exists("w:searchfold") && w:searchfold.bufnr == bufnr("") " restore settings; var has the right settings if exists, but " doesn't survive window split or win close/restore let &l:fdm = w:searchfold.fdm let &l:fdl = w:searchfold.fdl let &l:fdt = w:searchfold.fdt let &l:fen = w:searchfold.fen let &l:fml = w:searchfold.fml if &fdm == "manual" " remove all search folds (old folds are lost anyway): normal! zE endif unlet w:searchfold elseif exists("b:searchfold") && &fdt == s:foldtext " fallback only, may have wrong settings if overwritten let &l:fdm = b:searchfold.fdm let &l:fdl = b:searchfold.fdl let &l:fdt = b:searchfold.fdt let &l:fen = b:searchfold.fen let &l:fml = b:searchfold.fml if &fdm == "manual" normal! zE endif else let choice = input("Revert to global fold settings? (y/[n]/(s)how):")[0] let setargs = 'fdm< fdl< fdt< fen< fml<' if choice == "y" let cmd = 'setlocal '. setargs echo ':'. cmd exec cmd " call histadd(':', cmd) elseif choice == "s" let setargs = tr(setargs, "<","?") let cmd = 'setglobal '. setargs echo ':'. cmd exec cmd let cmd = 'setlocal '. setargs echo ':'. cmd exec cmd endif return endif if g:searchfold_postZ_do_zv normal! zv endif endfunc "" func! F() range "{{{1 " " commented out 2010 Jun 01 " " range arg: ignore range given by accident " let pat = input("Which regexp? ", @/) " if pat == "" " if exists("w:searchfold") " call SearchFoldRestore() " endif " return " endif " let @/ = pat " call histadd("search", @/) " call SearchFold() " endfunc " :call F() only for backwards compatibility func! SearchFold(...) "{{{1 let inverse = a:0>=1 && a:1 if v:count >= 1 let g:searchfold_foldlevel = v:count - 1 endif let nmatches = SearchFoldEnable(inverse) " at most one match per line counted if nmatches == 0 echohl ErrorMsg echomsg "Searchfold: Pattern not found:" @/ echohl none elseif nmatches == line("$") echomsg "Searchfold: Pattern found in every line:" @/ elseif nmatches == 1 echo "Searchfold: 1 line found" else echo "Searchfold:" nmatches "lines found" endif " 2011 Feb 06 commented out: " let &hls = &hls " redraw endfunc " Mappings: {{{1 nn SearchFoldNormal :call SearchFold(0) nn SearchFoldInverse :call SearchFold(1) nn SearchFoldRestore :call SearchFoldRestore() if g:searchfold_do_maps nmap z SearchFoldNormal nmap iz SearchFoldInverse nmap Z SearchFoldRestore endif " Modeline: {{{1 " vim:set fdm=marker ts=8 sts=4 sw=4 noet: