diff options
Diffstat (limited to 'autoload/vimtex/delim.vim')
-rw-r--r-- | autoload/vimtex/delim.vim | 1096 |
1 files changed, 0 insertions, 1096 deletions
diff --git a/autoload/vimtex/delim.vim b/autoload/vimtex/delim.vim deleted file mode 100644 index 1a9b965a..00000000 --- a/autoload/vimtex/delim.vim +++ /dev/null @@ -1,1096 +0,0 @@ -if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'latex') == -1 - -" vimtex - LaTeX plugin for Vim -" -" Maintainer: Karl Yngve LervÄg -" Email: karl.yngve@gmail.com -" - -function! vimtex#delim#init_buffer() abort " {{{1 - nnoremap <silent><buffer> <plug>(vimtex-delim-toggle-modifier) - \ :<c-u>call <sid>operator_setup('toggle_modifier_next') - \ <bar> normal! <c-r>=v:count ? v:count : ''<cr>g@l<cr> - - nnoremap <silent><buffer> <plug>(vimtex-delim-toggle-modifier-reverse) - \ :<c-u>call <sid>operator_setup('toggle_modifier_prev') - \ <bar> normal! <c-r>=v:count ? v:count : ''<cr>g@l<cr> - - xnoremap <silent><buffer> <plug>(vimtex-delim-toggle-modifier) - \ :<c-u>call vimtex#delim#toggle_modifier_visual()<cr> - - xnoremap <silent><buffer> <plug>(vimtex-delim-toggle-modifier-reverse) - \ :<c-u>call vimtex#delim#toggle_modifier_visual({'dir': -1})<cr> - - nnoremap <silent><buffer> <plug>(vimtex-delim-change-math) - \ :<c-u>call <sid>operator_setup('change')<bar>normal! g@l<cr> - - nnoremap <silent><buffer> <plug>(vimtex-delim-delete) - \ :<c-u>call <sid>operator_setup('delete')<bar>normal! g@l<cr> - - inoremap <silent><buffer> <plug>(vimtex-delim-close) - \ <c-r>=vimtex#delim#close()<cr> -endfunction - -" }}}1 - -function! vimtex#delim#close() abort " {{{1 - let l:save_pos = vimtex#pos#get_cursor() - let l:posval_cursor = vimtex#pos#val(l:save_pos) - let l:posval_current = l:posval_cursor - let l:posval_last = l:posval_cursor + 1 - - while l:posval_current < l:posval_last - let l:open = vimtex#delim#get_prev('all', 'open', - \ { 'syn_exclude' : 'texComment' }) - if empty(l:open) || get(l:open, 'name', '') ==# 'document' - break - endif - - let l:close = vimtex#delim#get_matching(l:open) - if empty(l:close.match) - call vimtex#pos#set_cursor(l:save_pos) - return l:open.corr - endif - - let l:posval_last = l:posval_current - let l:posval_current = vimtex#pos#val(l:open) - let l:posval_try = vimtex#pos#val(l:close) + strlen(l:close.match) - if l:posval_current != l:posval_cursor - \ && l:posval_try > l:posval_cursor - call vimtex#pos#set_cursor(l:save_pos) - return l:open.corr - endif - - call vimtex#pos#set_cursor(vimtex#pos#prev(l:open)) - endwhile - - call vimtex#pos#set_cursor(l:save_pos) - return '' -endfunction - -" }}}1 -function! vimtex#delim#toggle_modifier(...) abort " {{{1 - let l:args = a:0 > 0 ? a:1 : {} - call extend(l:args, { - \ 'count': v:count1, - \ 'dir': 1, - \ 'repeat': 1, - \ 'openclose': [], - \ }, 'keep') - - let [l:open, l:close] = !empty(l:args.openclose) - \ ? l:args.openclose - \ : vimtex#delim#get_surrounding('delim_modq_math') - if empty(l:open) | return | endif - - let l:direction = l:args.dir < 0 ? -l:args.count : l:args.count - - let newmods = ['', ''] - let modlist = [['', '']] + get(g:, 'vimtex_delim_toggle_mod_list', - \ [['\left', '\right']]) - let n = len(modlist) - for i in range(n) - let j = (i + l:direction) % n - if l:open.mod ==# modlist[i][0] - let newmods = modlist[j] - break - endif - endfor - - let line = getline(l:open.lnum) - let line = strpart(line, 0, l:open.cnum - 1) - \ . newmods[0] - \ . strpart(line, l:open.cnum + len(l:open.mod) - 1) - call setline(l:open.lnum, line) - - let l:cnum = l:close.cnum - if l:open.lnum == l:close.lnum - let n = len(newmods[0]) - len(l:open.mod) - let l:cnum += n - let pos = vimtex#pos#get_cursor() - if pos[2] > l:open.cnum + len(l:open.mod) - let pos[2] += n - call vimtex#pos#set_cursor(pos) - endif - endif - - let line = getline(l:close.lnum) - let line = strpart(line, 0, l:cnum - 1) - \ . newmods[1] - \ . strpart(line, l:cnum + len(l:close.mod) - 1) - call setline(l:close.lnum, line) - - return newmods -endfunction - -" }}}1 -function! vimtex#delim#toggle_modifier_visual(...) abort " {{{1 - let l:args = a:0 > 0 ? a:1 : {} - call extend(l:args, { - \ 'count': v:count1, - \ 'dir': 1, - \ 'reselect': 1, - \ }, 'keep') - - let l:save_pos = vimtex#pos#get_cursor() - let l:start_pos = getpos("'<") - let l:end_pos = getpos("'>") - let l:end_pos_val = vimtex#pos#val(l:end_pos) + 1000 - let l:cur_pos = l:start_pos - - " - " Check if selection is swapped - " - let l:end_pos[1] += 1 - call setpos("'>", l:end_pos) - let l:end_pos[1] -= 1 - normal! gv - let l:swapped = l:start_pos != getpos("'<") - - " - " First we generate a stack of all delimiters that should be toggled - " - let l:stack = [] - while vimtex#pos#val(l:cur_pos) < l:end_pos_val - call vimtex#pos#set_cursor(l:cur_pos) - let l:open = vimtex#delim#get_next('delim_modq_math', 'open') - if empty(l:open) | break | endif - - if vimtex#pos#val(l:open) >= l:end_pos_val - break - endif - - let l:close = vimtex#delim#get_matching(l:open) - if !empty(get(l:close, 'match')) - - if l:end_pos_val >= vimtex#pos#val(l:close) + strlen(l:close.match) - 1 - let l:newmods = vimtex#delim#toggle_modifier({ - \ 'repeat': 0, - \ 'count': l:args.count, - \ 'dir': l:args.dir, - \ 'openclose': [l:open, l:close], - \ }) - - let l:col_diff = (l:open.lnum == l:end_pos[1]) - \ ? strlen(newmods[0]) - strlen(l:open.mod) : 0 - let l:col_diff += (l:close.lnum == l:end_pos[1]) - \ ? strlen(newmods[1]) - strlen(l:close.mod) : 0 - - if l:col_diff != 0 - let l:end_pos[2] += l:col_diff - let l:end_pos_val += l:col_diff - endif - endif - endif - - let l:cur_pos = vimtex#pos#next(l:open) - endwhile - - " - " Finally we return to original position and reselect the region - " - call setpos(l:swapped? "'>" : "'<", l:start_pos) - call setpos(l:swapped? "'<" : "'>", l:end_pos) - call vimtex#pos#set_cursor(l:save_pos) - if l:args.reselect - normal! gv - endif -endfunction - -" }}}1 - -function! vimtex#delim#change(...) abort " {{{1 - let [l:open, l:close] = vimtex#delim#get_surrounding('delim_math') - if empty(l:open) | return | endif - - if a:0 > 0 - let l:new_delim = a:1 - else - let l:name = get(l:open, 'name', l:open.is_open - \ ? l:open.match . ' ... ' . l:open.corr - \ : l:open.match . ' ... ' . l:open.corr) - - let l:new_delim = vimtex#echo#input({ - \ 'info' : - \ ['Change surrounding delimiter: ', ['VimtexWarning', l:name]], - \ 'complete' : 'customlist,vimtex#delim#change_input_complete', - \}) - endif - - if empty(l:new_delim) | return | endif - call vimtex#delim#change_with_args(l:open, l:close, l:new_delim) -endfunction - -" }}}1 -function! vimtex#delim#change_with_args(open, close, new) abort " {{{1 - " - " Set target environment - " - if a:new ==# '' - let [l:beg, l:end] = ['', ''] - elseif index(['{', '}'], a:new) >= 0 - let [l:beg, l:end] = ['{', '}'] - else - let l:side = a:new =~# g:vimtex#delim#re.delim_math.close - let l:index = index(map(copy(g:vimtex#delim#lists.delim_math.name), - \ 'v:val[' . l:side . ']'), - \ a:new) - if l:index >= 0 - let [l:beg, l:end] = g:vimtex#delim#lists.delim_math.name[l:index] - else - let [l:beg, l:end] = [a:new, a:new] - endif - endif - - let l:line = getline(a:open.lnum) - call setline(a:open.lnum, - \ strpart(l:line, 0, a:open.cnum-1) - \ . l:beg - \ . strpart(l:line, a:open.cnum + len(a:open.match) - 1)) - - let l:c1 = a:close.cnum - let l:c2 = a:close.cnum + len(a:close.match) - 1 - if a:open.lnum == a:close.lnum - let n = len(l:beg) - len(a:open.match) - let l:c1 += n - let l:c2 += n - let pos = vimtex#pos#get_cursor() - if pos[2] > a:open.cnum + len(a:open.match) - 1 - let pos[2] += n - call vimtex#pos#set_cursor(pos) - endif - endif - - let l:line = getline(a:close.lnum) - call setline(a:close.lnum, - \ strpart(l:line, 0, l:c1-1) . l:end . strpart(l:line, l:c2)) -endfunction - -" }}}1 -function! vimtex#delim#change_input_complete(lead, cmdline, pos) abort " {{{1 - let l:all = deepcopy(g:vimtex#delim#lists.delim_all.name) - let l:open = map(copy(l:all), 'v:val[0]') - let l:close = map(copy(l:all), 'v:val[1]') - - let l:lead_re = escape(a:lead, '\$[]') - return filter(l:open + l:close, 'v:val =~# ''^' . l:lead_re . '''') -endfunction - -" }}}1 -function! vimtex#delim#delete() abort " {{{1 - let [l:open, l:close] = vimtex#delim#get_surrounding('delim_modq_math') - if empty(l:open) | return | endif - - call vimtex#delim#change_with_args(l:open, l:close, '') -endfunction - -" }}}1 - -function! vimtex#delim#get_next(type, side, ...) abort " {{{1 - return s:get_delim(extend({ - \ 'direction' : 'next', - \ 'type' : a:type, - \ 'side' : a:side, - \}, get(a:, '1', {}))) -endfunction - -" }}}1 -function! vimtex#delim#get_prev(type, side, ...) abort " {{{1 - return s:get_delim(extend({ - \ 'direction' : 'prev', - \ 'type' : a:type, - \ 'side' : a:side, - \}, get(a:, '1', {}))) -endfunction - -" }}}1 -function! vimtex#delim#get_current(type, side, ...) abort " {{{1 - return s:get_delim(extend({ - \ 'direction' : 'current', - \ 'type' : a:type, - \ 'side' : a:side, - \}, get(a:, '1', {}))) -endfunction - -" }}}1 -function! vimtex#delim#get_matching(delim) abort " {{{1 - if empty(a:delim) || !has_key(a:delim, 'lnum') | return {} | endif - - " - " Get the matching position - " - let l:save_pos = vimtex#pos#get_cursor() - call vimtex#pos#set_cursor(a:delim) - let [l:match, l:lnum, l:cnum] = a:delim.get_matching() - call vimtex#pos#set_cursor(l:save_pos) - - " - " Create the match result - " - let l:matching = deepcopy(a:delim) - let l:matching.lnum = l:lnum - let l:matching.cnum = l:cnum - let l:matching.match = l:match - let l:matching.corr = a:delim.match - let l:matching.side = a:delim.is_open ? 'close' : 'open' - let l:matching.is_open = !a:delim.is_open - let l:matching.re.corr = a:delim.re.this - let l:matching.re.this = a:delim.re.corr - - if l:matching.type ==# 'delim' - let l:matching.corr_delim = a:delim.delim - let l:matching.corr_mod = a:delim.mod - let l:matching.delim = a:delim.corr_delim - let l:matching.mod = a:delim.corr_mod - elseif l:matching.type ==# 'env' && has_key(l:matching, 'name') - if l:matching.is_open - let l:matching.env_cmd = vimtex#cmd#get_at(l:lnum, l:cnum) - else - unlet l:matching.env_cmd - endif - endif - - return l:matching -endfunction - -" }}}1 -function! vimtex#delim#get_surrounding(type) abort " {{{1 - let l:save_pos = vimtex#pos#get_cursor() - let l:pos_val_cursor = vimtex#pos#val(l:save_pos) - let l:pos_val_last = l:pos_val_cursor - let l:pos_val_open = l:pos_val_cursor - 1 - - while l:pos_val_open < l:pos_val_last - let l:open = vimtex#delim#get_prev(a:type, 'open') - if empty(l:open) | break | endif - - let l:close = vimtex#delim#get_matching(l:open) - let l:pos_val_try = vimtex#pos#val(l:close) + strlen(l:close.match) - 1 - if l:pos_val_try >= l:pos_val_cursor - call vimtex#pos#set_cursor(l:save_pos) - return [l:open, l:close] - else - call vimtex#pos#set_cursor(vimtex#pos#prev(l:open)) - let l:pos_val_last = l:pos_val_open - let l:pos_val_open = vimtex#pos#val(l:open) - endif - endwhile - - call vimtex#pos#set_cursor(l:save_pos) - return [{}, {}] -endfunction - -" }}}1 - -function! s:operator_setup(operator) abort " {{{1 - let &opfunc = s:snr() . 'operator_function' - - let s:operator = a:operator - - " Ask for user input if necessary/relevant - if s:operator ==# 'change' - let [l:open, l:close] = vimtex#delim#get_surrounding('delim_math') - if empty(l:open) | return | endif - - let l:name = get(l:open, 'name', l:open.is_open - \ ? l:open.match . ' ... ' . l:open.corr - \ : l:open.match . ' ... ' . l:open.corr) - - let s:operator_delim = vimtex#echo#input({ - \ 'info' : - \ ['Change surrounding delimiter: ', ['VimtexWarning', l:name]], - \ 'complete' : 'customlist,vimtex#delim#change_input_complete', - \}) - endif -endfunction - -" }}}1 -function! s:operator_function(_) abort " {{{1 - let l:delim = get(s:, 'operator_delim', '') - - execute 'call vimtex#delim#' . { - \ 'change': 'change(l:delim)', - \ 'delete': 'delete()', - \ 'toggle_modifier_next': 'toggle_modifier()', - \ 'toggle_modifier_prev': "toggle_modifier({'dir': -1})", - \}[s:operator] -endfunction - -" }}}1 -function! s:snr() abort " {{{1 - return matchstr(expand('<sfile>'), '<SNR>\d\+_') -endfunction - -" }}}1 -" - -function! s:get_delim(opts) abort " {{{1 - " - " Arguments: - " opts = { - " 'direction' : next - " prev - " current - " 'type' : env_tex - " env_math - " env_all - " delim_tex - " delim_math - " delim_modq_math (possibly modified math delimiter) - " delim_mod_math (modified math delimiter) - " delim_all - " all - " 'side' : open - " close - " both - " 'syn_exclude' : Don't match in given syntax - " } - " - " Returns: - " delim = { - " type : env | delim - " side : open | close - " name : name of environment [only for type env] - " lnum : number - " cnum : number - " match : unparsed matched delimiter - " corr : corresponding delimiter - " re : { - " open : regexp for the opening part - " close : regexp for the closing part - " } - " remove : method to remove the delimiter - " } - " - let l:save_pos = vimtex#pos#get_cursor() - let l:re = g:vimtex#delim#re[a:opts.type][a:opts.side] - while 1 - let [l:lnum, l:cnum] = a:opts.direction ==# 'next' - \ ? searchpos(l:re, 'cnW', line('.') + g:vimtex_delim_stopline) - \ : a:opts.direction ==# 'prev' - \ ? searchpos(l:re, 'bcnW', max([line('.') - g:vimtex_delim_stopline, 1])) - \ : searchpos(l:re, 'bcnW', line('.')) - if l:lnum == 0 | break | endif - - if has_key(a:opts, 'syn_exclude') - \ && vimtex#util#in_syntax(a:opts.syn_exclude, l:lnum, l:cnum) - call vimtex#pos#set_cursor(vimtex#pos#prev(l:lnum, l:cnum)) - continue - endif - - break - endwhile - call vimtex#pos#set_cursor(l:save_pos) - - let l:match = matchstr(getline(l:lnum), '^' . l:re, l:cnum-1) - - if a:opts.direction ==# 'current' - \ && l:cnum + strlen(l:match) + (mode() ==# 'i' ? 1 : 0) <= col('.') - let l:match = '' - let l:lnum = 0 - let l:cnum = 0 - endif - - let l:result = { - \ 'type' : '', - \ 'lnum' : l:lnum, - \ 'cnum' : l:cnum, - \ 'match' : l:match, - \ 'remove' : function('s:delim_remove'), - \} - - for l:type in s:types - if l:match =~# '^' . l:type.re - let l:result = extend( - \ l:type.parser(l:match, l:lnum, l:cnum, - \ a:opts.side, a:opts.type, a:opts.direction), - \ l:result, 'keep') - break - endif - endfor - - return empty(l:result.type) ? {} : l:result -endfunction - -" }}}1 - -function! s:delim_remove() dict abort " {{{1 - let l:line = getline(self.lnum) - let l:l1 = strpart(l:line, 0, self.cnum-1) - let l:l2 = strpart(l:line, self.cnum + strlen(self.match) - 1) - - if self.side ==# 'close' - let l:l1 = substitute(l:l1, '\s\+$', '', '') - if empty(l:l1) - let l:l2 = substitute(l:l2, '^\s\+', '', '') - endif - else - let l:l2 = substitute(l:l2, '^\s\+', '', '') - if empty(l:l2) - let l:l1 = substitute(l:l1, '\s\+$', '', '') - endif - endif - - call setline(self.lnum, l:l1 . l:l2) -endfunction - -" }}}1 - -function! s:parser_env(match, lnum, cnum, ...) abort " {{{1 - let result = {} - - let result.type = 'env' - let result.name = matchstr(a:match, '{\zs\k*\ze\*\?}') - let result.starred = match(a:match, '\*}$') > 0 - let result.side = a:match =~# '\\begin' ? 'open' : 'close' - let result.is_open = result.side ==# 'open' - let result.get_matching = function('s:get_matching_env') - - let result.gms_flags = result.is_open ? 'nW' : 'bnW' - let result.gms_stopline = result.is_open - \ ? line('.') + g:vimtex_delim_stopline - \ : max([1, line('.') - g:vimtex_delim_stopline]) - - if result.is_open - let result.env_cmd = vimtex#cmd#get_at(a:lnum, a:cnum) - endif - - let result.corr = result.is_open - \ ? substitute(a:match, 'begin', 'end', '') - \ : substitute(a:match, 'end', 'begin', '') - - let result.re = { - \ 'open' : '\m\\begin\s*{' . result.name . '\*\?}', - \ 'close' : '\m\\end\s*{' . result.name . '\*\?}', - \} - - let result.re.this = result.is_open ? result.re.open : result.re.close - let result.re.corr = result.is_open ? result.re.close : result.re.open - - return result -endfunction - -" }}}1 -function! s:parser_tex(match, lnum, cnum, side, type, direction) abort " {{{1 - " - " TeX shorthand are these - " - " $ ... $ (inline math) - " $$ ... $$ (displayed equations) - " - " The notation does not provide the delimiter side directly, which provides - " a slight problem. However, we can utilize the syntax information to parse - " the side. - " - let result = {} - let result.type = 'env' - let result.corr = a:match - let result.re = { - \ 'this' : '\m' . escape(a:match, '$'), - \ 'corr' : '\m' . escape(a:match, '$'), - \ 'open' : '\m' . escape(a:match, '$'), - \ 'close' : '\m' . escape(a:match, '$'), - \} - let result.side = vimtex#util#in_syntax( - \ (a:match ==# '$' ? 'texMathZoneX' : 'texMathZoneY'), - \ a:lnum, a:cnum+1) - \ ? 'open' : 'close' - let result.is_open = result.side ==# 'open' - let result.get_matching = function('s:get_matching_tex') - let result.gms_flags = result.is_open ? 'nW' : 'bnW' - let result.gms_stopline = result.is_open - \ ? line('.') + g:vimtex_delim_stopline - \ : max([1, line('.') - g:vimtex_delim_stopline]) - - if (a:side !=# 'both') && (a:side !=# result.side) - " - " The current match ($ or $$) is not the correct side, so we must - " continue the search recursively. We do this by changing the cursor - " position, since the function searchpos relies on the current cursor - " position. - " - let l:save_pos = vimtex#pos#get_cursor() - - " Move the cursor - call vimtex#pos#set_cursor(a:direction ==# 'next' - \ ? vimtex#pos#next(a:lnum, a:cnum) - \ : vimtex#pos#prev(a:lnum, a:cnum)) - - " Get new result - let result = s:get_delim({ - \ 'direction' : a:direction, - \ 'type' : a:type, - \ 'side' : a:side, - \}) - - " Restore the cursor - call vimtex#pos#set_cursor(l:save_pos) - endif - - return result -endfunction - -" }}}1 -function! s:parser_latex(match, lnum, cnum, ...) abort " {{{1 - let result = {} - - let result.type = 'env' - let result.side = a:match =~# '\\(\|\\\[' ? 'open' : 'close' - let result.is_open = result.side ==# 'open' - let result.get_matching = function('s:get_matching_latex') - let result.gms_flags = result.is_open ? 'nW' : 'bnW' - let result.gms_stopline = result.is_open - \ ? line('.') + g:vimtex_delim_stopline - \ : max([1, line('.') - g:vimtex_delim_stopline]) - - let result.corr = result.is_open - \ ? substitute(substitute(a:match, '\[', ']', ''), '(', ')', '') - \ : substitute(substitute(a:match, '\]', '[', ''), ')', '(', '') - - let result.re = { - \ 'open' : g:vimtex#re#not_bslash - \ . (a:match =~# '\\(\|\\)' ? '\m\\(' : '\m\\\['), - \ 'close' : g:vimtex#re#not_bslash - \ . (a:match =~# '\\(\|\\)' ? '\m\\)' : '\m\\\]'), - \} - - let result.re.this = result.is_open ? result.re.open : result.re.close - let result.re.corr = result.is_open ? result.re.close : result.re.open - - return result -endfunction - -" }}}1 -function! s:parser_delim(match, lnum, cnum, ...) abort " {{{1 - let result = {} - let result.type = 'delim' - let result.side = - \ a:match =~# g:vimtex#delim#re.delim_all.open ? 'open' : 'close' - let result.is_open = result.side ==# 'open' - let result.get_matching = function('s:get_matching_delim') - let result.gms_flags = result.is_open ? 'nW' : 'bnW' - let result.gms_stopline = result.is_open - \ ? line('.') + g:vimtex_delim_stopline - \ : max([1, line('.') - g:vimtex_delim_stopline]) - - " - " Find corresponding delimiter and the regexps - " - if a:match =~# '^' . g:vimtex#delim#re.mods.both - let m1 = matchstr(a:match, '^' . g:vimtex#delim#re.mods.both) - let d1 = substitute(strpart(a:match, len(m1)), '^\s*', '', '') - let s1 = !result.is_open - let re1 = s:parser_delim_get_regexp(m1, s1, 'mods') - \ . '\s*' . s:parser_delim_get_regexp(d1, s1, 'delim_math') - - let m2 = s:parser_delim_get_corr(m1, 'mods') - let d2 = s:parser_delim_get_corr(d1, 'delim_math') - let s2 = result.is_open - let re2 = s:parser_delim_get_regexp(m2, s2, 'mods') . '\s*' - \ . (m1 =~# '\\\%(left\|right\)' - \ ? '\%(' . s:parser_delim_get_regexp(d2, s2, 'delim_math') . '\|\.\)' - \ : s:parser_delim_get_regexp(d2, s2, 'delim_math')) - else - let d1 = a:match - let m1 = '' - let re1 = s:parser_delim_get_regexp(a:match, !result.is_open) - - let d2 = s:parser_delim_get_corr(a:match) - let m2 = '' - let re2 = s:parser_delim_get_regexp(d2, result.is_open) - endif - - let result.delim = d1 - let result.mod = m1 - let result.corr = m2 . d2 - let result.corr_delim = d2 - let result.corr_mod = m2 - let result.re = { - \ 'this' : re1, - \ 'corr' : re2, - \ 'open' : result.is_open ? re1 : re2, - \ 'close' : result.is_open ? re2 : re1, - \} - - return result -endfunction - -" }}}1 -function! s:parser_delim_unmatched(match, lnum, cnum, ...) abort " {{{1 - let result = {} - let result.type = 'delim' - let result.side = - \ a:match =~# g:vimtex#delim#re.delim_all.open ? 'open' : 'close' - let result.is_open = result.side ==# 'open' - let result.get_matching = function('s:get_matching_delim_unmatched') - let result.gms_flags = result.is_open ? 'nW' : 'bnW' - let result.gms_stopline = result.is_open - \ ? line('.') + g:vimtex_delim_stopline - \ : max([1, line('.') - g:vimtex_delim_stopline]) - let result.delim = '.' - let result.corr_delim = '.' - - " - " Find corresponding delimiter and the regexps - " - if result.is_open - let result.mod = '\left' - let result.corr_mod = '\right' - let result.corr = '\right.' - let re1 = '\\left\s*\.' - let re2 = s:parser_delim_get_regexp('\right', 1, 'mods') - \ . '\s*' . s:parser_delim_get_regexp('.', 0) - else - let result.mod = '\right' - let result.corr_mod = '\left' - let result.corr = '\left.' - let re1 = '\\right\s*\.' - let re2 = s:parser_delim_get_regexp('\left', 0, 'mods') - \ . '\s*' . s:parser_delim_get_regexp('.', 0) - endif - - let result.re = { - \ 'this' : re1, - \ 'corr' : re2, - \ 'open' : result.is_open ? re1 : re2, - \ 'close' : result.is_open ? re2 : re1, - \} - - return result -endfunction - -" }}}1 -function! s:parser_delim_get_regexp(delim, side, ...) abort " {{{1 - let l:type = a:0 > 0 ? a:1 : 'delim_all' - - " First check for unmatched math delimiter - if a:delim ==# '.' - return g:vimtex#delim#re.delim_math[a:side ? 'open' : 'close'] - endif - - " Next check normal delimiters - let l:index = index(map(copy(g:vimtex#delim#lists[l:type].name), - \ 'v:val[' . a:side . ']'), - \ a:delim) - return l:index >= 0 - \ ? g:vimtex#delim#lists[l:type].re[l:index][a:side] - \ : '' -endfunction - -" }}}1 -function! s:parser_delim_get_corr(delim, ...) abort " {{{1 - let l:type = a:0 > 0 ? a:1 : 'delim_all' - - for l:pair in g:vimtex#delim#lists[l:type].name - if a:delim ==# l:pair[0] - return l:pair[1] - elseif a:delim ==# l:pair[1] - return l:pair[0] - endif - endfor -endfunction - -" }}}1 - -function! s:get_matching_env() dict abort " {{{1 - try - let [lnum, cnum] = searchpairpos(self.re.open, '', self.re.close, - \ self.gms_flags, '', 0, s:get_timeout()) - catch /E118/ - let [lnum, cnum] = searchpairpos(self.re.open, '', self.re.close, - \ self.gms_flags, '', self.gms_stopline) - endtry - - let match = matchstr(getline(lnum), '^' . self.re.corr, cnum-1) - return [match, lnum, cnum] -endfunction - -" }}}1 -function! s:get_matching_tex() dict abort " {{{1 - let [lnum, cnum] = searchpos(self.re.corr, self.gms_flags, self.gms_stopline) - - let match = matchstr(getline(lnum), '^' . self.re.corr, cnum-1) - return [match, lnum, cnum] -endfunction - -" }}}1 -function! s:get_matching_latex() dict abort " {{{1 - let [lnum, cnum] = searchpos(self.re.corr, self.gms_flags, self.gms_stopline) - - let match = matchstr(getline(lnum), '^' . self.re.corr, cnum-1) - return [match, lnum, cnum] -endfunction - -" }}}1 -function! s:get_matching_delim() dict abort " {{{1 - try - let [lnum, cnum] = searchpairpos(self.re.open, '', self.re.close, - \ self.gms_flags, - \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "comment"', - \ 0, s:get_timeout()) - catch /E118/ - let [lnum, cnum] = searchpairpos(self.re.open, '', self.re.close, - \ self.gms_flags, - \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "comment"', - \ self.gms_stopline) - endtry - - let match = matchstr(getline(lnum), '^' . self.re.corr, cnum-1) - return [match, lnum, cnum] -endfunction - -" }}}1 -function! s:get_matching_delim_unmatched() dict abort " {{{1 - let tries = 0 - let misses = [] - while 1 - try - let [lnum, cnum] = searchpairpos(self.re.open, '', self.re.close, - \ self.gms_flags, - \ 'index(misses, [line("."), col(".")]) >= 0', - \ 0, s:get_timeout()) - catch /E118/ - let [lnum, cnum] = searchpairpos(self.re.open, '', self.re.close, - \ self.gms_flags, - \ 'index(misses, [line("."), col(".")]) >= 0', - \ self.gms_stopline) - endtry - let match = matchstr(getline(lnum), '^' . self.re.corr, cnum-1) - if lnum == 0 | break | endif - - let cand = vimtex#delim#get_matching(extend({ - \ 'type' : '', - \ 'lnum' : lnum, - \ 'cnum' : cnum, - \ 'match' : match, - \}, s:parser_delim(match, lnum, cnum))) - - if !empty(cand) && [self.lnum, self.cnum] == [cand.lnum, cand.cnum] - return [match, lnum, cnum] - else - let misses += [[lnum, cnum]] - let tries += 1 - if tries == 10 | break | endif - endif - endwhile - - return ['', 0, 0] -endfunction - -" }}}1 - -function! s:get_timeout() abort " {{{1 - return (empty(v:insertmode) ? mode() : v:insertmode) ==# 'i' - \ ? g:vimtex_delim_insert_timeout - \ : g:vimtex_delim_timeout -endfunction - -" }}}1 - - -function! s:init_delim_lists() abort " {{{1 - " Define the default value - let l:lists = { - \ 'env_tex' : { - \ 'name' : [['begin', 'end']], - \ 're' : [['\\begin\s*{[^}]*}', '\\end\s*{[^}]*}']], - \ }, - \ 'env_math' : { - \ 'name' : [ - \ ['\(', '\)'], - \ ['\[', '\]'], - \ ['$$', '$$'], - \ ['$', '$'], - \ ], - \ 're' : [ - \ ['\\(', '\\)'], - \ ['\\\@<!\\\[', '\\\]'], - \ ['\$\$', '\$\$'], - \ ['\$', '\$'], - \ ], - \ }, - \ 'delim_tex' : { - \ 'name' : [ - \ ['[', ']'], - \ ['{', '}'], - \ ], - \ 're' : [ - \ ['\[', '\]'], - \ ['\\\@<!{', '\\\@<!}'], - \ ] - \ }, - \ 'delim_math' : { - \ 'name' : [ - \ ['(', ')'], - \ ['[', ']'], - \ ['\{', '\}'], - \ ['\langle', '\rangle'], - \ ['\lbrace', '\rbrace'], - \ ['\lvert', '\rvert'], - \ ['\lVert', '\rVert'], - \ ['\lfloor', '\rfloor'], - \ ['\lceil', '\rceil'], - \ ['\ulcorner', '\urcorner'], - \ ] - \ }, - \ 'mods' : { - \ 'name' : [ - \ ['\left', '\right'], - \ ['\bigl', '\bigr'], - \ ['\Bigl', '\Bigr'], - \ ['\biggl', '\biggr'], - \ ['\Biggl', '\Biggr'], - \ ['\big', '\big'], - \ ['\Big', '\Big'], - \ ['\bigg', '\bigg'], - \ ['\Bigg', '\Bigg'], - \ ], - \ 're' : [ - \ ['\\left', '\\right'], - \ ['\\bigl', '\\bigr'], - \ ['\\Bigl', '\\Bigr'], - \ ['\\biggl', '\\biggr'], - \ ['\\Biggl', '\\Biggr'], - \ ['\\big\>', '\\big\>'], - \ ['\\Big\>', '\\Big\>'], - \ ['\\bigg\>', '\\bigg\>'], - \ ['\\Bigg\>', '\\Bigg\>'], - \ ] - \ }, - \} - - " Get user defined lists - call extend(l:lists, get(g:, 'vimtex_delim_list', {})) - - " Generate corresponding regexes if necessary - for l:type in values(l:lists) - if !has_key(l:type, 're') && has_key(l:type, 'name') - let l:type.re = map(deepcopy(l:type.name), - \ 'map(v:val, ''escape(v:val, ''''\$[]'''')'')') - endif - endfor - - " Generate combined lists - let l:lists.env_all = {} - let l:lists.delim_all = {} - let l:lists.all = {} - for k in ['name', 're'] - let l:lists.env_all[k] = l:lists.env_tex[k] + l:lists.env_math[k] - let l:lists.delim_all[k] = l:lists.delim_math[k] + l:lists.delim_tex[k] - let l:lists.all[k] = l:lists.env_all[k] + l:lists.delim_all[k] - endfor - - return l:lists -endfunction - -" }}}1 -function! s:init_delim_regexes() abort " {{{1 - let l:re = {} - let l:re.env_all = {} - let l:re.delim_all = {} - let l:re.all = {} - - let l:re.env_tex = s:init_delim_regexes_generator('env_tex') - let l:re.env_math = s:init_delim_regexes_generator('env_math') - let l:re.delim_tex = s:init_delim_regexes_generator('delim_tex') - let l:re.delim_math = s:init_delim_regexes_generator('delim_math') - let l:re.mods = s:init_delim_regexes_generator('mods') - - let l:o = join(map(copy(g:vimtex#delim#lists.delim_math.re), 'v:val[0]'), '\|') - let l:c = join(map(copy(g:vimtex#delim#lists.delim_math.re), 'v:val[1]'), '\|') - - " - " Matches modified math delimiters - " - let l:re.delim_mod_math = { - \ 'open' : '\%(\%(' . l:re.mods.open . '\)\)\s*\\\@<!\%(' - \ . l:o . '\)\|\\left\s*\.', - \ 'close' : '\%(\%(' . l:re.mods.close . '\)\)\s*\\\@<!\%(' - \ . l:c . '\)\|\\right\s*\.', - \ 'both' : '\%(\%(' . l:re.mods.both . '\)\)\s*\\\@<!\%(' - \ . l:o . '\|' . l:c . '\)\|\\\%(left\|right\)\s*\.', - \} - - " - " Matches possibly modified math delimiters - " - let l:re.delim_modq_math = { - \ 'open' : '\%(\%(' . l:re.mods.open . '\)\s*\)\?\\\@<!\%(' - \ . l:o . '\)\|\\left\s*\.', - \ 'close' : '\%(\%(' . l:re.mods.close . '\)\s*\)\?\\\@<!\%(' - \ . l:c . '\)\|\\right\s*\.', - \ 'both' : '\%(\%(' . l:re.mods.both . '\)\s*\)\?\\\@<!\%(' - \ . l:o . '\|' . l:c . '\)\|\\\%(left\|right\)\s*\.', - \} - - for k in ['open', 'close', 'both'] - let l:re.env_all[k] = l:re.env_tex[k] . '\|' . l:re.env_math[k] - let l:re.delim_all[k] = l:re.delim_modq_math[k] . '\|' . l:re.delim_tex[k] - let l:re.all[k] = l:re.env_all[k] . '\|' . l:re.delim_all[k] - endfor - - " - " Be explicit about regex mode (set magic mode) - " - for l:type in values(l:re) - for l:side in ['open', 'close', 'both'] - let l:type[l:side] = '\m' . l:type[l:side] - endfor - endfor - - return l:re -endfunction - -" }}}1 -function! s:init_delim_regexes_generator(list_name) abort " {{{1 - let l:list = g:vimtex#delim#lists[a:list_name] - let l:open = join(map(copy(l:list.re), 'v:val[0]'), '\|') - let l:close = join(map(copy(l:list.re), 'v:val[1]'), '\|') - - return { - \ 'open' : '\\\@<!\%(' . l:open . '\)', - \ 'close' : '\\\@<!\%(' . l:close . '\)', - \ 'both' : '\\\@<!\%(' . l:open . '\|' . l:close . '\)' - \} -endfunction - - " }}}1 - - -" {{{1 Initialize module - -" -" Initialize lists of delimiter pairs and regexes -" -let g:vimtex#delim#lists = s:init_delim_lists() -let g:vimtex#delim#re = s:init_delim_regexes() - -" -" Initialize script variables -" -let s:types = [ - \ { - \ 're' : '\\\%(begin\|end\)\>', - \ 'parser' : function('s:parser_env'), - \ }, - \ { - \ 're' : '\$\$\?', - \ 'parser' : function('s:parser_tex'), - \ }, - \ { - \ 're' : '\\\%((\|)\|\[\|\]\)', - \ 'parser' : function('s:parser_latex'), - \ }, - \ { - \ 're' : '\\\%(left\|right\)\s*\.', - \ 'parser' : function('s:parser_delim_unmatched'), - \ }, - \ { - \ 're' : g:vimtex#delim#re.delim_all.both, - \ 'parser' : function('s:parser_delim'), - \ }, - \] - -" }}}1 - -endif |