]>
Commit | Line | Data |
---|---|---|
0d23b6e5 BB |
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: |