Browse code

Add implementation

Robert Cranston authored on 03/06/2022 01:42:07
Showing 1 changed files
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