A huge refactoring of the delimiter engine

See entry on 2016-02-06 in the changelog in doc/vimtex.txt,
:h vimtex-changelog, for more details.

Issues:
  #258
  #314
  #316
  #329
This commit is contained in:
Karl Yngve Lervåg 2016-02-06 20:21:27 +01:00
parent bb49c3e946
commit 490a24daf0
11 changed files with 1141 additions and 977 deletions

View File

@ -37,7 +37,7 @@ disabled if desired.
- Change the surrounding command or environment with `csc`/`cse` - Change the surrounding command or environment with `csc`/`cse`
- Toggle starred environment with `tse` - Toggle starred environment with `tse`
- Toggle between e.g. `()` and `\left(\right)` with `tsd` - Toggle between e.g. `()` and `\left(\right)` with `tsd`
- Close the current environment in insert mode with `]]` - Close the current environment/delimiter in insert mode with `]]`
- Insert new command with `<F7>` - Insert new command with `<F7>`
- Convenient insert mode mappings for faster typing of e.g. maths - Convenient insert mode mappings for faster typing of e.g. maths
- Improved folding (`:h 'foldexpr'`) - Improved folding (`:h 'foldexpr'`)

View File

@ -341,15 +341,17 @@ function! s:init_mappings() " {{{1
call s:map('n', '<localleader>lx', '<plug>(vimtex-reload)') call s:map('n', '<localleader>lx', '<plug>(vimtex-reload)')
call s:map('n', '<localleader>ls', '<plug>(vimtex-toggle-main)') call s:map('n', '<localleader>ls', '<plug>(vimtex-toggle-main)')
call s:map('n', 'dse', '<plug>(vimtex-delete-env)') call s:map('n', 'dse', '<plug>(vimtex-env-delete)')
call s:map('n', 'dsc', '<plug>(vimtex-delete-cmd)') call s:map('n', 'cse', '<plug>(vimtex-env-change)')
call s:map('n', 'cse', '<plug>(vimtex-change-env)') call s:map('n', 'tse', '<plug>(vimtex-env-toggle-star)')
call s:map('n', 'csc', '<plug>(vimtex-change-cmd)')
call s:map('n', 'tse', '<plug>(vimtex-toggle-star)') call s:map('n', 'dsc', '<plug>(vimtex-cmd-delete)')
call s:map('n', 'tsd', '<plug>(vimtex-toggle-delim)') call s:map('n', 'csc', '<plug>(vimtex-cmd-change)')
call s:map('n', '<F7>', '<plug>(vimtex-create-cmd)') call s:map('n', '<F7>', '<plug>(vimtex-cmd-create)')
call s:map('i', '<F7>', '<plug>(vimtex-create-cmd)') call s:map('i', '<F7>', '<plug>(vimtex-cmd-create)')
call s:map('i', ']]', '<plug>(vimtex-close-env)')
call s:map('n', 'tsd', '<plug>(vimtex-delim-toggle-modifier)')
call s:map('i', ']]', '<plug>(vimtex-delim-close)')
if g:vimtex_latexmk_enabled if g:vimtex_latexmk_enabled
call s:map('n', '<localleader>ll', '<plug>(vimtex-compile-toggle)') call s:map('n', '<localleader>ll', '<plug>(vimtex-compile-toggle)')

View File

@ -1,483 +0,0 @@
" vimtex - LaTeX plugin for Vim
"
" Maintainer: Karl Yngve Lervåg
" Email: karl.yngve@gmail.com
"
function! vimtex#change#init_options() " {{{1
call vimtex#util#set_default('g:vimtex_change_complete_envs', [
\ 'itemize',
\ 'enumerate',
\ 'description',
\ 'center',
\ 'figure',
\ 'table',
\ 'equation',
\ 'multline',
\ 'align',
\ 'split',
\ '\[',
\ ])
call vimtex#util#set_default('g:vimtex_change_toggled_delims',
\ [['\\left', '\\right']])
call vimtex#util#set_default('g:vimtex_change_ignored_delims_pattern',
\ '\c\\bigg\?')
endfunction
" }}}1
function! vimtex#change#init_script() " {{{1
endfunction
" }}}1
function! vimtex#change#init_buffer() " {{{1
nnoremap <silent><buffer> <plug>(vimtex-delete-env)
\ :call vimtex#change#env('')<cr>
nnoremap <silent><buffer> <plug>(vimtex-delete-cmd)
\ :call vimtex#change#command_delete()<cr>
nnoremap <silent><buffer> <plug>(vimtex-change-env)
\ :call vimtex#change#env_prompt()<cr>
nnoremap <silent><buffer> <plug>(vimtex-change-cmd)
\ :call vimtex#change#command()<cr>
nnoremap <silent><buffer> <plug>(vimtex-toggle-star)
\ :call vimtex#change#toggle_env_star()<cr>
nnoremap <silent><buffer> <plug>(vimtex-toggle-delim)
\ :call vimtex#change#toggle_delim()<cr>
nnoremap <silent><buffer> <plug>(vimtex-create-cmd)
\ :call vimtex#change#to_command()<cr>i
inoremap <silent><buffer> <plug>(vimtex-create-cmd)
\ <c-r>=vimtex#change#to_command()<cr>
inoremap <silent><buffer> <plug>(vimtex-close-env)
\ <c-r>=vimtex#change#close_environment()<cr>
endfunction
" }}}1
function! vimtex#change#get_command(...) " {{{1
let l:position = a:0 > 0 ? a:1 : searchpos('\S', 'bcn')
let l:line = getline(l:position[0])
let l:char = l:line[l:position[1]-1]
" Lists of relevant syntax regions
let l:commands = [
\ 'texStatement',
\ 'texTypeSize',
\ 'texTypeStyle',
\ 'texBeginEnd',
\ ]
let l:argument = [
\ 'texMatcher',
\ 'texItalStyle',
\ 'texRefZone',
\ 'texBeginEndName',
\ ]
for l:syntax in reverse(map(call('synstack', l:position),
\ 'synIDattr(v:val, ''name'')'))
if index(l:commands, l:syntax) >= 0
let l:p = searchpos('\\', 'bcn')
let l:c = matchstr(l:line, '\\\zs\w\+', l:p[1]-1)
return [l:c] + l:p
elseif index(l:argument, l:syntax) >= 0
\ || (l:syntax ==# 'Delimiter' && l:char =~# '{\|}')
let l:curpos = exists('*getcurpos') ? getcurpos() : getpos('.')
keepjumps normal! vaBoh
let l:result = vimtex#change#get_command(searchpos('\S', 'bcn'))
call setpos('.', l:curpos)
return l:result
endif
endfor
return ['', 0, 0]
endfunction
function! vimtex#change#command() " {{{1
" Get old command
let [l:old, l:line, l:col] = vimtex#change#get_command()
if l:old ==# '' | return | endif
" Get new command
let l:new = input('Change ' . old . ' for: ')
let l:new = empty(l:new) ? l:old : l:new
" Store current cursor position
let l:curpos = exists('*getcurpos') ? getcurpos() : getpos('.')
if l:line == l:curpos[1]
let l:curpos[2] += len(l:new) - len(l:old)
endif
" This is a hack to make undo restore the correct position
normal! ix
normal! x
" Perform the change
let l:tmppos = copy(l:curpos)
let l:tmppos[1:2] = [l:line, l:col+1]
cal setpos('.', l:tmppos)
let l:savereg = @a
let @a = l:new
normal! cea
let @a = l:savereg
" Restore cursor position and create repeat hook
call setpos('.', l:curpos)
silent! call repeat#set("\<plug>(vimtex-change-cmd)" . new . ' ', v:count)
endfunction
function! vimtex#change#command_delete() " {{{1
" Get old command
let [l:old, l:line, l:col] = vimtex#change#get_command()
if l:old ==# '' | return | endif
" Store current cursor position
let l:curpos = exists('*getcurpos') ? getcurpos() : getpos('.')
if l:line == l:curpos[1]
let l:curpos[2] -= len(l:old)+1
endif
" Save selection
let l:vstart = [l:curpos[0], line("'<"), col("'<"), l:curpos[3]]
let l:vstop = [l:curpos[0], line("'<"), col("'>"), l:curpos[3]]
" This is a hack to make undo restore the correct position
normal! ix
normal! x
" Use temporary cursor position
let l:tmppos = copy(l:curpos)
let l:tmppos[1:2] = [l:line, l:col]
call setpos('.', l:tmppos)
normal! de
" Delete surrounding braces if present
if getline('.')[l:col-1 :] =~# '^\s*{'
call searchpos('{', 'c')
keepjumps normal! vaBomzoxg`zx
if l:line == l:curpos[1]
let l:curpos[2] -= 1
if l:curpos[2] < 0
let l:curpos[2] = 0
endif
endif
endif
" Restore cursor position and visual selection
call setpos('.', l:curpos)
call setpos("'<", l:vstart)
call setpos("'>", l:vstop)
" Create repeat hook
silent! call repeat#set("\<plug>(vimtex-delete-cmd)", v:count)
endfunction
function! vimtex#change#close_environment() " {{{1
" Get environment and delimiter info
let [env, env_l1, env_c1, env_l2, env_c2] = vimtex#util#get_env(1,0)
let [del_l1, del_c1] = searchpairpos('\C\\left\>', '', '\C\\right\>', 'bnW',
\ 'vimtex#util#in_comment()')
" Calculate scores
let env_score = env_l1*1000 + env_c1
let del_score = del_l1*1000 + del_c1
" Complete environment or delimiter, if any
if env_score > del_score
if env ==# '\['
return '\]'
elseif env ==# '\('
return '\)'
elseif env !=# ''
return '\end{' . env . '}'
endif
elseif del_score > 0
let line = strpart(getline(del_l1), del_c1 - 1)
let bracket = matchstr(line, '^\\left\zs\((\|\[\|\\{\||\|\.\)\ze')
for [open, close] in [
\ ['(', ')'],
\ ['\[', '\]'],
\ ['\\{', '\\}'],
\ ['|', '|'],
\ ['\.', '|'],
\ ]
let bracket = substitute(bracket, open, close, 'g')
endfor
return '\right' . bracket
endif
return ''
endfunction
function! vimtex#change#delim(open, close) " {{{1
let [d1, l1, c1, d2, l2, c2] = vimtex#util#get_delim()
let line = getline(l1)
let line = strpart(line,0,c1 - 1) . a:open . strpart(line, c1 + len(d1) - 1)
call setline(l1, line)
if l1 ==# l2
let n = len(a:open) - len(d1)
let c2 += n
let pos = getpos('.')
let pos[2] += n
call setpos('.', pos)
endif
let line = getline(l2)
let line = strpart(line,0,c2 - 1) . a:close . strpart(line, c2 + len(d2) - 1)
call setline(l2, line)
endfunction
function! vimtex#change#env(new) " {{{1
let [env, l1, c1, l2, c2] = vimtex#util#get_env(1)
if a:new ==# ''
let beg = ''
let end = ''
elseif a:new ==# '\[' || a:new ==# '['
let beg = '\['
let end = '\]'
elseif a:new ==# '\(' || a:new ==# '('
let beg = '\('
let end = '\)'
else
let beg = '\begin{' . a:new . '}'
let end = '\end{' . a:new . '}'
endif
let n1 = len(env) - 1
let n2 = len(env) - 1
if env !=# '\[' && env !=# '\('
let n1 += 8
let n2 += 6
endif
let line = getline(l1)
let line = strpart(line, 0, c1 - 1) . l:beg . strpart(line, c1 + n1)
call setline(l1, line)
let line = getline(l2)
let line = strpart(line, 0, c2 - 1) . l:end . strpart(line, c2 + n2)
call setline(l2, line)
if a:new ==# ''
silent! call repeat#set("\<plug>(vimtex-delete-env)", v:count)
else
silent! call repeat#set(
\ "\<plug>(vimtex-change-env)" . a:new . ' ', v:count)
endif
endfunction
function! vimtex#change#env_prompt() " {{{1
let new_env = input('Change ' . vimtex#util#get_env() . ' for: ', '',
\ 'customlist,' . s:sidwrap('input_complete'))
if empty(new_env)
return
else
call vimtex#change#env(new_env)
endif
endfunction
function! vimtex#change#to_command() " {{{1
" Get current line
let line = getline('.')
" Get cursor position
let pos = getpos('.')
" Return if there is no word at cursor
if mode() ==# 'n'
let column = pos[2] - 1
else
let column = pos[2] - 2
endif
if column <= 1 || line[column] =~# '\s'
return ''
endif
" Prepend a backslash to beginning of the current word
normal! B
let column = getpos('.')[2]
if line[column - 1] !=# '\'
let line = strpart(line, 0, column - 1) . '\' . strpart(line, column - 1)
call setline('.', line)
endif
" Append opening braces to the end of the current word
normal! E
let column = getpos('.')[2]
let pos[2] = column + 1
if line[column - 1] !=# '{'
let line = strpart(line, 0, column) . '{' . strpart(line, column)
call setline('.', line)
let pos[2] += 1
endif
" Restore cursor position
call setpos('.', pos)
return ''
endfunction
function! vimtex#change#toggle_delim() " {{{1
"
" Toggle \left and \right variants of delimiters
"
let [d1, l1, c1, d2, l2, c2] = vimtex#util#get_delim()
let [newd1, newd2] = s:toggle_delim_get_new(d1, d2)
if newd1 ==# '' | return 0 | endif
let line = getline(l1)
let line = strpart(line, 0, c1 - 1) . newd1 . strpart(line, c1 + len(d1) - 1)
call setline(l1, line)
if l1 ==# l2
let n = len(newd1) - len(d1)
let c2 += n
let pos = getpos('.')
let pos[2] += n
call setpos('.', pos)
endif
let line = getline(l2)
let line = strpart(line, 0, c2 - 1) . newd2 . strpart(line, c2 + len(d2) - 1)
call setline(l2, line)
silent! call repeat#set("\<plug>(vimtex-toggle-delim)", v:count)
endfunction
function! vimtex#change#toggle_env_star() " {{{1
let env = vimtex#util#get_env()
if env ==# '\('
return
elseif env ==# '\['
let new_env = equation
elseif env[-1:] ==# '*'
let new_env = env[:-2]
else
let new_env = env . '*'
endif
call vimtex#change#env(new_env)
silent! call repeat#set("\<plug>(vimtex-toggle-star)", v:count)
endfunction
function! vimtex#change#wrap_selection(wrapper) " {{{1
keepjumps normal! `>a}
execute 'keepjumps normal! `<i\' . a:wrapper . '{'
endfunction
function! vimtex#change#wrap_selection_prompt(...) " {{{1
let env = input('Environment: ', '',
\ 'customlist,' . s:sidwrap('input_complete'))
if empty(env)
return
endif
" Make sure custom indentation does not interfere
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
" }}}1
function! s:sidwrap(func) " {{{1
return matchstr(expand('<sfile>'), '\zs<SNR>\d\+_\ze.*$') . a:func
endfunction
function! s:input_complete(lead, cmdline, pos) " {{{1
return filter(g:vimtex_change_complete_envs, 'v:val =~# ''^' . a:lead . '''')
endfunction
function! s:search_and_skip_comments(pat, ...) " {{{1
" Usage: s:search_and_skip_comments(pat, [flags, stopline])
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 vimtex#util#in_comment()
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
" }}}1
function! s:toggle_delim_get_new(d1,d2) " {{{1
if a:d1 =~# g:vimtex_change_ignored_delims_pattern
return ['', '']
endif
let newd1 = ''
let newd2 = ''
let delim = g:vimtex_change_toggled_delims
for i in range(len(delim))
let d1_1 = type(delim[i]) == 3 ? delim[i][0] : delim[i]
let d2_1 = type(delim[i]) == 3 ? delim[i][1] : delim[i]
if a:d1 =~# d1_1 . '\>'
if i+1 == len(delim)
let newd1 = substitute(a:d1, d1_1, '', '')
let newd2 = substitute(a:d2, d2_1, '', '')
else
let d1_2 = type(delim[i+1]) == 3 ? delim[i+1][0] : delim[i+1]
let d2_2 = type(delim[i+1]) == 3 ? delim[i+1][1] : delim[i+1]
let newd1 = substitute(a:d1, d1_1, d1_2, '')
let newd2 = substitute(a:d2, d2_1, d2_2, '')
endif
break
endif
endfor
if newd1 ==# ''
if len(a:d1) > 0
let d1_1 = type(delim[0]) == 3 ? delim[0][0] : delim[0]
let d2_1 = type(delim[0]) == 3 ? delim[0][1] : delim[0]
let newd1 = substitute(a:d1, '^', d1_1, '')
let newd2 = substitute(a:d2, '^', d2_1, '')
endif
endif
return [newd1, newd2]
endfunction
" }}}1
" vim: fdm=marker sw=2

190
autoload/vimtex/cmd.vim Normal file
View File

@ -0,0 +1,190 @@
" vimtex - LaTeX plugin for Vim
"
" Maintainer: Karl Yngve Lervåg
" Email: karl.yngve@gmail.com
"
function! vimtex#cmd#init_options() " {{{1
endfunction
" }}}1
function! vimtex#cmd#init_script() " {{{1
endfunction
" }}}1
function! vimtex#cmd#init_buffer() " {{{1
nnoremap <silent><buffer> <plug>(vimtex-cmd-delete)
\ :call vimtex#cmd#delete()<cr>
nnoremap <silent><buffer> <plug>(vimtex-cmd-change)
\ :call vimtex#cmd#change()<cr>
nnoremap <silent><buffer> <plug>(vimtex-cmd-create)
\ :call vimtex#cmd#create()<cr>i
inoremap <silent><buffer> <plug>(vimtex-cmd-create)
\ <c-r>=vimtex#cmd#create()<cr>
endfunction
" }}}1
function! vimtex#cmd#get_command(...) " {{{1
let l:position = a:0 > 0 ? a:1 : searchpos('\S', 'bcn')
let l:line = getline(l:position[0])
let l:char = l:line[l:position[1]-1]
" Lists of relevant syntax regions
let l:commands = [
\ 'texStatement',
\ 'texTypeSize',
\ 'texTypeStyle',
\ 'texBeginEnd',
\ ]
let l:argument = [
\ 'texMatcher',
\ 'texItalStyle',
\ 'texRefZone',
\ 'texBeginEndName',
\ ]
for l:syntax in reverse(map(call('synstack', l:position),
\ 'synIDattr(v:val, ''name'')'))
if index(l:commands, l:syntax) >= 0
let l:p = searchpos('\\', 'bcn')
let l:c = matchstr(l:line, '\\\zs\w\+', l:p[1]-1)
return [l:c] + l:p
elseif index(l:argument, l:syntax) >= 0
\ || (l:syntax ==# 'Delimiter' && l:char =~# '{\|}')
let l:curpos = exists('*getcurpos') ? getcurpos() : getpos('.')
keepjumps normal! vaBoh
let l:result = vimtex#cmd#get_command()
call setpos('.', l:curpos)
return l:result
endif
endfor
return ['', 0, 0]
endfunction
function! vimtex#cmd#change() " {{{1
" Get old command
let [l:old, l:line, l:col] = vimtex#cmd#get_command()
if l:old ==# '' | return | endif
" Get new command
let l:new = input('Change ' . old . ' for: ')
let l:new = empty(l:new) ? l:old : l:new
" Store current cursor position
let l:curpos = exists('*getcurpos') ? getcurpos() : getpos('.')
if l:line == l:curpos[1]
let l:curpos[2] += len(l:new) - len(l:old)
endif
" This is a hack to make undo restore the correct position
normal! ix
normal! x
" Perform the change
let l:tmppos = copy(l:curpos)
let l:tmppos[1:2] = [l:line, l:col+1]
cal setpos('.', l:tmppos)
let l:savereg = @a
let @a = l:new
normal! cea
let @a = l:savereg
" Restore cursor position and create repeat hook
call setpos('.', l:curpos)
silent! call repeat#set("\<plug>(vimtex-cmd-change)" . new . ' ', v:count)
endfunction
function! vimtex#cmd#delete() " {{{1
" Get old command
let [l:old, l:line, l:col] = vimtex#cmd#get_command()
if l:old ==# '' | return | endif
" Store current cursor position
let l:curpos = exists('*getcurpos') ? getcurpos() : getpos('.')
if l:line == l:curpos[1]
let l:curpos[2] -= len(l:old)+1
endif
" Save selection
let l:vstart = [l:curpos[0], line("'<"), col("'<"), l:curpos[3]]
let l:vstop = [l:curpos[0], line("'<"), col("'>"), l:curpos[3]]
" This is a hack to make undo restore the correct position
normal! ix
normal! x
" Use temporary cursor position
let l:tmppos = copy(l:curpos)
let l:tmppos[1:2] = [l:line, l:col]
call setpos('.', l:tmppos)
normal! de
" Delete surrounding braces if present
if getline('.')[l:col-1 :] =~# '^\s*{'
call searchpos('{', 'c')
keepjumps normal! vaBomzoxg`zx
if l:line == l:curpos[1]
let l:curpos[2] -= 1
if l:curpos[2] < 0
let l:curpos[2] = 0
endif
endif
endif
" Restore cursor position and visual selection
call setpos('.', l:curpos)
call setpos("'<", l:vstart)
call setpos("'>", l:vstop)
" Create repeat hook
silent! call repeat#set("\<plug>(vimtex-cmd-delete)", v:count)
endfunction
function! vimtex#cmd#create() " {{{1
" Get current line
let line = getline('.')
" Get cursor position
let pos = getpos('.')
" Return if there is no word at cursor
if mode() ==# 'n'
let column = pos[2] - 1
else
let column = pos[2] - 2
endif
if column <= 1 || line[column] =~# '\s'
return ''
endif
" Prepend a backslash to beginning of the current word
normal! B
let column = getpos('.')[2]
if line[column - 1] !=# '\'
let line = strpart(line, 0, column - 1) . '\' . strpart(line, column - 1)
call setline('.', line)
endif
" Append opening braces to the end of the current word
normal! E
let column = getpos('.')[2]
let pos[2] = column + 1
if line[column - 1] !=# '{'
let line = strpart(line, 0, column) . '{' . strpart(line, column)
call setline('.', line)
let pos[2] += 1
endif
" Restore cursor position
call setpos('.', pos)
return ''
endfunction
" }}}1
" vim: fdm=marker sw=2

640
autoload/vimtex/delim.vim Normal file
View File

@ -0,0 +1,640 @@
" vimtex - LaTeX plugin for Vim
"
" Maintainer: Karl Yngve Lervåg
" Email: karl.yngve@gmail.com
"
function! vimtex#delim#init_options() " {{{1
endfunction
" }}}1
function! vimtex#delim#init_script() " {{{1
let s:delims = {}
let s:re = {}
let s:delims.env = {
\ 'list' : [
\ ['begin', 'end'],
\ ['\(', '\)'],
\ ['\[', '\]'],
\ ['$$', '$$'],
\ ['$', '$'],
\ ],
\ 're' : [
\ ['\\begin\s*{[^}]*}', '\\end\s*{[^}]*}'],
\ ['\\(', '\\)'],
\ ['\\\[', '\\\]'],
\ ['\$\$', '\$\$'],
\ ['\$', '\$'],
\ ],
\}
let s:delims.delim_tex = {
\ 'list' : [
\ ['(', ')'],
\ ['[', ']'],
\ ['{', '}'],
\ ],
\ 're' : [
\ ['(', ')'],
\ ['\[', '\]'],
\ ['{', '}'],
\ ],
\}
let s:delims.delim_mods = {
\ 'list' : [
\ ['\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'],
\ ],
\}
let s:delims.delim_math = {
\ 'list' : [
\ ['(', ')'],
\ ['[', ']'],
\ ['\{', '\}'],
\ ['\|', '\|'],
\ ['|', '|'],
\ ['\langle', '\rangle'],
\ ['\lvert', '\rvert'],
\ ['\lfloor', '\rfloor'],
\ ['\lceil', '\rceil'],
\ ['\ulcorner', '\urcorner'],
\ ],
\ 're' : [
\ ['(', ')'],
\ ['\[', '\]'],
\ ['\\{', '\\}'],
\ ['\\|', '\\|'],
\ ['|', '|'],
\ ['\\langle', '\\rangle'],
\ ['\\lvert', '\\rvert'],
\ ['\\lfloor', '\\rfloor'],
\ ['\\lceil', '\\rceil'],
\ ['\\ulcorner', '\\urcorner'],
\ ],
\}
let s:re.env = {
\ 'open' : '\%('
\ . join(map(copy(s:delims.env.re), 'v:val[0]'), '\|')
\ . '\)',
\ 'close' : '\%('
\ . join(map(copy(s:delims.env.re), 'v:val[1]'), '\|')
\ . '\)',
\ 'both' : '\%('
\ . join(map(copy(s:delims.env.re), 'v:val[0]'), '\|') . '\|'
\ . join(map(copy(s:delims.env.re), 'v:val[1]'), '\|')
\ . '\)'
\}
let s:re.delim_tex = {
\ 'open' : '\%('
\ . join(map(copy(s:delims.delim_tex.re), 'v:val[0]'), '\|')
\ . '\)',
\ 'close' : '\%('
\ . join(map(copy(s:delims.delim_tex.re), 'v:val[1]'), '\|')
\ . '\)',
\ 'both' : '\%('
\ . join(map(copy(s:delims.delim_tex.re), 'v:val[0]'), '\|') . '\|'
\ . join(map(copy(s:delims.delim_tex.re), 'v:val[1]'), '\|')
\ . '\)'
\}
let s:re.delim_mods = {
\ 'open' : '\\left\|\\[bB]igg\?l\?',
\ 'close' : '\\right\|\\[bB]igg\?r\?',
\ 'both' : '\\left\|\\right\|\\[bB]igg\?[lr]\?',
\}
let s:re.delim_math = {
\ 'open' : '\%(\%(' . s:re.delim_mods.open . '\)\s*\)\?\%('
\ . join(map(copy(s:delims.delim_math.re), 'v:val[0]'), '\|')
\ . '\)',
\ 'close' : '\%(\%(' . s:re.delim_mods.close . '\)\s*\)\?\%('
\ . join(map(copy(s:delims.delim_math.re), 'v:val[1]'), '\|')
\ . '\)',
\ 'both' : '\%(\%(' . s:re.delim_mods.both . '\)\s*\)\?\%('
\ . join(map(copy(s:delims.delim_math.re), 'v:val[0]'), '\|') . '\|'
\ . join(map(copy(s:delims.delim_math.re), 'v:val[1]'), '\|')
\ . '\)'
\}
let s:delims.delim_all = {}
let s:delims.all = {}
let s:re.delim_all = {}
let s:re.all = {}
for k in ['list', 're']
let s:delims.delim_all[k] = s:delims.delim_math[k]
let s:delims.all[k] = s:delims.env[k] + s:delims.delim_all[k]
endfor
for k in ['open', 'close', 'both']
let s:re.delim_all[k] = s:re.delim_math[k]
let s:re.all[k] = s:re.env[k] . '\|' . s:re.delim_all[k]
endfor
let s:types = [
\ {
\ 're' : '\\\%(begin\|end\)\>',
\ 'parser' : function('s:parser_env'),
\ },
\ {
\ 're' : '\$\$\?',
\ 'parser' : function('s:parser_tex'),
\ },
\ {
\ 're' : '\\\%((\|)\|\[\|\]\)',
\ 'parser' : function('s:parser_latex'),
\ },
\ {
\ 're' : s:re.delim_all.both,
\ 'parser' : function('s:parser_delim'),
\ },
\]
endfunction
" }}}1
function! vimtex#delim#init_buffer() " {{{1
nnoremap <silent><buffer> <plug>(vimtex-delim-toggle-modifier)
\ :call vimtex#delim#toggle_modifier()<cr>
inoremap <silent><buffer> <plug>(vimtex-delim-close)
\ <c-r>=vimtex#delim#close()<cr>
endfunction
" }}}1
function! vimtex#delim#get_valid_regexps(...) " {{{1
"
" Arguments: (Optional)
" line number
" column number
"
" Returns:
" [regexp_open_delims, regexp_close_delims]
"
return call('vimtex#util#in_mathzone', a:000)
\ ? [s:re.delim_math.open, s:re.delim_math.close]
\ : [s:re.delim_tex.open, s:re.delim_tex.close]
endfunction
" }}}1
function! vimtex#delim#close() " {{{1
let l:delim = vimtex#delim#get_prev('all', 'open')
return empty(l:delim)
\ || get(l:delim, 'name', '') ==# 'document'
\ ? ''
\ : l:delim.corr
endfunction
" }}}1
function! vimtex#delim#toggle_modifier() " {{{1
let [l:open, l:close] = vimtex#delim#get_surrounding('delim_math')
if empty(l:open) | return | endif
let newmods = []
let modlist = [['', '']]
\ + get(g:, 'vimtex_delim_toggle_mod_list',
\ s:delims.delim_mods.list)
let n = len(modlist)
for i in range(n)
let j = (i + 1) % 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 = getpos('.')
if pos[2] > l:open.cnum + len(l:open.mod)
let pos[2] += n
call setpos('.', 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)
silent! call repeat#set("\<plug>(vimtex-delim-toggle-modifier)", v:count)
endfunction
" }}}1
function! vimtex#delim#get_next(type, side) " {{{1
return s:get_delim('next', a:type, a:side)
endfunction
" }}}1
function! vimtex#delim#get_prev(type, side) " {{{1
return s:get_delim('prev', a:type, a:side)
endfunction
" }}}1
function! vimtex#delim#get_current(type, side) " {{{1
return s:get_delim('current', a:type, a:side)
endfunction
" }}}1
function! vimtex#delim#get_matching(delim) " {{{1
if empty(a:delim) || !has_key(a:delim, 'lnum') | return {} | endif
"
" Get the matching position
"
let l:save_pos = getpos('.')
call setpos('.', [0, a:delim.lnum, a:delim.cnum, 0])
let [l:match, l:lnum, l:cnum] = a:delim.get_matching()
call setpos('.', 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
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
endif
return l:matching
endfunction
" }}}1
function! vimtex#delim#get_surrounding(type) " {{{1
let l:save_pos = getpos('.')
let l:lnum = l:save_pos[1]
let l:pos_val_cursor = 10000*l:save_pos[1] + l:save_pos[2]
while l:lnum > 1
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 = 10000*l:close.lnum
\ + l:close.cnum + strlen(l:close.match)
if l:pos_val_try > l:pos_val_cursor
call setpos('.', l:save_pos)
return [l:open, l:close]
else
let l:lnum = l:open.lnum
call setpos('.', s:pos_prev(l:open.lnum, l:open.cnum))
endif
endwhile
call setpos('.', l:save_pos)
return [{}, {}]
endfunction
" }}}1
function! s:get_delim(direction, type, side) " {{{1
"
" Arguments:
" direction next
" prev
" current
" type env
" delim_tex
" delim_math
" delim_all
" all
" side open
" close
" both
"
" 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
" }
" }
"
let l:re = s:re[a:type][a:side]
let [l:lnum, l:cnum] = a:direction ==# 'next'
\ ? searchpos(l:re, 'cnW')
\ : a:direction ==# 'prev'
\ ? searchpos(l:re, 'bcnW')
\ : searchpos(l:re, 'bcnW', line('.'))
let l:match = matchstr(getline(l:lnum), '^' . l:re, l:cnum-1)
if a: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,
\}
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:side, a:type, a:direction),
\ l:result, 'keep')
break
endif
endfor
return empty(l:result.type) ? {} : l:result
endfunction
" }}}1
function! s:parser_env(match, lnum, cnum, ...) " {{{1
let result = {}
let result.type = 'env'
let result.name = matchstr(a:match, '{\zs.*\ze}')
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.corr = result.is_open
\ ? substitute(a:match, 'begin', 'end', '')
\ : substitute(a:match, 'end', 'begin', '')
let result.re = {
\ 'open' : '\\begin\s*{' . result.name . '}',
\ 'close' : '\\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) " {{{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 = a:match
let result.corr = a:match
let result.get_matching = function('s:get_matching_tex')
let result.re = {
\ 'this' : escape(a:match, '$'),
\ 'corr' : escape(a:match, '$'),
\ 'open' : escape(a:match, '$'),
\ 'close' : 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'
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 = getpos('.')
" Move the cursor
call setpos('.', a:direction ==# 'next'
\ ? s:pos_next(a:lnum, a:cnum)
\ : s:pos_prev(a:lnum, a:cnum))
" Get new result
let result = s:get_delim(a:direction, a:type, a:side)
" Restore the cursor
call setpos('.', l:save_pos)
endif
return result
endfunction
" }}}1
function! s:parser_latex(match, lnum, cnum, ...) " {{{1
let result = {}
let result.type = a:match =~# '\\(\|\\)' ? '\(' : '\['
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.corr = result.is_open
\ ? substitute(substitute(a:match, '\[', ']', ''), '(', ')', '')
\ : substitute(substitute(a:match, '\]', '[', ''), ')', '(', '')
let result.re = {
\ 'open' : result.type ==# '\(' ? '\\(' : '\\\[',
\ 'close' : result.type ==# '\(' ? '\\)' : '\\\]',
\}
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, ...) " {{{1
let result = {}
let result.type = 'delim'
let result.side = a:match =~# s:re.delim_all.open ? 'open' : 'close'
let result.is_open = result.side ==# 'open'
let result.get_matching = function('s:get_matching_delim')
"
" Find corresponding delimiter and the regexps
"
if a:match =~# '^' . s:re.delim_mods.both
let m1 = matchstr(a:match, '^' . s:re.delim_mods.both)
let d1 = substitute(strpart(a:match, len(m1)), '^\s*', '', '')
let m2 = s:parser_delim_get_corr(m1, 'delim_mods')
let d2 = s:parser_delim_get_corr(d1, 'delim_math')
let re = [
\ s:parser_delim_get_regexp(m1, 'delim_mods')
\ . '\s*' . s:parser_delim_get_regexp(d1, 'delim_math'),
\ s:parser_delim_get_regexp(m2, 'delim_mods')
\ . '\s*' . s:parser_delim_get_regexp(d2, 'delim_math')
\]
else
let d1 = a:match
let m1 = ''
let d2 = s:parser_delim_get_corr(a:match)
let m2 = ''
let re = [
\ s:parser_delim_get_regexp(a:match),
\ s:parser_delim_get_regexp(d2)
\]
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' : re[0],
\ 'corr' : re[1],
\ 'open' : result.is_open ? re[0] : re[1],
\ 'close' : result.is_open ? re[1] : re[0],
\}
return result
endfunction
" }}}1
function! s:parser_delim_get_regexp(delim, ...) " {{{1
let l:type = a:0 > 0 ? a:1 : 'delim_all'
" Check open delimiters
let index = index(map(copy(s:delims[l:type].list), 'v:val[0]'), a:delim)
if index >= 0
return s:delims[l:type].re[index][0]
endif
" Check close delimiters
let index = index(map(copy(s:delims[l:type].list), 'v:val[1]'), a:delim)
if index >= 0
return s:delims[l:type].re[index][1]
endif
endfunction
" }}}1
function! s:parser_delim_get_corr(delim, ...) " {{{1
let l:type = a:0 > 0 ? a:1 : 'delim_all'
for l:pair in s:delims[l:type].list
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 " {{{1
let [re, flags] = self.is_open
\ ? [self.re.close, 'nW']
\ : [self.re.open, 'bnW']
let [lnum, cnum] = searchpairpos(self.re.open, '', self.re.close, flags)
let match = matchstr(getline(lnum), '^' . re, cnum-1)
return [match, lnum, cnum]
endfunction
" }}}1
function! s:get_matching_tex() dict " {{{1
let [re, flags] = self.is_open
\ ? [self.re.open, 'nW']
\ : [self.re.open, 'bnW']
let [lnum, cnum] = searchpos(re, flags)
let match = matchstr(getline(lnum), '^' . re, cnum-1)
return [match, lnum, cnum]
endfunction
" }}}1
function! s:get_matching_latex() dict " {{{1
let [re, flags] = self.is_open
\ ? [self.re.close, 'nW']
\ : [self.re.open, 'bnW']
let [lnum, cnum] = searchpos(re, flags)
let match = matchstr(getline(lnum), '^' . re, cnum-1)
return [match, lnum, cnum]
endfunction
" }}}1
function! s:get_matching_delim() dict " {{{1
let [re, flags] = self.is_open
\ ? [self.re.close, 'nW']
\ : [self.re.open, 'bnW']
let [lnum, cnum] = searchpairpos(self.re.open, '', self.re.close, flags,
\ 'vimtex#util#in_comment()')
let match = matchstr(getline(lnum), '^' . re, cnum-1)
return [match, lnum, cnum]
endfunction
" }}}1
function! s:pos_next(lnum, cnum) " {{{1
return a:cnum < strlen(getline(a:lnum))
\ ? [0, a:lnum, a:cnum+1, 0]
\ : [0, a:lnum+1, 1, 0]
endfunction
" }}}1
function! s:pos_prev(lnum, cnum) " {{{1
return a:cnum > 1
\ ? [0, a:lnum, a:cnum-1, 0]
\ : [0, max([a:lnum-1, 1]), strlen(getline(a:lnum-1)), 0]
endfunction
" }}}1
" vim: fdm=marker sw=2

135
autoload/vimtex/env.vim Normal file
View File

@ -0,0 +1,135 @@
" vimtex - LaTeX plugin for Vim
"
" Maintainer: Karl Yngve Lervåg
" Email: karl.yngve@gmail.com
"
function! vimtex#env#init_options() " {{{1
call vimtex#util#set_default('g:vimtex_env_complete_list', [
\ 'itemize',
\ 'enumerate',
\ 'description',
\ 'center',
\ 'figure',
\ 'table',
\ 'equation',
\ 'multline',
\ 'align',
\ 'split',
\ '\[',
\ ])
endfunction
" }}}1
function! vimtex#env#init_script() " {{{1
endfunction
" }}}1
function! vimtex#env#init_buffer() " {{{1
nnoremap <silent><buffer> <plug>(vimtex-env-delete)
\ :call vimtex#env#change('')<cr>
nnoremap <silent><buffer> <plug>(vimtex-env-change)
\ :call vimtex#env#change_prompt()<cr>
nnoremap <silent><buffer> <plug>(vimtex-env-toggle-star)
\ :call vimtex#env#toggle_star()<cr>
endfunction
" }}}1
function! vimtex#env#change(new) " {{{1
let [l:open, l:close] = vimtex#delim#get_surrounding('env')
"
" Set target environment
"
if a:new ==# ''
let [l:beg, l:end] = ['', '']
elseif a:new ==# '$'
let [l:beg, l:end] = ['$', '$']
elseif a:new ==# '$$'
let [l:beg, l:end] = ['$$', '$$']
elseif a:new ==# '\['
let [l:beg, l:end] = ['\[', '\]']
elseif a:new ==# '\('
let [l:beg, l:end] = ['\(', '\)']
else
let l:beg = '\begin{' . a:new . '}'
let l:end = '\end{' . a:new . '}'
endif
let l:line = getline(l:open.lnum)
call setline(l:open.lnum,
\ strpart(l:line, 0, l:open.cnum-1)
\ . l:beg
\ . strpart(l:line, l:open.cnum + len(l:open.match) - 1))
let l:c1 = l:close.cnum
let l:c2 = l:close.cnum + len(l:close.match) - 1
if l:open.lnum == l:close.lnum
let n = len(l:beg) - len(l:open.match)
let l:c1 += n
let l:c2 += n
let pos = getpos('.')
if pos[2] > l:open.cnum + len(l:open.match) - 1
let pos[2] += n
call setpos('.', pos)
endif
endif
let l:line = getline(l:close.lnum)
call setline(l:close.lnum,
\ strpart(l:line, 0, l:c1-1) . l:end . strpart(l:line, l:c2))
if a:new ==# ''
silent! call repeat#set("\<plug>(vimtex-env-delete)", v:count)
else
silent! call repeat#set(
\ "\<plug>(vimtex-env-change)" . a:new . ' ', v:count)
endif
endfunction
function! vimtex#env#change_prompt() " {{{1
let [l:open, l:close] = vimtex#delim#get_surrounding('env')
let l:name = l:open.type ==# 'env' ? l:open.name : l:open.type
call vimtex#echo#status(['Change surrounding environment: ',
\ ['VimtexWarning', l:name]])
echohl VimtexMsg
let l:new_env = input('> ', '', 'customlist,' . s:sidwrap('input_complete'))
echohl None
if empty(l:new_env)
return
else
call vimtex#env#change(l:new_env)
endif
endfunction
function! vimtex#env#toggle_star() " {{{1
let [l:open, l:close] = vimtex#delim#get_surrounding('env')
if l:open.type !=# 'env' | return | endif
call vimtex#env#change(l:open.name[-1:] ==# '*'
\ ? l:open.name[:-2]
\ : l:open.name . '*'
\)
silent! call repeat#set("\<plug>(vimtex-env-toggle-star)", v:count)
endfunction
" }}}1
function! s:sidwrap(func) " {{{1
return matchstr(expand('<sfile>'), '\zs<SNR>\d\+_\ze.*$') . a:func
endfunction
" }}}1
function! s:input_complete(lead, cmdline, pos) " {{{1
return filter(g:vimtex_env_complete_list, 'v:val =~# ''^' . a:lead . '''')
endfunction
" }}}1
" vim: fdm=marker sw=2

View File

@ -19,7 +19,7 @@ function! vimtex#motion#init_script() " {{{1
if g:vimtex_motion_matchparen if g:vimtex_motion_matchparen
augroup vimtex_motion augroup vimtex_motion
autocmd! autocmd!
autocmd! CursorMoved *.tex call s:highlight_matching_pair(1) autocmd! CursorMoved *.tex call s:highlight_matching_pair()
autocmd! CursorMovedI *.tex call s:highlight_matching_pair() autocmd! CursorMovedI *.tex call s:highlight_matching_pair()
augroup END augroup END
endif endif
@ -34,32 +34,6 @@ function! vimtex#motion#init_script() " {{{1
" Not in a comment " Not in a comment
let s:notcomment = '\%(\%(\\\@<!\%(\\\\\)*\)\@<=%.*\)\@<!' let s:notcomment = '\%(\%(\\\@<!\%(\\\\\)*\)\@<=%.*\)\@<!'
" Patterns to match opening and closing delimiters/environments
let s:delimiters_open = [
\ '{',
\ '(',
\ '\[',
\ '\\{',
\ '\\(',
\ '\\\[',
\ '\\\Cbegin\s*{.\{-}}',
\ '\\\Cleft\s*\%([^\\a-zA-Z0-9]\|\\.\|\\\a*\)',
\ '\\\cbigg\?\((\|\[\|\\{\)',
\ ]
let s:delimiters_close = [
\ '}',
\ ')',
\ '\]',
\ '\\}',
\ '\\)',
\ '\\\]',
\ '\\\Cend\s*{.\{-}}',
\ '\\\Cright\s*\%([^\\a-zA-Z0-9]\|\\.\|\\\a*\)',
\ '\\\cbigg\?\()\|\]\|\\}\)',
\ ]
let s:delimiters = join(s:delimiters_open + s:delimiters_close, '\|')
let s:delimiters = '\(' . s:delimiters . '\|\$\)'
" Pattern to match section/chapter/... " Pattern to match section/chapter/...
let s:section = s:notcomment . '\v\s*\\' let s:section = s:notcomment . '\v\s*\\'
let s:section .= '((sub)*section|chapter|part|' let s:section .= '((sub)*section|chapter|part|'
@ -130,54 +104,19 @@ function! vimtex#motion#find_matching_pair(...) " {{{1
if vimtex#util#in_comment() | return | endif if vimtex#util#in_comment() | return | endif
" Save position let delim = vimtex#delim#get_current('all', 'both')
let nl = line('.')
let nc = col('.')
" Find delimiter under cursor
let [lnum, cnum] = searchpos(s:delimiters, 'cbnW', nl-2)
let delim = matchstr(getline(lnum), '^' . s:delimiters, cnum-1)
" If delimiter not found, try to search forward instead
if empty(delim) if empty(delim)
let [lnum, cnum] = searchpos(s:delimiters, 'cnW', nl+2) let delim = vimtex#delim#get_next('all', 'both')
let delim = matchstr(getline(lnum), '^'. s:delimiters, cnum-1) if empty(delim) | return | endif
if empty(delim)
return
endif
endif endif
" Utility pattern to NOT match the current cursor position let delim = vimtex#delim#get_matching(delim)
let not_cursor = '\%(\%'. lnum . 'l\%' . cnum . 'c\)\@!' if empty(delim) | return | endif
" Finally, find the matching delimiter call cursor(delim.lnum,
if delim =~# '^\$' \ (delim.is_open
let inline = s:notcomment . s:notbslash . '\$' \ ? delim.cnum
let [lnum0, cnum0] = searchpos('.', 'nW') \ : delim.cnum + strlen(delim.match) - 1))
if lnum0 && vimtex#util#in_syntax('texMathZoneX', lnum0, cnum0)
let [lnum2, cnum2] = searchpos(inline, 'nW', 0, 200)
else
let [lnum2, cnum2] = searchpos(not_cursor . inline, 'bnW', 0, 200)
endif
call cursor(lnum2,cnum2)
else
for i in range(len(s:delimiters))
let open_pat = '\C' . s:notbslash . s:delimiters_open[i]
let close_pat = '\C' . s:notbslash . s:delimiters_close[i]
if delim =~# '^' . open_pat
call searchpairpos(open_pat, '', close_pat,
\ 'W', 'vimtex#util#in_comment()', 0, 200)
call search(close_pat, 'ce')
return
elseif delim =~# '^' . close_pat
call searchpairpos(open_pat, '', not_cursor . close_pat,
\ 'bW', 'vimtex#util#in_comment()', 0, 200)
return
endif
endfor
endif
endfunction endfunction
" }}}1 " }}}1
@ -252,70 +191,21 @@ endfunction
" }}}1 " }}}1
function! s:highlight_matching_pair(...) " {{{1 function! s:highlight_matching_pair() " {{{1
if vimtex#util#in_comment() | return | endif if vimtex#util#in_comment() | return | endif
let hmode = a:0 > 0 ? 1 : 0
2match none 2match none
" Save position let l:d1 = vimtex#delim#get_current('all', 'both')
let nl = line('.') let l:d2 = vimtex#delim#get_matching(l:d1)
let nc = col('.') if empty(l:d2) | return | endif
let line = getline(nl)
" Find delimiter under cursor let [l1, c1, l2, c2] = l:d1.side ==# 'open'
let cnum = searchpos(s:delimiters, 'cbnW', nl)[1] \ ? [l:d1.lnum, l:d1.cnum, l:d2.lnum, l:d2.cnum]
let delim = matchstr(line, '^'. s:delimiters, cnum-1) \ : [l:d2.lnum, l:d2.cnum, l:d1.lnum, l:d1.cnum]
" Only highlight when cursor is on delimiters execute '2match MatchParen /'
if empty(delim) || strlen(delim)+cnum-hmode < nc \ . '\%' . l1 . 'l\%' . c1 . 'c' . l:d1.re.open
return \ . '\|\%' . l2 . 'l\%' . c2 . 'c' . l:d1.re.close . '/'
endif
if delim =~# '^\$'
"
" Match inline math
"
let [lnum0, cnum0] = searchpos('.', 'nW')
if lnum0 && vimtex#util#in_syntax('texMathZoneX', lnum0, cnum0)
let [lnum2, cnum2] = searchpos(s:notcomment . s:notbslash . '\$',
\ 'nW', line('w$'), 200)
else
let [lnum2, cnum2] = searchpos('\%(\%'. nl . 'l\%'
\ . cnum . 'c\)\@!'. s:notcomment . s:notbslash . '\$',
\ 'bnW', line('w0'), 200)
endif
execute '2match MatchParen /\%(\%' . nl . 'l\%'
\ . cnum . 'c\$' . '\|\%' . lnum2 . 'l\%' . cnum2 . 'c\$\)/'
else
"
" Match other delimitors
"
for i in range(len(s:delimiters_open))
let open_pat = '\C' . s:notbslash . s:delimiters_open[i]
let close_pat = '\C' . s:notbslash . s:delimiters_close[i]
if delim =~# '^' . open_pat
let [lnum2, cnum2] = searchpairpos(open_pat, '', close_pat,
\ 'nW', 'vimtex#util#in_comment()', line('w$'), 200)
execute '2match MatchParen /\%(\%' . nl . 'l\%' . cnum
\ . 'c' . s:delimiters_open[i] . '\|\%'
\ . lnum2 . 'l\%' . cnum2 . 'c'
\ . s:delimiters_close[i] . '\)/'
return
elseif delim =~# '^' . close_pat
let [lnum2, cnum2] = searchpairpos(open_pat, '',
\ '\C\%(\%'. nl . 'l\%' . cnum . 'c\)\@!' . close_pat,
\ 'bnW', 'vimtex#util#in_comment()', line('w0'), 200)
execute '2match MatchParen /\%(\%' . lnum2 . 'l\%' . cnum2
\ . 'c' . s:delimiters_open[i] . '\|\%'
\ . nl . 'l\%' . cnum . 'c'
\ . s:delimiters_close[i] . '\)/'
return
endif
endfor
endif
endfunction endfunction
" }}}1 " }}}1

View File

@ -56,108 +56,43 @@ endfunction
" }}}1 " }}}1
function! vimtex#text_obj#delimiters(...) " {{{1 function! vimtex#text_obj#delimiters(...) " {{{1
let inner = a:0 > 0 let [l:open, l:close] = vimtex#delim#get_surrounding('delim_all')
call s:text_obj_delim(l:open, l:close, a:0 > 0)
let [d1, l1, c1, d2, l2, c2] = vimtex#delim#get_surrounding()
if inner
let c1 += len(d1)
if c1 != len(getline(l1))
let l1 += 1
let c1 = 1
endif
endif
if inner
let c2 -= 1
if c2 < 1
let l2 -= 1
let c2 = len(getline(l2))
endif
else
let c2 += len(d2) - 1
endif
if l1 < l2 || (l1 == l2 && c1 < c2)
call cursor(l1,c1)
if visualmode() ==# 'V'
normal! V
else
normal! v
endif
call cursor(l2,c2)
endif
endfunction endfunction
" }}}1 " }}}1
function! vimtex#text_obj#environments(...) " {{{1 function! vimtex#text_obj#environments(...) " {{{1
let inner = a:0 > 0 let l:inner = a:0 > 0
let [env, lnum, cnum, lnum2, cnum2] = vimtex#util#get_env(1) let [l:open, l:close] = vimtex#delim#get_surrounding('env')
call cursor(lnum, cnum) if l:open.type !=# 'env' | return | endif
if inner
if env =~# '^\' if l:inner
call search('\\.\_\s*\S', 'eW') call cursor(l:open.lnum, l:open.cnum + strlen(l:open.match))
else call search('}\%(\_\s*\%(\[\_[^]]*\]\)\)\?\_\s*\S', 'eW')
call search('}\(\_\s*\(\[\_[^]]*\]\|{\_\S\{-}}\)\)\?\_\s*\S', 'eW') else
endif call cursor(l:open.lnum, l:open.cnum)
endif endif
if visualmode() ==# 'V' if visualmode() ==# 'V'
normal! V normal! V
else else
normal! v normal! v
endif endif
call cursor(lnum2, cnum2)
if inner if l:inner
call cursor(l:close.lnum, l:close.cnum)
call search('\S\_\s*', 'bW') call search('\S\_\s*', 'bW')
else else
if env =~# '^\' call cursor(l:close.lnum, l:close.cnum + strlen(l:close.match) - 1)
normal! l
else
call search('}', 'eW')
endif
endif endif
endfunction endfunction
" }}}1 " }}}1
function! vimtex#text_obj#inline_math(...) " {{{1 function! vimtex#text_obj#inline_math(...) " {{{1
let l:inner = a:0 > 0 let [l:open, l:close] = vimtex#delim#get_surrounding('env')
if empty(l:open) || l:open.type ==# 'env' | return | endif
let l:flags = 'bW' call s:text_obj_delim(l:open, l:close, a:0 > 0)
let l:dollar = 0
let l:dollar_pat = '\\\@<!\$'
if vimtex#util#in_syntax('texMathZoneX')
let l:dollar = 1
let l:pattern = [l:dollar_pat, l:dollar_pat]
let l:flags .= 'c'
elseif getline('.')[col('.') - 1] ==# '$'
let l:dollar = 1
let l:pattern = [l:dollar_pat, l:dollar_pat]
elseif vimtex#util#in_syntax('texMathZoneV')
let l:pattern = ['\\(', '\\)']
let l:flags .= 'c'
elseif getline('.')[col('.') - 2:col('.') - 1] ==# '\)'
let l:pattern = ['\\(', '\\)']
else
return
endif
call s:search_and_skip_comments(l:pattern[0], l:flags)
if l:inner
execute 'normal! ' l:dollar ? 'l' : 'll'
endif
execute 'normal! ' visualmode() ==# 'V' ? 'V' : 'v'
call s:search_and_skip_comments(l:pattern[1], 'W')
if l:inner
normal! h
elseif !l:dollar
normal! l
endif
endfunction endfunction
" }}}1 " }}}1
function! vimtex#text_obj#paragraphs(...) " {{{1 function! vimtex#text_obj#paragraphs(...) " {{{1
@ -177,34 +112,33 @@ endfunction
" }}}1 " }}}1
function! s:search_and_skip_comments(pat, ...) " {{{1 function! s:text_obj_delim(open, close, inner) " {{{1
" Usage: s:search_and_skip_comments(pat, [flags, stopline]) let [l1, c1, l2, c2] = [a:open.lnum, a:open.cnum, a:close.lnum, a:close.cnum]
let flags = a:0 >= 1 ? a:1 : ''
let stopline = a:0 >= 2 ? a:2 : 0
let saved_pos = getpos('.')
" search once if a:inner
let ret = search(a:pat, flags, stopline) let c1 += len(a:open.match)
let c2 -= 1
if ret if c1 >= len(getline(l1))
" do not match at current position if inside comment let l1 += 1
let flags = substitute(flags, 'c', '', 'g') let c1 = 1
endif
" keep searching while in comment if c2 < 1
while vimtex#util#in_comment() let l2 -= 1
let ret = search(a:pat, flags, stopline) let c2 = len(getline(l2))
if !ret endif
break else
endif let c2 += len(a:close.match) - 1
endwhile
endif endif
if !ret if l1 < l2 || (l1 == l2 && c1 < c2)
" if no match found, restore position call cursor(l1, c1)
call setpos('.', saved_pos) if visualmode() ==# 'V'
normal! V
else
normal! v
endif
call cursor(l2, c2)
endif endif
return ret
endfunction endfunction
" }}}1 " }}}1

View File

@ -9,21 +9,6 @@ endfunction
" }}}1 " }}}1
function! vimtex#util#init_script() " {{{1 function! vimtex#util#init_script() " {{{1
let s:delimiters_open = [
\ '(',
\ '\[',
\ '\\{',
\ '\\\Cleft\s*\%([^\\a-zA-Z0-9]\|\\.\|\\\a*\)',
\ '\\\cbigg\?\((\|\[\|\\{\)',
\ ]
let s:delimiters_close = [
\ ')',
\ '\]',
\ '\\}',
\ '\\\Cright\s*\%([^\\a-zA-Z0-9]\|\\.\|\\\a*\)',
\ '\\\cbigg\?\()\|\]\|\\}\)',
\ ]
endfunction endfunction
" }}}1 " }}}1
@ -141,133 +126,6 @@ function! vimtex#util#shellescape(cmd) " {{{1
endif endif
endfunction endfunction
" }}}1
function! vimtex#util#get_env(...) " {{{1
" vimtex#util#get_env([with_pos])
" Returns:
" - environment
" if with_pos is not given
" - [environment, lnum_begin, cnum_begin, lnum_end, cnum_end]
" if with_pos is nonzero
let with_pos = a:0 > 0 ? a:1 : 0
let move_crs = a:0 > 1 ? a:2 : 1
let begin_pat = '\C\\begin\_\s*{[^}]*}\|\\\@<!\\\[\|\\\@<!\\('
let end_pat = '\C\\end\_\s*{[^}]*}\|\\\@<!\\\]\|\\\@<!\\)'
let saved_pos = getpos('.')
" move to the left until on a backslash (this is necessary to match the
" current environment when the cursor is on the \end command)
let [bufnum, lnum, cnum, off] = getpos('.')
if move_crs
let line = getline(lnum)
while cnum > 1 && line[cnum - 1] !=# '\'
let cnum -= 1
endwhile
call cursor(lnum, cnum)
endif
" 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,
\ 'vimtex#util#in_comment()')
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,
\ 'vimtex#util#in_comment()')
call setpos('.', saved_pos)
return [env, lnum1, cnum1, lnum2, cnum2]
else
call setpos('.', saved_pos)
return env
endif
endfunction
" }}}1
function! vimtex#util#get_delim() " {{{1
" Save position in order to restore before finishing
let pos_original = getpos('.')
" Save position for more work later
let pos_save = getpos('.')
" Check if the cursor is on top of a closing delimiter
let close_pats = '\(' . join(s:delimiters_close, '\|') . '\)'
let lnum = pos_save[1]
let cnum = pos_save[2]
let [lnum, cnum] = searchpos(close_pats, 'cbnW', lnum)
let delim = matchstr(getline(lnum), '^'. close_pats, cnum-1)
if pos_save[2] <= (cnum + len(delim) - 1)
let pos_save[1] = lnum
let pos_save[2] = cnum
call setpos('.', pos_save)
endif
let d1=''
let d2=''
let l1=1000000
let l2=1000000
let c1=1000000
let c2=1000000
for i in range(len(s:delimiters_open))
call setpos('.', pos_save)
let open = s:delimiters_open[i]
let close = s:delimiters_close[i]
let flags = 'W'
" Check if the cursor is on top of an opening delimiter. If it is not,
" then we want to include matches at cursor position to match closing
" delimiters.
if searchpos(open, 'cn') != pos_save[1:2]
let flags .= 'c'
endif
" Search for closing delimiter
let pos = searchpairpos(open, '', close, flags, 'vimtex#util#in_comment()')
" Check if the current is pair is the closest pair
if pos[0] && pos[0]*1000 + pos[1] < l2*1000 + c2
let l2=pos[0]
let c2=pos[1]
let d2=matchstr(strpart(getline(l2), c2 - 1), close)
let pos = searchpairpos(open,'',close,'bW', 'vimtex#util#in_comment()')
let l1=pos[0]
let c1=pos[1]
let d1=matchstr(strpart(getline(l1), c1 - 1), open)
endif
endfor
" Restore cursor position and return delimiters and positions
call setpos('.', pos_original)
return [d1,l1,c1,d2,l2,c2]
endfunction
" }}}1 " }}}1
function! vimtex#util#get_os() " {{{1 function! vimtex#util#get_os() " {{{1
if has('win32') if has('win32')

View File

@ -103,7 +103,7 @@ Feature overview~
- Change the surrounding command or environment with `csc`/`cse` - Change the surrounding command or environment with `csc`/`cse`
- Toggle starred environment with `tse` - Toggle starred environment with `tse`
- Toggle between e.g. `()` and `\left(\right)` with `tsd` - Toggle between e.g. `()` and `\left(\right)` with `tsd`
- Close the current environment in insert mode with `]]` - Close the current environment/delimiter in insert mode with `]]`
- Insert new command with `<F7>` - Insert new command with `<F7>`
- Convenient insert mode mappings for faster typing of e.g. maths - Convenient insert mode mappings for faster typing of e.g. maths
- Folding - Folding
@ -276,53 +276,53 @@ In addition to the mappings listed below, |vimtex| provides convenient insert
mode mappings to make it easier and faster to type mathematical equations. mode mappings to make it easier and faster to type mathematical equations.
This feature is explained in more detail later, see |vimtex-imaps|. This feature is explained in more detail later, see |vimtex-imaps|.
-----------------------------------------------------------~ -------------------------------------------------------------~
LHS RHS MODE~ LHS RHS MODE~
-----------------------------------------------------------~ -------------------------------------------------------------~
<localleader>li |<plug>(vimtex-info)| `n` <localleader>li |<plug>(vimtex-info)| `n`
<localleader>lI |<plug>(vimtex-info-full)| `n` <localleader>lI |<plug>(vimtex-info-full)| `n`
<localleader>lt |<plug>(vimtex-toc-open)| `n` <localleader>lt |<plug>(vimtex-toc-open)| `n`
<localleader>lT |<plug>(vimtex-toc-toggle)| `n` <localleader>lT |<plug>(vimtex-toc-toggle)| `n`
<localleader>ly |<plug>(vimtex-labels-open)| `n` <localleader>ly |<plug>(vimtex-labels-open)| `n`
<localleader>lY |<plug>(vimtex-labels-toggle)| `n` <localleader>lY |<plug>(vimtex-labels-toggle)| `n`
<localleader>lv |<plug>(vimtex-view)| `n` <localleader>lv |<plug>(vimtex-view)| `n`
<localleader>lr |<plug>(vimtex-reverse-search)| `n` <localleader>lr |<plug>(vimtex-reverse-search)| `n`
<localleader>ll |<plug>(vimtex-compile-toggle)| `n` <localleader>ll |<plug>(vimtex-compile-toggle)| `n`
<localleader>lk |<plug>(vimtex-stop)| `n` <localleader>lk |<plug>(vimtex-stop)| `n`
<localleader>lK |<plug>(vimtex-stop-all)| `n` <localleader>lK |<plug>(vimtex-stop-all)| `n`
<localleader>le |<plug>(vimtex-errors)| `n` <localleader>le |<plug>(vimtex-errors)| `n`
<localleader>lo |<plug>(vimtex-compile-output)| `n` <localleader>lo |<plug>(vimtex-compile-output)| `n`
<localleader>lg |<plug>(vimtex-status)| `n` <localleader>lg |<plug>(vimtex-status)| `n`
<localleader>lG |<plug>(vimtex-status-all)| `n` <localleader>lG |<plug>(vimtex-status-all)| `n`
<localleader>lc |<plug>(vimtex-clean)| `n` <localleader>lc |<plug>(vimtex-clean)| `n`
<localleader>lC |<plug>(vimtex-clean-full)| `n` <localleader>lC |<plug>(vimtex-clean-full)| `n`
<localleader>lm |<plug>(vimtex-imaps-list)| `n` <localleader>lm |<plug>(vimtex-imaps-list)| `n`
<localleader>lx |<plug>(vimtex-reload)| `n` <localleader>lx |<plug>(vimtex-reload)| `n`
<localleader>ls |<plug>(vimtex-toggle-main)| `n` <localleader>ls |<plug>(vimtex-toggle-main)| `n`
dse |<plug>(vimtex-delete-env)| `n` dse |<plug>(vimtex-env-delete)| `n`
dsc |<plug>(vimtex-delete-cmd)| `n` dsc |<plug>(vimtex-cmd-delete)| `n`
cse |<plug>(vimtex-change-env)| `n` cse |<plug>(vimtex-env-change)| `n`
csc |<plug>(vimtex-change-cmd)| `n` csc |<plug>(vimtex-cmd-change)| `n`
tse |<plug>(vimtex-toggle-star)| `n` tse |<plug>(vimtex-env-toggle-star)| `n`
tsd |<plug>(vimtex-toggle-delim)| `n` tsd |<plug>(vimtex-delim-toggle-modifier)| `n`
<F7> |<plug>(vimtex-create-cmd)| `ni` <F7> |<plug>(vimtex-cmd-create)| `ni`
]] |<plug>(vimtex-close-env)| `i` ]] |<plug>(vimtex-delim-close)| `i`
a$ |<plug>(vimtex-a$)| `nxo` a$ |<plug>(vimtex-a$)| `nxo`
i$ |<plug>(vimtex-i$)| `nxo` i$ |<plug>(vimtex-i$)| `nxo`
ae |<plug>(vimtex-ae)| `nxo` ae |<plug>(vimtex-ae)| `nxo`
ie |<plug>(vimtex-ie)| `nxo` ie |<plug>(vimtex-ie)| `nxo`
ad |<plug>(vimtex-ad)| `nxo` ad |<plug>(vimtex-ad)| `nxo`
id |<plug>(vimtex-id)| `nxo` id |<plug>(vimtex-id)| `nxo`
ap |<plug>(vimtex-ap)| `nxo` ap |<plug>(vimtex-ap)| `nxo`
ip |<plug>(vimtex-ip)| `nxo` ip |<plug>(vimtex-ip)| `nxo`
% |<plug>(vimtex-%)| `nxo` % |<plug>(vimtex-%)| `nxo`
]] |<plug>(vimtex-]])| `nxo` ]] |<plug>(vimtex-]])| `nxo`
][ |<plug>(vimtex-][)| `nxo` ][ |<plug>(vimtex-][)| `nxo`
[] |<plug>(vimtex-[])| `nxo` [] |<plug>(vimtex-[])| `nxo`
[[ |<plug>(vimtex-[[)| `nxo` [[ |<plug>(vimtex-[[)| `nxo`
} |<plug>(vimtex-})| `nxo` } |<plug>(vimtex-})| `nxo`
{ |<plug>(vimtex-{)| `nxo` { |<plug>(vimtex-{)| `nxo`
-----------------------------------------------------------~ -------------------------------------------------------------~
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
Options~ Options~
@ -363,12 +363,12 @@ Options~
Default value: 0 Default value: 0
*g:vimtex_change_complete_envs* *g:vimtex_env_complete_list*
Define a list of environments that will be completed when changing the Define a list of environments that will be completed when changing the
surrounding environments (see |<plug>(vimtex-change-env)|). surrounding environments (see |<plug>(vimtex-env-change)|).
Default value: > Default value: >
let g:vimtex_change_complete_envs = [ let g:vimtex_env_complete_list = [
\ 'itemize', \ 'itemize',
\ 'enumerate', \ 'enumerate',
\ 'description', \ 'description',
@ -382,21 +382,17 @@ Options~
\ '\[', \ '\[',
\ ] \ ]
< <
*g:vimtex_change_toggled_delims* *g:vimtex_delim_toggle_mod_list*
Define a list of delimiters to toggle through with Define list of delimiter modifiers to toggle through with
|<plug>(vimtex-toggle-delim)|. |<plug>(vimtex-delim-toggle-modifier)|. If the list is not defined, then it uses
a default match list.
For example, to only toggle between no modifier and the `\left/\right` pair,
Default value: > use: >
let g:vimtex_change_toggled_delims = [['\\left', '\\right']] let g:vimtex_delim_toggle_mod_list = [['\\left', '\\right']]
< <
*g:vimtex_change_ignored_delims_pattern* Default value: `Undefined`
A pattern for delimiters that will be ignored when toggling delimiters, see
|<plug>(vimtex-toggle-delim)|.
Default value: >
let g:vimtex_change_ignored_delims_pattern = '\c\\bigg\?'
<
*g:vimtex_format_enabled* *g:vimtex_format_enabled*
If enabled, |vimtex| uses a custom |formatexpr| that should handle inline If enabled, |vimtex| uses a custom |formatexpr| that should handle inline
comments. That is, if it is enabled, then comments at end of lines will not comments. That is, if it is enabled, then comments at end of lines will not
@ -1082,32 +1078,31 @@ Map definitions~
(|g:vimtex_fold_manual|), then |vimtex| remaps |zx| and |zX| in such that (|g:vimtex_fold_manual|), then |vimtex| remaps |zx| and |zX| in such that
the folds are refreshed appropriately. the folds are refreshed appropriately.
*<plug>(vimtex-delete-env)* *<plug>(vimtex-env-delete)*
*<plug>(vimtex-change-env)* *<plug>(vimtex-env-change)*
Delete/Change surrounding environment. Delete/Change surrounding environment.
*<plug>(vimtex-delete-cmd)* *<plug>(vimtex-cmd-delete)*
*<plug>(vimtex-change-cmd)* *<plug>(vimtex-cmd-change)*
Delete/Change surrounding command. Note: <plug>(vimtex-delete-cmd) depends Delete/Change surrounding command. Note: |<plug>(vimtex-cmd-delete)| depends
on |surround.vim| (https://github.com/tpope/vim-surround). on |surround.vim| (https://github.com/tpope/vim-surround).
*<plug>(vimtex-toggle-star)* *<plug>(vimtex-env-toggle-star)*
Toggle starred environment. Toggle starred environment.
*<plug>(vimtex-toggle-delim)* *<plug>(vimtex-delim-toggle-modifier)*
Toggle delimiters, for instance between `(...)` and `\left(...\right)`. The Toggle delimiter modifiers, for instance between `(...)` and `\left(...\right)`.
behaviour is controlled through |g:vimtex_change_toggled_delims| and See also |g:vimtex_delim_toggle_mod_list|.
|g:vimtex_change_ignored_delims_pattern|.
*<plug>(vimtex-create-cmd)* *<plug>(vimtex-cmd-create)*
Convert the current word (word under or right before the cursor) into Convert the current word (word under or right before the cursor) into
a LaTeX command. That is, it prepends a backslash and adds an opening brace. a LaTeX command. That is, it prepends a backslash and adds an opening brace.
From normal mode, the mapping will leave you in insert mode. The mapping From normal mode, the mapping will leave you in insert mode. The mapping
works both in normal and in insert mode. It is by default mapped to <f7> works both in normal and in insert mode. It is by default mapped to <f7>
(see |vimtex-default-mappings| for a list of the default mappings). (see |vimtex-default-mappings| for a list of the default mappings).
*<plug>(vimtex-close-env)* *<plug>(vimtex-delim-close)*
Close the current environment (insert mode). Close the current environment or delimiter (insert mode).
*<plug>(vimtex-a$)* *<plug>(vimtex-a$)*
Text object for inline math (inclusive). Text object for inline math (inclusive).
@ -1784,6 +1779,34 @@ The following changelog only logs particularly important changes, such as
changes that break backwards compatibility. See the git log for the detailed changes that break backwards compatibility. See the git log for the detailed
changelog. changelog.
2016-02-06: Large refactoring of delimiter parsing~
I've refactored a lot of the code in order to make the parsing of delimiters
and features that rely on delimiter detection and similar more consistent.
This results in some changes in option names and similar, but it should make
it easier to provide improved and more robust features.
There is one feature change: The delimiter toggle now consistently toggles the
modifier, not the delimiter itself, and it toggles between a range of
modifiers by default. For customization, see |g:vimtex_delim_toggle_mod_list|.
The following options have changed names:
*g:vimtex_change_set_formatexpr* ---> |g:vimtex_format_enabled|
*g:vimtex_change_complete_envs* ---> |g:vimtex_env_complete_list|
*g:vimtex_change_toggled_delims* ---> |g:vimtex_delim_toggle_mod_list|
The following options have been removed:
*g:vimtex_change_ignored_delims_pattern* --- It was no longer necessary
The following mappings have been renamed:
*<plug>(vimtex-delete-env)* ---> |<plug>(vimtex-env-delete)|
*<plug>(vimtex-delete-cmd)* ---> |<plug>(vimtex-cmd-delete)|
*<plug>(vimtex-change-env)* ---> |<plug>(vimtex-env-change)|
*<plug>(vimtex-change-cmd)* ---> |<plug>(vimtex-cmd-change)|
*<plug>(vimtex-toggle-star)* ---> |<plug>(vimtex-env-toggle-star)|
*<plug>(vimtex-toggle-delim)* ---> |<plug>(vimtex-delim-toggle-modifier)|
*<plug>(vimtex-create-cmd)* ---> |<plug>(vimtex-cmd-create)|
*<plug>(vimtex-close-env)* ---> |<plug>(vimtex-delim-close)|
2015-10-19: Added convenient insert mode mappings~ 2015-10-19: Added convenient insert mode mappings~
I've merged the `math_mappings` branch (see #172 and #251). It adds the I've merged the `math_mappings` branch (see #172 and #251). It adds the
feature that is explained in |vimtex-imaps|. feature that is explained in |vimtex-imaps|.

View File

@ -89,13 +89,10 @@ function! VimtexIndent() " {{{1
endif endif
" Indent opening and closing delimiters " Indent opening and closing delimiters
let l:delims = match(map(synstack(v:lnum, max([col('.') - 1, 1])), let [l:re_open, l:re_close] = vimtex#delim#get_valid_regexps(v:lnum, col('.'))
\ 'synIDattr(v:val, ''name'')'), '^texMathZone') >= 0
\ ? [s:delimiters_open_math, s:delimiters_close_math]
\ : [s:delimiters_open_tex, s:delimiters_close_tex]
let ind += &sw*( let ind += &sw*(
\ max([s:count(pline, l:delims[0]) - s:count(pline, l:delims[1]), 0]) \ max([s:count(pline, l:re_open) - s:count(pline, l:re_close), 0])
\ - max([s:count(cline, l:delims[1]) - s:count(cline, l:delims[0]), 0])) \ - max([s:count(cline, l:re_close) - s:count(cline, l:re_open), 0]))
" Indent list items " Indent list items
if pline =~# '^\s*\\\(bib\)\?item' if pline =~# '^\s*\\\(bib\)\?item'
@ -138,28 +135,6 @@ let s:tikz_indented = 0
" Define some common patterns " Define some common patterns
let s:envs_lists = 'itemize\|description\|enumerate\|thebibliography' let s:envs_lists = 'itemize\|description\|enumerate\|thebibliography'
let s:envs_noindent = 'document\|verbatim\|lstlisting' let s:envs_noindent = 'document\|verbatim\|lstlisting'
let s:delimiters_open_tex = '\(' . join([
\ '{',
\ '\[',
\ '\\(',
\ ], '\|') . '\)'
let s:delimiters_close_tex = '\(' . join([
\ '}',
\ '\]',
\ '\\)',
\ ], '\|') . '\)'
let s:delimiters_open_math = '\(' . join([
\ '\\\[\s*$',
\ '\\{',
\ '\\\Cleft\s*\%([^\\]\|\\.\|\\\a*\)',
\ '\\\cbigg\?\((\|\[\|\\{\)',
\ ], '\|') . '\)'
let s:delimiters_close_math = '\(' . join([
\ '\\\]\s*$',
\ '\\}',
\ '\\\Cright\s*\%([^\\]\|\\.\|\\\a*\)',
\ '\\\cbigg\?\()\|\]\|\\}\)',
\ ], '\|') . '\)'
let s:tikz_commands = '\\\(' . join([ let s:tikz_commands = '\\\(' . join([
\ 'draw', \ 'draw',
\ 'fill', \ 'fill',