Skip to content

Commit

Permalink
merge: add motion to jump to math zones
Browse files Browse the repository at this point in the history
This adds the motions [n, [N, ]n, ]N for moving to the beginning or end
of the previous/next math zone.

refer: #1818
  • Loading branch information
lervag committed Oct 11, 2020
2 parents 2e26959 + e6b6a41 commit 61dcffc
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 29 deletions.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -106,6 +106,7 @@ disabled if desired.
- Motions
- Move between section boundaries with `[[`, `[]`, `][`, and `]]`
- Move between environment boundaries with `[m`, `[M`, `]m`, and `]M`
- Move between math environment boundaries with `[n`, `[N`, `]n`, and `]N`
- Move between comment boundaries with `[*` and `]*`
- Move between matching delimiters with `%`
- Text objects
Expand Down
13 changes: 13 additions & 0 deletions autoload/vimtex.vim
Expand Up @@ -193,6 +193,19 @@ function! s:init_default_mappings() abort " {{{1
call s:map(1, 'o', '[M', '<plug>(vimtex-[M)')
call s:map(1, 'o', '[m', '<plug>(vimtex-[m)')

call s:map(1, 'n', ']N', '<plug>(vimtex-]N)')
call s:map(1, 'n', ']n', '<plug>(vimtex-]n)')
call s:map(1, 'n', '[N', '<plug>(vimtex-[N)')
call s:map(1, 'n', '[n', '<plug>(vimtex-[n)')
call s:map(1, 'x', ']N', '<plug>(vimtex-]N)')
call s:map(1, 'x', ']n', '<plug>(vimtex-]n)')
call s:map(1, 'x', '[N', '<plug>(vimtex-[N)')
call s:map(1, 'x', '[n', '<plug>(vimtex-[n)')
call s:map(1, 'o', ']N', '<plug>(vimtex-]N)')
call s:map(1, 'o', ']n', '<plug>(vimtex-]n)')
call s:map(1, 'o', '[N', '<plug>(vimtex-[N)')
call s:map(1, 'o', '[n', '<plug>(vimtex-[n)')

call s:map(1, 'n', ']/', '<plug>(vimtex-]/)')
call s:map(1, 'n', ']*', '<plug>(vimtex-]*)')
call s:map(1, 'n', '[/', '<plug>(vimtex-[/)')
Expand Down
153 changes: 124 additions & 29 deletions autoload/vimtex/motion.vim
Expand Up @@ -30,14 +30,28 @@ function! vimtex#motion#init_buffer() abort " {{{1
xmap <silent><buffer> <plug>(vimtex-][) <sid>(vimtex-][)
xmap <silent><buffer> <plug>(vimtex-[]) <sid>(vimtex-[])
xmap <silent><buffer> <plug>(vimtex-[[) <sid>(vimtex-[[)
onoremap <silent><buffer> <plug>(vimtex-]])
\ :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-]])"<cr>
onoremap <silent><buffer> <plug>(vimtex-][)
\ :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-][)"<cr>
onoremap <silent><buffer> <plug>(vimtex-[])
\ :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-[])"<cr>
onoremap <silent><buffer> <plug>(vimtex-[[)
\ :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-[[)"<cr>
onoremap <silent><buffer> <plug>(vimtex-]]) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-]])"<cr>
onoremap <silent><buffer> <plug>(vimtex-][) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-][)"<cr>
onoremap <silent><buffer> <plug>(vimtex-[]) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-[])"<cr>
onoremap <silent><buffer> <plug>(vimtex-[[) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-[[)"<cr>
" Math environments ($-$, $$-$$, \(-\), \[-\], \begin-\end)
nnoremap <silent><buffer> <plug>(vimtex-]n) :<c-u>call vimtex#motion#math(1,0,0)<cr>
nnoremap <silent><buffer> <plug>(vimtex-]N) :<c-u>call vimtex#motion#math(0,0,0)<cr>
nnoremap <silent><buffer> <plug>(vimtex-[n) :<c-u>call vimtex#motion#math(1,1,0)<cr>
nnoremap <silent><buffer> <plug>(vimtex-[N) :<c-u>call vimtex#motion#math(0,1,0)<cr>
xnoremap <silent><buffer> <sid>(vimtex-]n) :<c-u>call vimtex#motion#math(1,0,1)<cr>
xnoremap <silent><buffer> <sid>(vimtex-]N) :<c-u>call vimtex#motion#math(0,0,1)<cr>
xnoremap <silent><buffer> <sid>(vimtex-[n) :<c-u>call vimtex#motion#math(1,1,1)<cr>
xnoremap <silent><buffer> <sid>(vimtex-[N) :<c-u>call vimtex#motion#math(0,1,1)<cr>
xmap <silent><buffer> <plug>(vimtex-]n) <sid>(vimtex-]n)
xmap <silent><buffer> <plug>(vimtex-]N) <sid>(vimtex-]N)
xmap <silent><buffer> <plug>(vimtex-[n) <sid>(vimtex-]n)
xmap <silent><buffer> <plug>(vimtex-[N) <sid>(vimtex-]N)
onoremap <silent><buffer> <plug>(vimtex-]n) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-]n)"<cr>
onoremap <silent><buffer> <plug>(vimtex-]N) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-]N)"<cr>
onoremap <silent><buffer> <plug>(vimtex-[n) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-[n)"<cr>
onoremap <silent><buffer> <plug>(vimtex-[N) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-[N)"<cr>
" Environments
nnoremap <silent><buffer> <plug>(vimtex-]m) :<c-u>call vimtex#motion#environment(1,0,0)<cr>
Expand All @@ -52,14 +66,10 @@ function! vimtex#motion#init_buffer() abort " {{{1
xmap <silent><buffer> <plug>(vimtex-]M) <sid>(vimtex-]M)
xmap <silent><buffer> <plug>(vimtex-[m) <sid>(vimtex-[m)
xmap <silent><buffer> <plug>(vimtex-[M) <sid>(vimtex-[M)
onoremap <silent><buffer> <plug>(vimtex-]m)
\ :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-]m)"<cr>
onoremap <silent><buffer> <plug>(vimtex-]M)
\ :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-]M)"<cr>
onoremap <silent><buffer> <plug>(vimtex-[m)
\ :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-[m)"<cr>
onoremap <silent><buffer> <plug>(vimtex-[M)
\ :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-[M)"<cr>
onoremap <silent><buffer> <plug>(vimtex-]m) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-]m)"<cr>
onoremap <silent><buffer> <plug>(vimtex-]M) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-]M)"<cr>
onoremap <silent><buffer> <plug>(vimtex-[m) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-[m)"<cr>
onoremap <silent><buffer> <plug>(vimtex-[M) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-[M)"<cr>
" Comments
nnoremap <silent><buffer> <plug>(vimtex-]/) :<c-u>call vimtex#motion#comment(1,0,0)<cr>
Expand All @@ -74,14 +84,10 @@ function! vimtex#motion#init_buffer() abort " {{{1
xmap <silent><buffer> <plug>(vimtex-]*) <sid>(vimtex-]*)
xmap <silent><buffer> <plug>(vimtex-[/) <sid>(vimtex-[/)
xmap <silent><buffer> <plug>(vimtex-[*) <sid>(vimtex-[*)
onoremap <silent><buffer> <plug>(vimtex-]/)
\ :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-]/)"<cr>
onoremap <silent><buffer> <plug>(vimtex-]*)
\ :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-]*)"<cr>
onoremap <silent><buffer> <plug>(vimtex-[/)
\ :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-[/)"<cr>
onoremap <silent><buffer> <plug>(vimtex-[*)
\ :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-[*)"<cr>
onoremap <silent><buffer> <plug>(vimtex-]/) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-]/)"<cr>
onoremap <silent><buffer> <plug>(vimtex-]*) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-]*)"<cr>
onoremap <silent><buffer> <plug>(vimtex-[/) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-[/)"<cr>
onoremap <silent><buffer> <plug>(vimtex-[*) :execute "normal \<sid>(V)" . v:count1 . "\<sid>(vimtex-[*)"<cr>
endfunction

" }}}1
Expand All @@ -101,6 +107,7 @@ function! vimtex#motion#find_matching_pair(...) abort " {{{1
if empty(delim) | return | endif
if empty(delim.match) | return | endif

" Hack to update the jump list so CTRL-o jumps back to the right place
normal! m`
call vimtex#pos#set_cursor(delim.lnum,
\ (delim.is_open
Expand Down Expand Up @@ -161,6 +168,12 @@ function! vimtex#motion#section(type, backwards, visual) abort " {{{1
endfor
endfunction

" Patterns to match section/chapter/...
let s:re_sec = '\v^\s*\\%(%(sub)?paragraph|%(sub)*section|chapter|part|'
\ . 'appendi%(x|ces)|%(front|back|main)matter)>'
let s:re_sec_t1 = '\v%(' . s:re_sec . '|^\s*%(\\end\{document\}|%$))'
let s:re_sec_t2 = '\v%(' . s:re_sec . '|^\s*\\end\{document\})'

" }}}1
function! vimtex#motion#environment(begin, backwards, visual) abort " {{{1
let l:count = v:count1
Expand Down Expand Up @@ -194,10 +207,92 @@ function! vimtex#motion#comment(begin, backwards, visual) abort " {{{1
endfunction

" }}}1
function! vimtex#motion#math(begin, backwards, visual) abort " {{{1
let l:curpos_saved = vimtex#pos#get_cursor()
let l:count = v:count1
if a:visual
normal! gv
endif

" Hack to update the jump list so CTRL-o jumps back to the right place
normal! m`

" Patterns to match section/chapter/...
let s:re_sec = '\v^\s*\\%(%(sub)?paragraph|%(sub)*section|chapter|part|'
\ . 'appendi%(x|ces)|%(front|back|main)matter)>'
let s:re_sec_t1 = '\v%(' . s:re_sec . '|^\s*%(\\end\{document\}|%$))'
let s:re_sec_t2 = '\v%(' . s:re_sec . '|^\s*\\end\{document\})'
" Search for math environment group delimiters
let l:re = g:vimtex#re#not_comment . (a:begin
\ ? '%((\\\[)|(\\\()|(\\begin\s*\{)|(\$\$)|(\$))'
\ : '%((\\\])|(\\\))|(\\end\s*\{)|(\$\$)|(\$))')

" The p flag is key here and is used to specify for search to return the sub
" group that matches
let l:flags = 'Wp' . (a:backwards ? 'b' : '')

for l:_ in range(l:count)
let l:success = 0

" Iterate a maximum of 6 times to ensure we are not going into an infinite
" loop. The number 6 is arbitrary, but typically good enough to find the
" math zone in the text currently visible in the window.
let l:iter = 0
while l:iter <= 5
let l:iter += 1
let l:submatch = search(l:re, l:flags)
let l:pos = vimtex#pos#get_cursor()

if l:submatch == 0 | break | endif

" Jump directly to \[, \], \(, \)
if l:submatch < 4
let l:success = 1
break
endif

if l:submatch == 4
" The target position is inside a \begin ... \end delimited math
" environment, where the syntax group is properly recognized on both
" sides.
if vimtex#syntax#in_mathzone(l:pos[1], l:pos[2])
let l:success = 1
break
endif
else
" The target position is inside a $ ... $ or $$ ... $$ based math zone.
" In this case, the beginning delimiter is syntax matched as a math
" zone, whereas the ending delimiter is not.
if a:begin
if vimtex#syntax#in_mathzone(l:pos[1], l:pos[2])
let l:success = 1
break
endif
else
" First check that the current search position is at least 2 columns
" left from the initial position and not in mathzone already. The
" check works because only opening $ and $$ are in mathzone, not
" the closing ones.
if vimtex#syntax#in_mathzone(l:pos[1], l:pos[2])
\ || vimtex#pos#val(l:curpos_saved) - vimtex#pos#val(l:pos) == 1
continue
endif

" Now check if previous position is inside a mathzone or not
let l:pos = vimtex#pos#prev(vimtex#pos#prev(l:pos))
if vimtex#syntax#in_mathzone(l:pos[1], l:pos[2])
let l:success = 1
break
endif
endif
endif
endwhile

" If a math group delimiter is found, update the saved cursor position. If
" not, then we restore the last saved position and quit. This ensures that
" we achieve the expected behaviour with the jumps with counts.
if l:success
let l:curpos_saved = vimtex#pos#get_cursor()
else
call vimtex#pos#set_cursor(l:curpos_saved)
break
endif
endfor
endfunction

" }}}1
21 changes: 21 additions & 0 deletions doc/vimtex.txt
Expand Up @@ -171,6 +171,7 @@ FEATURE OVERVIEW *vimtex-features*
- Motions
- Move between section boundaries with `[[`, `[]`, `][`, and `]]`
- Move between environment boundaries with `[m`, `[M`, `]m`, and `]M`
- Move between math environment boundaries with `[n`, `[N`, `]n`, and `]N`
- Move between comment boundaries with `[*` and `]*`
- Move between matching delimiters with `%`
- Text objects
Expand Down Expand Up @@ -780,6 +781,10 @@ This feature is explained in more detail later, see |vimtex-imaps|.
]M |<plug>(vimtex-]M)| `nxo`
[m |<plug>(vimtex-[m)| `nxo`
[M |<plug>(vimtex-[M)| `nxo`
]n |<plug>(vimtex-]n)| `nxo`
]N |<plug>(vimtex-]N)| `nxo`
[n |<plug>(vimtex-[n)| `nxo`
[N |<plug>(vimtex-[N)| `nxo`
]/ |<plug>(vimtex-]/| `nxo`
]* |<plug>(vimtex-]star| `nxo`
[/ |<plug>(vimtex-[/| `nxo`
Expand Down Expand Up @@ -2927,6 +2932,22 @@ object, see |text-objects|.
go to [count] previous end of an environment `\end`.
|exlusive| motion.

*<plug>(vimtex-]n)*
go to [count] next start of a math zone.
|exlusive| motion.

*<plug>(vimtex-]N)*
go to [count] next end of a math zone.
|exlusive| motion.

*<plug>(vimtex-[n)*
go to [count] previous start of a math zone.
|exlusive| motion.

*<plug>(vimtex-[N)*
go to [count] previous end of a math zone.
|exlusive| motion.

*<plug>(vimtex-]/)*
go to [count] next start of a LaTeX comment "%".
|exlusive| motion.
Expand Down
31 changes: 31 additions & 0 deletions test/tests/test-motions/test-math-motions.tex
@@ -0,0 +1,31 @@
\documentclass[]{article}

\begin{document}

Test file for math motion.

\begin{equation} % [n[n or 2[n
1 + 1 = 2
\end{equation} % [N[N or 2[N

\( 1 + 1 = 2 \) % Opening: [n, Closing: [N

\begin{enumerate}
\item 1
\item 2
\item 3
\item 4
\end{enumerate}
% CURSOR
\[ % ]n
1 + 1 = 2
\] % ]N

\begin{itemize}
\item $1 + 1 = 2$. % Opening: ]n]n or 2]n, Closing: ]N]N or 2]N
\end{itemize}

$1 + 1 = 2$ % Opening: ]n]n]n or 3]n, Closing: ]N]N]N or 3]N
$$1 + 1 = 2$$ % Opening: ]n]n]n]n or 4]n, Closing: ]N]N]N]N or 4]N

\end{document}
26 changes: 26 additions & 0 deletions test/tests/test-motions/test-math-motions.vim
@@ -0,0 +1,26 @@
set nocompatible
let &rtp = '../../..,' . &rtp
filetype plugin on
syntax on

nnoremap q :qall!<cr>
silent edit test-math-motions.tex

if empty($INMAKE) | finish | endif

" vint: -ProhibitCommandRelyOnUser

normal 19G2]n
call vimtex#test#assert_equal(line('.'), 25)

normal 19G]N
call vimtex#test#assert_equal(line('.'), 22)

normal 19G2[n
call vimtex#test#assert_equal(line('.'), 7)

normal 19G[N
call vimtex#test#assert_equal(line('.'), 11)

quit!

0 comments on commit 61dcffc

Please sign in to comment.