summaryrefslogtreecommitdiffstats
path: root/ftplugin/latex-box
diff options
context:
space:
mode:
Diffstat (limited to 'ftplugin/latex-box')
-rw-r--r--ftplugin/latex-box/common.vim351
-rw-r--r--ftplugin/latex-box/complete.vim805
-rw-r--r--ftplugin/latex-box/findmain.vim64
-rw-r--r--ftplugin/latex-box/folding.vim317
-rw-r--r--ftplugin/latex-box/latexmk.vim442
-rw-r--r--ftplugin/latex-box/mappings.vim96
-rw-r--r--ftplugin/latex-box/motion.vim518
7 files changed, 2593 insertions, 0 deletions
diff --git a/ftplugin/latex-box/common.vim b/ftplugin/latex-box/common.vim
new file mode 100644
index 00000000..91026720
--- /dev/null
+++ b/ftplugin/latex-box/common.vim
@@ -0,0 +1,351 @@
+" LaTeX Box common functions
+
+" Error Format {{{
+" Note: The error formats assume we're using the -file-line-error with
+" [pdf]latex.
+
+" Check for options
+if !exists("g:LatexBox_show_warnings")
+ let g:LatexBox_show_warnings=1
+endif
+if !exists("g:LatexBox_ignore_warnings")
+ let g:LatexBox_ignore_warnings =
+ \['Underfull',
+ \ 'Overfull',
+ \ 'specifier changed to']
+endif
+
+" See |errorformat-LaTeX|
+setlocal efm=%E!\ LaTeX\ %trror:\ %m
+setlocal efm+=%E%f:%l:\ %m
+
+" Show or ignore warnings
+if g:LatexBox_show_warnings
+ for w in g:LatexBox_ignore_warnings
+ let warning = escape(substitute(w, '[\,]', '%\\\\&', 'g'), ' ')
+ exe 'setlocal efm+=%-G%.%#'. warning .'%.%#'
+ endfor
+ setlocal efm+=%+WLaTeX\ %.%#Warning:\ %.%#line\ %l%.%#
+ setlocal efm+=%+W%.%#\ at\ lines\ %l--%*\\d
+ setlocal efm+=%+WLaTeX\ %.%#Warning:\ %m
+ setlocal efm+=%+W%.%#%.%#Warning:\ %m
+else
+ setlocal efm+=%-WLaTeX\ %.%#Warning:\ %.%#line\ %l%.%#
+ setlocal efm+=%-W%.%#\ at\ lines\ %l--%*\\d
+ setlocal efm+=%-WLaTeX\ %.%#Warning:\ %m
+ setlocal efm+=%-W%.%#%.%#Warning:\ %m
+endif
+
+" Consider the remaining statements that starts with "!" as errors
+setlocal efm+=%E!\ %m
+
+" Push file to file stack
+setlocal efm+=%+P**%f
+
+" Ignore unmatched lines
+setlocal efm+=%-G\\s%#
+setlocal efm+=%-G%.%#
+" }}}
+
+" Vim Windows {{{
+
+" Width of vertical splits
+if !exists('g:LatexBox_split_width')
+ let g:LatexBox_split_width = 30
+endif
+
+" Where vertical splits appear
+if !exists('g:LatexBox_split_side')
+ let g:LatexBox_split_side = "leftabove"
+endif
+
+" Resize when split?
+if !exists('g:LatexBox_split_resize')
+ let g:LatexBox_split_resize = 0
+endif
+
+" Toggle help info
+if !exists('g:LatexBox_toc_hidehelp')
+ let g:LatexBox_toc_hidehelp = 0
+endif
+" }}}
+
+" Filename utilities {{{
+function! LatexBox_GetMainTexFile()
+
+ " 1. check for the b:main_tex_file variable
+ if exists('b:main_tex_file') && filereadable(b:main_tex_file)
+ return b:main_tex_file
+ endif
+
+
+ " 2. scan the first few lines of the file for root = filename
+ for linenum in range(1,5)
+ let linecontents = getline(linenum)
+ if linecontents =~ 'root\s*='
+ " Remove everything but the filename
+ let b:main_tex_file = substitute(linecontents,
+ \ '.*root\s*=\s*', "", "")
+ let b:main_tex_file = substitute(b:main_tex_file, '\s*$', "", "")
+ " Prepend current directory if this isn't an absolute path
+ if b:main_tex_file !~ '^/'
+ let b:main_tex_file = expand('%:p:h') . '/' . b:main_tex_file
+ endif
+ let b:main_tex_file = fnamemodify(b:main_tex_file, ":p")
+ if b:main_tex_file !~ '\.tex$'
+ let b:main_tex_file .= '.tex'
+ endif
+ return b:main_tex_file
+ endif
+ endfor
+
+ " 3. scan current file for "\begin{document}"
+ if &filetype == 'tex' && search('\C\\begin\_\s*{document}', 'nw') != 0
+ return expand('%:p')
+ endif
+
+ " 4 borrow the Vim-Latex-Suite method of finding it
+ if Tex_GetMainFileName() != expand('%:p')
+ let b:main_tex_file = Tex_GetMainFileName()
+ return b:main_tex_file
+ endif
+
+ " 5. prompt for file with completion
+ let b:main_tex_file = s:PromptForMainFile()
+ return b:main_tex_file
+endfunction
+
+function! s:PromptForMainFile()
+ let saved_dir = getcwd()
+ execute 'cd ' . fnameescape(expand('%:p:h'))
+ let l:file = ''
+ while !filereadable(l:file)
+ let l:file = input('main LaTeX file: ', '', 'file')
+ if l:file !~ '\.tex$'
+ let l:file .= '.tex'
+ endif
+ endwhile
+ let l:file = fnamemodify(l:file, ':p')
+ execute 'cd ' . fnameescape(saved_dir)
+ return l:file
+endfunction
+
+" Return the directory of the main tex file
+function! LatexBox_GetTexRoot()
+ return fnamemodify(LatexBox_GetMainTexFile(), ':h')
+endfunction
+
+function! LatexBox_GetTexBasename(with_dir)
+ if a:with_dir
+ return fnamemodify(LatexBox_GetMainTexFile(), ':r')
+ else
+ return fnamemodify(LatexBox_GetMainTexFile(), ':t:r')
+ endif
+endfunction
+
+function! LatexBox_GetAuxFile()
+ " 1. check for b:build_dir variable
+ if exists('b:build_dir') && isdirectory(b:build_dir)
+ return b:build_dir . '/' . LatexBox_GetTexBasename(0) . '.aux'
+ endif
+
+ " 2. check for g:LatexBox_build_dir variable
+ if exists('g:LatexBox_build_dir') && isdirectory(g:LatexBox_build_dir)
+ return g:LatexBox_build_dir . '/' . LatexBox_GetTexBasename(0) . '.aux'
+ endif
+
+ " 3. use the base name of main tex file
+ return LatexBox_GetTexBasename(1) . '.aux'
+endfunction
+
+function! LatexBox_GetLogFile()
+ " 1. check for b:build_dir variable
+ if exists('b:build_dir') && isdirectory(b:build_dir)
+ return b:build_dir . '/' . LatexBox_GetTexBasename(0) . '.log'
+ endif
+
+ " 2. check for g:LatexBox_build_dir variable
+ if exists('g:LatexBox_build_dir') && isdirectory(g:LatexBox_build_dir)
+ return g:LatexBox_build_dir . '/' . LatexBox_GetTexBasename(0) . '.log'
+ endif
+
+ " 3. use the base name of main tex file
+ return LatexBox_GetTexBasename(1) . '.log'
+endfunction
+
+function! LatexBox_GetOutputFile()
+ " 1. check for b:build_dir variable
+ if exists('b:build_dir') && isdirectory(b:build_dir)
+ return b:build_dir . '/' . LatexBox_GetTexBasename(0)
+ \ . '.' . g:LatexBox_output_type
+ endif
+
+ " 2. check for g:LatexBox_build_dir variable
+ if exists('g:LatexBox_build_dir') && isdirectory(g:LatexBox_build_dir)
+ return g:LatexBox_build_dir . '/' . LatexBox_GetTexBasename(0)
+ \ . '.' . g:LatexBox_output_type
+ endif
+
+ " 3. use the base name of main tex file
+ return LatexBox_GetTexBasename(1) . '.' . g:LatexBox_output_type
+endfunction
+" }}}
+
+" View Output {{{
+
+" Default pdf viewer
+if !exists('g:LatexBox_viewer')
+ if has('win32')
+ " On windows, 'running' a file will open it with the default program
+ let g:LatexBox_viewer = ''
+ else
+ let g:LatexBox_viewer = 'xdg-open'
+ endif
+endif
+
+function! LatexBox_View()
+ let outfile = LatexBox_GetOutputFile()
+ if !filereadable(outfile)
+ echomsg fnamemodify(outfile, ':.') . ' is not readable'
+ return
+ endif
+ let cmd = g:LatexBox_viewer . ' ' . shellescape(outfile)
+ if has('win32')
+ let cmd = '!start /b' . cmd . ' >nul'
+ else
+ let cmd = '!' . cmd . ' >/dev/null &'
+ endif
+ silent execute cmd
+ if !has("gui_running")
+ redraw!
+ endif
+endfunction
+
+command! LatexView call LatexBox_View()
+" }}}
+
+" In Comment {{{
+
+" LatexBox_InComment([line], [col])
+" return true if inside comment
+function! LatexBox_InComment(...)
+ let line = a:0 >= 1 ? a:1 : line('.')
+ let col = a:0 >= 2 ? a:2 : col('.')
+ return synIDattr(synID(line, col, 0), "name") =~# '^texComment'
+endfunction
+" }}}
+
+" Get Current Environment {{{
+
+" LatexBox_GetCurrentEnvironment([with_pos])
+" Returns:
+" - environment
+" if with_pos is not given
+" - [envirnoment, lnum_begin, cnum_begin, lnum_end, cnum_end]
+" if with_pos is nonzero
+function! LatexBox_GetCurrentEnvironment(...)
+ if a:0 > 0
+ let with_pos = a:1
+ else
+ let with_pos = 0
+ endif
+
+ let begin_pat = '\C\\begin\_\s*{[^}]*}\|\\\@<!\\\[\|\\\@<!\\('
+ let end_pat = '\C\\end\_\s*{[^}]*}\|\\\@<!\\\]\|\\\@<!\\)'
+ let saved_pos = getpos('.')
+
+ " move to the left until on a backslash
+ let [bufnum, lnum, cnum, off] = getpos('.')
+ let line = getline(lnum)
+ while cnum > 1 && line[cnum - 1] != '\'
+ let cnum -= 1
+ endwhile
+ call cursor(lnum, cnum)
+
+ " match begin/end pairs but skip comments
+ let flags = 'bnW'
+ if strpart(getline('.'), col('.') - 1) =~ '^\%(' . begin_pat . '\)'
+ let flags .= 'c'
+ endif
+ let [lnum1, cnum1] = searchpairpos(begin_pat, '', end_pat, flags,
+ \ 'LatexBox_InComment()')
+
+ let env = ''
+
+ if lnum1
+ let line = strpart(getline(lnum1), cnum1 - 1)
+
+ if empty(env)
+ let env = matchstr(line, '^\C\\begin\_\s*{\zs[^}]*\ze}')
+ endif
+ if empty(env)
+ let env = matchstr(line, '^\\\[')
+ endif
+ if empty(env)
+ let env = matchstr(line, '^\\(')
+ endif
+ endif
+
+ if with_pos == 1
+ let flags = 'nW'
+ if !(lnum1 == lnum && cnum1 == cnum)
+ let flags .= 'c'
+ endif
+
+ let [lnum2, cnum2] = searchpairpos(begin_pat, '', end_pat, flags,
+ \ 'LatexBox_InComment()')
+
+ call setpos('.', saved_pos)
+ return [env, lnum1, cnum1, lnum2, cnum2]
+ else
+ call setpos('.', saved_pos)
+ return env
+ endif
+endfunction
+" }}}
+
+" Tex To Tree {{{
+" stores nested braces in a tree structure
+function! LatexBox_TexToTree(str)
+ let tree = []
+ let i1 = 0
+ let i2 = -1
+ let depth = 0
+ while i2 < len(a:str)
+ let i2 = match(a:str, '[{}]', i2 + 1)
+ if i2 < 0
+ let i2 = len(a:str)
+ endif
+ if i2 >= len(a:str) || a:str[i2] == '{'
+ if depth == 0
+ let item = substitute(strpart(a:str, i1, i2 - i1),
+ \ '^\s*\|\s*$', '', 'g')
+ if !empty(item)
+ call add(tree, item)
+ endif
+ let i1 = i2 + 1
+ endif
+ let depth += 1
+ else
+ let depth -= 1
+ if depth == 0
+ call add(tree, LatexBox_TexToTree(strpart(a:str, i1, i2 - i1)))
+ let i1 = i2 + 1
+ endif
+ endif
+ endwhile
+ return tree
+endfunction
+" }}}
+
+" Tree To Tex {{{
+function! LatexBox_TreeToTex(tree)
+ if type(a:tree) == type('')
+ return a:tree
+ else
+ return '{' . join(map(a:tree, 'LatexBox_TreeToTex(v:val)'), '') . '}'
+ endif
+endfunction
+" }}}
+
+" vim:fdm=marker:ff=unix:noet:ts=4:sw=4
diff --git a/ftplugin/latex-box/complete.vim b/ftplugin/latex-box/complete.vim
new file mode 100644
index 00000000..76f909b9
--- /dev/null
+++ b/ftplugin/latex-box/complete.vim
@@ -0,0 +1,805 @@
+" LaTeX Box completion
+
+setlocal omnifunc=LatexBox_Complete
+
+" <SID> Wrap {{{
+function! s:GetSID()
+ return matchstr(expand('<sfile>'), '\zs<SNR>\d\+_\ze.*$')
+endfunction
+let s:SID = s:GetSID()
+function! s:SIDWrap(func)
+ return s:SID . a:func
+endfunction
+" }}}
+
+" Completion {{{
+if !exists('g:LatexBox_completion_close_braces')
+ let g:LatexBox_completion_close_braces = 1
+endif
+if !exists('g:LatexBox_bibtex_wild_spaces')
+ let g:LatexBox_bibtex_wild_spaces = 1
+endif
+
+if !exists('g:LatexBox_cite_pattern')
+ let g:LatexBox_cite_pattern = '\C\\\a*cite\a*\*\?\(\[[^\]]*\]\)*\_\s*{'
+endif
+if !exists('g:LatexBox_ref_pattern')
+ let g:LatexBox_ref_pattern = '\C\\v\?\(eq\|page\|[cC]\)\?ref\*\?\_\s*{'
+endif
+
+if !exists('g:LatexBox_completion_environments')
+ let g:LatexBox_completion_environments = [
+ \ {'word': 'itemize', 'menu': 'bullet list' },
+ \ {'word': 'enumerate', 'menu': 'numbered list' },
+ \ {'word': 'description', 'menu': 'description' },
+ \ {'word': 'center', 'menu': 'centered text' },
+ \ {'word': 'figure', 'menu': 'floating figure' },
+ \ {'word': 'table', 'menu': 'floating table' },
+ \ {'word': 'equation', 'menu': 'equation (numbered)' },
+ \ {'word': 'align', 'menu': 'aligned equations (numbered)' },
+ \ {'word': 'align*', 'menu': 'aligned equations' },
+ \ {'word': 'document' },
+ \ {'word': 'abstract' },
+ \ ]
+endif
+
+if !exists('g:LatexBox_completion_commands')
+ let g:LatexBox_completion_commands = [
+ \ {'word': '\begin{' },
+ \ {'word': '\end{' },
+ \ {'word': '\item' },
+ \ {'word': '\label{' },
+ \ {'word': '\ref{' },
+ \ {'word': '\eqref{eq:' },
+ \ {'word': '\cite{' },
+ \ {'word': '\chapter{' },
+ \ {'word': '\section{' },
+ \ {'word': '\subsection{' },
+ \ {'word': '\subsubsection{' },
+ \ {'word': '\paragraph{' },
+ \ {'word': '\nonumber' },
+ \ {'word': '\bibliography' },
+ \ {'word': '\bibliographystyle' },
+ \ ]
+endif
+
+if !exists('g:LatexBox_complete_inlineMath')
+ let g:LatexBox_complete_inlineMath = 0
+endif
+
+if !exists('g:LatexBox_eq_env_patterns')
+ let g:LatexBox_eq_env_patterns = 'equation\|gather\|multiline\|align\|flalign\|alignat\|eqnarray'
+endif
+
+" }}}
+
+"LatexBox_kpsewhich {{{
+function! LatexBox_kpsewhich(file)
+ let old_dir = getcwd()
+ execute 'lcd ' . fnameescape(LatexBox_GetTexRoot())
+ let out = system('kpsewhich "' . a:file . '"')
+
+ " If kpsewhich has found something, it returns a non-empty string with a
+ " newline at the end; otherwise the string is empty
+ if len(out)
+ " Remove the trailing newline
+ let out = fnamemodify(out[:-2], ':p')
+ endif
+
+ execute 'lcd ' . fnameescape(old_dir)
+
+ return out
+endfunction
+"}}}
+
+" Omni Completion {{{
+
+let s:completion_type = ''
+
+function! LatexBox_Complete(findstart, base)
+ if a:findstart
+ " return the starting position of the word
+ let line = getline('.')
+ let pos = col('.') - 1
+ while pos > 0 && line[pos - 1] !~ '\\\|{'
+ let pos -= 1
+ endwhile
+
+ let line_start = line[:pos-1]
+ if line_start =~ '\C\\begin\_\s*{$'
+ let s:completion_type = 'begin'
+ elseif line_start =~ '\C\\end\_\s*{$'
+ let s:completion_type = 'end'
+ elseif line_start =~ g:LatexBox_ref_pattern . '$'
+ let s:completion_type = 'ref'
+ elseif line_start =~ g:LatexBox_cite_pattern . '$'
+ let s:completion_type = 'bib'
+ " check for multiple citations
+ let pos = col('.') - 1
+ while pos > 0 && line[pos - 1] !~ '{\|,'
+ let pos -= 1
+ endwhile
+ elseif s:LatexBox_complete_inlineMath_or_not()
+ let s:completion_type = 'inlineMath'
+ let pos = s:eq_pos
+ else
+ let s:completion_type = 'command'
+ if line[pos - 1] == '\'
+ let pos -= 1
+ endif
+ endif
+ return pos
+ else
+ " return suggestions in an array
+ let suggestions = []
+
+ if s:completion_type == 'begin'
+ " suggest known environments
+ for entry in g:LatexBox_completion_environments
+ if entry.word =~ '^' . escape(a:base, '\')
+ if g:LatexBox_completion_close_braces && !s:NextCharsMatch('^}')
+ " add trailing '}'
+ let entry = copy(entry)
+ let entry.abbr = entry.word
+ let entry.word = entry.word . '}'
+ endif
+ call add(suggestions, entry)
+ endif
+ endfor
+ elseif s:completion_type == 'end'
+ " suggest known environments
+ let env = LatexBox_GetCurrentEnvironment()
+ if env != ''
+ if g:LatexBox_completion_close_braces && !s:NextCharsMatch('^\s*[,}]')
+ call add(suggestions, {'word': env . '}', 'abbr': env})
+ else
+ call add(suggestions, env)
+ endif
+ endif
+ elseif s:completion_type == 'command'
+ " suggest known commands
+ for entry in g:LatexBox_completion_commands
+ if entry.word =~ '^' . escape(a:base, '\')
+ " do not display trailing '{'
+ if entry.word =~ '{'
+ let entry.abbr = entry.word[0:-2]
+ endif
+ call add(suggestions, entry)
+ endif
+ endfor
+ elseif s:completion_type == 'ref'
+ let suggestions = s:CompleteLabels(a:base)
+ elseif s:completion_type == 'bib'
+ " suggest BibTeX entries
+ let suggestions = LatexBox_BibComplete(a:base)
+ elseif s:completion_type == 'inlineMath'
+ let suggestions = s:LatexBox_inlineMath_completion(a:base)
+ endif
+ if !has('gui_running')
+ redraw!
+ endif
+ return suggestions
+ endif
+endfunction
+" }}}
+
+" BibTeX search {{{
+
+" find the \bibliography{...} commands
+" the optional argument is the file name to be searched
+
+function! s:FindBibData(...)
+ if a:0 == 0
+ let file = LatexBox_GetMainTexFile()
+ else
+ let file = a:1
+ endif
+
+ if !filereadable(file)
+ return ''
+ endif
+
+ let bibliography_cmds = [
+ \ '\\bibliography',
+ \ '\\addbibresource',
+ \ '\\addglobalbib',
+ \ '\\addsectionbib',
+ \ ]
+
+ let lines = readfile(file)
+
+ let bibdata_list = []
+
+ for cmd in bibliography_cmds
+ let bibdata_list += map(filter(copy(lines),
+ \ 'v:val =~ ''\C' . cmd . '\s*{[^}]\+}'''),
+ \ 'matchstr(v:val, ''\C' . cmd . '\s*{\zs[^}]\+\ze}'')')
+ endfor
+
+ let bibdata_list += map(filter(copy(lines),
+ \ 'v:val =~ ''\C\\\%(input\|include\)\s*{[^}]\+}'''),
+ \ 's:FindBibData(LatexBox_kpsewhich(matchstr(v:val,'
+ \ . '''\C\\\%(input\|include\)\s*{\zs[^}]\+\ze}'')))')
+
+ let bibdata_list += map(filter(copy(lines),
+ \ 'v:val =~ ''\C\\\%(input\|include\)\s\+\S\+'''),
+ \ 's:FindBibData(LatexBox_kpsewhich(matchstr(v:val,'
+ \ . '''\C\\\%(input\|include\)\s\+\zs\S\+\ze'')))')
+
+ return join(bibdata_list, ',')
+endfunction
+
+let s:bstfile = expand('<sfile>:p:h') . '/vimcomplete'
+
+function! LatexBox_BibSearch(regexp)
+ let res = []
+
+ " Find data from bib files
+ let bibdata = s:FindBibData()
+ if bibdata != ''
+
+ " write temporary aux file
+ let tmpbase = LatexBox_GetTexRoot() . '/_LatexBox_BibComplete'
+ let auxfile = tmpbase . '.aux'
+ let bblfile = tmpbase . '.bbl'
+ let blgfile = tmpbase . '.blg'
+
+ call writefile(['\citation{*}', '\bibstyle{' . s:bstfile . '}',
+ \ '\bibdata{' . bibdata . '}'], auxfile)
+
+ silent execute '! cd ' shellescape(LatexBox_GetTexRoot()) .
+ \ ' ; bibtex -terse '
+ \ . fnamemodify(auxfile, ':t') . ' >/dev/null'
+
+ let lines = split(substitute(join(readfile(bblfile), "\n"),
+ \ '\n\n\@!\(\s\=\)\s*\|{\|}', '\1', 'g'), "\n")
+
+ for line in filter(lines, 'v:val =~ a:regexp')
+ let matches = matchlist(line,
+ \ '^\(.*\)||\(.*\)||\(.*\)||\(.*\)||\(.*\)')
+ if !empty(matches) && !empty(matches[1])
+ call add(res, {
+ \ 'key': matches[1],
+ \ 'type': matches[2],
+ \ 'author': matches[3],
+ \ 'year': matches[4],
+ \ 'title': matches[5],
+ \ })
+ endif
+ endfor
+
+ call delete(auxfile)
+ call delete(bblfile)
+ call delete(blgfile)
+ endif
+
+ " Find data from 'thebibliography' environments
+ let lines = readfile(LatexBox_GetMainTexFile())
+ if match(lines, '\C\\begin{thebibliography}')
+ for line in filter(filter(lines, 'v:val =~ ''\C\\bibitem'''),
+ \ 'v:val =~ a:regexp')
+ let match = matchlist(line, '\\bibitem{\([^}]*\)')[1]
+ call add(res, {
+ \ 'key': match,
+ \ 'type': '',
+ \ 'author': '',
+ \ 'year': '',
+ \ 'title': match,
+ \ })
+ endfor
+ endif
+
+ return res
+endfunction
+" }}}
+
+" BibTeX completion {{{
+function! LatexBox_BibComplete(regexp)
+
+ " treat spaces as '.*' if needed
+ if g:LatexBox_bibtex_wild_spaces
+ "let regexp = substitute(a:regexp, '\s\+', '.*', 'g')
+ let regexp = '.*' . substitute(a:regexp, '\s\+', '\\\&.*', 'g')
+ else
+ let regexp = a:regexp
+ endif
+
+ let res = []
+ for m in LatexBox_BibSearch(regexp)
+ let type = m['type'] == '' ? '[-]' : '[' . m['type'] . '] '
+ let auth = m['author'] == '' ? '' : m['author'][:20] . ' '
+ let year = m['year'] == '' ? '' : '(' . m['year'] . ')'
+ let w = { 'word': m['key'],
+ \ 'abbr': type . auth . year,
+ \ 'menu': m['title'] }
+
+ " close braces if needed
+ if g:LatexBox_completion_close_braces && !s:NextCharsMatch('^\s*[,}]')
+ let w.word = w.word . '}'
+ endif
+
+ call add(res, w)
+ endfor
+ return res
+endfunction
+" }}}
+
+" ExtractLabels {{{
+" Generate list of \newlabel commands in current buffer.
+"
+" Searches the current buffer for commands of the form
+" \newlabel{name}{{number}{page}.*
+" and returns list of [ name, number, page ] tuples.
+function! s:ExtractLabels()
+ call cursor(1,1)
+
+ let matches = []
+ let [lblline, lblbegin] = searchpos( '\\newlabel{', 'ecW' )
+
+ while [lblline, lblbegin] != [0,0]
+ let [nln, nameend] = searchpairpos( '{', '', '}', 'W' )
+ if nln != lblline
+ let [lblline, lblbegin] = searchpos( '\\newlabel{', 'ecW' )
+ continue
+ endif
+ let curname = strpart( getline( lblline ), lblbegin, nameend - lblbegin - 1 )
+
+ " Ignore cref entries (because they are duplicates)
+ if curname =~ "\@cref"
+ continue
+ endif
+
+ if 0 == search( '{\w*{', 'ce', lblline )
+ let [lblline, lblbegin] = searchpos( '\\newlabel{', 'ecW' )
+ continue
+ endif
+
+ let numberbegin = getpos('.')[2]
+ let [nln, numberend] = searchpairpos( '{', '', '}', 'W' )
+ if nln != lblline
+ let [lblline, lblbegin] = searchpos( '\\newlabel{', 'ecW' )
+ continue
+ endif
+ let curnumber = strpart( getline( lblline ), numberbegin, numberend - numberbegin - 1 )
+
+ if 0 == search( '\w*{', 'ce', lblline )
+ let [lblline, lblbegin] = searchpos( '\\newlabel{', 'ecW' )
+ continue
+ endif
+
+ let pagebegin = getpos('.')[2]
+ let [nln, pageend] = searchpairpos( '{', '', '}', 'W' )
+ if nln != lblline
+ let [lblline, lblbegin] = searchpos( '\\newlabel{', 'ecW' )
+ continue
+ endif
+ let curpage = strpart( getline( lblline ), pagebegin, pageend - pagebegin - 1 )
+
+ let matches += [ [ curname, curnumber, curpage ] ]
+
+ let [lblline, lblbegin] = searchpos( '\\newlabel{', 'ecW' )
+ endwhile
+
+ return matches
+endfunction
+"}}}
+
+" ExtractInputs {{{
+" Generate list of \@input commands in current buffer.
+"
+" Searches the current buffer for \@input{file} entries and
+" returns list of all files.
+function! s:ExtractInputs()
+ call cursor(1,1)
+
+ let matches = []
+ let [inline, inbegin] = searchpos( '\\@input{', 'ecW' )
+
+ while [inline, inbegin] != [0,0]
+ let [nln, inend] = searchpairpos( '{', '', '}', 'W' )
+ if nln != inline
+ let [inline, inbegin] = searchpos( '\\@input{', 'ecW' )
+ continue
+ endif
+ let matches += [ LatexBox_kpsewhich(strpart( getline( inline ), inbegin, inend - inbegin - 1 )) ]
+
+ let [inline, inbegin] = searchpos( '\\@input{', 'ecW' )
+ endwhile
+
+ " Remove empty strings for nonexistant .aux files
+ return filter(matches, 'v:val != ""')
+endfunction
+"}}}
+
+" LabelCache {{{
+" Cache of all labels.
+"
+" LabelCache is a dictionary mapping filenames to tuples
+" [ time, labels, inputs ]
+" where
+" * time is modification time of the cache entry
+" * labels is a list like returned by ExtractLabels
+" * inputs is a list like returned by ExtractInputs
+let s:LabelCache = {}
+"}}}
+
+" GetLabelCache {{{
+" Extract labels from LabelCache and update it.
+"
+" Compares modification time of each entry in the label
+" cache and updates it, if necessary. During traversal of
+" the LabelCache, all current labels are collected and
+" returned.
+function! s:GetLabelCache(file)
+ if !filereadable(a:file)
+ return []
+ endif
+
+ if !has_key(s:LabelCache , a:file) || s:LabelCache[a:file][0] != getftime(a:file)
+ " Open file in temporary split window for label extraction.
+ silent execute '1sp +let\ labels=s:ExtractLabels()|let\ inputs=s:ExtractInputs()|quit! ' . a:file
+ let s:LabelCache[a:file] = [ getftime(a:file), labels, inputs ]
+ endif
+
+ " We need to create a copy of s:LabelCache[fid][1], otherwise all inputs'
+ " labels would be added to the current file's label cache upon each
+ " completion call, leading to duplicates/triplicates/etc. and decreased
+ " performance.
+ " Also, because we don't anything with the list besides matching copies,
+ " we can get away with a shallow copy for now.
+ let labels = copy(s:LabelCache[a:file][1])
+
+ for input in s:LabelCache[a:file][2]
+ let labels += s:GetLabelCache(input)
+ endfor
+
+ return labels
+endfunction
+"}}}
+
+" Complete Labels {{{
+function! s:CompleteLabels(regex)
+ let labels = s:GetLabelCache(LatexBox_GetAuxFile())
+
+ let matches = filter( copy(labels), 'match(v:val[0], "' . a:regex . '") != -1' )
+ if empty(matches)
+ " also try to match label and number
+ let regex_split = split(a:regex)
+ if len(regex_split) > 1
+ let base = regex_split[0]
+ let number = escape(join(regex_split[1:], ' '), '.')
+ let matches = filter( copy(labels), 'match(v:val[0], "' . base . '") != -1 && match(v:val[1], "' . number . '") != -1' )
+ endif
+ endif
+ if empty(matches)
+ " also try to match number
+ let matches = filter( copy(labels), 'match(v:val[1], "' . a:regex . '") != -1' )
+ endif
+
+ let suggestions = []
+ for m in matches
+ let entry = {'word': m[0], 'menu': printf("%7s [p. %s]", '('.m[1].')', m[2])}
+ if g:LatexBox_completion_close_braces && !s:NextCharsMatch('^\s*[,}]')
+ " add trailing '}'
+ let entry = copy(entry)
+ let entry.abbr = entry.word
+ let entry.word = entry.word . '}'
+ endif
+ call add(suggestions, entry)
+ endfor
+
+ return suggestions
+endfunction
+" }}}
+
+" Complete Inline Math Or Not {{{
+" Return 1, when cursor is in a math env:
+" 1, there is a single $ in the current line on the left of cursor
+" 2, there is an open-eq-env on/above the current line
+" (open-eq-env : \(, \[, and \begin{eq-env} )
+" Return 0, when cursor is not in a math env
+function! s:LatexBox_complete_inlineMath_or_not()
+
+ " switch of inline math completion feature
+ if g:LatexBox_complete_inlineMath == 0
+ return 0
+ endif
+
+ " env names that can't appear in an eq env
+ if !exists('s:LatexBox_doc_structure_patterns')
+ let s:LatexBox_doc_structure_patterns = '\%(' . '\\begin\s*{document}\|' .
+ \ '\\\%(chapter\|section\|subsection\|subsubsection\)\*\?\s*{' . '\)'
+ endif
+
+ if !exists('s:LatexBox_eq_env_open_patterns')
+ let s:LatexBox_eq_env_open_patterns = ['\\(','\\\[']
+ endif
+ if !exists('s:LatexBox_eq_env_close_patterns')
+ let s:LatexBox_eq_env_close_patterns = ['\\)','\\\]']
+ endif
+
+ let notcomment = '\%(\%(\\\@<!\%(\\\\\)*\)\@<=%.*\)\@<!'
+
+ let lnum_saved = line('.')
+ let cnum_saved = col('.') -1
+
+ let line = getline('.')
+ let line_start_2_cnum_saved = line[:cnum_saved]
+
+ " determine whether there is a single $ before cursor
+ let cursor_dollar_pair = 0
+ while matchend(line_start_2_cnum_saved, '\$[^$]\+\$', cursor_dollar_pair) >= 0
+ " find the end of dollar pair
+ let cursor_dollar_pair = matchend(line_start_2_cnum_saved, '\$[^$]\+\$', cursor_dollar_pair)
+ endwhile
+ " find single $ after cursor_dollar_pair
+ let cursor_single_dollar = matchend(line_start_2_cnum_saved, '\$', cursor_dollar_pair)
+
+ " if single $ is found
+ if cursor_single_dollar >= 0
+ " check whether $ is in \(...\), \[...\], or \begin{eq}...\end{eq}
+
+ " check current line,
+ " search for LatexBox_eq_env_close_patterns: \[ and \(
+ let lnum = line('.')
+ for i in range(0, (len(s:LatexBox_eq_env_open_patterns)-1))
+ call cursor(lnum_saved, cnum_saved)
+ let cnum_close = searchpos(''. s:LatexBox_eq_env_close_patterns[i].'', 'cbW', lnum_saved)[1]
+ let cnum_open = matchend(line_start_2_cnum_saved, s:LatexBox_eq_env_open_patterns[i], cnum_close)
+ if cnum_open >= 0
+ let s:eq_dollar_parenthesis_bracket_empty = ''
+ let s:eq_pos = cursor_single_dollar - 1
+ return 1
+ end
+ endfor
+
+ " check the lines above
+ " search for s:LatexBox_doc_structure_patterns, and end-of-math-env
+ let lnum -= 1
+ while lnum > 0
+ let line = getline(lnum)
+ if line =~ notcomment . '\(' . s:LatexBox_doc_structure_patterns .
+ \ '\|' . '\\end\s*{\(' . g:LatexBox_eq_env_patterns . '\)\*\?}\)'
+ " when s:LatexBox_doc_structure_patterns or g:LatexBox_eq_env_patterns
+ " are found first, complete math, leave with $ at both sides
+ let s:eq_dollar_parenthesis_bracket_empty = '$'
+ let s:eq_pos = cursor_single_dollar
+ break
+ elseif line =~ notcomment . '\\begin\s*{\(' . g:LatexBox_eq_env_patterns . '\)\*\?}'
+ " g:LatexBox_eq_env_patterns is found, complete math, remove $
+ let s:eq_dollar_parenthesis_bracket_empty = ''
+ let s:eq_pos = cursor_single_dollar - 1
+ break
+ endif
+ let lnum -= 1
+ endwhile
+
+ return 1
+ else
+ " no $ is found, then search for \( or \[ in current line
+ " 1, whether there is \(
+ call cursor(lnum_saved, cnum_saved)
+ let cnum_parenthesis_close = searchpos('\\)', 'cbW', lnum_saved)[1]
+ let cnum_parenthesis_open = matchend(line_start_2_cnum_saved, '\\(', cnum_parenthesis_close)
+ if cnum_parenthesis_open >= 0
+ let s:eq_dollar_parenthesis_bracket_empty = '\)'
+ let s:eq_pos = cnum_parenthesis_open
+ return 1
+ end
+
+ " 2, whether there is \[
+ call cursor(lnum_saved, cnum_saved)
+ let cnum_bracket_close = searchpos('\\\]', 'cbW', lnum_saved)[1]
+ let cnum_bracket_open = matchend(line_start_2_cnum_saved, '\\\[', cnum_bracket_close)
+ if cnum_bracket_open >= 0
+ let s:eq_dollar_parenthesis_bracket_empty = '\]'
+ let s:eq_pos = cnum_bracket_open
+ return 1
+ end
+
+ " not inline math completion
+ return 0
+ endif
+
+endfunction
+" }}}
+
+" Complete inline euqation{{{
+function! s:LatexBox_inlineMath_completion(regex, ...)
+
+ if a:0 == 0
+ let file = LatexBox_GetMainTexFile()
+ else
+ let file = a:1
+ endif
+
+ if empty(glob(file, 1))
+ return ''
+ endif
+
+ if empty(s:eq_dollar_parenthesis_bracket_empty)
+ let inline_pattern1 = '\$\s*\(' . escape(substitute(a:regex[1:], '^\s\+', '', ""), '\.*^') . '[^$]*\)\s*\$'
+ let inline_pattern2 = '\\(\s*\(' . escape(substitute(a:regex[1:], '^\s\+', '', ""), '\.*^') . '.*\)\s*\\)'
+ else
+ let inline_pattern1 = '\$\s*\(' . escape(substitute(a:regex, '^\s\+', '', ""), '\.*^') . '[^$]*\)\s*\$'
+ let inline_pattern2 = '\\(\s*\(' . escape(substitute(a:regex, '^\s\+', '', ""), '\.*^') . '.*\)\s*\\)'
+ endif
+
+
+ let suggestions = []
+ let line_num = 0
+ for line in readfile(file)
+ let line_num = line_num + 1
+
+ let suggestions += s:LatexBox_inlineMath_mathlist(line,inline_pattern1 , line_num) + s:LatexBox_inlineMath_mathlist( line,inline_pattern2, line_num)
+
+ " search for included files
+ let included_file = matchstr(line, '^\\@input{\zs[^}]*\ze}')
+ if included_file != ''
+ let included_file = LatexBox_kpsewhich(included_file)
+ call extend(suggestions, s:LatexBox_inlineMath_completion(a:regex, included_file))
+ endif
+ endfor
+
+ return suggestions
+endfunction
+" }}}
+
+" Search for inline maths {{{
+" search for $ ... $ and \( ... \) in each line
+function! s:LatexBox_inlineMath_mathlist(line,inline_pattern, line_num)
+ let col_start = 0
+ let suggestions = []
+ while 1
+ let matches = matchlist(a:line, a:inline_pattern, col_start)
+ if !empty(matches)
+
+ " show line number of inline math
+ let entry = {'word': matches[1], 'menu': '[' . a:line_num . ']'}
+
+ if s:eq_dollar_parenthesis_bracket_empty != ''
+ let entry = copy(entry)
+ let entry.abbr = entry.word
+ let entry.word = entry.word . s:eq_dollar_parenthesis_bracket_empty
+ endif
+ call add(suggestions, entry)
+
+ " update col_start
+ let col_start = matchend(a:line, a:inline_pattern, col_start)
+ else
+ break
+ endif
+ endwhile
+
+ return suggestions
+endfunction
+" }}}
+
+" Close Current Environment {{{
+function! s:CloseCurEnv()
+ " first, try with \left/\right pairs
+ let [lnum, cnum] = searchpairpos('\C\\left\>', '', '\C\\right\>', 'bnW', 'LatexBox_InComment()')
+ if lnum
+ let line = strpart(getline(lnum), cnum - 1)
+ let bracket = matchstr(line, '^\\left\zs\((\|\[\|\\{\||\|\.\)\ze')
+ for [open, close] in [['(', ')'], ['\[', '\]'], ['\\{', '\\}'], ['|', '|'], ['\.', '|']]
+ let bracket = substitute(bracket, open, close, 'g')
+ endfor
+ return '\right' . bracket
+ endif
+
+ " second, try with environments
+ let env = LatexBox_GetCurrentEnvironment()
+ if env == '\['
+ return '\]'
+ elseif env == '\('
+ return '\)'
+ elseif env != ''
+ return '\end{' . env . '}'
+ endif
+ return ''
+endfunction
+" }}}
+
+" Wrap Selection {{{
+function! s:WrapSelection(wrapper)
+ keepjumps normal! `>a}
+ execute 'keepjumps normal! `<i\' . a:wrapper . '{'
+endfunction
+" }}}
+
+" Wrap Selection in Environment with Prompt {{{
+function! s:PromptEnvWrapSelection(...)
+ let env = input('environment: ', '', 'customlist,' . s:SIDWrap('GetEnvironmentList'))
+ if empty(env)
+ return
+ endif
+ " LaTeXBox's custom indentation can interfere with environment
+ " insertion when environments are indented (common for nested
+ " environments). Temporarily disable it for this operation:
+ let ieOld = &indentexpr
+ setlocal indentexpr=""
+ if visualmode() ==# 'V'
+ execute 'keepjumps normal! `>o\end{' . env . '}'
+ execute 'keepjumps normal! `<O\begin{' . env . '}'
+ " indent and format, if requested.
+ if a:0 && a:1
+ normal! gv>
+ normal! gvgq
+ endif
+ else
+ execute 'keepjumps normal! `>a\end{' . env . '}'
+ execute 'keepjumps normal! `<i\begin{' . env . '}'
+ endif
+ exe "setlocal indentexpr=" . ieOld
+endfunction
+" }}}
+
+" Change Environment {{{
+function! s:ChangeEnvPrompt()
+
+ let [env, lnum, cnum, lnum2, cnum2] = LatexBox_GetCurrentEnvironment(1)
+
+ let new_env = input('change ' . env . ' for: ', '', 'customlist,' . s:SIDWrap('GetEnvironmentList'))
+ if empty(new_env)
+ return
+ endif
+
+ if new_env == '\[' || new_env == '['
+ let begin = '\['
+ let end = '\]'
+ elseif new_env == '\(' || new_env == '('
+ let begin = '\('
+ let end = '\)'
+ else
+ let l:begin = '\begin{' . new_env . '}'
+ let l:end = '\end{' . new_env . '}'
+ endif
+
+ if env == '\[' || env == '\('
+ let line = getline(lnum2)
+ let line = strpart(line, 0, cnum2 - 1) . l:end . strpart(line, cnum2 + 1)
+ call setline(lnum2, line)
+
+ let line = getline(lnum)
+ let line = strpart(line, 0, cnum - 1) . l:begin . strpart(line, cnum + 1)
+ call setline(lnum, line)
+ else
+ let line = getline(lnum2)
+ let line = strpart(line, 0, cnum2 - 1) . l:end . strpart(line, cnum2 + len(env) + 5)
+ call setline(lnum2, line)
+
+ let line = getline(lnum)
+ let line = strpart(line, 0, cnum - 1) . l:begin . strpart(line, cnum + len(env) + 7)
+ call setline(lnum, line)
+ endif
+endfunction
+
+function! s:GetEnvironmentList(lead, cmdline, pos)
+ let suggestions = []
+ for entry in g:LatexBox_completion_environments
+ let env = entry.word
+ if env =~ '^' . a:lead
+ call add(suggestions, env)
+ endif
+ endfor
+ return suggestions
+endfunction
+" }}}
+
+" Next Charaters Match {{{
+function! s:NextCharsMatch(regex)
+ let rest_of_line = strpart(getline('.'), col('.') - 1)
+ return rest_of_line =~ a:regex
+endfunction
+" }}}
+
+" Mappings {{{
+inoremap <silent> <Plug>LatexCloseCurEnv <C-R>=<SID>CloseCurEnv()<CR>
+vnoremap <silent> <Plug>LatexWrapSelection :<c-u>call <SID>WrapSelection('')<CR>i
+vnoremap <silent> <Plug>LatexEnvWrapSelection :<c-u>call <SID>PromptEnvWrapSelection()<CR>
+vnoremap <silent> <Plug>LatexEnvWrapFmtSelection :<c-u>call <SID>PromptEnvWrapSelection(1)<CR>
+nnoremap <silent> <Plug>LatexChangeEnv :call <SID>ChangeEnvPrompt()<CR>
+" }}}
+
+" vim:fdm=marker:ff=unix:noet:ts=4:sw=4
diff --git a/ftplugin/latex-box/findmain.vim b/ftplugin/latex-box/findmain.vim
new file mode 100644
index 00000000..0b9c404f
--- /dev/null
+++ b/ftplugin/latex-box/findmain.vim
@@ -0,0 +1,64 @@
+" Tex_GetMainFileName: gets the name of the main file being compiled. {{{
+" Description: returns the full path name of the main file.
+" This function checks for the existence of a .latexmain file
+" which might point to the location of a "main" latex file.
+" If .latexmain exists, then return the full path name of the
+" file being pointed to by it.
+"
+" Otherwise, return the full path name of the current buffer.
+"
+" You can supply an optional "modifier" argument to the
+" function, which will optionally modify the file name before
+" returning.
+" NOTE: From version 1.6 onwards, this function always trims
+" away the .latexmain part of the file name before applying the
+" modifier argument.
+function! Tex_GetMainFileName(...)
+ if a:0 > 0
+ let modifier = a:1
+ else
+ let modifier = ':p'
+ endif
+
+ let s:origdir = fnameescape(getcwd())
+
+ let dirmodifier = '%:p:h'
+ let dirLast = fnameescape(expand(dirmodifier))
+ exe 'cd '.dirLast
+
+ " move up the directory tree until we find a .latexmain file.
+ " TODO: Should we be doing this recursion by default, or should there be a
+ " setting?
+ while glob('*.latexmain') == ''
+ let dirmodifier = dirmodifier.':h'
+ let dirNew = fnameescape(expand(dirmodifier))
+ " break from the loop if we cannot go up any further.
+ if dirNew == dirLast
+ break
+ endif
+ let dirLast = dirNew
+ exe 'cd '.dirLast
+ endwhile
+
+ let lheadfile = glob('*.latexmain')
+ if lheadfile != ''
+ " Remove the trailing .latexmain part of the filename... We never want
+ " that.
+ let lheadfile = fnamemodify(substitute(lheadfile, '\.latexmain$', '', ''), modifier)
+ else
+ " If we cannot find any main file, just modify the filename of the
+ " current buffer.
+ let lheadfile = expand('%'.modifier)
+ endif
+
+ if lheadfile !~ '\.tex$'
+ let lheadfile .= '.tex'
+ endif
+ exe 'cd '.s:origdir
+
+ " NOTE: The caller of this function needs to escape the file name with
+ " fnameescape() . The reason its not done here is that escaping is not
+ " safe if this file is to be used as part of an external command on
+ " certain platforms.
+ return lheadfile
+endfunction
diff --git a/ftplugin/latex-box/folding.vim b/ftplugin/latex-box/folding.vim
new file mode 100644
index 00000000..36f1577b
--- /dev/null
+++ b/ftplugin/latex-box/folding.vim
@@ -0,0 +1,317 @@
+" Folding support for LaTeX
+"
+" Options
+" g:LatexBox_Folding - Turn on/off folding
+" 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
+"
+
+" {{{1 Set options
+if exists('g:LatexBox_Folding') && g:LatexBox_Folding == 1
+ setl foldmethod=expr
+ setl foldexpr=LatexBox_FoldLevel(v:lnum)
+ setl foldtext=LatexBox_FoldText()
+ "
+ " 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 setlocal foldmethod=manual
+ autocmd InsertLeave *.tex setlocal foldmethod=expr
+ augroup end
+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_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
+
+
+" {{{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\|'
+ \ . '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*\\documentclass'
+ return ">1"
+ elseif line =~# '^\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
+
+ " Fold environments
+ if g:LatexBox_fold_envs == 1
+ if line =~# s:envbeginpattern
+ return "a1"
+ elseif line =~# '^\s*\\end{document}'
+ " Never fold \end{document}
+ return 0
+ elseif line =~# s:envendpattern
+ return "s1"
+ 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
+
+" {{{1 LatexBox_FoldText
+function! LatexBox_FoldText()
+ " Initialize
+ let line = getline(v:foldstart)
+ let nlines = v:foldend - v:foldstart + 1
+ let level = ''
+ let title = 'Not defined'
+
+ " 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)
+
+ " Preamble
+ if line =~ '\s*\\documentclass'
+ let title = "Preamble"
+ endif
+
+ " Parts, sections and fakesections
+ let sections = '\(\(sub\)*section\|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
+
+ let title = strpart(title, 0, 68)
+ return printf('%-3s %-68s #%5d', level, title, nlines)
+endfunction
+
+" {{{1 Footer
+" vim:fdm=marker:ff=unix:ts=4:sw=4
diff --git a/ftplugin/latex-box/latexmk.vim b/ftplugin/latex-box/latexmk.vim
new file mode 100644
index 00000000..4ea3ff09
--- /dev/null
+++ b/ftplugin/latex-box/latexmk.vim
@@ -0,0 +1,442 @@
+" LaTeX Box latexmk functions
+
+" Options and variables {{{
+
+if !exists('g:LatexBox_latexmk_options')
+ let g:LatexBox_latexmk_options = ''
+endif
+if !exists('g:LatexBox_latexmk_async')
+ let g:LatexBox_latexmk_async = 0
+endif
+if !exists('g:LatexBox_latexmk_preview_continuously')
+ let g:LatexBox_latexmk_preview_continuously = 0
+endif
+if !exists('g:LatexBox_output_type')
+ let g:LatexBox_output_type = 'pdf'
+endif
+if !exists('g:LatexBox_autojump')
+ let g:LatexBox_autojump = 0
+endif
+if ! exists('g:LatexBox_quickfix')
+ let g:LatexBox_quickfix = 1
+endif
+
+" }}}
+
+" Process ID management (used for asynchronous and continuous mode) {{{
+
+" A dictionary of latexmk PID's (basename: pid)
+if !exists('g:latexmk_running_pids')
+ let g:latexmk_running_pids = {}
+endif
+
+" Set PID {{{
+function! s:LatexmkSetPID(basename, pid)
+ let g:latexmk_running_pids[a:basename] = a:pid
+endfunction
+" }}}
+
+" kill_latexmk_process {{{
+function! s:kill_latexmk_process(pid)
+ if has('win32')
+ silent execute '!taskkill /PID ' . a:pid . ' /T /F'
+ else
+ if g:LatexBox_latexmk_async
+ " vim-server mode
+ let pids = []
+ let tmpfile = tempname()
+ silent execute '!ps x -o pgid,pid > ' . tmpfile
+ for line in readfile(tmpfile)
+ let new_pid = matchstr(line, '^\s*' . a:pid . '\s\+\zs\d\+\ze')
+ if !empty(new_pid)
+ call add(pids, new_pid)
+ endif
+ endfor
+ call delete(tmpfile)
+ if !empty(pids)
+ silent execute '!kill ' . join(pids)
+ endif
+ else
+ " single background process
+ silent execute '!kill ' . a:pid
+ endif
+ endif
+ if !has('gui_running')
+ redraw!
+ endif
+endfunction
+" }}}
+
+" kill_all_latexmk_processes {{{
+function! s:kill_all_latexmk_processes()
+ for pid in values(g:latexmk_running_pids)
+ call s:kill_latexmk_process(pid)
+ endfor
+endfunction
+" }}}
+
+" }}}
+
+" Setup for vim-server {{{
+function! s:SIDWrap(func)
+ if !exists('s:SID')
+ let s:SID = matchstr(expand('<sfile>'), '\zs<SNR>\d\+_\ze.*$')
+ endif
+ return s:SID . a:func
+endfunction
+
+function! s:LatexmkCallback(basename, status)
+ " Only remove the pid if not in continuous mode
+ if !g:LatexBox_latexmk_preview_continuously
+ call remove(g:latexmk_running_pids, a:basename)
+ call LatexBox_LatexErrors(a:status, a:basename)
+ endif
+endfunction
+
+function! s:setup_vim_server()
+ if !exists('g:vim_program')
+
+ " attempt autodetection of vim executable
+ let g:vim_program = ''
+ if has('win32')
+ " Just drop through to the default for windows
+ else
+ if match(&shell, '/\(bash\|zsh\)$') >= 0
+ let ppid = '$PPID'
+ else
+ let ppid = '$$'
+ endif
+
+ let tmpfile = tempname()
+ silent execute '!ps -o command= -p ' . ppid . ' > ' . tmpfile
+ for line in readfile(tmpfile)
+ let line = matchstr(line, '^\S\+\>')
+ if !empty(line) && executable(line)
+ let g:vim_program = line . ' -g'
+ break
+ endif
+ endfor
+ call delete(tmpfile)
+ endif
+
+ if empty(g:vim_program)
+ if has('gui_macvim')
+ let g:vim_program
+ \ = '/Applications/MacVim.app/Contents/MacOS/Vim -g'
+ else
+ let g:vim_program = v:progname
+ endif
+ endif
+ endif
+endfunction
+" }}}
+
+" Latexmk {{{
+
+function! LatexBox_Latexmk(force)
+ " Define often used names
+ let basepath = LatexBox_GetTexBasename(1)
+ let basename = fnamemodify(basepath, ':t')
+ let texroot = shellescape(LatexBox_GetTexRoot())
+ let mainfile = fnameescape(fnamemodify(LatexBox_GetMainTexFile(), ':t'))
+
+ " Check if already running
+ if has_key(g:latexmk_running_pids, basepath)
+ echomsg "latexmk is already running for `" . basename . "'"
+ return
+ endif
+
+ " Set wrap width in log file
+ let max_print_line = 2000
+ if has('win32')
+ let env = 'set max_print_line=' . max_print_line . ' & '
+ elseif match(&shell, '/tcsh$') >= 0
+ let env = 'setenv max_print_line ' . max_print_line . '; '
+ else
+ let env = 'max_print_line=' . max_print_line
+ endif
+
+ " Set latexmk command with options
+ if has('win32')
+ " Make sure to switch drive as well as directory
+ let cmd = 'cd /D ' . texroot . ' && '
+ else
+ let cmd = 'cd ' . texroot . ' && '
+ endif
+ let cmd .= env . ' latexmk'
+ let cmd .= ' -' . g:LatexBox_output_type
+ let cmd .= ' -quiet '
+ let cmd .= g:LatexBox_latexmk_options
+ if a:force
+ let cmd .= ' -g'
+ endif
+ if g:LatexBox_latexmk_preview_continuously
+ let cmd .= ' -pvc'
+ endif
+ let cmd .= ' -e ' . shellescape('$pdflatex =~ s/ / -file-line-error /')
+ let cmd .= ' -e ' . shellescape('$latex =~ s/ / -file-line-error /')
+ let cmd .= ' ' . mainfile
+
+ " Redirect output to null
+ if has('win32')
+ let cmd .= ' >nul'
+ else
+ let cmd .= ' &>/dev/null'
+ endif
+
+ if g:LatexBox_latexmk_async
+ " Check if VIM server exists
+ if empty(v:servername)
+ echoerr "cannot run latexmk in background without a VIM server"
+ echoerr "set g:LatexBox_latexmk_async to 0 to change compiling mode"
+ return
+ endif
+
+ " Start vim server if necessary
+ call s:setup_vim_server()
+
+ let setpidfunc = s:SIDWrap('LatexmkSetPID')
+ let callbackfunc = s:SIDWrap('LatexmkCallback')
+ if has('win32')
+ let vim_program = substitute(g:vim_program,
+ \ 'gvim\.exe$', 'vim.exe', '')
+
+ " Define callback to set the pid
+ let callsetpid = setpidfunc . '(''' . basepath . ''', %CMDPID%)'
+ let vimsetpid = vim_program . ' --servername ' . v:servername
+ \ . ' --remote-expr ' . shellescape(callsetpid)
+
+ " Define callback after latexmk is finished
+ let callback = callbackfunc . '(''' . basepath . ''', %LATEXERR%)'
+ let vimcmd = vim_program . ' --servername ' . v:servername
+ \ . ' --remote-expr ' . shellescape(callback)
+
+ let asyncbat = tempname() . '.bat'
+ call writefile(['setlocal',
+ \ 'set T=%TEMP%\sthUnique.tmp',
+ \ 'wmic process where (Name="WMIC.exe" AND CommandLine LIKE "%%%TIME%%%") '
+ \ . 'get ParentProcessId /value | find "ParentProcessId" >%T%',
+ \ 'set /P A=<%T%',
+ \ 'set CMDPID=%A:~16% & del %T%',
+ \ vimsetpid,
+ \ cmd,
+ \ 'set LATEXERR=%ERRORLEVEL%',
+ \ vimcmd,
+ \ 'endlocal'], asyncbat)
+
+ " Define command
+ let cmd = '!start /b ' . asyncbat . ' & del ' . asyncbat
+ else
+ " Define callback to set the pid
+ let callsetpid = shellescape(setpidfunc).'"(\"'.basepath.'\",$$)"'
+ let vimsetpid = g:vim_program . ' --servername ' . v:servername
+ \ . ' --remote-expr ' . callsetpid
+
+ " Define callback after latexmk is finished
+ let callback = shellescape(callbackfunc).'"(\"'.basepath.'\",$?)"'
+ let vimcmd = g:vim_program . ' --servername ' . v:servername
+ \ . ' --remote-expr ' . callback
+
+ " Define command
+ " Note: Here we escape '%' because it may be given as a user option
+ " through g:LatexBox_latexmk_options, for instance with
+ " g:Latex..._options = "-pdflatex='pdflatex -synctex=1 \%O \%S'"
+ let cmd = vimsetpid . ' ; ' . escape(cmd, '%') . ' ; ' . vimcmd
+ let cmd = '! (' . cmd . ') >/dev/null &'
+ endif
+
+ if g:LatexBox_latexmk_preview_continuously
+ echo 'Compiling to ' . g:LatexBox_output_type
+ \ . ' with continuous preview.'
+ else
+ echo 'Compiling to ' . g:LatexBox_output_type . ' ...'
+ endif
+ silent execute cmd
+ else
+ if g:LatexBox_latexmk_preview_continuously
+ if has('win32')
+ let cmd = '!start /b cmd /s /c "' . cmd . '"'
+ else
+ let cmd = '!' . cmd . ' &'
+ endif
+ echo 'Compiling to ' . g:LatexBox_output_type . ' ...'
+ silent execute cmd
+
+ " Save PID in order to be able to kill the process when wanted.
+ if has('win32')
+ let tmpfile = tempname()
+ let pidcmd = 'cmd /c "wmic process where '
+ \ . '(CommandLine LIKE "latexmk\%'.mainfile.'\%") '
+ \ . 'get ProcessId /value | find "ProcessId" '
+ \ . '>'.tmpfile.' "'
+ silent execute '! ' . pidcmd
+ let pids = readfile(tmpfile)
+ let pid = strpart(pids[0], 10)
+ let g:latexmk_running_pids[basepath] = pid
+ else
+ let pid = substitute(system('pgrep -f "perl.*'
+ \ . mainfile . '"'),'\D','','')
+ let g:latexmk_running_pids[basepath] = pid
+ endif
+ else
+ " Execute command and check for errors
+ echo 'Compiling to ' . g:LatexBox_output_type . ' ... (async off!)'
+ call system(cmd)
+ call LatexBox_LatexErrors(v:shell_error)
+ endif
+ endif
+
+ " Redraw screen if necessary
+ if !has("gui_running")
+ redraw!
+ endif
+endfunction
+" }}}
+
+" LatexmkClean {{{
+function! LatexBox_LatexmkClean(cleanall)
+ let basename = LatexBox_GetTexBasename(1)
+ if has_key(g:latexmk_running_pids, basename)
+ echomsg "don't clean when latexmk is running"
+ return
+ endif
+
+ if has('win32')
+ let cmd = 'cd /D ' . shellescape(LatexBox_GetTexRoot()) . ' & '
+ else
+ let cmd = 'cd ' . shellescape(LatexBox_GetTexRoot()) . ';'
+ endif
+ if a:cleanall
+ let cmd .= 'latexmk -C '
+ else
+ let cmd .= 'latexmk -c '
+ endif
+ let cmd .= shellescape(LatexBox_GetMainTexFile())
+ if has('win32')
+ let cmd .= ' >nul'
+ else
+ let cmd .= ' >&/dev/null'
+ endif
+
+ call system(cmd)
+ if !has('gui_running')
+ redraw!
+ endif
+
+ echomsg "latexmk clean finished"
+endfunction
+" }}}
+
+" LatexErrors {{{
+function! LatexBox_LatexErrors(status, ...)
+ if a:0 >= 1
+ let log = a:1 . '.log'
+ else
+ let log = LatexBox_GetLogFile()
+ endif
+
+ cclose
+
+ " set cwd to expand error file correctly
+ let l:cwd = fnamemodify(getcwd(), ':p')
+ execute 'lcd ' . fnameescape(LatexBox_GetTexRoot())
+ try
+ if g:LatexBox_autojump
+ execute 'cfile ' . fnameescape(log)
+ else
+ execute 'cgetfile ' . fnameescape(log)
+ endif
+ finally
+ " restore cwd
+ execute 'lcd ' . fnameescape(l:cwd)
+ endtry
+
+ " Always open window if started by LatexErrors command
+ if a:status < 0
+ botright copen
+ else
+ " Write status message to screen
+ redraw
+ if a:status > 0 || len(getqflist())>1
+ echomsg 'Compiling to ' . g:LatexBox_output_type . ' ... failed!'
+ else
+ echomsg 'Compiling to ' . g:LatexBox_output_type . ' ... success!'
+ endif
+
+ " Only open window when an error/warning is detected
+ if g:LatexBox_quickfix
+ belowright cw
+ if g:LatexBox_quickfix==2
+ wincmd p
+ endif
+ endif
+ endif
+endfunction
+" }}}
+
+" LatexmkStatus {{{
+function! LatexBox_LatexmkStatus(detailed)
+ if a:detailed
+ if empty(g:latexmk_running_pids)
+ echo "latexmk is not running"
+ else
+ let plist = ""
+ for [basename, pid] in items(g:latexmk_running_pids)
+ if !empty(plist)
+ let plist .= '; '
+ endif
+ let plist .= fnamemodify(basename, ':t') . ':' . pid
+ endfor
+ echo "latexmk is running (" . plist . ")"
+ endif
+ else
+ let basename = LatexBox_GetTexBasename(1)
+ if has_key(g:latexmk_running_pids, basename)
+ echo "latexmk is running"
+ else
+ echo "latexmk is not running"
+ endif
+ endif
+endfunction
+" }}}
+
+" LatexmkStop {{{
+function! LatexBox_LatexmkStop(silent)
+ if empty(g:latexmk_running_pids)
+ if !a:silent
+ let basepath = LatexBox_GetTexBasename(1)
+ let basename = fnamemodify(basepath, ':t')
+ echoerr "latexmk is not running for `" . basename . "'"
+ endif
+ else
+ let basepath = LatexBox_GetTexBasename(1)
+ let basename = fnamemodify(basepath, ':t')
+ if has_key(g:latexmk_running_pids, basepath)
+ call s:kill_latexmk_process(g:latexmk_running_pids[basepath])
+ call remove(g:latexmk_running_pids, basepath)
+ if !a:silent
+ echomsg "latexmk stopped for `" . basename . "'"
+ endif
+ elseif !a:silent
+ echoerr "latexmk is not running for `" . basename . "'"
+ endif
+ endif
+endfunction
+" }}}
+
+" Commands {{{
+
+command! -bang Latexmk call LatexBox_Latexmk(<q-bang> == "!")
+command! -bang LatexmkClean call LatexBox_LatexmkClean(<q-bang> == "!")
+command! -bang LatexmkStatus call LatexBox_LatexmkStatus(<q-bang> == "!")
+command! LatexmkStop call LatexBox_LatexmkStop(0)
+command! LatexErrors call LatexBox_LatexErrors(-1)
+
+if g:LatexBox_latexmk_async || g:LatexBox_latexmk_preview_continuously
+ autocmd BufUnload <buffer> call LatexBox_LatexmkStop(1)
+ autocmd VimLeave * call <SID>kill_all_latexmk_processes()
+endif
+
+" }}}
+
+" vim:fdm=marker:ff=unix:noet:ts=4:sw=4
diff --git a/ftplugin/latex-box/mappings.vim b/ftplugin/latex-box/mappings.vim
new file mode 100644
index 00000000..509f5f19
--- /dev/null
+++ b/ftplugin/latex-box/mappings.vim
@@ -0,0 +1,96 @@
+" LaTeX Box mappings
+
+if exists("g:LatexBox_no_mappings")
+ finish
+endif
+
+" latexmk {{{
+map <buffer> <LocalLeader>ll :Latexmk<CR>
+map <buffer> <LocalLeader>lL :Latexmk!<CR>
+map <buffer> <LocalLeader>lc :LatexmkClean<CR>
+map <buffer> <LocalLeader>lC :LatexmkClean!<CR>
+map <buffer> <LocalLeader>lg :LatexmkStatus<CR>
+map <buffer> <LocalLeader>lG :LatexmkStatus!<CR>
+map <buffer> <LocalLeader>lk :LatexmkStop<CR>
+map <buffer> <LocalLeader>le :LatexErrors<CR>
+" }}}
+
+" View {{{
+map <buffer> <LocalLeader>lv :LatexView<CR>
+" }}}
+
+" TOC {{{
+map <silent> <buffer> <LocalLeader>lt :LatexTOC<CR>
+" }}}
+
+" Jump to match {{{
+if !exists('g:LatexBox_loaded_matchparen')
+ nmap <buffer> % <Plug>LatexBox_JumpToMatch
+ vmap <buffer> % <Plug>LatexBox_JumpToMatch
+ omap <buffer> % <Plug>LatexBox_JumpToMatch
+endif
+" }}}
+
+" Define text objects {{{
+vmap <buffer> ie <Plug>LatexBox_SelectCurrentEnvInner
+vmap <buffer> ae <Plug>LatexBox_SelectCurrentEnvOuter
+omap <buffer> ie :normal vie<CR>
+omap <buffer> ae :normal vae<CR>
+vmap <buffer> i$ <Plug>LatexBox_SelectInlineMathInner
+vmap <buffer> a$ <Plug>LatexBox_SelectInlineMathOuter
+omap <buffer> i$ :normal vi$<CR>
+omap <buffer> a$ :normal va$<CR>
+" }}}
+
+" Jump between sections {{{
+function! s:LatexBoxNextSection(type, backwards, visual)
+ " Restore visual mode if desired
+ if a:visual
+ normal! gv
+ endif
+
+ " For the [] and ][ commands we move up or down before the search
+ if a:type == 1
+ if a:backwards
+ normal! k
+ else
+ normal! j
+ endif
+ endif
+
+ " Define search pattern and do the search while preserving "/
+ let save_search = @/
+ let flags = 'W'
+ if a:backwards
+ let flags = 'b' . flags
+ endif
+ let notcomment = '\%(\%(\\\@<!\%(\\\\\)*\)\@<=%.*\)\@<!'
+ let pattern = notcomment . '\v\s*\\(' . join([
+ \ '(sub)*section',
+ \ 'chapter',
+ \ 'part',
+ \ 'appendix',
+ \ '(front|back|main)matter'], '|') . ')>'
+ call search(pattern, flags)
+ let @/ = save_search
+
+ " For the [] and ][ commands we move down or up after the search
+ if a:type == 1
+ if a:backwards
+ normal! j
+ else
+ normal! k
+ endif
+ endif
+endfunction
+noremap <buffer> <silent> ]] :call <SID>LatexBoxNextSection(0,0,0)<CR>
+noremap <buffer> <silent> ][ :call <SID>LatexBoxNextSection(1,0,0)<CR>
+noremap <buffer> <silent> [] :call <SID>LatexBoxNextSection(1,1,0)<CR>
+noremap <buffer> <silent> [[ :call <SID>LatexBoxNextSection(0,1,0)<CR>
+vnoremap <buffer> <silent> ]] :<c-u>call <SID>LatexBoxNextSection(0,0,1)<CR>
+vnoremap <buffer> <silent> ][ :<c-u>call <SID>LatexBoxNextSection(1,0,1)<CR>
+vnoremap <buffer> <silent> [] :<c-u>call <SID>LatexBoxNextSection(1,1,1)<CR>
+vnoremap <buffer> <silent> [[ :<c-u>call <SID>LatexBoxNextSection(0,1,1)<CR>
+" }}}
+
+" vim:fdm=marker:ff=unix:noet:ts=4:sw=4
diff --git a/ftplugin/latex-box/motion.vim b/ftplugin/latex-box/motion.vim
new file mode 100644
index 00000000..58a6fb42
--- /dev/null
+++ b/ftplugin/latex-box/motion.vim
@@ -0,0 +1,518 @@
+" LaTeX Box motion functions
+
+" Motion options {{{
+" Opening and closing patterns
+if !exists('g:LatexBox_open_pats')
+ let g:LatexBox_open_pats = [ '\\{','{','\\(','(','\\\[','\[',
+ \ '\\begin\s*{.\{-}}', '\\left\s*\%([^\\]\|\\.\|\\\a*\)']
+ let g:LatexBox_close_pats = [ '\\}','}','\\)',')','\\\]','\]',
+ \ '\\end\s*{.\{-}}', '\\right\s*\%([^\\]\|\\.\|\\\a*\)']
+endif
+" }}}
+
+" HasSyntax {{{
+" s:HasSyntax(syntaxName, [line], [col])
+function! s:HasSyntax(syntaxName, ...)
+ let line = a:0 >= 1 ? a:1 : line('.')
+ let col = a:0 >= 2 ? a:2 : col('.')
+ return index(map(synstack(line, col),
+ \ 'synIDattr(v:val, "name") == "' . a:syntaxName . '"'),
+ \ 1) >= 0
+endfunction
+" }}}
+
+" Search and Skip Comments {{{
+" s:SearchAndSkipComments(pattern, [flags], [stopline])
+function! s:SearchAndSkipComments(pat, ...)
+ let flags = a:0 >= 1 ? a:1 : ''
+ let stopline = a:0 >= 2 ? a:2 : 0
+ let saved_pos = getpos('.')
+
+ " search once
+ let ret = search(a:pat, flags, stopline)
+
+ if ret
+ " do not match at current position if inside comment
+ let flags = substitute(flags, 'c', '', 'g')
+
+ " keep searching while in comment
+ while LatexBox_InComment()
+ let ret = search(a:pat, flags, stopline)
+ if !ret
+ break
+ endif
+ endwhile
+ endif
+
+ if !ret
+ " if no match found, restore position
+ call setpos('.', saved_pos)
+ endif
+
+ return ret
+endfunction
+" }}}
+
+" Finding Matching Pair {{{
+function! s:FindMatchingPair(mode)
+
+ if a:mode =~ 'h\|i'
+ 2match none
+ elseif a:mode == 'v'
+ normal! gv
+ endif
+
+ if LatexBox_InComment() | return | endif
+
+ " open/close pairs (dollars signs are treated apart)
+ let dollar_pat = '\$'
+ let notbslash = '\%(\\\@<!\%(\\\\\)*\)\@<='
+ let notcomment = '\%(\%(\\\@<!\%(\\\\\)*\)\@<=%.*\)\@<!'
+ let anymatch = '\('
+ \ . join(g:LatexBox_open_pats + g:LatexBox_close_pats, '\|')
+ \ . '\|' . dollar_pat . '\)'
+
+ let lnum = line('.')
+ let cnum = searchpos('\A', 'cbnW', lnum)[1]
+ " if the previous char is a backslash
+ if strpart(getline(lnum), 0, cnum-1) !~ notbslash . '$' | let cnum = cnum-1 | endif
+ let delim = matchstr(getline(lnum), '\C^'. anymatch , cnum - 1)
+
+ if empty(delim) || strlen(delim)+cnum-1< col('.')
+ if a:mode =~ 'n\|v\|o'
+ " if not found, search forward
+ let cnum = match(getline(lnum), '\C'. anymatch , col('.') - 1) + 1
+ if cnum == 0 | return | endif
+ call cursor(lnum, cnum)
+ let delim = matchstr(getline(lnum), '\C^'. anymatch , cnum - 1)
+ elseif a:mode =~ 'i'
+ " if not found, move one char bacward and search
+ let cnum = searchpos('\A', 'bnW', lnum)[1]
+ " if the previous char is a backslash
+ if strpart(getline(lnum), 0, cnum-1) !~ notbslash . '$' | let cnum = cnum-1 | endif
+ let delim = matchstr(getline(lnum), '\C^'. anymatch , cnum - 1)
+ if empty(delim) || strlen(delim)+cnum< col('.') | return | endif
+ elseif a:mode =~ 'h'
+ return
+ endif
+ endif
+
+ if delim =~ '^\$'
+
+ " match $-pairs
+ " check if next character is in inline math
+ let [lnum0, cnum0] = searchpos('.', 'nW')
+ if lnum0 && s:HasSyntax('texMathZoneX', lnum0, cnum0)
+ let [lnum2, cnum2] = searchpos(notcomment . notbslash. dollar_pat, 'nW', line('w$')*(a:mode =~ 'h\|i') , 200)
+ else
+ let [lnum2, cnum2] = searchpos('\%(\%'. lnum . 'l\%' . cnum . 'c\)\@!'. notcomment . notbslash . dollar_pat, 'bnW', line('w0')*(a:mode =~ 'h\|i') , 200)
+ endif
+
+ if a:mode =~ 'h\|i'
+ execute '2match MatchParen /\%(\%' . lnum . 'l\%' . cnum . 'c\$' . '\|\%' . lnum2 . 'l\%' . cnum2 . 'c\$\)/'
+ elseif a:mode =~ 'n\|v\|o'
+ call cursor(lnum2,cnum2)
+ endif
+
+ else
+ " match other pairs
+ for i in range(len(g:LatexBox_open_pats))
+ let open_pat = notbslash . g:LatexBox_open_pats[i]
+ let close_pat = notbslash . g:LatexBox_close_pats[i]
+
+ if delim =~# '^' . open_pat
+ " if on opening pattern, search for closing pattern
+ let [lnum2, cnum2] = searchpairpos('\C' . open_pat, '', '\C'
+ \ . close_pat, 'nW', 'LatexBox_InComment()',
+ \ line('w$')*(a:mode =~ 'h\|i') , 200)
+ if a:mode =~ 'h\|i'
+ execute '2match MatchParen /\%(\%' . lnum . 'l\%' . cnum
+ \ . 'c' . g:LatexBox_open_pats[i] . '\|\%'
+ \ . lnum2 . 'l\%' . cnum2 . 'c'
+ \ . g:LatexBox_close_pats[i] . '\)/'
+ elseif a:mode =~ 'n\|v\|o'
+ call cursor(lnum2,cnum2)
+ if strlen(close_pat)>1 && a:mode =~ 'o'
+ call cursor(lnum2, matchend(getline('.'), '\C'
+ \ . close_pat, col('.')-1))
+ endif
+ endif
+ break
+ elseif delim =~# '^' . close_pat
+ " if on closing pattern, search for opening pattern
+ let [lnum2, cnum2] = searchpairpos('\C' . open_pat, '',
+ \ '\C\%(\%'. lnum . 'l\%' . cnum . 'c\)\@!'
+ \ . close_pat, 'bnW', 'LatexBox_InComment()',
+ \ line('w0')*(a:mode =~ 'h\|i') , 200)
+ if a:mode =~ 'h\|i'
+ execute '2match MatchParen /\%(\%' . lnum2 . 'l\%' . cnum2
+ \ . 'c' . g:LatexBox_open_pats[i] . '\|\%'
+ \ . lnum . 'l\%' . cnum . 'c'
+ \ . g:LatexBox_close_pats[i] . '\)/'
+ elseif a:mode =~ 'n\|v\|o'
+ call cursor(lnum2,cnum2)
+ endif
+ break
+ endif
+ endfor
+
+ endif
+endfunction
+
+" Allow to disable functionality if desired
+if !exists('g:LatexBox_loaded_matchparen')
+ " Disable matchparen autocommands
+ augroup LatexBox_HighlightPairs
+ autocmd BufEnter * if !exists("g:loaded_matchparen") || !g:loaded_matchparen | runtime plugin/matchparen.vim | endif
+ autocmd BufEnter *.tex 3match none | unlet! g:loaded_matchparen | au! matchparen
+ autocmd! CursorMoved *.tex call s:FindMatchingPair('h')
+ autocmd! CursorMovedI *.tex call s:FindMatchingPair('i')
+ augroup END
+endif
+
+" Use LatexBox'es FindMatchingPair as '%' (enable jump between e.g. $'s)
+nnoremap <silent> <Plug>LatexBox_JumpToMatch :call <SID>FindMatchingPair('n')<CR>
+vnoremap <silent> <Plug>LatexBox_JumpToMatch :call <SID>FindMatchingPair('v')<CR>
+onoremap <silent> <Plug>LatexBox_JumpToMatch v:call <SID>FindMatchingPair('o')<CR>
+
+" }}}
+
+" select inline math {{{
+" s:SelectInlineMath(seltype)
+" where seltype is either 'inner' or 'outer'
+function! s:SelectInlineMath(seltype)
+
+ let dollar_pat = '\\\@<!\$'
+
+ if s:HasSyntax('texMathZoneX')
+ call s:SearchAndSkipComments(dollar_pat, 'cbW')
+ elseif getline('.')[col('.') - 1] == '$'
+ call s:SearchAndSkipComments(dollar_pat, 'bW')
+ else
+ return
+ endif
+
+ if a:seltype == 'inner'
+ normal! l
+ endif
+
+ if visualmode() ==# 'V'
+ normal! V
+ else
+ normal! v
+ endif
+
+ call s:SearchAndSkipComments(dollar_pat, 'W')
+
+ if a:seltype == 'inner'
+ normal! h
+ endif
+endfunction
+
+vnoremap <silent> <Plug>LatexBox_SelectInlineMathInner
+ \ :<C-U>call <SID>SelectInlineMath('inner')<CR>
+vnoremap <silent> <Plug>LatexBox_SelectInlineMathOuter
+ \ :<C-U>call <SID>SelectInlineMath('outer')<CR>
+" }}}
+
+" select current environment {{{
+function! s:SelectCurrentEnv(seltype)
+ let [env, lnum, cnum, lnum2, cnum2] = LatexBox_GetCurrentEnvironment(1)
+ call cursor(lnum, cnum)
+ if a:seltype == 'inner'
+ if env =~ '^\'
+ call search('\\.\_\s*\S', 'eW')
+ else
+ call search('}\(\_\s*\[\_[^]]*\]\)\?\_\s*\S', 'eW')
+ endif
+ endif
+ if visualmode() ==# 'V'
+ normal! V
+ else
+ normal! v
+ endif
+ call cursor(lnum2, cnum2)
+ if a:seltype == 'inner'
+ call search('\S\_\s*', 'bW')
+ else
+ if env =~ '^\'
+ normal! l
+ else
+ call search('}', 'eW')
+ endif
+ endif
+endfunction
+vnoremap <silent> <Plug>LatexBox_SelectCurrentEnvInner :<C-U>call <SID>SelectCurrentEnv('inner')<CR>
+vnoremap <silent> <Plug>LatexBox_SelectCurrentEnvOuter :<C-U>call <SID>SelectCurrentEnv('outer')<CR>
+" }}}
+
+" Jump to the next braces {{{
+"
+function! LatexBox_JumpToNextBraces(backward)
+ let flags = ''
+ if a:backward
+ normal h
+ let flags .= 'b'
+ else
+ let flags .= 'c'
+ endif
+ if search('[][}{]', flags) > 0
+ normal l
+ endif
+ let prev = strpart(getline('.'), col('.') - 2, 1)
+ let next = strpart(getline('.'), col('.') - 1, 1)
+ if next =~ '[]}]' && prev !~ '[][{}]'
+ return "\<Right>"
+ else
+ return ''
+ endif
+endfunction
+" }}}
+
+" Table of Contents {{{
+
+" Special UTF-8 conversion
+function! s:ConvertBack(line)
+ let line = a:line
+ if !exists('g:LatexBox_plaintext_toc')
+ let line = substitute(line, "\\\\IeC\s*{\\\\'a}", 'á', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\`a}", 'à', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\^a}", 'à', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\¨a}", 'ä', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\\"a}", 'ä', 'g')
+
+ let line = substitute(line, "\\\\IeC\s*{\\\\'e}", 'é', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\`e}", 'è', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\^e}", 'ê', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\¨e}", 'ë', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\\"e}", 'ë', 'g')
+
+ let line = substitute(line, "\\\\IeC\s*{\\\\'i}", 'í', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\`i}", 'î', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\^i}", 'ì', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\¨i}", 'ï', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\\"i}", 'ï', 'g')
+
+ let line = substitute(line, "\\\\IeC\s*{\\\\'o}", 'ó', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\`o}", 'ò', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\^o}", 'ô', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\¨o}", 'ö', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\\"o}", 'ö', 'g')
+
+ let line = substitute(line, "\\\\IeC\s*{\\\\'u}", 'ú', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\`u}", 'ù', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\^u}", 'û', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\¨u}", 'ü', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\\"u}", 'ü', 'g')
+
+ let line = substitute(line, "\\\\IeC\s*{\\\\'A}", 'Á', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\`A}", 'À', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\^A}", 'À', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\¨A}", 'Ä', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\\"A}", 'Ä', 'g')
+
+ let line = substitute(line, "\\\\IeC\s*{\\\\'E}", 'É', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\`E}", 'È', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\^E}", 'Ê', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\¨E}", 'Ë', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\\"E}", 'Ë', 'g')
+
+ let line = substitute(line, "\\\\IeC\s*{\\\\'I}", 'Í', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\`I}", 'Î', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\^I}", 'Ì', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\¨I}", 'Ï', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\\"I}", 'Ï', 'g')
+
+ let line = substitute(line, "\\\\IeC\s*{\\\\'O}", 'Ó', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\`O}", 'Ò', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\^O}", 'Ô', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\¨O}", 'Ö', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\\"O}", 'Ö', 'g')
+
+ let line = substitute(line, "\\\\IeC\s*{\\\\'U}", 'Ú', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\`U}", 'Ù', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\^U}", 'Û', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\¨U}", 'Ü', 'g')
+ let line = substitute(line, "\\\\IeC\s*{\\\\\"U}", 'Ü', 'g')
+ else
+ " substitute stuff like '\IeC{\"u}' (utf-8 umlauts in section heading)
+ " to plain 'u'
+ let line = substitute(line, "\\\\IeC\s*{\\\\.\\(.\\)}", '\1', 'g')
+ endif
+ return line
+endfunction
+
+function! s:ReadTOC(auxfile, texfile, ...)
+ let texfile = a:texfile
+ let prefix = fnamemodify(a:auxfile, ':p:h')
+
+ if a:0 != 2
+ let toc = []
+ let fileindices = { texfile : [] }
+ else
+ let toc = a:1
+ let fileindices = a:2
+ let fileindices[ texfile ] = []
+ endif
+
+ for line in readfile(a:auxfile)
+ let included = matchstr(line, '^\\@input{\zs[^}]*\ze}')
+ if included != ''
+ " append the input TOX to `toc` and `fileindices`
+ let newaux = prefix . '/' . included
+ let newtex = fnamemodify(fnamemodify(newaux, ':t:r') . '.tex', ':p')
+ call s:ReadTOC(newaux, newtex, toc, fileindices)
+ continue
+ endif
+
+ " Parse statements like:
+ " \@writefile{toc}{\contentsline {section}{\numberline {secnum}Section Title}{pagenumber}}
+ " \@writefile{toc}{\contentsline {section}{\tocsection {}{1}{Section Title}}{pagenumber}}
+ " \@writefile{toc}{\contentsline {section}{\numberline {secnum}Section Title}{pagenumber}{otherstuff}}
+
+ let line = matchstr(line,
+ \ '\\@writefile{toc}{\\contentsline\s*\zs.*\ze}\s*$')
+ if empty(line)
+ continue
+ endif
+
+ let tree = LatexBox_TexToTree(line)
+
+ if len(tree) < 3
+ " unknown entry type: just skip it
+ continue
+ endif
+
+ " parse level
+ let level = tree[0][0]
+ " parse page
+ if !empty(tree[2])
+ let page = tree[2][0]
+ else
+ let page = ''
+ endif
+ " parse section number
+ if len(tree[1]) > 3 && empty(tree[1][1])
+ call remove(tree[1], 1)
+ endif
+ if len(tree[1]) > 1
+ if !empty(tree[1][1])
+ let secnum = LatexBox_TreeToTex(tree[1][1])
+ let secnum = s:ConvertBack(secnum)
+ let secnum = substitute(secnum, '\\\S\+\s', '', 'g')
+ let secnum = substitute(secnum, '\\\S\+{\(.\{-}\)}', '\1', 'g')
+ let secnum = substitute(secnum, '^{\+\|}\+$', '', 'g')
+ endif
+ let tree = tree[1][2:]
+ else
+ let secnum = ''
+ let tree = tree[1]
+ endif
+ " parse section title
+ let text = LatexBox_TreeToTex(tree)
+ let text = s:ConvertBack(text)
+ let text = substitute(text, '^{\+\|}\+$', '', 'g')
+
+ " add TOC entry
+ call add(fileindices[texfile], len(toc))
+ call add(toc, {'file': texfile,
+ \ 'level': level,
+ \ 'number': secnum,
+ \ 'text': text,
+ \ 'page': page})
+ endfor
+
+ return [toc, fileindices]
+
+endfunction
+
+function! LatexBox_TOC(...)
+
+ " Check if window already exists
+ let winnr = bufwinnr(bufnr('LaTeX TOC'))
+ if winnr >= 0
+ if a:0 == 0
+ silent execute winnr . 'wincmd w'
+ else
+ " Supplying an argument to this function causes toggling instead
+ " of jumping to the TOC window
+ if g:LatexBox_split_resize
+ silent exe "set columns-=" . g:LatexBox_split_width
+ endif
+ silent execute 'bwipeout' . bufnr('LaTeX TOC')
+ endif
+ return
+ endif
+
+ " Read TOC
+ let [toc, fileindices] = s:ReadTOC(LatexBox_GetAuxFile(),
+ \ LatexBox_GetMainTexFile())
+ let calling_buf = bufnr('%')
+
+ " Find closest section in current buffer
+ let closest_index = s:FindClosestSection(toc,fileindices)
+
+ " Create TOC window and set local settings
+ if g:LatexBox_split_resize
+ silent exe "set columns+=" . g:LatexBox_split_width
+ endif
+ silent exe g:LatexBox_split_side g:LatexBox_split_width . 'vnew LaTeX\ TOC'
+ let b:toc = toc
+ let b:toc_numbers = 1
+ let b:calling_win = bufwinnr(calling_buf)
+ setlocal filetype=latextoc
+
+ " Add TOC entries and jump to the closest section
+ for entry in toc
+ call append('$', entry['number'] . "\t" . entry['text'])
+ endfor
+ if !g:LatexBox_toc_hidehelp
+ call append('$', "")
+ call append('$', "<Esc>/q: close")
+ call append('$', "<Space>: jump")
+ call append('$', "<Enter>: jump and close")
+ call append('$', "s: hide numbering")
+ endif
+ 0delete _
+
+ execute 'normal! ' . (closest_index + 1) . 'G'
+
+ " Lock buffer
+ setlocal nomodifiable
+endfunction
+
+" Binary search for the closest section
+" return the index of the TOC entry
+function! s:FindClosestSection(toc, fileindices)
+ let file = expand('%:p')
+ if !has_key(a:fileindices, file)
+ echoe 'Current file is not included in main tex file ' . LatexBox_GetMainTexFile() . '.'
+ endif
+
+ let imax = len(a:fileindices[file])
+ let imin = 0
+ while imin < imax - 1
+ let i = (imax + imin) / 2
+ let tocindex = a:fileindices[file][i]
+ let entry = a:toc[tocindex]
+ let titlestr = entry['text']
+ let titlestr = escape(titlestr, '\')
+ let titlestr = substitute(titlestr, ' ', '\\_\\s\\+', 'g')
+ let [lnum, cnum] = searchpos('\\' . entry['level'] . '\_\s*{' . titlestr . '}', 'nW')
+ if lnum
+ let imax = i
+ else
+ let imin = i
+ endif
+ endwhile
+
+ return a:fileindices[file][imin]
+endfunction
+" }}}
+
+" TOC Command {{{
+command! LatexTOC call LatexBox_TOC()
+command! LatexTOCToggle call LatexBox_TOC(1)
+" }}}
+
+" vim:fdm=marker:ff=unix:noet:ts=4:sw=4