A Neovim plugin that allows you to repeat movements to the next or previous instance of any movement functionality.
Since nvim-treesitter-textobjects main branch no longer supports repeatable movements with custom functions, this plugin provides a workaround to achieve the same functionality.
Note
This plugin is NOT an official nvim-treesitter plugin.
Since it plugs into nvim-treesitter-textobjects, if they change their internal API, this plugin may break.
If that happens, I will do my best to implement a working solution. But for now, this is the simplest way to achieve the desired functionality.
Using lazy.nvim:
{
"kiyoon/repeatable-move.nvim",
dependencies= { "nvim-treesitter/nvim-treesitter-textobjects" },
},Step 1: follow nvim-treesitter-textobjects for setting up keybinds for ;, ,, f, t, F, T.
localts_repeat_move=require"nvim-treesitter-textobjects.repeatable_move"-- Repeat movement with ; and ,-- ensure ; goes forward and , goes backward regardless of the last directionvim.keymap.set({ "n", "x", "o" }, ";", ts_repeat_move.repeat_last_move_next)
vim.keymap.set({ "n", "x", "o" }, ",", ts_repeat_move.repeat_last_move_previous)
-- vim way: ; goes to the direction you were moving.-- vim.keymap.set({ "n", "x", "o" }, ";", ts_repeat_move.repeat_last_move)-- vim.keymap.set({ "n", "x", "o" }, ",", ts_repeat_move.repeat_last_move_opposite)-- Optionally, make builtin f, F, t, T also repeatable with ; and ,vim.keymap.set({ "n", "x", "o" }, "f", ts_repeat_move.builtin_f_expr, { expr=true })
vim.keymap.set({ "n", "x", "o" }, "F", ts_repeat_move.builtin_F_expr, { expr=true })
vim.keymap.set({ "n", "x", "o" }, "t", ts_repeat_move.builtin_t_expr, { expr=true })
vim.keymap.set({ "n", "x", "o" }, "T", ts_repeat_move.builtin_T_expr, { expr=true })Step 2: Use this plugin for registering your custom movements.
- Use
make_repeatable_move_pairfunction to create a pair of repeatable move functions for next and previous movements. - Use
make_repeatable_movefunction to create a single repeatable move function. - Use
set_last_movefunction to manually set the last move function if needed.
Example: make gitsigns.nvim movement repeatable with ; and , keys.
localrepeat_move=require("repeatable_move")
localgs=require("gitsigns")
-- make sure forward function comes firstlocalnext_hunk_repeat, prev_hunk_repeat=repeat_move.make_repeatable_move_pair(gs.next_hunk, gs.prev_hunk)
vim.keymap.set({ "n", "x", "o" }, "]h", next_hunk_repeat)
vim.keymap.set({ "n", "x", "o" }, "[h", prev_hunk_repeat)Example: LSP next/previous diagnostic
vim.api.nvim_create_autocmd("LspAttach", {
callback=function(args)
localrepeat_move=require("repeatable_move")
localnext_warn=function()
vim.diagnostic.jump({ count=1, severity=vim.diagnostic.severity.WARN })
endlocalprev_warn=function()
vim.diagnostic.jump({ count=-1, severity=vim.diagnostic.severity.WARN })
endlocalnext_err=function()
vim.diagnostic.jump({ count=1, severity=vim.diagnostic.severity.ERROR })
endlocalprev_err=function()
vim.diagnostic.jump({ count=-1, severity=vim.diagnostic.severity.ERROR })
end-- make sure forward function comes firstnext_warn, prev_warn=repeat_move.make_repeatable_move_pair(next_warn, prev_warn)
next_err, prev_err=repeat_move.make_repeatable_move_pair(next_err, prev_err)
localopts= { buffer=args.buf }
vim.keymap.set({ "n", "x", "o" }, "]w", next_warn, vim.tbl_extend("force", opts, { desc="Next warning" }))
vim.keymap.set({ "n", "x", "o" }, "[w", prev_warn, vim.tbl_extend("force", opts, { desc="Previous warning" }))
vim.keymap.set({ "n", "x", "o" }, "]e", next_err, vim.tbl_extend("force", opts, { desc="Next error" }))
vim.keymap.set({ "n", "x", "o" }, "[e", prev_err, vim.tbl_extend("force", opts, { desc="Previous error" }))
end,
})Example: vim built-in keybindings like cnext/cprev (]q/[q)
localfunctionqf_next()
vim.cmd("cnext " ..vim.v.count1)
endlocalfunctionqf_prev()
vim.cmd("cprev " ..vim.v.count1)
endlocalrepeat_move=require("repeatable_move")
localqf_next_repeat, qf_prev_repeat=repeat_move.make_repeatable_move_pair(qf_next, qf_prev)
vim.keymap.set("n", "]q", qf_next_repeat, { desc="Next quickfix item" })
vim.keymap.set("n", "[q", qf_prev_repeat, { desc="Previous quickfix item" })Example: todo-comments.nvim
{
"folke/todo-comments.nvim",
dependencies="nvim-lua/plenary.nvim",
config=function()
localtodo_comments=require("todo-comments")
localnext_todo, prev_todolocalrepeat_move=require("repeatable_move")
-- make sure forward function comes firstnext_todo, prev_todo=repeat_move.make_repeatable_move_pair(todo_comments.jump_next, todo_comments.jump_prev)
vim.keymap.set("n", "]t", next_todo, { desc="Next todo comment" })
vim.keymap.set("n", "[t", prev_todo, { desc="Previous todo comment" })
end,
},Example: aeriel.nvim
{
"stevearc/aerial.nvim",
event="BufReadPre",
config=function()
localaerial=require("aerial")
aerial.setup({
on_attach=function(bufnr)
localanext, aprevlocalrepeat_move=require("repeatable_move")
-- make sure forward function comes firstanext, aprev=repeat_move.make_repeatable_move_pair(aerial.next, aerial.prev)
vim.keymap.set("n", "]r", anext, { buffer=bufnr, desc="Aerial next" })
vim.keymap.set("n", "[r", aprev, { buffer=bufnr, desc="Aerial prev" })
end,
})
end,
},