How can I create mappings that work only if the cursor is enclosed by certain characters?

As an example, I want to map ja to \alpha if the cursor is enclosed by $$ or by \[\] so as to make

This jar has an $ja$ engraved on it.

be remapped to

This jar has an $\alpha$ engraved on it.

instead of

This \alphar has an $\alpha$ engraved on it.

So, how can I ask vim to check if the cursor is enclosed by $$'s and \[\]'s?

An immediate complication that I can think of is the following:

The jar has an $ja$ engraved on it (note: $ja$ is a greek character).

That is, the check should work just like vim does for checking unclosed parentheses, where there may be several closed ones back too. (Or like LaTeX for checking mathmode). This would be done only on the current line, for scalability.

Here is a scheme of how I would do it for $$ (if only I knew how to actually check these using vim syntax):

  1. Take position of cursor;
  2. Scan to the left for the number of $'s;
  3. Do the same for the right;
  4. If these are even, work as if in normal text (e.g. A jar becomes A jar)
  5. If these are odd, do the mappings (e.g. A jar becomes A \alphar)

For \[\] I think it could be done in the same way as a check for balanced paratheses...

Since, this question is asked having LaTeX in mind, is there a shortcut when using vim-latex, vim-tex, or other TeX plugins for vim?


This is actually a complicated matter. $...$ is a difficult construct to parse, because it has no clear beginning or end. That is, in math here? $ or math here? $ math here?, how do you know which region is really a math region? You need to search further back to know.

However, there are some possibilities. In particular, I would take advantage of the fact that there is already a good syntax file for LaTeX that detects these inner match structures. Thus, you can check if you are inside a math environment by looking at the syntax groups. I've implemented this in vimtex. The solution is basically as follows:

imap <silent><expr> ja MyFunc('ja', '\alpha')

function! MyFunc(lhs, rhs) abort
  return s:is_math() ? a:rhs : a:lhs

function! s:is_math() abort
  return match(map(synstack(line('.'), max([col('.') - 1, 1])),
        \ 'synIDattr(v:val, ''name'')'), '^texMathZone[A-Z]S\?$') >= 0

If you want to use vimtex directly, than you can read :help vimtex-imaps. There are already a bunch of such mappings defined by default. See VIMTEX/autoload/vimtex.vim for the complete list.

You may also add your own, custom maps, see :help vimtex#imaps#add_map.

  • 1
    Thank you so much! It worked like a charm! (Also, you convinced me to switch from latex-suite to vimtex. Really, thank you!) – Théo de Oliveira Santos Dec 2 '18 at 18:40
  • No problem, happy to help :) – Karl Yngve Lervåg Dec 3 '18 at 7:57

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.