" 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 = '\%(\\\@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 LatexBox_JumpToMatch :call FindMatchingPair('n') vnoremap LatexBox_JumpToMatch :call FindMatchingPair('v') onoremap LatexBox_JumpToMatch v:call FindMatchingPair('o') " }}} " select inline math {{{ " s:SelectInlineMath(seltype) " where seltype is either 'inner' or 'outer' function! s:SelectInlineMath(seltype) let dollar_pat = '\\\@ LatexBox_SelectInlineMathInner \ :call SelectInlineMath('inner') vnoremap LatexBox_SelectInlineMathOuter \ :call SelectInlineMath('outer') " }}} " 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 LatexBox_SelectCurrentEnvInner :call SelectCurrentEnv('inner') vnoremap LatexBox_SelectCurrentEnvOuter :call SelectCurrentEnv('outer') " }}} " 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 "\" 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('$', "/q: close") call append('$', ": jump") call append('$', ": 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