Скрипт Vim: как передать Varargs в лямбду в timer_start
Я настраиваю свой vim с помощью Vim Script.
Я установил свою собственную функцию grep, как показано ниже:
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>)
Приведенный выше код работал, как ожидалось. Я могу выполнить :Grep xxxсвой собственный grep в моем vim.
Теперь я хочу использовать функцию vim 8: timer_startчтобы сделать мой собственный grep async, насколько я понимаю, мне нужно только перекодировать функцию 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
Но я всегда получал ошибку: Invalid argument.
Как это исправить?
Кроме того, как вы видите, я использовал timer_start, я знал, что он сгенерирует идентификатор задания, который позволяет нам приостановить / остановить задание. В моем случае нужно ли останавливать работу явно?
Добавить еще одну вещь
Есть еще одна проблема: как передать varargs из лямбды в функции CustomGrepв функцию CustomGrepCore.
Я нашел эту ссылку: https://www.reddit.com/r/vim/comments/3761po/vimscript_question_passing_arguments_from_a/
Но у нас здесь другой случай. Поскольку мы использовали execute()в функции CustomGrep. Так можно ли передать varargs из execute()функции CustomGrepCore? Параметр execute()- это строка, но если мы преобразуем ее a:000в string ( string(a:000)), мне придется изменить функцию CustomGrepCore, потому что, если я перейду string(a:000)к CustomGrepCore, значение a:0всегда будет 1.
Так можно ли передать varargs из execute()другой функции? Если нет, то я должен изменить функцию CustomGrepCore.
Ответы
Этот вопрос в основном касается некоторых последующих вопросов, которые OP имел в ответ на ответ, который я дал на другой вопрос, который они разместили. Здесь также говорится о решении, включающем лямбда-функции и замыкания, которое является допустимым, но более сложным, чем необходимо для большинства случаев использования. По обеим этим причинам я рекомендую вам сначала ознакомиться с этими вопросами и ответами: Как запустить асинхронную функцию в Vim 8 . Если вам нужно немного больше узнать о теме (и некоторых второстепенных темах), вы, конечно, можете вернуться к этой теме позже.
Мне интересно, почему вы используете FuncRef в исходном коде для вызова CustomGrepCore(). Похоже, в этом нет явной необходимости. В новом коде это усложняет задачу, потому что вам, по сути, приходится иметь дело с двумя уровнями косвенного обращения (FuncRef + lambda) вместо одного для лямбда.
Итак, мое первое предложение - использовать что-то подобное, call CustomGrepCore(param)прежде чем даже касаться таймера.
Однако, несмотря на вышесказанное, давайте посмотрим на второй аргумент timer_start. Без сомнения, это непросто, но вы действительно отклонились от того, что я написал. Есть три несоответствия между тем, что у вас есть
{param -> execute("call F(param)")}
и что у меня есть
{-> execute("call LongRunningFun('" . a:patt . "')", "")}
Во-первых, откуда взялось paramпредыдущее ->? Брось это.
Во-вторых, я передаю два аргумента для выполнения, а вы передаете один. Вы можете понять, что это означает, и сделали это намеренно, но на всякий случай это не так ... Отключение второго параметра эквивалентно запуску команд Ex в первом аргументе с :silent. Передача пустой строки в качестве второго параметра эквивалентна их запуску без :silent . При первом кодировании я лично не стал бы использовать молчание. После того, как все заработает, я могу добавить его.
Наконец, ваш первый параметр - это одна статическая строка, а мой - это объединение двух статических строк и выражения ( a:patt). Пока вы намереваетесь передать значение (я), содержащееся в локальной переменной, paramв execute()то, что вы на самом деле делаете, - это передача буквальной строки «param». Это касается всего, что содержится в кавычках.
В любом случае, чтобы не затягивать с этим, я покажу вам, что я буду делать. Во-первых, я бы не стал использовать -nargs=?в вашей команде. Я бы использовал +или *вместо, в ?зависимости от того, является ли нулевой args ( :Grep) допустимым вызовом. Затем каждый аргумент будет помещен в отдельный слот в списке параметров. Кроме того, не используйте varargs ( ...), если они вам действительно не нужны. Они добавляют уровень косвенности, что усложняет задачу. Возможно, в данном случае это невозможно. Поэтому измените CustomGrepCore, чтобы он принимал единственный аргумент, который будет списком. Вот демонстрация того, как это может работать, если мы сделаем все timer_startправильно.
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'
Со всем этим мы можем получить что-то, что работает ...
" 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:000через string(). execute()принимает в качестве первого параметра строку (которая оценивается как выражение). Вы не можете объединить строку и список. Вы получите сообщение об ошибке, если попытаетесь. Поэтому нам нужно преобразовать список в строковое представление, а затем объединить.
Дополнительное примечание: исключая явную необходимость поступить иначе, настоятельно рекомендуется добавить слово «прерывание» в конце сигнатуры функций, чтобы ваши функции «быстро выходили из строя», а не продолжали работать, даже если внутренняя команда не работает.
Обновление: OP спросил, можно ли сохранить varargs CustomGrepCore(). Это создает некоторые проблемы, но ... Но ничего. Прочтите обновление ответа, указанного в первом абзаце этого вопроса. Этот метод представляет собой самый простой способ обработки варианта использования передачи varargs от вызывающего timer_start функции обратного вызова, вызываемой таймером, когда эта функция также принимает varargs.