1 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,252 @@ |
1 |
+"" Guard |
|
2 |
+if exists('g:autoloaded_unobtrusive_folds') || &compatible |
|
3 |
+ finish |
|
4 |
+endif |
|
5 |
+let g:autoloaded_unobtrusive_folds = 1 |
|
6 |
+ |
|
7 |
+"" Configuration |
|
8 |
+ |
|
9 |
+"" |
|
10 |
+" @section Configuration, config |
|
11 |
+" Since this method of folding is more involved than the built-in ones it can |
|
12 |
+" be slow for very big files. Options are provided to mitigate this. |
|
13 |
+ |
|
14 |
+""" g:unobtrusive_fold_max_lines |
|
15 |
+if !exists('g:unobtrusive_fold_max_lines') |
|
16 |
+ "" |
|
17 |
+ " Set to non-zero value to make the commands below do nothing if the number |
|
18 |
+ " of lines in the current buffer is higher than the given value. Defaults to |
|
19 |
+ " 0. |
|
20 |
+ let g:unobtrusive_fold_max_lines = 0 |
|
21 |
+endif |
|
22 |
+ |
|
23 |
+""" g:unobtrusive_fold_max_prevlines |
|
24 |
+if !exists('g:unobtrusive_fold_max_prevlines') |
|
25 |
+ "" |
|
26 |
+ " Set to non-zero value to look at at most this many previous lines when |
|
27 |
+ " calculating folds. Defaults to 0. |
|
28 |
+ let g:unobtrusive_fold_max_prevlines = 0 |
|
29 |
+endif |
|
30 |
+ |
|
31 |
+"" Constants |
|
32 |
+ |
|
33 |
+let s:pattern_prefix = '\V\c\^\s\*' |
|
34 |
+ |
|
35 |
+"" Functions |
|
36 |
+ |
|
37 |
+""" #text() |
|
38 |
+ |
|
39 |
+"" |
|
40 |
+" @public |
|
41 |
+" Set 'foldtext' to this function to get an improved version of |
|
42 |
+" `getline(v:foldstart)`. Leading tabs are expanded to 'tabstop' spaces. If |
|
43 |
+" 'fillchars' `fold:` is not space, the number of lines included in the fold is |
|
44 |
+" printed, padded with `fold:` so that it lines up with one column short of the |
|
45 |
+" right edge of the window, or 'textwidth' (if not 0), whichever is smaller. |
|
46 |
+" The empty column at the far right is nice for people who set 'colorcolumn' to |
|
47 |
+" `+1`. The rest of the line is cleared (i.e. not filled automatically by Vim |
|
48 |
+" with 'fillchars' `fold:`). A good value for `fold:` might be the box drawing |
|
49 |
+" character `─`. |
|
50 |
+function! unobtrusive_fold#text() abort |
|
51 |
+ let line = getline(v:foldstart) |
|
52 |
+ let line = substitute(line, '\t', repeat(' ', &l:tabstop), 'g') |
|
53 |
+ let fillchar = matchstr(&fillchars, '.*\(^\|,\)fold:\zs.\ze') |
|
54 |
+ if empty(fillchar) |
|
55 |
+ let fillchar = '-' |
|
56 |
+ endif |
|
57 |
+ if fillchar !=# ' ' |
|
58 |
+ let foldlines = v:foldend - v:foldstart + 1 |
|
59 |
+ let signs = len(split(execute('sign place buffer=' . bufnr()), '\n')) - 1 |
|
60 |
+ let numbers = &l:number ? line('$') : &l:relativenumber ? winheight(0) : 0 |
|
61 |
+ let winwidth = winwidth(0) |
|
62 |
+ \ - &l:foldcolumn |
|
63 |
+ \ - (signs && &l:signcolumn ==# 'auto' || &l:signcolumn ==# 'yes' ? 2 : 0) |
|
64 |
+ \ - (numbers ? max([&l:numberwidth, strlen(numbers) + 1]) : 0) |
|
65 |
+ let width = winwidth - 1 |
|
66 |
+ if &l:textwidth |
|
67 |
+ let width = min([width, &l:textwidth]) |
|
68 |
+ endif |
|
69 |
+ let fill = repeat(fillchar, width - strlen(line) - strlen(foldlines) - 2) |
|
70 |
+ let pad = repeat(' ', winwidth - width) |
|
71 |
+ let line = line . ' ' . fill . ' ' . foldlines . pad |
|
72 |
+ endif |
|
73 |
+ return line |
|
74 |
+endfunction |
|
75 |
+ |
|
76 |
+""" #comment() |
|
77 |
+ |
|
78 |
+function! unobtrusive_fold#comment(bang) abort |
|
79 |
+ if empty(&l:commentstring) |
|
80 |
+ return |
|
81 |
+ endif |
|
82 |
+ let commentstart = escape(split(&l:commentstring, '\s*%s')[0], '\') |
|
83 |
+ let commentlast = escape(commentstart[-1:], '\') |
|
84 |
+ call unobtrusive_fold#set( |
|
85 |
+ \ a:bang, |
|
86 |
+ \ commentstart . '\zs' . commentlast . '\+\ze', |
|
87 |
+ \ commentstart, |
|
88 |
+ \ [] |
|
89 |
+ \ ) |
|
90 |
+endfunction |
|
91 |
+ |
|
92 |
+""" #char() |
|
93 |
+ |
|
94 |
+function! unobtrusive_fold#char(bang, char) abort |
|
95 |
+ let char = escape(a:char, '\') |
|
96 |
+ call unobtrusive_fold#set( |
|
97 |
+ \ a:bang, |
|
98 |
+ \ '\zs' . char . '\+\ze', |
|
99 |
+ \ '', |
|
100 |
+ \ ['Comment', 'Code', 'Snippet'] |
|
101 |
+ \ ) |
|
102 |
+endfunction |
|
103 |
+ |
|
104 |
+""" #set() |
|
105 |
+ |
|
106 |
+function! unobtrusive_fold#set(bang, pattern, commentstart, ignore) abort |
|
107 |
+ if g:unobtrusive_fold_max_lines && line('$') > g:unobtrusive_fold_max_lines |
|
108 |
+ return |
|
109 |
+ endif |
|
110 |
+ let w:unobtrusive_fold_indent = !empty(a:bang) |
|
111 |
+ let w:unobtrusive_fold_pattern = a:pattern . '\s\*\S' |
|
112 |
+ let w:unobtrusive_fold_ignore = a:ignore |
|
113 |
+ let w:unobtrusive_fold_commentstart = a:commentstart |
|
114 |
+ let &l:foldexpr = 'unobtrusive_fold#expr()' |
|
115 |
+ let &l:foldmethod = 'expr' |
|
116 |
+endfunction |
|
117 |
+ |
|
118 |
+""" s:foldlevel() |
|
119 |
+ |
|
120 |
+function! s:foldlevel(lnum) abort |
|
121 |
+ let foldlevel = strlen(matchstr( |
|
122 |
+ \ getline(a:lnum), |
|
123 |
+ \ s:pattern_prefix . w:unobtrusive_fold_pattern |
|
124 |
+ \ )) |
|
125 |
+ if foldlevel && !empty(w:unobtrusive_fold_ignore) |
|
126 |
+ for syntax in map(synstack(a:lnum, 1), 'synIDattr(v:val, "name")') |
|
127 |
+ for ignore in w:unobtrusive_fold_ignore |
|
128 |
+ if syntax =~# '\v' . '(^|[a-z])' . ignore . '([A-Z]|$)' |
|
129 |
+ return 0 |
|
130 |
+ endif |
|
131 |
+ endfor |
|
132 |
+ endfor |
|
133 |
+ endif |
|
134 |
+ return foldlevel |
|
135 |
+endfunction |
|
136 |
+ |
|
137 |
+""" s:indent() |
|
138 |
+ |
|
139 |
+function! s:indent(lnum, ...) abort |
|
140 |
+ if (a:lnum < 1 && line('$') < a:lnum) || (a:0 && s:foldlevel(a:lnum)) |
|
141 |
+ return 0 |
|
142 |
+ endif |
|
143 |
+ return !empty(getline(a:lnum)) + indent(a:lnum) / shiftwidth() |
|
144 |
+endfunction |
|
145 |
+ |
|
146 |
+""" s:prevfoldlevelbase() |
|
147 |
+ |
|
148 |
+function! s:prevfoldlevelbase(lnum) abort |
|
149 |
+ let stopline = g:unobtrusive_fold_max_prevlines |
|
150 |
+ \ ? max(1, a:lnum - g:unobtrusive_fold_max_prevlines) |
|
151 |
+ \ : 0 |
|
152 |
+ let pattern_column = '\%<' . (indent(a:lnum) + 2) . 'v' |
|
153 |
+ let pos = getpos('.') |
|
154 |
+ call cursor(a:lnum, 1) |
|
155 |
+ let prev = search( |
|
156 |
+ \ s:pattern_prefix . pattern_column . w:unobtrusive_fold_pattern, |
|
157 |
+ \ 'bW', |
|
158 |
+ \ stopline |
|
159 |
+ \ ) |
|
160 |
+ call setpos('.', pos) |
|
161 |
+ if !prev |
|
162 |
+ return [0, 0] |
|
163 |
+ endif |
|
164 |
+ let foldlevel = s:foldlevel(prev) |
|
165 |
+ return [foldlevel, foldlevel - indent(prev) / shiftwidth()] |
|
166 |
+endfunction |
|
167 |
+ |
|
168 |
+""" s:nextnonblank() |
|
169 |
+ |
|
170 |
+function! s:nextnonblank(lnum) |
|
171 |
+ if empty(w:unobtrusive_fold_commentstart) |
|
172 |
+ return nextnonblank(a:lnum) |
|
173 |
+ endif |
|
174 |
+ let pos = getpos('.') |
|
175 |
+ call cursor(a:lnum, 1) |
|
176 |
+ let next = search( |
|
177 |
+ \ '\V\^\(' . w:unobtrusive_fold_commentstart . '\[[:punct:]]\*\$\)\@!\.', |
|
178 |
+ \ 'Wc' |
|
179 |
+ \ ) |
|
180 |
+ call setpos('.', pos) |
|
181 |
+ return next |
|
182 |
+endfunction |
|
183 |
+ |
|
184 |
+""" #expr() |
|
185 |
+ |
|
186 |
+function! unobtrusive_fold#expr() abort |
|
187 |
+ """" Empty line |
|
188 |
+ if empty(getline(v:lnum)) |
|
189 |
+ return '=' |
|
190 |
+ endif |
|
191 |
+ """" Marker... |
|
192 |
+ """" ...start |
|
193 |
+ let foldlevel = s:foldlevel(v:lnum) |
|
194 |
+ if foldlevel |
|
195 |
+ return '>' . foldlevel |
|
196 |
+ endif |
|
197 |
+ """" ...end... |
|
198 |
+ let next = s:nextnonblank(v:lnum + 1) |
|
199 |
+ """" ...foldlevel |
|
200 |
+ let nextfoldlevel = s:foldlevel(next) |
|
201 |
+ if nextfoldlevel |
|
202 |
+ let prevfoldlevel = s:prevfoldlevelbase(v:lnum)[0] |
|
203 |
+ return '<' . (min([nextfoldlevel, prevfoldlevel]) + 1) |
|
204 |
+ endif |
|
205 |
+ """" ...indent |
|
206 |
+ if indent(next) < indent(v:lnum) |
|
207 |
+ let prevfoldlevel = s:prevfoldlevelbase(v:lnum)[0] |
|
208 |
+ let nextfoldlevel = s:prevfoldlevelbase(next)[0] |
|
209 |
+ if nextfoldlevel < prevfoldlevel |
|
210 |
+ return '<' . (nextfoldlevel + 1) |
|
211 |
+ endif |
|
212 |
+ endif |
|
213 |
+ """" Indent... |
|
214 |
+ if w:unobtrusive_fold_indent |
|
215 |
+ let indent = s:indent(v:lnum) |
|
216 |
+ let nextindent = s:indent(next) |
|
217 |
+ let aboveindent = s:indent(v:lnum - 1, 1) |
|
218 |
+ let belowindent = s:indent(v:lnum + 1, 1) |
|
219 |
+ """" ...start |
|
220 |
+ if aboveindent < indent && (belowindent >= indent || nextindent > indent) |
|
221 |
+ let prevbase = s:prevfoldlevelbase(v:lnum)[1] |
|
222 |
+ return '>' . (prevbase + indent) |
|
223 |
+ endif |
|
224 |
+ """" ...end |
|
225 |
+ if belowindent < indent && (aboveindent || belowindent) |
|
226 |
+ let prevbase = s:prevfoldlevelbase(v:lnum)[1] |
|
227 |
+ return '<' . (prevbase + min([indent, nextindent + !!belowindent])) |
|
228 |
+ endif |
|
229 |
+ endif |
|
230 |
+ """" No change |
|
231 |
+ return '=' |
|
232 |
+endfunction |
|
233 |
+ |
|
234 |
+""" #debug() |
|
235 |
+ |
|
236 |
+function! unobtrusive_fold#debug() abort |
|
237 |
+ let lines = range(1, line('$')) |
|
238 |
+ let exprs = map(copy(lines), 'execute("let v:lnum=v:val|echon ".&foldexpr)') |
|
239 |
+ for l in lines |
|
240 |
+ let e = exprs[l-1] |
|
241 |
+ if len(e) == 1 |
|
242 |
+ let exprs[l-1] = e =~# '^[0-9]$' ? ' '.e : e.' ' |
|
243 |
+ endif |
|
244 |
+ endfor |
|
245 |
+ let levels = map(copy(lines), 'foldlevel(v:val)') |
|
246 |
+ let debugs = map(copy(lines), '"[".exprs[v:val-1]."|".levels[v:val-1]."]"') |
|
247 |
+ |
|
248 |
+ let pos = getpos('.') |
|
249 |
+ call setreg('"', join(debugs, "\n"), 'b') |
|
250 |
+ normal! gg0""P |
|
251 |
+ call setpos('.', pos) |
|
252 |
+endfunction |