" Vim global plugin for folding text around search results " Last change: Wed Aug 10 10:06:31 BST 2011 " Maintainer: Damian Conway " License: This file is placed in the public domain. " If already loaded, we're done... if exists("loaded_foldsearch") finish endif let loaded_foldsearch = 1 " Preserve external compatibility options, then enable full vim compatibility... let s:save_cpo = &cpo set cpo&vim " Remember default behaviours... let s:DEFFOLDMETHOD = &foldmethod let s:DEFFOLDEXPR = &foldexpr let s:DEFFOLDTEXT = &foldtext let s:DEFFOLDLEVEL = 1000 " This is what the options are changed to... let s:FOLDEXPR = 'FS_FoldSearchLevel()' let s:FOLDTEXT = { \ 'visible' : "'___/ line ' . (v:foldend+1) . ' \\' . repeat('_',200) ", \ 'invisible' : "repeat(' ',200)", \} " Turn the mechanism on and off... function! FS_ToggleFoldAroundSearch (opts) " How much context to show... let b:FOLDCONTEXT = get(a:opts, 'context', 1) let &foldminlines = b:FOLDCONTEXT " Show folds??? let folds_visible = get(a:opts, 'folds', 'visible') " Make sure we can remember the previous setup... if !exists('b:foldsearch') let b:foldsearch = { 'active' : 0 } endif " Turn off, if it's on... if b:foldsearch.active let &foldmethod = get(b:foldsearch, 'prevfoldmethod', s:DEFFOLDMETHOD) let &foldtext = get(b:foldsearch, 'prevfoldtext', s:DEFFOLDTEXT) let &foldlevel = get(b:foldsearch, 'prevfoldlevel', s:DEFFOLDLEVEL) let &foldexpr = get(b:foldsearch, 'prevfoldexpr', s:DEFFOLDEXPR) " Remove autocommands for refolding for each new search... augroup FoldSearch autocmd! augroup END " Remember that it's off... let b:foldsearch.active = 0 " Disable special behaviour... nunmap return 'zE' " Turn on, if it's off... else " Save old settings... let b:foldsearch.prevfoldmethod = &foldmethod let b:foldsearch.prevfoldexpr = &foldexpr let b:foldsearch.prevfoldtext = &foldtext let b:foldsearch.prevfoldlevel = &foldlevel " Set up new behaviour... let &foldtext = s:FOLDTEXT[folds_visible] let &foldexpr = s:FOLDEXPR let &foldmethod = 'expr' let &foldlevel = 0 " Recalculate folding for each new search... augroup FoldSearch autocmd! autocmd CursorMoved * let b:inopenfold = foldlevel('.') && foldclosed('.') == -1 autocmd CursorMoved * let &foldexpr = &foldexpr autocmd CursorMoved * let &foldlevel = 0 autocmd CursorMoved * call ReopenFold() function! ReopenFold () if b:inopenfold normal zo endif endfunction augroup END " Enable special behaviour... nnoremap foldlevel('.') ? 'zA' : '' " Remember that it's on... let b:foldsearch.active = 1 return ":redraw!\" endif endfunction " Search for a particular target and turn search folding on (if not already on)... function! FS_FoldAroundTarget (target, opts) let context = get(a:opts, 'context', 1 ) let folds_visible = get(a:opts, 'folds', 'visible') " If already in a foldsearch... if exists('b:foldsearch') if b:foldsearch.active " If already folding this pattern... if @/ == a:target " Toggle off... return ":nohlsearch\:exec 'normal ' . FS_ToggleFoldAroundSearch({'context':1})\" " Otherwise stay in foldsearch and switch to target... else let b:FOLDCONTEXT = get(a:opts, 'context', 1) let &foldtext = s:FOLDTEXT[folds_visible] let &foldminlines = 1 return '/' . a:target . "\" endif endif endif " If not already in a foldsearch, search for target then toggle on... return '/' . a:target . "\:exec 'normal ' . FS_ToggleFoldAroundSearch(".string(a:opts).")\" endfunction " Utility function implements folding expression... function! FS_FoldSearchLevel () " Allow one line of context before and after... let startline = v:lnum > 1 ? v:lnum - b:FOLDCONTEXT : v:lnum let endline = v:lnum < line('$') ? v:lnum + b:FOLDCONTEXT : v:lnum let context = getline(startline, endline) " Simulate smartcase matching... let matchpattern = @/ if &smartcase && matchpattern =~ '\u' let matchpattern = '\C'.matchpattern endif " Line is folded if surrounding context doesn't match last search pattern... return match(context, matchpattern) == -1 endfunction " Restore previous external compatibility options let &cpo = s:save_cpo