]> git.r.bdr.sh - rbdr/dotfiles/blob - vim/plugin/searchfold_0.9.vim
1fb9ba8f99f437aaee2e6c1240414a2819126498
[rbdr/dotfiles] / vim / plugin / searchfold_0.9.vim
1 " Vim global plugin -- create folds based on last search pattern
2 " General: {{{1
3 " File: searchfold.vim
4 " Created: 2008 Jan 19
5 " Last Change: 2011 May 24
6 " Rev Days: 18
7 " Author: Andy Wokula <anwoku@yahoo.de>
8 " Credits: Antonio Colombo's f.vim (Vimscript #318, 2005 May 10)
9 " Vim Version: Vim 7.0
10 " Version: 0.9
11
12 " Description:
13 " Provide mappings to fold away lines not matching the last search pattern
14 " and to restore old fold settings afterwards. Uses manual fold method,
15 " which allows for nested folds to hide or show more context. Doesn't
16 " preserve the user's manual folds.
17
18 " Usage:
19 " <Leader>z fold away lines not matching the last search pattern.
20 "
21 " With [count], change the initial foldlevel to ([count] minus
22 " one). The setting will be stored in g:searchfold_foldlevel
23 " and will be reused when [count] is omitted.
24 "
25 " <Leader>iz fold away lines that do match the last search pattern
26 " (inverse folding), also with [count].
27 "
28 " <Leader>Z restore the previous fold settings.
29 "
30 " Minor Extra: If already in restored state, show a dialog to
31 " revert all involved local fold options to the global
32 " defaults. The "(s)how" just prints info.
33
34 " Customization:
35 " :let g:searchfold_maxdepth = 7
36 " (number)
37 " maximum fold depth
38 "
39 " :let g:searchfold_usestep = 1
40 " (boolean)
41 " Controls how folds are organized: If 1 (default), each "zr"
42 " (after "\z") unfolds the same amount of lines above and
43 " below a match. If 0, only one more line is unfolded above a
44 " match. This applies for next "\z" or "\iz".
45 "
46 " :let g:searchfold_postZ_do_zv = 1
47 " (boolean)
48 " If 1, execute "zv" (view cursor line) after <Leader>Z.
49 "
50 " :let g:searchfold_foldlevel = 0
51 " (number)
52 " Initial 'foldlevel' to set for <Leader>z and <Leader>iz.
53 "
54 " :let g:searchfold_do_maps = 1
55 " (boolean)
56 " Whether to map the default keys or not.
57 "
58 " Hint: For boolean options, 1 actually means any non-zero number.
59
60 " Related: Vimscript #158 (foldutil.vim) ... still to be checked out
61 " http://www.noah.org/wiki/Vim#Folding
62 " Vimscript #2302 (foldsearch.vim)
63 " Vimscript #578 (allfold.tar.gz)
64 "
65 " Changes:
66 " v0.9 redraw removed, plug map renamed, comments (usestep ...)
67 " v0.8 added inverse folding (<Leader>iz), g:searchfold_foldlevel,
68 " count for <Leader>z, <Plug> mappings, disabled F(), (fixes)
69 " v0.7 b:searchfold fallback, s:foldtext check
70 " v0.6 (after v0.4) added customization vars (usestep, maxdepth, Zpost)
71 " reverting global fold settings adds to cmd-history
72 " v0.4 decreasing fold step always 1
73 " maxdepth 7 (before: 6)
74 " v0.3 (after v0.1) added a modified F() from f.vim
75 " functions now with return values
76 " v0.2 (skipped)
77
78 " Init Folklore: {{{1
79 if exists("loaded_searchfold")
80 finish
81 endif
82 let loaded_searchfold = 1
83
84 if v:version<700
85 echo "Searchfold: you need at least Vim 7.0"
86 finish
87 endif
88
89 " Customization: {{{1
90 if !exists("g:searchfold_maxdepth")
91 let g:searchfold_maxdepth = 7
92 endif
93 if !exists("g:searchfold_usestep")
94 let g:searchfold_usestep = 1
95 endif
96 if !exists("g:searchfold_postZ_do_zv")
97 let g:searchfold_postZ_do_zv = 1
98 endif
99 if !exists("g:searchfold_foldlevel")
100 let g:searchfold_foldlevel = 0
101 endif
102 if !exists("g:searchfold_do_maps")
103 let g:searchfold_do_maps = 1
104 endif
105
106 " s:variables {{{1
107 let s:foldtext = "(v:folddashes.'').((v:foldend)-(v:foldstart)+(1))"
108 " use unique notation of 'foldtext' to identify active searchfold in a
109 " window
110
111 func! s:FoldNested(from, to) " {{{1
112 " create one fold from line a:from to line a:to, with more nested folds
113 " return 1 if folds were created
114 " return 0 if from > to
115 let nlines = a:to - a:from
116 if nlines < 0
117 return 0
118 elseif nlines < 3
119 " range of 1 line possible
120 exec a:from.",".a:to. "fold"
121 return 1
122 endif
123
124 " calc folds, start with most outer fold
125 " - range of inner folds at least 2 lines (from<to)
126 " - limit nesting (depth)
127 " - snap folds at start and end of file
128 " - at greater "depth" (here depth->0), don't create folds with few
129 " lines only (check to-from>step)
130 if g:searchfold_maxdepth < 1 || g:searchfold_maxdepth > 12
131 let g:searchfold_maxdepth = 7
132 endif
133 let depth = g:searchfold_maxdepth
134 let step = 1 " decstep:''
135 let step1 = 1 " (const) decstep:'1'
136 let from = a:from
137 let to = a:to
138 " let decstep = exists("g:searchfold_usestep") && g:searchfold_usestep ? "" : "1"
139 let decstep = g:searchfold_usestep ? "" : "1"
140 let foldranges = []
141 let lined = line("$")
142 while depth>0 && from<to && to-from>step
143 call insert(foldranges, from.",".to)
144 let from += from>1 ? step : 0
145 " let to -= to<lined ? 1 : 0
146 let to -= to<lined ? step{decstep} : 0
147 let step += step " arbitrary
148 let depth -= 1
149 endwhile
150
151 " create folds, start with most inner fold
152 for range in foldranges
153 exec range. "fold"
154 endfor
155
156 return 1
157 endfunc
158
159 func! s:CreateFolds(inverse) " {{{1
160 " create search folds for the whole buffer based on last search pattern
161 let sav_cur = getpos(".")
162
163 let matches = [] " list of lnums
164 if !a:inverse
165 global//call add(matches, line("."))
166 else
167 vglobal//call add(matches, line("."))
168 endif
169
170 let nmatches = len(matches)
171 if nmatches > 0
172 call s:FoldNested(1, matches[0]-1)
173 let imax = nmatches - 1
174 let i = 0
175 while i < imax
176 if matches[i]+1 < matches[i+1]
177 call s:FoldNested(matches[i]+1, matches[i+1]-1)
178 endif
179 let i += 1
180 endwhile
181 call s:FoldNested(matches[imax]+1, line("$"))
182 endif
183
184 let &l:foldlevel = g:searchfold_foldlevel
185 call cursor(sav_cur[1:])
186
187 return nmatches
188 endfunc
189
190 func! <sid>SearchFoldEnable(inverse) "{{{1
191 " return number of matches
192 if !search("", "n")
193 " last search pattern not found, do nothing
194 return 0
195 endif
196 if (!exists("w:searchfold") || w:searchfold.bufnr != bufnr(""))
197 \ && &fdt != s:foldtext
198 " remember settings
199 let w:searchfold = { "bufnr": bufnr(""),
200 \ "fdm": &fdm,
201 \ "fdl": &fdl,
202 \ "fdt": &fdt,
203 \ "fen": &fen,
204 \ "fml": &fml }
205 " else: do not remember settings if already enabled
206 endif
207 setlocal foldmethod=manual
208 setlocal foldlevel=0
209 let &l:foldtext=s:foldtext
210 setlocal foldenable
211 setlocal foldminlines=0
212 normal! zE
213 if exists("w:searchfold")
214 let b:searchfold = w:searchfold
215 endif
216 return s:CreateFolds(a:inverse)
217 endfunc
218 func! SearchFoldRestore() "{{{1
219 " turn off
220 if exists("w:searchfold") && w:searchfold.bufnr == bufnr("")
221 " restore settings; var has the right settings if exists, but
222 " doesn't survive window split or win close/restore
223 let &l:fdm = w:searchfold.fdm
224 let &l:fdl = w:searchfold.fdl
225 let &l:fdt = w:searchfold.fdt
226 let &l:fen = w:searchfold.fen
227 let &l:fml = w:searchfold.fml
228 if &fdm == "manual"
229 " remove all search folds (old folds are lost anyway):
230 normal! zE
231 endif
232 unlet w:searchfold
233 elseif exists("b:searchfold") && &fdt == s:foldtext
234 " fallback only, may have wrong settings if overwritten
235 let &l:fdm = b:searchfold.fdm
236 let &l:fdl = b:searchfold.fdl
237 let &l:fdt = b:searchfold.fdt
238 let &l:fen = b:searchfold.fen
239 let &l:fml = b:searchfold.fml
240 if &fdm == "manual"
241 normal! zE
242 endif
243 else
244 let choice = input("Revert to global fold settings? (y/[n]/(s)how):")[0]
245 let setargs = 'fdm< fdl< fdt< fen< fml<'
246 if choice == "y"
247 let cmd = 'setlocal '. setargs
248 echo ':'. cmd
249 exec cmd
250 " call histadd(':', cmd)
251 elseif choice == "s"
252 let setargs = tr(setargs, "<","?")
253 let cmd = 'setglobal '. setargs
254 echo ':'. cmd
255 exec cmd
256 let cmd = 'setlocal '. setargs
257 echo ':'. cmd
258 exec cmd
259 endif
260 return
261 endif
262 if g:searchfold_postZ_do_zv
263 normal! zv
264 endif
265 endfunc
266
267 "" func! F() range "{{{1
268 " " commented out 2010 Jun 01
269 " " range arg: ignore range given by accident
270 " let pat = input("Which regexp? ", @/)
271 " if pat == ""
272 " if exists("w:searchfold")
273 " call SearchFoldRestore()
274 " endif
275 " return
276 " endif
277 " let @/ = pat
278 " call histadd("search", @/)
279 " call SearchFold()
280 " endfunc
281
282 " :call F() only for backwards compatibility
283
284 func! SearchFold(...) "{{{1
285 let inverse = a:0>=1 && a:1
286 if v:count >= 1
287 let g:searchfold_foldlevel = v:count - 1
288 endif
289 let nmatches = <sid>SearchFoldEnable(inverse)
290 " at most one match per line counted
291 if nmatches == 0
292 echohl ErrorMsg
293 echomsg "Searchfold: Pattern not found:" @/
294 echohl none
295 elseif nmatches == line("$")
296 echomsg "Searchfold: Pattern found in every line:" @/
297 elseif nmatches == 1
298 echo "Searchfold: 1 line found"
299 else
300 echo "Searchfold:" nmatches "lines found"
301 endif
302 " 2011 Feb 06 commented out:
303 " let &hls = &hls
304 " redraw
305 endfunc
306
307 " Mappings: {{{1
308 nn <silent> <Plug>SearchFoldNormal :<C-U>call SearchFold(0)<CR>
309 nn <silent> <Plug>SearchFoldInverse :<C-U>call SearchFold(1)<CR>
310 nn <silent> <Plug>SearchFoldRestore :<C-U>call SearchFoldRestore()<CR>
311
312 if g:searchfold_do_maps
313 nmap <Leader>z <Plug>SearchFoldNormal
314 nmap <Leader>iz <Plug>SearchFoldInverse
315 nmap <Leader>Z <Plug>SearchFoldRestore
316 endif
317
318 " Modeline: {{{1
319 " vim:set fdm=marker ts=8 sts=4 sw=4 noet: