layers.nvim
provides a toolkit and Lua library to craft temporary keymap overlays which can also
act as lightweight layered modes. To this end, it uses some syntactic sugar and added functionality
around two builtin Neovim functions to save and restore mappings (see :help layers.nvim
for more
details).
This effectively means you could overlay any keys with different functionality, with a builtin way to turn back your keymaps to what they were when you started using the layer. I.e. the keymap you overlaid will be restored to what it was instead of unmapped. This works for any keymap, whether it is a builtin, set by your config, set by a distro, or set by a plugin.
It is supposed to be used thorough your config, for instance to enhance other plugins. As such, it
is thoroughly documented (:help layers.nvim
) and comes with Lua type annotations, making
configuration and usage a breeze if you have a Lua LSP such as lazydev.nvim configured.
For lazy.nvim you can use this snippet either as a top level plugin or as a dependency to
any plugin that uses the Layers
global in its own config. Feel free to lazy load arbitrarily,
although the plugin itself does nothing without being used.
{
"debugloop/layers.nvim",
opts = {}, -- see :help Layers.config
},
If you are using a different package manager, you can add it in a very similar way, but take care to
call require("layers").setup({})
as Lazy does on your behalf.
All configuration options and defaults can be found in :help Layers.config
. At this time, all
options apply to the mode functionalitie's help window, which acts as a visual hint for overlaid
mappings.
After calling setup
, a global Layers
table is available for use in your config. You can use it
to create very lightweight mapping overlays using the Layers.map
table, or to create more
elaborate layered modes using the Layers.mode
table. Both of these offer elaborate :help
files.
The general feel and controlflow is shared between both tables: You create an instance using the
.new()
constructor and add your mappings to it using the methods exposed by it.
This section tries to showcase various ideas that this plugin can be used for.
This is the use case I originally built this for. My config used to to something similar, but this is streamlined by this plugins capabilities. Also note how this does not prevent any events or highlights from being updated: The current step position is visualized correctly at all times, as this plugin does not rely on tricks to overlay mappings (see Comparisons below).
Code
{
"mfussenegger/nvim-dap",
dependencies = {
{
"debugloop/layers.nvim",
opts = {},
},
},
keys = {
{
"<leader>d",
function()
local dap = require("dap")
if dap.session() ~= nil then
DEBUG_MODE:activate()
return
end
dap.continue()
end,
desc = "launch debugger",
},
},
opts = { ... }
config = function(_, opts)
local dap = require("dap")
-- do the setup you'd do anyway for your language of choice
dap.adapters = opts.adapters
dap.configurations = opts.configurations
-- this is where the example starts
DEBUG_MODE = Layers.mode.new() -- global, accessible from anywhere
DEBUG_MODE:auto_show_help()
-- this actually relates to the next example, but it is most convenient to add here
DEBUG_MODE:add_hook(function(_)
vim.cmd("redrawstatus") -- update status line when toggled
end)
-- nvim-dap hooks
dap.listeners.after.event_initialized["debug_mode"] = function()
DEBUG_MODE:activate()
end
dap.listeners.before.event_terminated["debug_mode"] = function()
DEBUG_MODE:deactivate()
end
dap.listeners.before.event_exited["debug_mode"] = function()
DEBUG_MODE:deactivate()
end
-- map our custom mode keymaps
DEBUG_MODE:keymaps({
n = {
{
"s",
function()
dap.step_over()
end,
{ desc = "step forward" },
},
{
"c",
function()
dap.continue()
end,
{ desc = "continue" },
},
{ -- this acts as a way to leave debug mode without quitting the debugger
"<esc>",
function()
DEBUG_MODE:deactivate()
end,
{ desc = "exit" },
},
-- and so on...
},
})
}
This is the little cherry on top of the above debug mode example. The example uses mini.statusline but is easily adapted to other statuslines. Contributions welcome!
Code
{
"echasnovski/mini.statusline",
dependencies = {
{
"debugloop/layers.nvim",
opts = {},
},
},
opts = {
content = {
active = function() -- this is the default, see :help MiniStatusline-example-content
local mode, mode_hl = MiniStatusline.section_mode({ trunc_width = 120 })
local git = MiniStatusline.section_git({ trunc_width = 40 })
local diff = MiniStatusline.section_diff({ trunc_width = 75 })
local diagnostics = MiniStatusline.section_diagnostics({ trunc_width = 75 })
local lsp = MiniStatusline.section_lsp({ trunc_width = 75 })
local filename = MiniStatusline.section_filename({ trunc_width = 140 })
local fileinfo = MiniStatusline.section_fileinfo({ trunc_width = 120 })
local location = MiniStatusline.section_location({ trunc_width = 75 })
local search = MiniStatusline.section_searchcount({ trunc_width = 75 })
-- this if statement is the only non-default thing in here
if DEBUG_MODE ~= nil and DEBUG_MODE:active() then
mode = "DEBUG"
mode_hl = "Substitute"
end
return MiniStatusline.combine_groups({
{ hl = mode_hl, strings = { mode } },
{ hl = 'MiniStatuslineDevinfo', strings = { git, diff, diagnostics, lsp } },
'%<', -- Mark general truncate point
{ hl = 'MiniStatuslineFilename', strings = { filename } },
'%=', -- End left alignment
{ hl = 'MiniStatuslineFileinfo', strings = { fileinfo } },
{ hl = mode_hl, strings = { search, location } },
})
end
},
},
},
- anuvyklack/hydra.nvim:
- Uses different mechanisms internally.
- Has a lot broader scope and more features, but comes in with 10 times as much code too.
- Some colors involve
vim.fn.getchar
, which interferes with nvim's event and autocommand processing.
- echasnovski/mini.clue:
- Has a different goal but can achieve similar sub modes.
- Does not create keymaps, but documents them.
- Uses
vim.fn.getchar
exclusively, interfering with nvim's event and autocommand processing while the clue window is active.
- add tests, learn how
mini.test
works - add github actions for
- autogenerating help (
lua require("mini.doc").generate()
) - tests
- autogenerating help (
- add mode managing functionality
- makes named modes possible
- makes exclusive mode active status possible
- To echasnovski and his
mini.nvim
suite, from which I have learned a thing or eight - To all future contributors ❤️