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. – phil pirozhkov 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 ;) – Christian Brabandt Nov 9 '16 at 17:13
  • @ChristianBrabandt moved everything to a ftplugin, but still have to double escape | – phil pirozhkov 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. – Luc Hermitte 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. – phil pirozhkov 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! – phil pirozhkov 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) – Luc Hermitte 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. – Luc Hermitte 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.