summaryrefslogtreecommitdiffstats
path: root/ftplugin/latex-box/motion.vim
diff options
context:
space:
mode:
authorAdam Stankiewicz <sheerun@sher.pl>2017-12-30 11:10:32 +0100
committerAdam Stankiewicz <sheerun@sher.pl>2017-12-30 11:10:32 +0100
commitbb85059bacd5b415a012f25679111a0e55d3c6d9 (patch)
tree315b2238ec4840aa4bf00ae48276bdffded07673 /ftplugin/latex-box/motion.vim
parent11f53253ad9fd0cd3e7a44ed9f8c80a4f265b46e (diff)
downloadvim-polyglot-bb85059bacd5b415a012f25679111a0e55d3c6d9.tar.gz
vim-polyglot-bb85059bacd5b415a012f25679111a0e55d3c6d9.zip
Update
Diffstat (limited to 'ftplugin/latex-box/motion.vim')
-rw-r--r--ftplugin/latex-box/motion.vim548
1 files changed, 548 insertions, 0 deletions
diff --git a/ftplugin/latex-box/motion.vim b/ftplugin/latex-box/motion.vim
new file mode 100644
index 00000000..2053149c
--- /dev/null
+++ b/ftplugin/latex-box/motion.vim
@@ -0,0 +1,548 @@
+if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1
+
+" 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), cnum-2, 1) == '\'
+ 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), cnum-2, 1) == '\'
+ 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')
+ "
+ " Substitute stuff like '\IeC{\"u}' to plain 'u'
+ "
+ let line = substitute(line, '\\IeC\s*{\\.\(.\)}', '\1', 'g')
+ else
+ "
+ " Substitute stuff like '\IeC{\"u}' to corresponding unicode symbols
+ "
+ for [pat, symbol] in s:ConvBackPats
+ let line = substitute(line, pat, symbol, 'g')
+ endfor
+ 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(newaux, ':r') . '.tex'
+ 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(s:ConvertBack(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
+ let secnum = ''
+ let tree = tree[1]
+ if len(tree) > 3 && empty(tree[1])
+ call remove(tree, 1)
+ endif
+ if len(tree) > 1 && type(tree[0]) == type("") && tree[0] =~ '^\\\(\(chapter\)\?numberline\|tocsection\)'
+ let secnum = LatexBox_TreeToTex(tree[1])
+ let secnum = substitute(secnum, '\\\S\+\s', '', 'g')
+ let secnum = substitute(secnum, '\\\S\+{\(.\{-}\)}', '\1', 'g')
+ let secnum = substitute(secnum, '^{\+\|}\+$', '', 'g')
+ call remove(tree, 1)
+ endif
+ " parse section title
+ let text = LatexBox_TreeToTex(tree)
+ let text = substitute(text, '^{\+\|}\+$', '', 'g')
+ let text = substitute(text, '\m^\\\(no\)\?\(chapter\)\?numberline\s*', '', '')
+ 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'))
+ " Two types of splits, horizontal and vertical
+ let l:hori = "new"
+ let l:vert = "vnew"
+
+ " Set General Vars and initialize size
+ let l:type = g:LatexBox_split_type
+ let l:size = 10
+
+ " Size detection
+ if l:type == l:hori
+ let l:size = g:LatexBox_split_length
+ elseif l:type == l:vert
+ let l:size = g:LatexBox_split_width
+ endif
+
+ 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-=" . l:size
+ 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+=" . l:size
+ endif
+ silent exe g:LatexBox_split_side l:size . l:type . ' 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)
+ return 0
+ endif
+
+ let imax = len(a:fileindices[file])
+ if imax > 0
+ 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]
+ else
+ return 0
+ endif
+endfunction
+
+let s:ConvBackPats = map([
+ \ ['\\''A}' , 'Á'],
+ \ ['\\`A}' , 'À'],
+ \ ['\\^A}' , 'À'],
+ \ ['\\¨A}' , 'Ä'],
+ \ ['\\"A}' , 'Ä'],
+ \ ['\\''a}' , 'á'],
+ \ ['\\`a}' , 'à'],
+ \ ['\\^a}' , 'à'],
+ \ ['\\¨a}' , 'ä'],
+ \ ['\\"a}' , 'ä'],
+ \ ['\\''E}' , 'É'],
+ \ ['\\`E}' , 'È'],
+ \ ['\\^E}' , 'Ê'],
+ \ ['\\¨E}' , 'Ë'],
+ \ ['\\"E}' , 'Ë'],
+ \ ['\\''e}' , 'é'],
+ \ ['\\`e}' , 'è'],
+ \ ['\\^e}' , 'ê'],
+ \ ['\\¨e}' , 'ë'],
+ \ ['\\"e}' , 'ë'],
+ \ ['\\''I}' , 'Í'],
+ \ ['\\`I}' , 'Î'],
+ \ ['\\^I}' , 'Ì'],
+ \ ['\\¨I}' , 'Ï'],
+ \ ['\\"I}' , 'Ï'],
+ \ ['\\''i}' , 'í'],
+ \ ['\\`i}' , 'î'],
+ \ ['\\^i}' , 'ì'],
+ \ ['\\¨i}' , 'ï'],
+ \ ['\\"i}' , 'ï'],
+ \ ['\\''{\?\\i }' , 'í'],
+ \ ['\\''O}' , 'Ó'],
+ \ ['\\`O}' , 'Ò'],
+ \ ['\\^O}' , 'Ô'],
+ \ ['\\¨O}' , 'Ö'],
+ \ ['\\"O}' , 'Ö'],
+ \ ['\\''o}' , 'ó'],
+ \ ['\\`o}' , 'ò'],
+ \ ['\\^o}' , 'ô'],
+ \ ['\\¨o}' , 'ö'],
+ \ ['\\"o}' , 'ö'],
+ \ ['\\''U}' , 'Ú'],
+ \ ['\\`U}' , 'Ù'],
+ \ ['\\^U}' , 'Û'],
+ \ ['\\¨U}' , 'Ü'],
+ \ ['\\"U}' , 'Ü'],
+ \ ['\\''u}' , 'ú'],
+ \ ['\\`u}' , 'ù'],
+ \ ['\\^u}' , 'û'],
+ \ ['\\¨u}' , 'ü'],
+ \ ['\\"u}' , 'ü'],
+ \ ['\\`N}' , 'Ǹ'],
+ \ ['\\\~N}' , 'Ñ'],
+ \ ['\\''n}' , 'ń'],
+ \ ['\\`n}' , 'ǹ'],
+ \ ['\\\~n}' , 'ñ'],
+ \], '[''\C\(\\IeC\s*{\)\?'' . v:val[0], v:val[1]]')
+" }}}
+
+" TOC Command {{{
+command! LatexTOC call LatexBox_TOC()
+command! LatexTOCToggle call LatexBox_TOC(1)
+" }}}
+
+" vim:fdm=marker:ff=unix:noet:ts=4:sw=4
+
+endif