Skip to content

Instantly share code, notes, and snippets.

@bpj
Last active December 11, 2015 15:55
Show Gist options
  • Save bpj/0727f00b5c566bdd8fb0 to your computer and use it in GitHub Desktop.
Save bpj/0727f00b5c566bdd8fb0 to your computer and use it in GitHub Desktop.
A foldexpr function to fold both Pod and Perl code sensibly in the same file
" vim: set ft=vim et tw=80:
" A foldexpr function to fold both Pod and Perl code sensibly in the same
" file. It folds Pod according to its logical structure and code by
" indentation.
"
" Author: Benct Philip Jonsson <bpjonsson@gmail.com>
" Version: 2015-12-11-14 (YYYY-MM-DD-HH)
"
" It requires that the Pod blocks are well-behaved and start with `=pod` or
" `=encoding`.
"
" * The first line of the file starts a level 1 fold
" * Each `=pod`/`=encoding` line starts a level 1 fold.
" * Each `=cut` line ends a level 1 fold.
" * Each `=headN` line starts a level N+1 fold
" * Each `=begin` or `=over` line increases the current fold level by one
" * Each `=end` or `=back` line decreases the current fold level by one
" * FIXME: If there is a variable `b:fold_items` and it has a non-empty value:
" * FIXME: Each `=item` line increases the current fold level by one
" * FIXME: Each `=for enditem` line decreases the current fold level by one
" * The line *after* each `=cut` line starts a level 1 fold
" * If a line has *more or less* indentation than the previous line
" we do our best to 'fake' folding by indent:
" * If the difference is positive the fold level is increased by
" the_difference/&softtabstop
" * If the difference is negative the fold level is decreased by
" abs the_difference/&softtabstop
" * Vim modeline(s) are start a level 1 fold
" * All other lines inherit the nearest lower foldlevel
"
" FIXME: Find a way to fold `=items`, preferably without closing pseudo-markup.
setlocal foldmethod=expr
setlocal foldexpr=GetPerlFold(v:lnum)
if empty(&l:foldcolumn)
setlocal foldcolumn=4
endif
" let s:pod_re='\v^\=%((encoding|pod)|(cut)|head(\d)|(begin)|(end)|(over)|(back)|(item)|(for enditem))>'
let s:pod_re='\v^\=%((encoding|pod)|(cut)|head(\d)|(begin)|(end)|(over)|(back))>'
" 1 pod 2 cut 3 head 4 begin 5 end 6 over 7 back 8 item 9 for enditem
function! GetPerlFold(lnum)
if a:lnum==1
return '>1'
else
let thisline=getline(a:lnum)
if thisline =~ '\v\c^\#\s+vim\:' " modeline
return '>1'
endif
let matches = matchlist(thisline, s:pod_re)
if !empty(matches)
if !empty(matches[1]) " pod/encoding
return '>1'
elseif !empty(matches[2]) " cut
return '<1'
elseif !empty(matches[3]) " head(\d)
return '>' . (matches[3]+1)
elseif !empty(matches[4]) " begin
return 'a1'
elseif !empty(matches[5]) " end
return 's1'
elseif !empty(matches[6]) " over
return 'a1'
elseif !empty(matches[7]) " back
return 's1'
" FIXME: find a way to do =item folding which works!
" elseif exists('b:fold_items') and !empty(b:fold_items)
" if !empty(matches[8]) " item
" return 'a1'
" elseif !empty(matches[9]) " for enditem
" return 's1'
" else
" return '='
" endif
else
return '='
endif
elseif empty(matches)
let prevline = getline(a:lnum-1)
if prevline =~ '\v^\=cut>'
return '>1'
" elseif thisline =~ '\v^\s*$'
" return '='
else " 'fake' indent folding
let thisindent = indent(a:lnum)
let previndent = indent(a:lnum-1)
if thisindent != previndent
let diffindent = float2nr(round(abs(thisindent-previndent)/&softtabstop))
return thisindent > previndent ? 'a' . diffindent : 's' . diffindent
else
return '='
endif
endif
endif
endif
return '0'
endfunction
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment