Повторить последнюю команду Vim/Neovim, включая привязки клавиш
Узнайте, как сделать повторяемыми команды Vim/Neovim, включая привязки клавиш, с помощью операторных привязок и плагинов для повторения команд Lspsaga.
В Vim/Neovim, как можно повторить последнюю команду, включая те, которые выполнены привязками клавиш?
Я настроил привязку клавиш для «перейти к определению» с помощью Lspsaga:
vim.keymap.set("n", "<leader>gd", ":Lspsaga goto_definition<Enter>", { desc = "Lspsaga goto definition" })
Часто мне нужно следовать цепочкам определений, что требует многократного нажатия этой привязки. Это неэффективно. Я знаю, что обычная команда повторения (.) работает для обычных команд, как dd, но не работает для привязанных клавиш.
Я пробовал использовать @: для повторения последней командной строки, но появляется сообщение «no previous command line», потому что команды, выполненные через привязки клавиш, не записываются в историю команд.
Есть ли способ повторить последнюю команду, включая те, которые выполнены привязками клавиш? Есть ли плагины Neovim, которые могут помочь с этой функциональностью?
Я мог бы создать более эффективные привязки клавиш, но меня интересует общий метод повторения команд, который сэкономил бы привязки для других целей.
Точка (.) в Vim/Neovim предназначена для повторения последнего изменения, но по умолчанию она работает только с встроенными командами, которые изменяют содержимое буфера. Команды, привязанные к клавишам, как ваш Lspsaga goto definition, не становятся автоматически повторяемыми точкой, потому что они обычно выполняются через команды Ex или сложные маппинги, а не простыми операциями Vim. Тем не менее, существует несколько способов сделать привязанные к клавишам команды повторяемыми.
Содержание
- Понимание ограничения оператора точки
- Маппинги в режиме оператора для повторения точкой
- Плагины для расширенной функциональности повторения
- Решения на основе макросов
- Пользовательские настройки
- Практическая реализация для вашего случая
Понимание ограничения оператора точки
Оператор точки (.) в Vim работает, записывая последнее изменение и воспроизводя его. Согласно блогу Викаса Раджбанши о повторении точкой, «по умолчанию повторение точкой работает только для действий, которые изменяют содержимое буфера, таких как вставка, удаление, замена текста и т.д.» Это ограничение существует, потому что механизм повторения Vim ориентирован на текстовые операции, а не на произвольное выполнение команд.
Когда вы используете привязку клавиш, например <leader>gd, которая выполняет :Lspsaga goto_definition, команда не записывается как изменение, а как команда Ex. Поэтому @: показывает «нет предыдущей команды», поскольку привязанные к клавишам команды обычно не попадают в историю команд так, чтобы их можно было повторить.
Маппинги в режиме оператора для повторения точкой
Самое надёжное решение – преобразовать вашу привязку в режим оператора, что делает её совместимой с точкой. Согласно документации Neovim, «Повторить последнее изменение, заменив счётчик на [count]. Также повторяет команду yank, когда флаг ‘y’ включён в ‘cpoptions’».
Чтобы сделать Lspsaga goto definition повторяемым точкой, можно создать маппинг в режиме оператора:
vim.keymap.set("n", "<leader>gd", function()
vim.go.operatorfunc = "v:lua.GotoDefinition"
return "g@"
end, { expr = true, desc = "Lspsaga goto definition" })
vim.keymap.set("o", "gd", "<cmd>lua GotoDefinition()<CR>", { desc = "Lspsaga goto definition" })
Затем определите функцию:
function GotoDefinition()
vim.cmd("Lspsaga goto_definition")
end
Этот подход рассматривает вашу команду как оператор, делая её совместимой с точкой.
Плагины для расширенной функциональности повторения
Несколько плагинов расширяют возможности повторения Vim:
cyclops.vim
Плагин cyclops.vim предоставляет «повторение точкой (.) или парой (;,) для любой карты или оператора». Он фактически делает любую привязку повторяемой, оборачивая её в специальную структуру.
smart‑repeat.nvim
Другой популярный вариант – smart-repeat.nvim, который позволяет повторять через привязки, сохраняя историю выполненных команд.
dot‑repeat.nvim
Этот плагин специально ориентирован на совместимость пользовательских привязок с оператором точки, предоставляя фреймворк для отслеживания и повторения произвольных команд.
Решения на основе макросов
Традиционные макросы Vim предлагают ещё один подход. Как упоминалось в обсуждениях на Super User, вы можете «записать и повторно выполнить последний макрос с @@».
Для вашего случая можно:
- Записать макрос командой
qq<L>gdq(где<L>– ваш лидирующий ключ) - Выполнить его командой
@q - Повторить с
@@
Однако этот подход имеет ограничения, как отмечено в обсуждениях на Reddit: «Точка отлична, но иногда приходится подбирать команду так, чтобы она была повторяемой. Макросы такие же. Я не думаю, что можно получить идеальную повторяемость бесплатно».
Пользовательские настройки
Вы можете создать собственное решение, используя operatorfunc и функциональность repeat. Согласно примерам на GitHub, вы можете манипулировать механизмом повторения, чтобы «принудительно» задать, какое действие будет повторено.
Ниже приведена полная реализация:
-- Отслеживаем последнюю выполненную команду
_G.last_command = ""
-- Функция для выполнения и сохранения команды
function ExecuteAndStore(cmd)
_G.last_command = cmd
vim.cmd(cmd)
end
-- Функция для повторения последней команды
function RepeatLastCommand()
if _G.last_command ~= "" then
vim.cmd(_G.last_command)
else
print("No command to repeat")
end
end
-- Настройка привязки, сохраняющей команду
vim.keymap.set("n", "<leader>gd", function()
ExecuteAndStore("Lspsaga goto_definition")
end, { desc = "Lspsaga goto definition" })
-- Настройка клавиши повторения (можно привязать к . или другой клавише)
vim.keymap.set("n", "<leader>.", RepeatLastCommand, { desc = "Repeat last command" })
Практическая реализация для вашего случая
Для вашего конкретного требования – перехода по цепочке определений – я рекомендую подход с режимом оператора, поскольку он наиболее «нативный» и эффективный:
-- Настройка маппинга в режиме оператора для Lspsaga goto definition
vim.keymap.set("n", "gd", function()
vim.go.operatorfunc = "v:lua.GotoDefinition"
return "g@"
end, { expr = true, desc = "Goto definition with dot repeat" })
-- Определяем функцию оператора
function GotoDefinition()
vim.cmd("Lspsaga goto_definition")
end
-- Также добавляем поддержку визуального режима
vim.keymap.set("v", "gd", "<Esc><cmd>Lspsaga goto_definition<CR>", { desc = "Goto definition from visual" })
С этой настройкой:
- Используйте
gdодин раз, чтобы перейти к определению - Используйте
.для повторения перехода к определению - Используйте
5gd, чтобы пройти через 5 определений подряд - Работает с счётчиками и визуальным выделением
Этот подход эффективен, потому что использует нативный механизм повторения Vim, а не требует дополнительных привязок или плагинов. Решение также более общее и может применяться к другим командам, которые вы хотите сделать повторяемыми.
Источники
- Dot‑Repeat in Vim and Neovim – Vikas Rajbanshi
- A basic overview of how to manage dot‑repeating in your Neovim plugin – GitHub Gist
- Repeat – Neovim Documentation
- Repeat last normal mode command, including moves, in Vim – Super User
- cyclops.vim – GitHub
- How to repeat the last change in Vim – Vim From Scratch
- Is there an easy way to repeat previous command? – Reddit/r/vim
Заключение
- Оператор точки (
.) в Vim/Neovim по умолчанию не работает с привязанными к клавишам командами, потому что он предназначен для текстовых операций. - Маппинги в режиме оператора предоставляют самое элегантное решение, делая команды совместимыми с нативным механизмом повторения.
- Плагины, такие как cyclops.vim, предлагают альтернативы для повторения произвольных привязок.
- Пользовательские решения с
operatorfuncи отслеживанием команд дают гибкость для сложных рабочих процессов. - Для вашего случая с
Lspsaga goto definitionпреобразование в режим оператора сgdи.является самым эффективным подходом.