Другое

Повторить последнюю команду Vim/Neovim, включая привязки клавиш

Узнайте, как сделать повторяемыми команды Vim/Neovim, включая привязки клавиш, с помощью операторных привязок и плагинов для повторения команд Lspsaga.

В Vim/Neovim, как можно повторить последнюю команду, включая те, которые выполнены привязками клавиш?

Я настроил привязку клавиш для «перейти к определению» с помощью Lspsaga:

vim
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
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" })

Затем определите функцию:

lua
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, вы можете «записать и повторно выполнить последний макрос с @@».

Для вашего случая можно:

  1. Записать макрос командой qq<L>gdq (где <L> – ваш лидирующий ключ)
  2. Выполнить его командой @q
  3. Повторить с @@

Однако этот подход имеет ограничения, как отмечено в обсуждениях на Reddit: «Точка отлична, но иногда приходится подбирать команду так, чтобы она была повторяемой. Макросы такие же. Я не думаю, что можно получить идеальную повторяемость бесплатно».


Пользовательские настройки

Вы можете создать собственное решение, используя operatorfunc и функциональность repeat. Согласно примерам на GitHub, вы можете манипулировать механизмом повторения, чтобы «принудительно» задать, какое действие будет повторено.

Ниже приведена полная реализация:

lua
-- Отслеживаем последнюю выполненную команду
_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" })

Практическая реализация для вашего случая

Для вашего конкретного требования – перехода по цепочке определений – я рекомендую подход с режимом оператора, поскольку он наиболее «нативный» и эффективный:

lua
-- Настройка маппинга в режиме оператора для 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, а не требует дополнительных привязок или плагинов. Решение также более общее и может применяться к другим командам, которые вы хотите сделать повторяемыми.


Источники

  1. Dot‑Repeat in Vim and Neovim – Vikas Rajbanshi
  2. A basic overview of how to manage dot‑repeating in your Neovim plugin – GitHub Gist
  3. Repeat – Neovim Documentation
  4. Repeat last normal mode command, including moves, in Vim – Super User
  5. cyclops.vim – GitHub
  6. How to repeat the last change in Vim – Vim From Scratch
  7. Is there an easy way to repeat previous command? – Reddit/r/vim

Заключение

  • Оператор точки (.) в Vim/Neovim по умолчанию не работает с привязанными к клавишам командами, потому что он предназначен для текстовых операций.
  • Маппинги в режиме оператора предоставляют самое элегантное решение, делая команды совместимыми с нативным механизмом повторения.
  • Плагины, такие как cyclops.vim, предлагают альтернативы для повторения произвольных привязок.
  • Пользовательские решения с operatorfunc и отслеживанием команд дают гибкость для сложных рабочих процессов.
  • Для вашего случая с Lspsaga goto definition преобразование в режим оператора с gd и . является самым эффективным подходом.
Авторы
Проверено модерацией
Модерация