Working on a more lightweight vim-endwise replacement (endwise breaks imap <expr> <CR> mappings).

In the following text:

def name<-cursor here

it adds a newline and another line with an end:

def name
  <-cursor here

This mapping does the job:

autocmd FileType ruby inoremap <buffer> <expr> <CR> match(getline('.'), 'def') >= 0 ? '<CR><CR>end<Esc>-cc' : '<CR>'

However, I'd like to extend this to also understand if, case and do, but the following does not work:

autocmd FileType ruby inoremap <buffer> <expr> <CR> match(getline('.'), 'def\|if\|case\|do') >= 0 ? '<CR><CR>end<Esc>-cc' : '<CR>'

Even though match(" case abc", 'def\|if\|case\|do') >= 0 returns 1 (e.g. true).

What am I doing wrong?

  • Extracting to a function does not make things easier: '\<CR>' returns text, not a newline, an unescaped '<CR>' errors out, even though match seems to work properly. Nov 9 '16 at 13:31
  • 2
    Do yourself a favor and put that into a normal ftplugin and simply call a function for that filetype. Then you don't have to worry about escaping all possible metachars and even better 2 days from today, you still understand what you are doing ;) Nov 9 '16 at 17:13
  • @ChristianBrabandt moved everything to a ftplugin, but still have to double escape | Nov 9 '16 at 20:27
  • @philpirozhkov There is another advice in Christian's comment: define a function. This way your regex will be simplified, and much easier to maintain. Nov 9 '16 at 22:58
  • @LucHermitte See above, in a function output needs to be escaped, e.g. '<CR>' does not work, nor '\<CR>' does, I've seen people use ""<CR' and '\<lt>CR>', but none of them worked in a function. I'm pretty satisfied with my current solution, will dig deeper when I decide I need to add a closing curly brace when I open one and press CR. Nov 11 '16 at 16:46

IIRC, it's related to the bar character in command lines. You need to escape the bar twice, e.g.:

" ftplugin/ruby/myshortcuts.vim 
inoremap <buffer> <expr> <CR> match(getline('.'), 'if\\|def\\|case\\|do') >= 0 ? '<CR><CR>end<Esc>-cc' : '<CR>

BTW, you should make sure this is exactly def (and not define), that you aren't in string, nor comments.

  • alternatively, you can use <bar>, in place of |, i.e. 'def\<bar>if\<bar>case\<bar>do' worlks
    – nobe4
    Nov 9 '16 at 13:34
  • autocmd FileType ruby inoremap <expr> <CR> match(getline('.'), 'if\\|def\\|case\\|do') >= 0 ? '<CR><CR>end<Esc>-cc' : '<CR> worked as expected, thanks! Nov 9 '16 at 13:35
  • @nobe4 it wouldn't have been enough. A backslash is required when there is no magic around (:h /\v) Nov 9 '16 at 13:42
  • @LucHermitte, Yup, a \<bar> then ? (that's what I've put in the example)
    – nobe4
    Nov 9 '16 at 13:46
  • 1
    @philpirozhkov. Always. Mappings that make sense only in a restrictive list of filetypes/buffers shall always be declared with <buffer>. Otherwise the next time you open a buffer with a different filetype, the mapping will be defined and could be triggered. Which is not what we want. Nov 9 '16 at 22:56

Alternatively, you can use <bar> instead of |:

autocmd FileType ruby inoremap <buffer> <expr> <CR> match(getline('.'), 'def\<bar>if\<bar>case\<bar>do') >= 0 ? '<CR><CR>end<Esc>-cc' : '<CR>'

see: :h <bar>

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.