This plugin provide apis to create a submode and manipulate it.
These apis can be used on-the-fly: no need to call config that other plugin need.
This plugin is in development stage and breaking changes may occure to apis. We recommend you to pin version for prevent unexpected change.
- Neovim >= 0.10.0
This plugin allow users to create submode, which has almost same keymaps as the parent mode like normal, insert, etc, but some keymaps is changed and is defined by user.
For example, when we try to move around windows, we need to press <C-w>h, <C-w>j, <C-w>k and <C-w>l multiple times.
Therefore, it would be useful to be able to press <C-w> and then hjkl to move the window.
Fortunately, you can define such submode as follow.
local submode = require("submode")
submode.create("WinMove", {
mode = "n",
enter = "<C-w>",
leave = { "q", "<ESC>" },
default = function(register)
register("h", "<C-w>h")
register("j", "<C-w>j")
register("k", "<C-w>k")
register("l", "<C-w>l")
end
})This submode has default mappings hjkl for moving around windows, and you can enter this submode by pressing <C-w> when in normal mode. Once you enter this submode, you can use hjkl. You can leave from this submode by pressing q or escape, and after that hjkl cannot be used to move windows anymore.
Next, sometimes you may want to add a mappings to exist submode to extend the behavior of the submode. Is it possible in this plugin? The answer is yes.
For example, you have a submode defined as follow.
local submode = require("submode")
submode.create("test", {
mode = "n",
enter = "]",
leave = { "q", "<ESC>" },
default = function(register)
register("1", function() vim.notify("1") end)
end,
})If you want to add 2 to notify 2, you can achieve it with the following code.
submode.set("test", "2", function() vim.notify("2") end)This interface is compatible with vim.keymap.set, so you can easily define mappings the way you are used to.
Just as neovim provides vim.keymap.del, this plugin provides its compatible interface: submode.del. You can use it like as vim.keymap.set.
submode.del("test", "2")One additional notable point is that default mappings created by submode.create doesn't change even if submode.set and submode.del is called in the order.
For example, if we call submode.set("test", "1", ""), this disable the behavior of 1 in test, but if we call submode.del("test", "1") after that, pressing 1 will notify 1.
We introduced two type of mappings: default mappings defined by submode.create and mappings defined by submode.set. So, how to use different?
The first is intended to provide a default mappings to user by submode creator, and second intended that user extend exist submode provided by someone.
As far as personal use, you can use either of way freely.
For example, the submode WinMove also can be defined as follow:
local submode = require("submode")
submode.create("WinMove", {
mode = "n",
enter = "<C-w>",
leave = { "q", "<ESC>" },
})
submode.set("WinMove", "h", "<C-w>h")
submode.set("WinMove", "j", "<C-w>j")
submode.set("WinMove", "k", "<C-w>k")
submode.set("WinMove", "l", "<C-w>l")If you created a plugin and want to provide a submode using this, it is preferred submode.create to define mappings as it allow user to override default mappings or remove overrided mappings to restore default mappings.
With lazy.nvim
return {
"pogyomo/submode.nvim",
lazy = true,
-- (recommended) specify version to prevent unexpected change.
-- version = "6.0.0",
}- Submode to switch to lsp-related keymaps.
local submode = require("submode")
submode.create("LspOperator", {
mode = "n",
enter = "<Space>l",
leave = { "q", "<ESC>" },
default = function(register)
register("d", vim.lsp.buf.definition)
register("D", vim.lsp.buf.declaration)
register("H", vim.lsp.buf.hover)
register("i", vim.lsp.buf.implementation)
register("r", vim.lsp.buf.references)
end,
})- Enable keymaps which is appropriate for reading help when open help.
local submode = require("submode")
submode.create("DocReader", {
mode = "n",
default = function(register)
register("<Enter>", "<C-]>")
register("u", "<cmd>po<cr>")
register("r", "<cmd>ta<cr>")
register("U", "<cmd>ta<cr>")
register("q", "<cmd>q<cr>")
end,
})
vim.api.nvim_create_augroup("DocReaderAugroup", {})
vim.api.nvim_create_autocmd("BufEnter", {
group = "DocReaderAugroup",
callback = function()
if vim.opt.ft:get() == "help" and not vim.bo.modifiable then
submode.enter("DocReader")
end
end,
})
vim.api.nvim_create_autocmd({ "BufLeave", "CmdwinEnter" }, {
group = "DocReaderAugroup",
callback = function()
if submode.mode() == "DocReader" then
submode.leave()
end
end,
})The following user events will be triggered.
SubmodeEnterPre- Emitted when
submode.entercalled and before process anything. dataattribute will holdnamefor corresponding submode name.
- Emitted when
SubmodeEnterPost- Emitted when
submode.entercalled and after all process done. dataattribute will holdnamefor corresponding submode name.
- Emitted when
SubmodeLeavePre- Emitted when
submode.leavecalled and before process anything. dataattribute will holdnamefor corresponding submode name.
- Emitted when
SubmodeLeavePost- Emitted when
submode.leavecalled and after all process done. dataattribute will holdnamefor corresponding submode name.
- Emitted when
You can use these events with the following code:
vim.api.nvim_create_autocmd("User", {
group = vim.api.nvim_create_augroup("user-event", {}),
pattern = "SubmodeEnterPre", -- Name of user events
callback = function(env)
vim.notify("SubmodeEnterPre fired")
vim.notify(string.format("submode: %s", env.data.name))
end
})-
create(name, opts, default)- Create a new submode.
name: stringName of this submode.opts: tableOptions of this submode. Have the following fields.mode: stringParent mode of this submode like"n","v", etc.show_mode?: booleanFalse to suppressmode()returns the submode name.mode_name?: string | fun(): stringChange the valuemode()returns.enter?: string | string[]Keys to enter to this submode.leave?: string | string[]Keys to leave from this submode.hook: tableHolds callbacks called on specific timing.on_enter: fun()Executed on same timing asSubmodeEnterPost.on_leave: fun()Executed on same timing asSubmodeLeavePost.
default?: functionCallback to register default mappings. Take a following value:register: fun(lhs: string, rhs: string | function, opts?: table)When called, this callback register given default mapping to this submode.
leave_when_mode_changed?: booleanWhether leave from current submode or not when parent mode is changed i.e. changed normal mode to visual mode. Default is false.override_behavior?: stringBehavior when the submode already exist. Accept following strings."error"Throw error. This is default."keep"Keep current submode."override"Override old submode.
default?: functionSame functional asopts.default. This will be removed in future.
-
set(name, lhs, rhs, opts)- Add a mapping to
name. Same interface asvim.keymap.set. name: stringName of target submode.lhs: stringLhs of mapping.rhs: string | fun():string?Rhs of mapping. Can be function.opts?: tableOptions of this mapping. Same asoptsofvim.keymap.set.
- Add a mapping to
-
del(name, lhs, opts)- Delete a mapping from
name. Same interface asvim.keymap.del. name: stringName of target submode.lhs: stringLhs of mapping.opts?: tableOptions for this deletion. Same asoptsofvim.keymap.del.
- Delete a mapping from
-
enter(name)- Enter the submode. This function only have effect if parent mode of the submode is same as current mode.
name: stringName of submode to enter.
-
leave()- Leave from current submode. Nothing happen when we are not in submode.
-
mode(): string | nil- Get current submode's name. Returns nil if not in submode, or
show_modeisfalse.
- Get current submode's name. Returns nil if not in submode, or