378 lines
11 KiB
VimL
378 lines
11 KiB
VimL
" Folding support for LaTeX
|
|
|
|
"
|
|
" Options
|
|
" g:LatexBox_Folding - Turn on/off folding
|
|
" g:LatexBox_fold_text - Turn on/off LatexBox fold text function
|
|
" g:LatexBox_fold_preamble - Turn on/off folding of preamble
|
|
" g:LatexBox_fold_parts - Define parts (eq. appendix, frontmatter) to fold
|
|
" g:LatexBox_fold_sections - Define section levels to fold
|
|
" g:LatexBox_fold_envs - Turn on/off folding of environments
|
|
" g:LatexBox_fold_toc - Turn on/off folding of TOC
|
|
" g:LatexBox_fold_toc_levels - Set max TOC fold level
|
|
"
|
|
" {{{1 Initialize options to default values.
|
|
if !exists('g:LatexBox_Folding')
|
|
let g:LatexBox_Folding=0
|
|
endif
|
|
if !exists('g:LatexBox_fold_text')
|
|
let g:LatexBox_fold_text=1
|
|
endif
|
|
if !exists('g:LatexBox_fold_preamble')
|
|
let g:LatexBox_fold_preamble=1
|
|
endif
|
|
if !exists('g:LatexBox_fold_envs')
|
|
let g:LatexBox_fold_envs=1
|
|
endif
|
|
if !exists('g:LatexBox_fold_envs_force')
|
|
let g:LatexBox_fold_envs_force = []
|
|
endif
|
|
if !exists('g:LatexBox_fold_parts')
|
|
let g:LatexBox_fold_parts=[
|
|
\ "appendix",
|
|
\ "frontmatter",
|
|
\ "mainmatter",
|
|
\ "backmatter"
|
|
\ ]
|
|
endif
|
|
if !exists('g:LatexBox_fold_sections')
|
|
let g:LatexBox_fold_sections=[
|
|
\ "part",
|
|
\ "chapter",
|
|
\ "section",
|
|
\ "subsection",
|
|
\ "subsubsection"
|
|
\ ]
|
|
endif
|
|
if !exists('g:LatexBox_fold_toc')
|
|
let g:LatexBox_fold_toc=0
|
|
endif
|
|
if !exists('g:LatexBox_fold_toc_levels')
|
|
let g:LatexBox_fold_toc_levels=1
|
|
endif
|
|
if !exists('g:LatexBox_fold_automatic')
|
|
let g:LatexBox_fold_automatic=1
|
|
endif
|
|
" }}}1
|
|
|
|
if g:LatexBox_Folding == 0
|
|
finish
|
|
endif
|
|
|
|
" {{{1 Set folding options for vim
|
|
setl foldexpr=LatexBox_FoldLevel(v:lnum)
|
|
if g:LatexBox_fold_text == 1
|
|
setl foldtext=LatexBox_FoldText()
|
|
endif
|
|
if g:LatexBox_fold_automatic == 1
|
|
setl foldmethod=expr
|
|
|
|
"
|
|
" The foldexpr function returns "=" for most lines, which means it can become
|
|
" slow for large files. The following is a hack that is based on this reply to
|
|
" a discussion on the Vim Developer list:
|
|
" http://permalink.gmane.org/gmane.editors.vim.devel/14100
|
|
"
|
|
augroup FastFold
|
|
autocmd!
|
|
autocmd InsertEnter *.tex if !&diff | setlocal foldmethod=manual | endif
|
|
autocmd InsertLeave *.tex if !&diff | setlocal foldmethod=expr | endif
|
|
augroup end
|
|
else
|
|
setl foldmethod=manual
|
|
endif
|
|
|
|
function! LatexBox_FoldOnDemand()
|
|
setl foldmethod=expr
|
|
normal! zx
|
|
setl foldmethod=manual
|
|
endfunction
|
|
|
|
command! LatexFold call LatexBox_FoldOnDemand()
|
|
|
|
" {{{1 LatexBox_FoldLevel help functions
|
|
|
|
" This function parses the tex file to find the sections that are to be folded
|
|
" and their levels, and then predefines the patterns for optimized folding.
|
|
function! s:FoldSectionLevels()
|
|
" Initialize
|
|
let level = 1
|
|
let foldsections = []
|
|
|
|
" If we use two or more of the *matter commands, we need one more foldlevel
|
|
let nparts = 0
|
|
for part in g:LatexBox_fold_parts
|
|
let i = 1
|
|
while i < line("$")
|
|
if getline(i) =~ '^\s*\\' . part . '\>'
|
|
let nparts += 1
|
|
break
|
|
endif
|
|
let i += 1
|
|
endwhile
|
|
if nparts > 1
|
|
let level = 2
|
|
break
|
|
endif
|
|
endfor
|
|
|
|
" Combine sections and levels, but ignore unused section commands: If we
|
|
" don't use the part command, then chapter should have the highest
|
|
" level. If we don't use the chapter command, then section should be the
|
|
" highest level. And so on.
|
|
let ignore = 1
|
|
for part in g:LatexBox_fold_sections
|
|
" For each part, check if it is used in the file. We start adding the
|
|
" part patterns to the fold sections array whenever we find one.
|
|
let partpattern = '^\s*\(\\\|% Fake\)' . part . '\>'
|
|
if ignore
|
|
let i = 1
|
|
while i < line("$")
|
|
if getline(i) =~# partpattern
|
|
call insert(foldsections, [partpattern, level])
|
|
let level += 1
|
|
let ignore = 0
|
|
break
|
|
endif
|
|
let i += 1
|
|
endwhile
|
|
else
|
|
call insert(foldsections, [partpattern, level])
|
|
let level += 1
|
|
endif
|
|
endfor
|
|
|
|
return foldsections
|
|
endfunction
|
|
|
|
" {{{1 LatexBox_FoldLevel
|
|
|
|
" Parse file to dynamically set the sectioning fold levels
|
|
let b:LatexBox_FoldSections = s:FoldSectionLevels()
|
|
|
|
" Optimize by predefine common patterns
|
|
let s:notbslash = '\%(\\\@<!\%(\\\\\)*\)\@<='
|
|
let s:notcomment = '\%(\%(\\\@<!\%(\\\\\)*\)\@<=%.*\)\@<!'
|
|
let s:envbeginpattern = s:notcomment . s:notbslash . '\\begin\s*{.\{-}}'
|
|
let s:envendpattern = s:notcomment . s:notbslash . '\\end\s*{.\{-}}'
|
|
let s:foldparts = '^\s*\\\%(' . join(g:LatexBox_fold_parts, '\|') . '\)'
|
|
let s:folded = '\(% Fake\|\\\(document\|begin\|end\|paragraph\|'
|
|
\ . 'front\|main\|back\|app\|sub\|section\|chapter\|part\)\)'
|
|
|
|
function! LatexBox_FoldLevel(lnum)
|
|
" Check for normal lines first (optimization)
|
|
let line = getline(a:lnum)
|
|
if line !~ s:folded
|
|
return "="
|
|
endif
|
|
|
|
" Fold preamble
|
|
if g:LatexBox_fold_preamble == 1
|
|
if line =~# s:notcomment . s:notbslash . '\s*\\documentclass'
|
|
return ">1"
|
|
elseif line =~# s:notcomment . s:notbslash . '\s*\\begin\s*{\s*document\s*}'
|
|
return "0"
|
|
endif
|
|
endif
|
|
|
|
" Fold parts (\frontmatter, \mainmatter, \backmatter, and \appendix)
|
|
if line =~# s:foldparts
|
|
return ">1"
|
|
endif
|
|
|
|
" Fold chapters and sections
|
|
for [part, level] in b:LatexBox_FoldSections
|
|
if line =~# part
|
|
return ">" . level
|
|
endif
|
|
endfor
|
|
|
|
" Never fold \end{document}
|
|
if line =~# '^\s*\\end{document}'
|
|
return 0
|
|
endif
|
|
|
|
" Fold environments
|
|
if line =~# s:envbeginpattern && line =~# s:envendpattern
|
|
" If the begin and end pattern are on the same line , do not fold
|
|
return "="
|
|
else
|
|
if line =~# s:envbeginpattern
|
|
if g:LatexBox_fold_envs == 1
|
|
return "a1"
|
|
else
|
|
let env = matchstr(line,'\\begin\*\?{\zs\w*\*\?\ze}')
|
|
if index(g:LatexBox_fold_envs_force, env) >= 0
|
|
return "a1"
|
|
else
|
|
return "="
|
|
endif
|
|
endif
|
|
elseif line =~# s:envendpattern
|
|
if g:LatexBox_fold_envs == 1
|
|
return "s1"
|
|
else
|
|
let env = matchstr(line,'\\end\*\?{\zs\w*\*\?\ze}')
|
|
if index(g:LatexBox_fold_envs_force, env) >= 0
|
|
return "s1"
|
|
else
|
|
return "="
|
|
endif
|
|
endif
|
|
endif
|
|
endif
|
|
|
|
" Return foldlevel of previous line
|
|
return "="
|
|
endfunction
|
|
|
|
" {{{1 LatexBox_FoldText help functions
|
|
function! s:LabelEnv()
|
|
let i = v:foldend
|
|
while i >= v:foldstart
|
|
if getline(i) =~ '^\s*\\label'
|
|
return matchstr(getline(i), '^\s*\\label{\zs.*\ze}')
|
|
end
|
|
let i -= 1
|
|
endwhile
|
|
return ""
|
|
endfunction
|
|
|
|
function! s:CaptionEnv()
|
|
let i = v:foldend
|
|
while i >= v:foldstart
|
|
if getline(i) =~ '^\s*\\caption'
|
|
return matchstr(getline(i), '^\s*\\caption\(\[.*\]\)\?{\zs.\+')
|
|
end
|
|
let i -= 1
|
|
endwhile
|
|
return ""
|
|
endfunction
|
|
|
|
function! s:CaptionTable()
|
|
let i = v:foldstart
|
|
while i <= v:foldend
|
|
if getline(i) =~ '^\s*\\caption'
|
|
return matchstr(getline(i), '^\s*\\caption\(\[.*\]\)\?{\zs.\+')
|
|
end
|
|
let i += 1
|
|
endwhile
|
|
return ""
|
|
endfunction
|
|
|
|
function! s:CaptionFrame(line)
|
|
" Test simple variants first
|
|
let caption1 = matchstr(a:line,'\\begin\*\?{.*}{\zs.\+\ze}')
|
|
let caption2 = matchstr(a:line,'\\begin\*\?{.*}{\zs.\+')
|
|
|
|
if len(caption1) > 0
|
|
return caption1
|
|
elseif len(caption2) > 0
|
|
return caption2
|
|
else
|
|
let i = v:foldstart
|
|
while i <= v:foldend
|
|
if getline(i) =~ '^\s*\\frametitle'
|
|
return matchstr(getline(i),
|
|
\ '^\s*\\frametitle\(\[.*\]\)\?{\zs.\+')
|
|
end
|
|
let i += 1
|
|
endwhile
|
|
|
|
return ""
|
|
endif
|
|
endfunction
|
|
|
|
function! LatexBox_FoldText_title()
|
|
let line = getline(v:foldstart)
|
|
let title = 'Not defined'
|
|
|
|
" Preamble
|
|
if line =~ '\s*\\documentclass'
|
|
return "Preamble"
|
|
endif
|
|
|
|
" Parts, sections and fakesections
|
|
let sections = '\(\(sub\)*\(section\|paragraph\)\|part\|chapter\)'
|
|
let secpat1 = '^\s*\\' . sections . '\*\?\s*{'
|
|
let secpat2 = '^\s*\\' . sections . '\*\?\s*\['
|
|
if line =~ '\\frontmatter'
|
|
let title = "Frontmatter"
|
|
elseif line =~ '\\mainmatter'
|
|
let title = "Mainmatter"
|
|
elseif line =~ '\\backmatter'
|
|
let title = "Backmatter"
|
|
elseif line =~ '\\appendix'
|
|
let title = "Appendix"
|
|
elseif line =~ secpat1 . '.*}'
|
|
let title = matchstr(line, secpat1 . '\zs.\{-}\ze}')
|
|
elseif line =~ secpat1
|
|
let title = matchstr(line, secpat1 . '\zs.*')
|
|
elseif line =~ secpat2 . '.*\]'
|
|
let title = matchstr(line, secpat2 . '\zs.\{-}\ze\]')
|
|
elseif line =~ secpat2
|
|
let title = matchstr(line, secpat2 . '\zs.*')
|
|
elseif line =~ 'Fake' . sections . ':'
|
|
let title = matchstr(line,'Fake' . sections . ':\s*\zs.*')
|
|
elseif line =~ 'Fake' . sections
|
|
let title = matchstr(line, 'Fake' . sections)
|
|
endif
|
|
|
|
" Environments
|
|
if line =~ '\\begin'
|
|
" Capture environment name
|
|
let env = matchstr(line,'\\begin\*\?{\zs\w*\*\?\ze}')
|
|
|
|
" Set caption based on type of environment
|
|
if env == 'frame'
|
|
let label = ''
|
|
let caption = s:CaptionFrame(line)
|
|
elseif env == 'table'
|
|
let label = s:LabelEnv()
|
|
let caption = s:CaptionTable()
|
|
else
|
|
let label = s:LabelEnv()
|
|
let caption = s:CaptionEnv()
|
|
endif
|
|
|
|
" If no caption found, check for a caption comment
|
|
if caption == ''
|
|
let caption = matchstr(line,'\\begin\*\?{.*}\s*%\s*\zs.*')
|
|
endif
|
|
|
|
" Create title based on caption and label
|
|
if caption . label == ''
|
|
let title = env
|
|
elseif label == ''
|
|
let title = printf('%-12s%s', env . ':',
|
|
\ substitute(caption, '}\s*$', '',''))
|
|
elseif caption == ''
|
|
let title = printf('%-12s%56s', env, '(' . label . ')')
|
|
else
|
|
let title = printf('%-12s%-30s %21s', env . ':',
|
|
\ strpart(substitute(caption, '}\s*$', '',''),0,34),
|
|
\ '(' . label . ')')
|
|
endif
|
|
endif
|
|
|
|
return title
|
|
endfunction
|
|
|
|
" {{{1 LatexBox_FoldText
|
|
function! LatexBox_FoldText()
|
|
let nlines = v:foldend - v:foldstart + 1
|
|
let title = strpart(LatexBox_FoldText_title(), 0, 68)
|
|
let level = ''
|
|
|
|
" Fold level
|
|
let level = strpart(repeat('-', v:foldlevel-1) . '*',0,3)
|
|
if v:foldlevel > 3
|
|
let level = strpart(level, 1) . v:foldlevel
|
|
endif
|
|
let level = printf('%-3s', level)
|
|
|
|
return printf('%-3s %-68s #%5d', level, title, nlines)
|
|
endfunction
|
|
|
|
" {{{1 Footer
|
|
" vim:fdm=marker:ff=unix:ts=4:sw=4
|