1

Is it possible to do this:

function Foo(func_ref) abort
  nnoremap <buffer> <cr> :find_a_way_to_call_func_ref
endfunction

function s:callback() abort
  echo "123"
endfunction

command Test call Foo(function('s:callback'))

To be clear, :h Funcref is not String, it's type is v:t_func.

  • 1
    I like the question! But you might still want to clarify it. What kind of func_ref are you passing? A string with a function name? Or a lambda? I can see how the latter is a lot more challenging... – filbranden Apr 17 at 11:43
  • @filbranden v:t_func – dedowsdi Apr 17 at 11:56
  • 1
    Yes, in fact :help Funcref talks more about that and mentions lambdas explicitly... What I'm saying is that you might want to clarify your question with an example call of Foo (perhaps with a lambda?) to make it absolutely clear what you're trying to accomplish... As it stands, your question is likely to attract incorrect answers (such as the one with execute), which might not be the case if you had an example call that would make it clear that wouldn't have worked. – filbranden Apr 17 at 12:24
  • 1
    @filbranden I thought it should be clear as I use capital Funcref, but you are right, it's still confusing, I clarified it in the update. – dedowsdi Apr 17 at 12:32
  • Awesome! Looks pretty clear now! (Unfortunately I don't know that this is actually possible to do...) – filbranden Apr 17 at 12:43
3

It's really doable with execute... and call()

function Foo(func_ref) abort
    execute printf('nnoremap <buffer><cr> :call call(%s, [])<CR>',
        \ string(get(a:func_ref, 'name')))
 endfunction
| improve this answer | |
  • Interesting, when closures were added to vim? – Maxim Kim Apr 17 at 17:49
  • 1
    @MaximKim Around the time the lambdas were added (version 7.4.xxxx). – Matt Apr 17 at 18:07
  • Wow, don't know how did I miss it! Thx! – Maxim Kim Apr 17 at 18:09
  • @dedowsdi Ah, sorry. So you need execute with proper quotes... – Matt Apr 18 at 5:11
  • 1
    @dedowsdi Yes, but you must ensure that lambda won't be dropped by Vim by that moment (i.e. storing Funcref keeps a referred function/lambda alive, but storing just a name string, obviously, does not). – Matt Apr 18 at 7:05
2

I don't think it is possible to directly call this funcref from within mapping -- by the time you press mapped key funcref is already disposed/freed/unavailable.

You can try to persist funcref though (either in global or script local dict):

let g:my_funcs={}

function! Foo(func_ref) abort
    let g:my_funcs.f = a:func_ref
    nnoremap <buffer> <cr> :call g:my_funcs.f()<CR>
endfunction

function! Callback() abort
  echom "123"
endfunction

command! Test call Foo(function('Callback'))

enter image description here

It is not necessary to use dictionary, you can have regular global var I think. But for the dictionary you can come up with some clever things, like having array of functions, caching or whatever else.

| improve this answer | |
  • 1
    Thanks, this should work. Will accept if no other better answer in a few days. – dedowsdi Apr 17 at 13:46
  • 1
    Similar to my approach github.com/benknoble/vim-simpl – D. Ben Knoble Apr 17 at 13:54
  • 1
    @D.BenKnoble simpl#register? indeed! – Maxim Kim Apr 17 at 14:07
  • Confirmed that this works with a function(...) including arguments, with a lambda and with a funcref() for a function that has been overwritten. Good job! – filbranden Apr 17 at 14:30
  • @dedowsdi glad I could help you – Maxim Kim Apr 17 at 14:55
0

Sure, you want to use the execute command, which acts like an eval:

function Foo(func_ref) abort
  exe"nnoremap <buffer> <cr> :".func_ref."()<cr>"
endfunction
| improve this answer | |

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.