214888c9 |
"" Guard
if exists('g:autoloaded_unobtrusive_folds') || &compatible
finish
endif
let g:autoloaded_unobtrusive_folds = 1
"" Configuration
""
" @section Configuration, config
" Since this method of folding is more involved than the built-in ones it can
" be slow for very big files. Options are provided to mitigate this.
""" g:unobtrusive_fold_max_lines
if !exists('g:unobtrusive_fold_max_lines')
""
" Set to non-zero value to make the commands below do nothing if the number
" of lines in the current buffer is higher than the given value. Defaults to
" 0.
let g:unobtrusive_fold_max_lines = 0
endif
""" g:unobtrusive_fold_max_prevlines
if !exists('g:unobtrusive_fold_max_prevlines')
""
" Set to non-zero value to look at at most this many previous lines when
" calculating folds. Defaults to 0.
let g:unobtrusive_fold_max_prevlines = 0
endif
"" Constants
let s:pattern_prefix = '\V\c\^\s\*'
"" Functions
""" #text()
""
" @public
" Set 'foldtext' to this function to get an improved version of
" `getline(v:foldstart)`. Leading tabs are expanded to 'tabstop' spaces. If
" 'fillchars' `fold:` is not space, the number of lines included in the fold is
" printed, padded with `fold:` so that it lines up with one column short of the
" right edge of the window, or 'textwidth' (if not 0), whichever is smaller.
" The empty column at the far right is nice for people who set 'colorcolumn' to
" `+1`. The rest of the line is cleared (i.e. not filled automatically by Vim
" with 'fillchars' `fold:`). A good value for `fold:` might be the box drawing
" character `─`.
function! unobtrusive_fold#text() abort
let line = getline(v:foldstart)
let line = substitute(line, '\t', repeat(' ', &l:tabstop), 'g')
let fillchar = matchstr(&fillchars, '.*\(^\|,\)fold:\zs.\ze')
if empty(fillchar)
let fillchar = '-'
endif
if fillchar !=# ' '
let foldlines = v:foldend - v:foldstart + 1
let signs = len(split(execute('sign place buffer=' . bufnr()), '\n')) - 1
let numbers = &l:number ? line('$') : &l:relativenumber ? winheight(0) : 0
let winwidth = winwidth(0)
\ - &l:foldcolumn
\ - (signs && &l:signcolumn ==# 'auto' || &l:signcolumn ==# 'yes' ? 2 : 0)
\ - (numbers ? max([&l:numberwidth, strlen(numbers) + 1]) : 0)
let width = winwidth - 1
if &l:textwidth
let width = min([width, &l:textwidth])
endif
let fill = repeat(fillchar, width - strlen(line) - strlen(foldlines) - 2)
let pad = repeat(' ', winwidth - width)
let line = line . ' ' . fill . ' ' . foldlines . pad
endif
return line
endfunction
""" #comment()
function! unobtrusive_fold#comment(bang) abort
if empty(&l:commentstring)
return
endif
let commentstart = escape(split(&l:commentstring, '\s*%s')[0], '\')
let commentlast = escape(commentstart[-1:], '\')
call unobtrusive_fold#set(
\ a:bang,
\ commentstart . '\zs' . commentlast . '\+\ze',
\ commentstart,
\ []
\ )
endfunction
""" #char()
function! unobtrusive_fold#char(bang, char) abort
let char = escape(a:char, '\')
call unobtrusive_fold#set(
\ a:bang,
\ '\zs' . char . '\+\ze',
\ '',
\ ['Comment', 'Code', 'Snippet']
\ )
endfunction
""" #set()
function! unobtrusive_fold#set(bang, pattern, commentstart, ignore) abort
if g:unobtrusive_fold_max_lines && line('$') > g:unobtrusive_fold_max_lines
return
endif
let w:unobtrusive_fold_indent = !empty(a:bang)
let w:unobtrusive_fold_pattern = a:pattern . '\s\*\S'
let w:unobtrusive_fold_ignore = a:ignore
let w:unobtrusive_fold_commentstart = a:commentstart
let &l:foldexpr = 'unobtrusive_fold#expr()'
let &l:foldmethod = 'expr'
endfunction
""" s:foldlevel()
function! s:foldlevel(lnum) abort
let foldlevel = strlen(matchstr(
\ getline(a:lnum),
\ s:pattern_prefix . w:unobtrusive_fold_pattern
\ ))
if foldlevel && !empty(w:unobtrusive_fold_ignore)
for syntax in map(synstack(a:lnum, 1), 'synIDattr(v:val, "name")')
for ignore in w:unobtrusive_fold_ignore
if syntax =~# '\v' . '(^|[a-z])' . ignore . '([A-Z]|$)'
return 0
endif
endfor
endfor
endif
return foldlevel
endfunction
""" s:indent()
function! s:indent(lnum, ...) abort
if (a:lnum < 1 && line('$') < a:lnum) || (a:0 && s:foldlevel(a:lnum))
return 0
endif
return !empty(getline(a:lnum)) + indent(a:lnum) / shiftwidth()
endfunction
""" s:prevfoldlevelbase()
function! s:prevfoldlevelbase(lnum) abort
let stopline = g:unobtrusive_fold_max_prevlines
\ ? max(1, a:lnum - g:unobtrusive_fold_max_prevlines)
\ : 0
let pattern_column = '\%<' . (indent(a:lnum) + 2) . 'v'
let pos = getpos('.')
call cursor(a:lnum, 1)
let prev = search(
\ s:pattern_prefix . pattern_column . w:unobtrusive_fold_pattern,
\ 'bW',
\ stopline
\ )
call setpos('.', pos)
if !prev
return [0, 0]
endif
let foldlevel = s:foldlevel(prev)
return [foldlevel, foldlevel - indent(prev) / shiftwidth()]
endfunction
""" s:nextnonblank()
function! s:nextnonblank(lnum)
if empty(w:unobtrusive_fold_commentstart)
return nextnonblank(a:lnum)
endif
let pos = getpos('.')
call cursor(a:lnum, 1)
let next = search(
\ '\V\^\(' . w:unobtrusive_fold_commentstart . '\[[:punct:]]\*\$\)\@!\.',
\ 'Wc'
\ )
call setpos('.', pos)
return next
endfunction
""" #expr()
function! unobtrusive_fold#expr() abort
"""" Empty line
if empty(getline(v:lnum))
return '='
endif
"""" Marker...
"""" ...start
let foldlevel = s:foldlevel(v:lnum)
if foldlevel
return '>' . foldlevel
endif
"""" ...end...
let next = s:nextnonblank(v:lnum + 1)
"""" ...foldlevel
let nextfoldlevel = s:foldlevel(next)
if nextfoldlevel
let prevfoldlevel = s:prevfoldlevelbase(v:lnum)[0]
return '<' . (min([nextfoldlevel, prevfoldlevel]) + 1)
endif
"""" ...indent
if indent(next) < indent(v:lnum)
let prevfoldlevel = s:prevfoldlevelbase(v:lnum)[0]
let nextfoldlevel = s:prevfoldlevelbase(next)[0]
if nextfoldlevel < prevfoldlevel
return '<' . (nextfoldlevel + 1)
endif
endif
"""" Indent...
if w:unobtrusive_fold_indent
let indent = s:indent(v:lnum)
let nextindent = s:indent(next)
let aboveindent = s:indent(v:lnum - 1, 1)
let belowindent = s:indent(v:lnum + 1, 1)
"""" ...start
if aboveindent < indent && (belowindent >= indent || nextindent > indent)
let prevbase = s:prevfoldlevelbase(v:lnum)[1]
return '>' . (prevbase + indent)
endif
"""" ...end
if belowindent < indent && (aboveindent || belowindent)
let prevbase = s:prevfoldlevelbase(v:lnum)[1]
return '<' . (prevbase + min([indent, nextindent + !!belowindent]))
endif
endif
"""" No change
return '='
endfunction
""" #debug()
function! unobtrusive_fold#debug() abort
let lines = range(1, line('$'))
let exprs = map(copy(lines), 'execute("let v:lnum=v:val|echon ".&foldexpr)')
for l in lines
let e = exprs[l-1]
if len(e) == 1
let exprs[l-1] = e =~# '^[0-9]$' ? ' '.e : e.' '
endif
endfor
let levels = map(copy(lines), 'foldlevel(v:val)')
let debugs = map(copy(lines), '"[".exprs[v:val-1]."|".levels[v:val-1]."]"')
let pos = getpos('.')
call setreg('"', join(debugs, "\n"), 'b')
normal! gg0""P
call setpos('.', pos)
endfunction
|