This question is largely addressing some follow-up questions OP had in response to an answer I gave to another question that they posted. This one also talks about a solution involving lambda functions and closures that is valid but more complicated than needed for most use cases. For both those reasons I recommend you check out that Q&A first: How to start an async function in Vim 8 . If you want a little extra insight about the topic (and some peripheral topics) you can, of course, come back to this one afterwards.
I'm wondering why you are using a FuncRef in the original code to make the call to CustomGrepCore()
. There doesn't seem to be an explicit need for it. In the new code it makes things complicated because you have to deal with, in effect, two levels of indirection (FuncRef + lambda) instead of just one for the lambda.
So my first suggestion is to use something like call CustomGrepCore(param)
before even touching the timer stuff.
Regardless of the above, though, let's look at the second argument to timer_start. This stuff is tricky, no doubt, but you really strayed from what I wrote. There are three mismatches between what you have
{param -> execute("call F(param)")}
and what I have
{-> execute("call LongRunningFun('" . a:patt . "')", "")}
First, where did the param
preceding ->
come from? Toss it.
Second, I am passing two args to execute and you are passing one. You may understand what this means and have done so intentionally but just in case that's not the case... Leaving off the second param is equivalent to running the Ex commands in the first argument with :silent
. Passing empty string as second param is equivalent to running them without :silent
. When first coding this up I personally wouldn't use silent. After things are working I might add it.
Finally, your first param is a single, static string while mine is a concatenation of two static strings and an expression (a:patt
). While you intend to pass the value(s) contained in the local variable param
to execute()
what you're actually doing is passing the literal string "param". This applies to anything contained in quotes.
Anyways, to avoid dragging this out I'll show you what I would do. First, I wouldn't use -nargs=?
in your command. I would use +
or *
in place of ?
depending on whether zero args (:Grep
) is a valid call. This will then put each argument in a separate slot in the params list. Also, don't use varargs (...
) unless you really need them. They add a level of indirection that makes things tricky. Maybe impossible in this particular case. So change CustomGrepCore so it accepts a single argument which will be a list. Here's a demonstration of how it could work once we get the timer_start
part right.
function! CustomGrepCore(args) abort
if len(a:args)
echom "First item in args list is " . a:args[0]
endif
endfunction
command! -nargs=* Grep :call CustomGrep(<f-args>)
Grep hello " prints 'First item in args list is hello'
With all that we can get something that works...
" You can't use string(a:000) directly in param expression. Not yet sure why.
let arglist = string(a:000)
call timer_start(50, { -> execute("call CustomGrepCore(" . arglist . ")", "")})
A non-obvious part of this is the need to pass the varags list, a:000
, through string()
. execute()
takes as its first param a string (which it evaluates as an expression). You can't concatenate a string and a list. You'll get an error if you try. So we need to convert to a string representation of the list and then concatenate.
Additional note:
Barring an explicit need to do otherwise, strongly consider adding "abort" at the end of your function signatures so your functions "fail fast" rather continue on even when a command within fails.
Update: OP asked if it's possible to keep varargs in CustomGrepCore()
. This poses some challenges but ... But nothing. Read the update to the answer referenced in the first paragraph of this question. The method there is the simplest path to handling the use case of passing varargs from the caller of timer_start to the callback function invoked by the timer when that function also takes varargs.
call CustomGrepCore(param)
in thetimer_start
so that I can make the grep async. Butcall timer_start(10, {param -> execute("call CustomGrepCore(param)")}, "")
will generate an error. It doesn't seem thatparam
can be passed into the lambda correctly.