Complex Keymap Management In Neovim
Neovim’s vim.keymap.set
and similar utilities are very useful for
creating and managing keymaps, however they lack some ergonomics when
building more complex keymaps that require setting and deleting the keymaps
periodically. A simply utility function makes this really clean and easy to
do.
I’ve called this function map_fn
(because I honestly couldn’t come up
with a better name), and when called it will return a class containing
chainable set
and del
methods.
--- @class Map
--- @field set fun(): Map
--- @field del fun(): Map
--- Creates a keymap function
--- @param mode string|table
--- @param key string
--- @param cmd string|function
--- @param opts_or_desc table|string|nil
--- @return Map
local map_fn = function(mode, key, cmd, opts_or_desc)
local Map = {}
Map.__index = Map
function Map:new()
local instance = setmetatable({}, self)
return instance
end
function Map:set()
local opts
if type(opts_or_desc) == "string" then
opts = { desc = opts_or_desc, silent = true }
else
opts = vim.tbl_extend("keep", opts_or_desc or {}, { silent = true })
end
vim.keymap.set(mode, key, cmd, normalize_map_opts(opts_or_desc))
return self
end
function Map:del()
vim.keymap.del(mode, key)
return self
end
return Map:new():set()
end
Combining this with the
augroup
function I created
in a previous Byte, we can build a complex mapping and autocmd setup like
this with very little code:
local command_line = map_fn("n", "q;", "q:", "Open command line window"):set()
augroup("command_line", function(autocmd)
autocmd("RecordingEnter", { callback = command_line.del })
autocmd("RecordingLeave", { callback = command_line.set })
end)
What this does is create a mapping q;
which is equivalent to q:
to open
the command line window. However, because the macro recording stop keymap
is q
, we only want to enable this mapping when recording is not active,
so our autocmd will handle deleting and re-creating the keymap for us when
the autocmds fire.