2

I have a remap that calls a function with a key combination as an argument. In this function I want to execute another remap to yet another function. But whatever I do, it complains about a missing closing quote at the iHello... part. This seems to be happening inside s:Test().

This also happens when I replace the literal carriage return (^M) with <cr>, or when I remove the string() function from the s:Test() function.

function! s:Test(obj)
    execute 'inoremap <silent> <tab> <esc>:<c-u>call <sid>Test2('.string(a:obj).')<cr>'
endfunction

function! s:Test2(obj)
    " do something
endfunction

nnoremap <localleader>a :call <sid>Test({'cmd':'Hello^MWorld'})<cr>
  • +1, I am also facing this issue. Replacing with ^M, <CR> didn't work – SibiCoder Jun 4 '16 at 15:47
  • Did you tried giving different quotes like single quotes as the outermost and double quoted for innermost ones? – SibiCoder Jun 4 '16 at 15:49
  • @SibiCoder sadly doesn't work. Also single quotes are already the most literal kind of quotation there is. – hgiesel Jun 4 '16 at 15:52
  • 1
    You can try \r, \<CR>, <lt>CR> as well – SibiCoder Jun 4 '16 at 16:42
  • 1
    Too many levels of indirection, and objects to strings and back to objects. By this point you need to escape the ^M like so: 'Hello^V^V^V^V^V^V^V^V^V^V^MWorld'. Yep, ten ^Vs. Maybe you need to rethink your overall approach. – Antony Jun 6 '16 at 21:03
3

This is doable, though quite complicated. Here's how.

Step 1. Having ^M in a mapping does not work, it is treated as if you hit <CR>, which would execute the :call command which is not yet complete. To make it work we need to replace it with something which is not interpreted as an Enter hit -- e.g. \n:

nnoremap <localleader>a :call <sid>Test({'cmd':"Hello\nWorld"})<cr>

Note: the string has to be double-quoted, otherwise \n is treated as two characters instead of one ^M character.

Step 2. s:Test tries to convert this to a string and pass on to another mapping command. The mapping is successfully created, but when run -- has the same problem as in step 1 above -- object when converted to string has ^M in it, which is treated as a <cr> hit, and executes the :call command. This time we have no luxury of replacing with \n. But we're within a function now and can do other tricks. For example, we can now save this object to some script-variable:

let s:tab_argument = a:obj

This can now be used instead of a:obj in the mapping. Also, since we don't have anything variable now, we can omit execute and define mapping directly:

inoremap <silent> <tab> <esc>:<c-u>call <sid>Test2(<sid>tab_argument)<cr>

Step 3. Unfortunately, :map does not seem to be able to understand script-local variables. Let's work around this by defining another function with no arguments:

function! s:tab_lambda()
  call s:Test2(s:tab_argument)
endfunction

Step 4. Now let's put this all together:

function! s:Test(obj)
    let s:tab_argument = a:obj
    function! s:tab_lambda()
        call s:Test2(s:tab_argument)
    endfunction
    inoremap <silent> <tab> <esc>:<c-u>call <sid>tab_lambda()<cr>
endfunction

function! s:Test2(obj)
    " do something
endfunction

nnoremap <localleader>a :call <sid>Test({'cmd':"Hello\nWorld"})<cr>

Note: I may be stating obvious, but make sure you don't have local <tab> mappings. If you do, inoremap will succeed, but hitting <tab> won't call the Test2 function.

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.