547 lines
18 KiB
VimL
547 lines
18 KiB
VimL
"=============================================================================
|
|
" File: folding.vim
|
|
" Author: Srinath Avadhanula
|
|
" modifications/additions by Zhang Linbo, Gerd Wachsmuth
|
|
" Created: Tue Apr 23 05:00 PM 2002 PST
|
|
"
|
|
" Description: functions to interact with Syntaxfolds.vim
|
|
"=============================================================================
|
|
|
|
nnoremap <Plug>Tex_RefreshFolds :call MakeTexFolds(1, 1)<cr>
|
|
|
|
augroup LatexSuite
|
|
au LatexSuite User LatexSuiteFileType
|
|
\ call Tex_Debug('folding.vim: catching LatexSuiteFileType', 'fold') |
|
|
\ call Tex_SetFoldOptions()
|
|
augroup END
|
|
|
|
" Tex_SetFoldOptions: sets maps for every buffer {{{
|
|
" Description:
|
|
function! Tex_SetFoldOptions()
|
|
if exists('b:doneSetFoldOptions')
|
|
return
|
|
endif
|
|
let b:doneSetFoldOptions = 1
|
|
|
|
setlocal foldtext=TexFoldTextFunction()
|
|
|
|
if g:Tex_Folding
|
|
call MakeTexFolds(0, 0)
|
|
endif
|
|
|
|
let s:ml = '<Leader>'
|
|
|
|
call Tex_MakeMap(s:ml."rf", "<Plug>Tex_RefreshFolds", 'n', '<silent> <buffer>')
|
|
|
|
" Setup a local autocommand, if FileChangedShellPost is available
|
|
if exists('##FileChangedShellPost')
|
|
augroup LatexSuite
|
|
autocmd FileChangedShellPost <buffer> call MakeTexFolds(1, 0)
|
|
augroup END
|
|
endif
|
|
|
|
endfunction " }}}
|
|
" Tex_FoldSections: creates section folds {{{
|
|
" Description:
|
|
" This function takes a comma seperated list of "sections" and creates fold
|
|
" definitions for them. The first item is supposed to be the "shallowest" field
|
|
" and the last is the "deepest". See g:Tex_FoldedSections for the default
|
|
" definition of the lst input argument.
|
|
"
|
|
" **works recursively**
|
|
function! Tex_FoldSections(lst, endpat)
|
|
let i = match(a:lst, ',')
|
|
if i > 0
|
|
let s = strpart(a:lst, 0, i)
|
|
else
|
|
let s = a:lst
|
|
endif
|
|
if s =~ '%%fakesection'
|
|
let s = '^\s*' . s
|
|
else
|
|
let pattern = ''
|
|
let prefix = ''
|
|
for label in split( s, "|" )
|
|
let pattern .= prefix . '\\' . label . '\|' . '%%fake' . label
|
|
let prefix = '\|'
|
|
endfor
|
|
" The line before the pattern could contain a mixture of "% =_" (within a
|
|
" comment).
|
|
" The pattern itself is ended by a non-word character "\W" or a newline.
|
|
let s = '^\%(%[% =-]*\n\)\?\s*' . '\%(' . pattern . '\)' . '\%(\W\|\n\)'
|
|
endif
|
|
let endpat = s . '\|' . a:endpat
|
|
if i > 0
|
|
call Tex_FoldSections(strpart(a:lst,i+1), endpat)
|
|
endif
|
|
call AddSyntaxFoldItem(s, endpat, 0, -1)
|
|
endfunction
|
|
" }}}
|
|
" MakeTexFolds: function to create fold items for latex. {{{
|
|
"
|
|
" used in conjunction with MakeSyntaxFolds().
|
|
" see ../plugin/syntaxFolds.vim for documentation
|
|
function! MakeTexFolds(force, manual)
|
|
if exists('g:Tex_Folding') && !g:Tex_Folding
|
|
return
|
|
endif
|
|
if &ft != 'tex'
|
|
return
|
|
end
|
|
|
|
" Setup folded items lists g:Tex_Foldedxxxx
|
|
" 1. Use default value if g:Tex_Foldedxxxxxx is not defined
|
|
" 2. prepend default value to g:Tex_Foldedxxxxxx if it starts with ','
|
|
" 3. append default value to g:Tex_Foldedxxxxxx if it ends with ','
|
|
|
|
" Folding items which are not caught in any of the standard commands,
|
|
" environments or sections.
|
|
let s = 'item,slide,preamble,<<<'
|
|
if !exists('g:Tex_FoldedMisc')
|
|
let g:Tex_FoldedMisc = s
|
|
elseif g:Tex_FoldedMisc[0] == ','
|
|
let g:Tex_FoldedMisc = s . g:Tex_FoldedMisc
|
|
elseif g:Tex_FoldedMisc =~ ',$'
|
|
let g:Tex_FoldedMisc = g:Tex_FoldedMisc . s
|
|
endif
|
|
|
|
" By default do not fold any commands. It looks like trying to fold
|
|
" commands is a difficult problem since commands can be arbitrarily nested
|
|
" and the end patterns are not unique unlike the case of environments.
|
|
" For this to work well, we need a regexp which will match a line only if
|
|
" a command begins on that line but does not end on that line. This
|
|
" requires a regexp which will match unbalanced curly braces and that is
|
|
" apparently not doable with regexps.
|
|
let s = ''
|
|
if !exists('g:Tex_FoldedCommands')
|
|
let g:Tex_FoldedCommands = s
|
|
elseif g:Tex_FoldedCommands[0] == ','
|
|
let g:Tex_FoldedCommands = s . g:Tex_FoldedCommands
|
|
elseif g:Tex_FoldedCommands =~ ',$'
|
|
let g:Tex_FoldedCommands = g:Tex_FoldedCommands . s
|
|
endif
|
|
|
|
let s = 'verbatim,comment,eq,gather,align,figure,table,thebibliography,'
|
|
\. 'keywords,abstract,titlepage'
|
|
if !exists('g:Tex_FoldedEnvironments')
|
|
let g:Tex_FoldedEnvironments = s
|
|
elseif g:Tex_FoldedEnvironments[0] == ','
|
|
let g:Tex_FoldedEnvironments = s . g:Tex_FoldedEnvironments
|
|
elseif g:Tex_FoldedEnvironments =~ ',$'
|
|
let g:Tex_FoldedEnvironments = g:Tex_FoldedEnvironments . s
|
|
endif
|
|
|
|
if !exists('g:Tex_FoldedSections')
|
|
let g:Tex_FoldedSections = 'part,chapter,section,'
|
|
\. 'subsection,subsubsection,paragraph'
|
|
endif
|
|
|
|
" the order in which these calls are made decides the nestedness. in
|
|
" latex, a table environment will always be embedded in either an item or
|
|
" a section etc. not the other way around. so we first fold up all the
|
|
" tables. and then proceed with the other regions.
|
|
|
|
let b:numFoldItems = 0
|
|
|
|
" ========================================================================
|
|
" How to add new folding items {{{
|
|
" ========================================================================
|
|
"
|
|
" Each of the following function calls defines a syntax fold region. Each
|
|
" definition consists of a call to the AddSyntaxFoldItem() function.
|
|
"
|
|
" The order in which the folds are defined is important. Juggling the
|
|
" order of the function calls will create havoc with folding. The
|
|
" "deepest" folding item needs to be called first. For example, if
|
|
" the \begin{table} environment is a subset (or lies within) the \section
|
|
" environment, then add the definition for the \table first.
|
|
"
|
|
" The AddSyntaxFoldItem() function takes either 4 or 6 arguments. When it
|
|
" is called with 4 arguments, it is equivalent to calling it with 6
|
|
" arguments with the last two left blank (i.e as empty strings)
|
|
"
|
|
" The explanation for each argument is as follows:
|
|
" startpat: a line matching this pattern defines the beginning of a fold.
|
|
" endpat : a line matching this pattern defines the end of a fold.
|
|
" startoff: this is the offset from the starting line at which folding will
|
|
" actually start
|
|
" endoff : like startoff, but gives the offset of the actual fold end from
|
|
" the line satisfying endpat.
|
|
" startoff and endoff are necessary when the folding region does
|
|
" not have a specific end pattern corresponding to a start
|
|
" pattern. for example in latex,
|
|
" \begin{section}
|
|
" defines the beginning of a section, but its not necessary to
|
|
" have a corresponding
|
|
" \end{section}
|
|
" the section is assumed to end 1 line _before_ another section
|
|
" starts.
|
|
" startskip: a pattern which defines the beginning of a "skipped" region.
|
|
"
|
|
" For example, suppose we define a \itemize fold as follows:
|
|
" startpat = '^\s*\\item',
|
|
" endpat = '^\s*\\item\|^\s*\\end{\(enumerate\|itemize\|description\)}',
|
|
" startoff = 0,
|
|
" endoff = -1
|
|
"
|
|
" This defines a fold which starts with a line beginning with an
|
|
" \item and ending one line before a line beginning with an
|
|
" \item or \end{enumerate} etc.
|
|
"
|
|
" Then, as long as \item's are not nested things are fine.
|
|
" However, once items begin to nest, the fold started by one
|
|
" \item can end because of an \item in an \itemize
|
|
" environment within this \item. i.e, the following can happen:
|
|
"
|
|
" \begin{itemize}
|
|
" \item Some text <------- fold will start here
|
|
" This item will contain a nested item
|
|
" \begin{itemize} <----- fold will end here because next line contains \item...
|
|
" \item Hello
|
|
" \end{itemize} <----- ... instead of here.
|
|
" \item Next item of the parent itemize
|
|
" \end{itemize}
|
|
"
|
|
" Therefore, in order to completely define a folding item which
|
|
" allows nesting, we need to also define a "skip" pattern.
|
|
" startskip and end skip do that.
|
|
" Leave '' when there is no nesting.
|
|
" endskip: the pattern which defines the end of the "skip" pattern for
|
|
" nested folds.
|
|
"
|
|
" Example:
|
|
" 1. A syntax fold region for a latex section is
|
|
" startpat = "\\section{"
|
|
" endpat = "\\section{"
|
|
" startoff = 0
|
|
" endoff = -1
|
|
" startskip = ''
|
|
" endskip = ''
|
|
" Note that the start and end patterns are thus the same and endoff has a
|
|
" negative value to capture the effect of a section ending one line before
|
|
" the next starts.
|
|
" 2. A syntax fold region for the \itemize environment is:
|
|
" startpat = '^\s*\\item',
|
|
" endpat = '^\s*\\item\|^\s*\\end{\(enumerate\|itemize\|description\)}',
|
|
" startoff = 0,
|
|
" endoff = -1,
|
|
" startskip = '^\s*\\begin{\(enumerate\|itemize\|description\)}',
|
|
" endskip = '^\s*\\end{\(enumerate\|itemize\|description\)}'
|
|
" Note the use of startskip and endskip to allow nesting.
|
|
"
|
|
"
|
|
" }}}
|
|
" ========================================================================
|
|
|
|
" {{{ comment lines
|
|
if g:Tex_FoldedMisc =~ '\<comments\>'
|
|
call AddSyntaxFoldItem (
|
|
\ '^%\([^%]\|[^f]\|[^a]\|[^k]\|[^e]\)',
|
|
\ '^[^%]',
|
|
\ 0,
|
|
\ -1
|
|
\ )
|
|
endif
|
|
" }}}
|
|
|
|
" {{{ items
|
|
if g:Tex_FoldedMisc =~ '\<item\>'
|
|
call AddSyntaxFoldItem (
|
|
\ '^\s*\\item',
|
|
\ '^\s*\\item\|^\s*\\end{\(enumerate\|itemize\|description\)}',
|
|
\ 0,
|
|
\ -1,
|
|
\ '^\s*\\begin{\(enumerate\|itemize\|description\)}',
|
|
\ '^\s*\\end{\(enumerate\|itemize\|description\)}'
|
|
\ )
|
|
endif
|
|
" }}}
|
|
|
|
" {{{ title
|
|
if g:Tex_FoldedMisc =~ '\<title\>'
|
|
call AddSyntaxFoldItem (
|
|
\ '^\s*\\title\W',
|
|
\ '^\s*\\maketitle',
|
|
\ 0,
|
|
\ 0
|
|
\ )
|
|
endif
|
|
" }}}
|
|
|
|
" Commands and Environments {{{
|
|
" Fold the commands and environments in 2 passes.
|
|
let pass = 0
|
|
while pass < 2
|
|
if pass == 0
|
|
let lst = g:Tex_FoldedCommands
|
|
else
|
|
let lst = g:Tex_FoldedEnvironments
|
|
endif
|
|
while lst != ''
|
|
let i = match(lst, ',')
|
|
if i > 0
|
|
let s = strpart(lst, 0, i)
|
|
let lst = strpart(lst, i+1)
|
|
else
|
|
let s = lst
|
|
let lst = ''
|
|
endif
|
|
if s != ''
|
|
if pass == 0
|
|
" NOTE: This pattern ensures that a command which is
|
|
" terminated on the same line will not start a fold.
|
|
" However, it will also refuse to fold certain commands
|
|
" which have not terminated. eg:
|
|
" \commandname{something \textbf{text} and
|
|
" will _not_ start a fold.
|
|
" In other words, the pattern is safe, but not exact.
|
|
call AddSyntaxFoldItem('^\s*\\'.s.'{[^{}]*$','^[^}]*}',0,0)
|
|
else
|
|
if s =~ 'itemize\|enumerate\|description'
|
|
" These environments can nest.
|
|
call AddSyntaxFoldItem('^\s*\\begin{'.s,'\(^\|\s\)\s*\\end{'.s,0,0,'^\s*\\begin{'.s,'\(^\|\s\)\s*\\end{'.s)
|
|
else
|
|
call AddSyntaxFoldItem('^\s*\\begin{'.s,'\(^\|\s\)\s*\\end{'.s,0,0,'','')
|
|
endif
|
|
endif
|
|
endif
|
|
endwhile
|
|
let pass = pass + 1
|
|
endwhile
|
|
" }}}
|
|
|
|
" Sections {{{
|
|
if g:Tex_FoldedSections != ''
|
|
call Tex_FoldSections(g:Tex_FoldedSections,
|
|
\ '^\s*\\\%(frontmatter\|mainmatter\|backmatter\)\|'
|
|
\. '^\s*\\begin{thebibliography\|^\s*\\endinput\|'
|
|
\. '^\s*\\begin{slide\|^\s*\\\%(begin\|end\){document\|'
|
|
\. '^\s*\\\%(\%(begin\|end\){appendix}\|appendix\)')
|
|
endif
|
|
" }}}
|
|
|
|
" {{{ slide
|
|
if g:Tex_FoldedMisc =~ '\<slide\>'
|
|
call AddSyntaxFoldItem (
|
|
\ '^\s*\\begin{slide',
|
|
\ '^\s*\\appendix\W\|^\s*\\chapter\W\|^\s*\\end{slide\|^\s*\\end{document',
|
|
\ 0,
|
|
\ 0
|
|
\ )
|
|
endif
|
|
" }}}
|
|
|
|
" {{{ preamble
|
|
if g:Tex_FoldedMisc =~ '\<preamble\>'
|
|
call AddSyntaxFoldItem (
|
|
\ '^\s*\\document\(class\|style\)\>',
|
|
\ '^\s*\\begin{document}',
|
|
\ 0,
|
|
\ -1
|
|
\ )
|
|
endif
|
|
" }}}
|
|
|
|
" Manually folded regions {{{
|
|
if g:Tex_FoldedMisc =~ '\(^\|,\)<<<\(,\|$\)'
|
|
call AddSyntaxFoldItem (
|
|
\ '<<<',
|
|
\ '>>>',
|
|
\ 0,
|
|
\ 0
|
|
\ )
|
|
endif
|
|
" }}}
|
|
|
|
call MakeSyntaxFolds(a:force)
|
|
|
|
" Open all folds if this function was triggered automatically
|
|
" and g:Tex_AutoFolding is disabled
|
|
if !a:manual && !g:Tex_AutoFolding
|
|
normal! zR
|
|
endif
|
|
endfunction
|
|
|
|
" }}}
|
|
" TexFoldTextFunction: create fold text for folds {{{
|
|
function! TexFoldTextFunction()
|
|
" The dashes indicating the foldlevel together with
|
|
" the number of lines are aligned to width '7'.
|
|
let lines = v:foldend - v:foldstart + 1
|
|
let myfoldtext = repeat('-', v:foldlevel-1) . '+'
|
|
\. repeat(' ', 7-(v:foldlevel-1)-len(lines))
|
|
\. lines . ' lines: '
|
|
|
|
" Add some indent per foldlevel
|
|
let myfoldtext .= repeat('> ', v:foldlevel-1)
|
|
|
|
if getline(v:foldstart) =~ '^\s*\\begin{'
|
|
let header = matchstr(getline(v:foldstart),
|
|
\ '^\s*\\begin{\zs\([:alpha:]*\)[^}]*\ze}')
|
|
let title = ''
|
|
let caption = ''
|
|
let label = ''
|
|
let i = v:foldstart
|
|
while i <= v:foldend
|
|
if getline(i) =~ '\\caption'
|
|
" distinguish between
|
|
" \caption{fulldesc} - fulldesc will be displayed
|
|
" \caption[shortdesc]{fulldesc} - shortdesc will be displayed
|
|
if getline(i) =~ '\\caption\['
|
|
let caption = matchstr(getline(i), '\\caption\[\zs[^\]]*')
|
|
let caption = substitute(caption, '\zs\]{.*}[^}]*$', '', '')
|
|
else
|
|
let caption = matchstr(getline(i), '\\caption{\zs.*')
|
|
let caption = substitute(caption, '\zs}[^}]*$', '', '')
|
|
end
|
|
elseif getline(i) =~ '\\label'
|
|
let label = matchstr(getline(i), '\\label{\zs.*')
|
|
" :FIXME: this does not work when \label contains a
|
|
" newline or a }-character
|
|
let label = substitute(label, '\([^}]*\)}.*$', '\1', '')
|
|
elseif header =~ 'frame' && getline(i) =~ '\\begin{frame}.*{[^{}]*}\|\\frametitle\|%'
|
|
if getline(i) =~ '\\begin{frame}'
|
|
" The first argument inside {} is the frame title (the
|
|
" second one is a subtitle)
|
|
let title = matchstr(getline(i), '\\begin{frame}.\{-}{\zs[^{}]*\ze}')
|
|
elseif getline(i) =~ '\\frametitle'
|
|
let title = matchstr(getline(i), '\\frametitle{\zs[^}]*\ze}')
|
|
elseif getline(i) =~ '%' && title == ''
|
|
let title = substitute(getline(i), '^\(\s\|%\)*', '', '')
|
|
endif
|
|
end
|
|
|
|
let i = i + 1
|
|
endwhile
|
|
|
|
if header =~ 'frame'
|
|
if title == ''
|
|
let title = getline(v:foldstart + 1)
|
|
end
|
|
" Count frames
|
|
let frnum = 0
|
|
for line in getline(1,v:foldstart)
|
|
if line =~ '\\begin{frame}'
|
|
let frnum=frnum+1
|
|
endif
|
|
endfor
|
|
" Pad with spaces to length 2
|
|
let frnum = repeat(' ', 2-len(frnum)) . frnum
|
|
return myfoldtext . ': Frame ' . frnum . ': ' . title
|
|
end
|
|
|
|
" if no caption found, then use the second line.
|
|
if caption == ''
|
|
let caption = getline(v:foldstart + 1)
|
|
end
|
|
|
|
return myfoldtext . header. ' ('.label.'): '.caption
|
|
|
|
elseif getline(v:foldstart) =~ '^\s*%\+[% =-]*$'
|
|
" Useless comment. Use the next line.
|
|
return myfoldtext . getline(v:foldstart+1)
|
|
elseif getline(v:foldstart) =~ '^\s*%%fake'
|
|
" Just strip one '%' from the fakesection.
|
|
return myfoldtext . substitute(getline(v:foldstart), '^\s*%%fake', '%', '')
|
|
elseif getline(v:foldstart) =~ '^\s*%'
|
|
" It's any other comment. Use it.
|
|
return myfoldtext . getline(v:foldstart)
|
|
elseif getline(v:foldstart) =~ '^\s*\\document\(class\|style\).*{'
|
|
" This is the preamble.
|
|
return myfoldtext . 'Preamble: ' . getline(v:foldstart)
|
|
end
|
|
|
|
let section_pattern = substitute(g:Tex_FoldedSections, ',\||', '\\|', 'g')
|
|
let section_pattern = '\\\%(' . section_pattern .'\)\>'
|
|
|
|
if getline(v:foldstart) =~ '^\s*' . section_pattern
|
|
" This is a section. Search for the content of the mandatory argument {...}
|
|
let type = matchstr(getline(v:foldstart), '^\s*\zs' . section_pattern)
|
|
return myfoldtext . type . s:ParseSectionTitle(v:foldstart, section_pattern)
|
|
else
|
|
" This is something.
|
|
return myfoldtext . getline(v:foldstart)
|
|
end
|
|
endfunction
|
|
" }}}
|
|
" s:ParseSectionTitle: create fold text for sections {{{
|
|
" Search for the mandatory argument of the \section command and ignore the
|
|
" optional argument.
|
|
function! s:ParseSectionTitle(foldstart, section_pattern)
|
|
let currlinenr = a:foldstart
|
|
let currline = s:StripLine(getline(currlinenr))
|
|
let currlinelen = strlen(currline)
|
|
|
|
" Look for the section title after the section macro
|
|
let index = match(currline, '^\s*' . a:section_pattern . '\zs')
|
|
|
|
let maxlines = 10
|
|
|
|
" Current depth of nested [] and {}:
|
|
let currdepth = 0
|
|
" Do we have found the mandatory argument?
|
|
" (We are looking for '{' at depth 0)
|
|
let found_mandatory = 0
|
|
|
|
let string = ''
|
|
|
|
while (currdepth > 0) || !found_mandatory
|
|
if index >= currlinelen
|
|
" Read a new line.
|
|
let maxlines = maxlines - 1
|
|
if maxlines < 0
|
|
return string . ' Scanned too many lines'
|
|
endif
|
|
let currlinenr = currlinenr + 1
|
|
let currline = s:StripLine(getline(currlinenr))
|
|
let currlinelen = strlen(currline)
|
|
|
|
let index = 0
|
|
|
|
if found_mandatory
|
|
let string .= ' '
|
|
endif
|
|
continue
|
|
endif
|
|
|
|
" Look for [] and {} at current position
|
|
if currline[index] =~ '[[{]'
|
|
if(currdepth == 0) && (currline[index] =~ '{')
|
|
let found_mandatory = 1
|
|
end
|
|
let currdepth += 1
|
|
elseif currline[index] =~ '[]}]'
|
|
let currdepth -= 1
|
|
endif
|
|
|
|
" Look for the next interesting character
|
|
let next_index = match( currline, '[{}[\]]', index + 1 )
|
|
if next_index == -1
|
|
let next_index = currlinelen + 1
|
|
endif
|
|
|
|
" Update the string
|
|
if found_mandatory
|
|
let string .= currline[index:next_index-1]
|
|
endif
|
|
let index = next_index
|
|
endwhile
|
|
|
|
return string
|
|
endfunction
|
|
" }}}
|
|
" s:StripLine: strips whitespace and comments {{{
|
|
function! s:StripLine( string )
|
|
let string = matchstr( a:string, '^\s*\zs.*$')
|
|
let comment = match( string, '\\\@<!\%(\\\\\)*\zs%')
|
|
if comment > 0
|
|
let string = string[0:comment-1]
|
|
elseif comment == 0
|
|
let string = ''
|
|
endif
|
|
return string
|
|
endfunction
|
|
" }}}
|
|
|
|
" vim:fdm=marker:ff=unix:noet:ts=4:sw=4
|