Repeat-able movements reborn!
By default vim allows repeating default movements like f
/F
/t
/T
and others using ;
and ,
.
However, the builtin mechanism is not extendable and as soon as we start using some custom movements we are left to implement
the repeating on your own. Some plugins provide that options some don't. But even when they do,
they do it in a way that steals ;
for themselves.
While I don't use repeating movements often with default motions, I would like to use them with motions like next-treesitter-method, next-diagnostic, next-git-change, etc that comes from many different plugins.
This plugin is a repeatable movements engine that other plugins can hook into.
You can think of it as of nvim-cmp
but for movements.
The idea is that other plugins like for example git-signs will expose logic to perform some movement, and then we will wrap it with an adapter and plug into that engine.
I have been using this plugin for a while now and it seems to be working fine. Sure my usage-patterns might not cover every use-case so if you find a bug please raise an issue.
First you need to initialize nvim-next. This will map ;
and ,
to respective nvim-next functions.
require("nvim-next").setup({
default_mappings = {
repeat_style = "original",
},
})
The repeat_style
parameter controls if the repetition preserves the original
direction of the move, or if it uses the direction (directional
) of the repeat key: ;
- forward, ,
- backward.
You might also want to override the builtin f
/t
functions to have a consistent behavior with the rest of the movements.
The easiest way to do this is by extending the setup configuration:
local next = require("nvim-next").setup({
default_mappings = {
repeat_style = "original",
},
items = {
nvim_next_builtins.f,
nvim_next_builtins.t
}
})
Alternatively, you can map it on your own:
local functions = require("nvim-next.builtins.functions")
local f_backward, f_forward = next.make_repeatable_pair(functions.F, functions.f)
vim.keymap.set("n", "f", f_forward)
vim.keymap.set("n", "F", f_backward)
Nvim-next comes with multiple integrations out of the box for many popular plugins. Can't find your favorite one? Don't hesitate and create an issue. PRs are more than welcome.
local next_integrations = require("nvim-next.integrations")
require("gitsigns").setup({
on_attach = function(bufnr)
local gs = package.loaded.gitsigns
local function map(mode, l, r, opts)
opts = opts or {}
opts.buffer = bufnr
vim.keymap.set(mode, l, r, opts)
end
local nngs = next_integrations.gitsigns(gs)
-- Navigation
map('n', ']c', function()
if vim.wo.diff then return ']c' end
vim.schedule(function() nngs.next_hunk() end)
return '<Ignore>'
end, { expr = true })
map('n', '[c', function()
if vim.wo.diff then return '[c' end
vim.schedule(function() nngs.prev_hunk() end)
return '<Ignore>'
end, { expr = true })
end,
})
local next_integrations = require("nvim-next.integrations")
local nndiag = next_integrations.diagnostic()
vim.keymap.set("n", "[d", nndiag.goto_prev({ severity = { min = diag.severity.WARN } }), { desc = "previous diagnostic" })
vim.keymap.set("n", "]d", nndiag.goto_next({ severity = { min = diag.severity.WARN } }), { desc = "next diagnostic" })
-- first initialize intgration module
require("nvim-next.integrations").treesitter_textobjects()
-- setup treesitter
require("nvim-treesitter.configs").setup({
textobjects = {
swap = {
enable = true,
swap_next = {
["<leader>a"] = "@parameter.inner",
},
swap_previous = {
["<leader>A"] = "@parameter.inner",
},
},
},
nvim_next = {
enable = true,
textobjects = {
--instead of defining the move section in the textobjects scope we move it under nvim_next
move = {
goto_next_start = {
["]m"] = "@function.outer",
["]]"] = { query = "@class.outer", desc = "Next class start" },
},
goto_next_end = {
["]M"] = "@function.outer",
["]["] = "@class.outer",
},
goto_previous_start = {
["[m"] = "@function.outer",
["[["] = "@class.outer",
},
goto_previous_end = {
["[M"] = "@function.outer",
["[]"] = "@class.outer",
},
}
}
}
})
local next_integrations = require("nvim-next.integrations")
local nqf = next_integrations.quickfix()
vim.keymap.set("n", "[d", nqf.cprevious, { desc = "previous quickfix list item" })
vim.keymap.set("n", "]d", nqf.cnext, { desc = "next quickfix list item" })
The protocol for func_next
and func_prev
is defined as follows:
They need to accept a structure:
{ result = nil --here goes results of whatever your function returned,
--nil if that is a first invocation ,
repeating = true --if the call is repeated, false otherwise
args = {} -- table with original arguments
}
Example:
local next_move = require("nvim-next.move")
local prev_qf_item, next_qf_item = next_move.make_repeatable_pair(function(_)
local status, err = pcall(vim.cmd, "cprevious")
if not status then
vim.notify("No more items", vim.log.levels.INFO)
end
end, function(_)
local status, err = pcall(vim.cmd, "cnext")
if not status then
vim.notify("No more items", vim.log.levels.INFO)
end
end)
map("n", "]q", next_qf_item, { desc = "nvim-next: next qfix" })
map("n", "[q", prev_qf_item, { desc = "nvim-next: prev qfix" })
The initial code for that plugin was created by @kiyoon during work at nvim-treesitter/nvim-treesitter-textobjects#359