I'm writing a lot of LaTeX recently, and am used to hitting the $ key for inline math, but have heard that \( and \) is better (apparently it confuses vim less, especially with $$, which I'm also not using).

However, \( and \) are hard to type quickly—$ is effectively one keystroke. Can I make $ insert the appropriate opening and closing brackets at once?


Yes you can.

The preliminaries: we will need to determine if the last opening \( comes before or after the last closing \) in order to determine which delimiter to insert. (This also means we don't have a literal mapping—we are going to use <expr> instead.) So I created ~/.vim/autoload/pos.vim with the following contents:

" true iff p1 is before p2 in the buffer
" a pos is [lnum, col]
function! pos#before(p1, p2) abort
  return a:p1[0] < a:p2[0] || (a:p1[0] == a:p2[0] && a:p1[1] <= a:p2[1])

Then, we need to write our function to return the correct delimiter. In ~/.vim/autoload/tex.vim:

function! tex#inline() abort
  let l:open_pattern = '\m\\('
  let l:close_pattern = '\m\\)'
  let l:open_pos = searchpos(l:open_pattern, 'bn')
  let l:close_pos = searchpos(l:close_pattern, 'bn')
  if pos#before(l:open_pos, l:close_pos)
    return '\('
    return '\)'

Finally, we want this to take effect. In ~/.vim/after/ftplugin/tex.vim:

inoremap <buffer> <expr> $ tex#inline()

(Make sure to add the unmapping of $ to b:undo_ftplugin by whatever mechanism you use.)

| improve this answer | |
  • 1
    Note that this is the kind of use case which fits pretty well a snippet engine. And as a snippet engine might be a bit overkill for only one snippet I would have tried an abbreviation like this one: inoremap <buffer> $ \(\)<left><left> – statox Jun 26 '19 at 13:40
  • 1
    @statox agreed; I’m not a big fan of snippets (yet? We shall see...). But I prefer to type the delimiters myself. – D. Ben Knoble Jun 26 '19 at 13:41

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