fix: Switch latex to LaTeX-Box-Team/LaTeX-Box, fixes #6

This commit is contained in:
Adam Stankiewicz
2013-09-17 01:43:28 +02:00
parent ff3cc42bf3
commit ba1305772d
38 changed files with 2886 additions and 9253 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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