Script de Vim: Cómo pasar Varargs a una lambda en timer_start
Estoy configurando mi vim con Vim Script.
He configurado mi propia función grep personalizada de la siguiente manera:
function! CustomGrepCore(...)
if a:0 == 0
" do something
else if a:0 == 1
" do something2
else
" do something3
endfunction
function! CustomGrep(...)
let param = a:000
let F = {p -> call(function('CustomGrepCore'), p)}
call F(param)
endfunction
command! -nargs=? Grep :call CustomGrep(<f-args>)
El código anterior funcionó como se esperaba. Puedo ejecutar :Grep xxxpara hacer mi grep personalizado en mi vim.
Ahora quiero usar la función vim 8: timer_startpara hacer mi grep personalizado asíncrono, según tengo entendido, solo necesito volver a codificar la función CustomGrep:
function! CustomGrep(...)
let param = a:000
let F = {p -> call(function('CustomGrepCore'), p)}
call timer_start(10, {param -> execute("call F(param)")}, "") " ERROR: Invalid argument
endfunction
Pero siempre me dieron el error: Invalid argument.
¿Cómo arreglar esto?
Además, como ve, usé el timer_start, sabía que generaría una identificación de trabajo, lo que nos permite pausar / detener el trabajo. En mi caso, ¿necesito detener el trabajo explícitamente?
Agrega una cosa más
Hay un problema más: cómo pasar los varargs de lambda en la función CustomGrepa la función CustomGrepCore.
Encontré este enlace: https://www.reddit.com/r/vim/comments/3761po/vimscript_question_passing_arguments_from_a/
Pero tenemos un caso diferente aquí. Porque usamos execute()en la función CustomGrep. Entonces, ¿es posible pasar varargs de execute()a la función CustomGrepCore? El parámetro de execute()es una cadena, pero si lo convertimos a:000a cadena ( string(a:000)), tengo que cambiar la función CustomGrepCore, porque si paso string(a:000)a CustomGrepCore, el valor de a:0siempre será 1.
Entonces, ¿es posible pasar los varargs de la execute()función a otra? Si no, bueno, tengo que cambiar la función CustomGrepCore.
Respuestas
Esta pregunta aborda en gran medida algunas preguntas de seguimiento que OP tuvo en respuesta a una respuesta que di a otra pregunta que publicaron. Este también habla de una solución que involucra funciones lambda y cierres que es válida pero más complicada de lo necesario para la mayoría de los casos de uso. Por ambas razones, le recomiendo que consulte primero las preguntas y respuestas: Cómo iniciar una función asíncrona en Vim 8 . Si desea un poco más de información adicional sobre el tema (y algunos temas periféricos), por supuesto, puede volver a este después.
Me pregunto por qué está utilizando un FuncRef en el código original para realizar la llamada a CustomGrepCore(). No parece haber una necesidad explícita para ello. En el nuevo código, las cosas se complican porque tienes que lidiar, en efecto, con dos niveles de indirección (FuncRef + lambda) en lugar de solo uno para el lambda.
Entonces, mi primera sugerencia es usar algo como call CustomGrepCore(param)antes incluso de tocar el temporizador.
Sin embargo, independientemente de lo anterior, veamos el segundo argumento de timer_start. Esto es complicado, sin duda, pero realmente te desviaste de lo que escribí. Hay tres desajustes entre lo que tienes
{param -> execute("call F(param)")}
y lo que tengo
{-> execute("call LongRunningFun('" . a:patt . "')", "")}
Primero, ¿de dónde vino lo paramanterior ->? Echarlo.
En segundo lugar, estoy pasando dos argumentos para ejecutar y tú estás pasando uno. Puede entender lo que esto significa y haberlo hecho intencionalmente, pero en caso de que ese no sea el caso ... Dejar el segundo parámetro es equivalente a ejecutar los comandos Ex en el primer argumento con :silent. Pasar una cadena vacía como segundo parámetro es equivalente a ejecutarlos sin :silent . Al codificar esto por primera vez, personalmente no usaría el silencio. Después de que las cosas funcionen, podría agregarlo.
Finalmente, su primer parámetro es una única cadena estática , mientras que el mío es una concatenación de dos cadenas estáticas y una expresión ( a:patt). Mientras que la intención de pasar el valor (s) contenido en la variable local parama execute()lo que en realidad está haciendo es pasar la cadena literal "parámetro". Esto se aplica a todo lo que esté entre comillas.
De todos modos, para evitar alargar esto, les mostraré lo que haría. Primero, no lo usaría -nargs=?en su comando. Usaría +o *en lugar de ?dependiendo de si zero args ( :Grep) es una llamada válida. Esto luego colocará cada argumento en un espacio separado en la lista de parámetros. Además, no use varargs ( ...) a menos que realmente los necesite. Añaden un nivel de indirecta que complica las cosas. Quizás imposible en este caso particular. Así que cambie CustomGrepCore para que acepte un solo argumento que será una lista. Aquí hay una demostración de cómo podría funcionar una vez que tengamos la timer_startpieza correcta.
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'
Con todo eso podemos conseguir algo que funcione ...
" 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 . ")", "")})
Una parte no obvia de esto es la necesidad de pasar la lista varags, a:000, a través de string(). execute()toma como primer parámetro una cadena (que evalúa como expresión). No puede concatenar una cadena y una lista. Obtendrá un error si lo intenta. Por lo tanto, debemos convertir la lista en una representación de cadena y luego concatenar.
Nota adicional: salvo que exista una necesidad explícita de hacer lo contrario, considere seriamente agregar "abortar" al final de las firmas de sus funciones para que sus funciones "fallen rápidamente" en lugar de continuar incluso cuando un comando interno falle.
Actualización: OP preguntó si es posible mantener varargs adentro CustomGrepCore(). Esto plantea algunos desafíos pero ... Pero nada. Lea la actualización de la respuesta a la que se hace referencia en el primer párrafo de esta pregunta. El método es el camino más simple para manejar el caso de uso de pasar varargs del llamador de timer_start a la función de devolución de llamada invocada por el temporizador cuando esa función también toma varargs.