summaryrefslogtreecommitdiffstats
path: root/ftplugin/latex-box/motion.vim
diff options
context:
space:
mode:
Diffstat (limited to 'ftplugin/latex-box/motion.vim')
-rw-r--r--ftplugin/latex-box/motion.vim518
1 files changed, 518 insertions, 0 deletions
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