Mutable keymap store backed by ETS for lock-free reads.
Holds the active leader trie, per-mode binding overrides, filetype-scoped
bindings, and per-scope overrides. Initialized from Minga.Keymap.Defaults
on startup, then mutated by user config via bind/4 and bind/5.
Backed by ETS with read_concurrency: true so keystroke processing reads
bindings without a GenServer round-trip. The GenServer exists only to own
the ETS table lifecycle. Writes go directly to ETS (no serialization
needed since binds only happen during config evaluation, which is
single-threaded).
Binding modes
User bindings can target any vim mode:
:normal— leader sequences (SPC ...) and single-key overrides:insert— single-key or multi-key sequences in insert mode:visual— bindings active in visual mode:operator_pending— bindings active in operator-pending mode:command— bindings active in command mode
Filetype-scoped bindings
Bindings scoped to a filetype appear under the SPC m leader prefix. Pass
filetype: :elixir to bind/5 to register a binding that only activates
when the active buffer's filetype matches.
Per-scope overrides
Bindings scoped to a keymap scope (:agent, :file_tree) override the
defaults declared in the scope module. Pass a {scope, vim_state} tuple
as the mode to target a specific scope.
Summary
Types
Per-{filetype, mode} binding tries for filetype-scoped non-normal overrides.
Per-filetype binding tries for SPC m.
Per-mode binding tries for insert, visual, operator_pending, command.
Per-scope, per-vim-state binding overrides from user config.
Functions
Binds a key sequence to a command in the given mode.
Binds a key sequence to a command with options.
Returns a specification to start this module under a supervisor.
Returns the filetype-scoped binding trie for a specific mode.
Returns the filetype-scoped binding trie for SPC m.
Returns the current leader trie (defaults + user overrides).
Returns the binding trie for a specific mode (insert, visual, etc.).
Returns the merged normal-mode bindings (defaults + user overrides).
Returns normal-mode binding overrides as a map.
Resets all bindings to defaults (removes user overrides).
Resolves a key binding for a mode with filetype priority.
Returns scope-specific binding overrides from user config.
Returns the override trie for a specific scope and vim state.
Starts the keymap store and creates the backing ETS table.
Removes a key binding from the given mode.
Removes a filetype-scoped key binding.
Types
@type filetype_mode_tries() :: %{ required({atom(), atom()}) => Minga.Keymap.Bindings.node_t() }
Per-{filetype, mode} binding tries for filetype-scoped non-normal overrides.
@type filetype_tries() :: %{required(atom()) => Minga.Keymap.Bindings.node_t()}
Per-filetype binding tries for SPC m.
@type mode_tries() :: %{required(atom()) => Minga.Keymap.Bindings.node_t()}
Per-mode binding tries for insert, visual, operator_pending, command.
@type scope_overrides() :: %{ required(Minga.Keymap.Scope.scope_name()) => %{ required(Minga.Keymap.Scope.vim_state()) => Minga.Keymap.Bindings.node_t() } }
Per-scope, per-vim-state binding overrides from user config.
Outer key is the scope name, inner key is the vim state.
Functions
Binds a key sequence to a command in the given mode.
For normal mode, leader sequences (starting with SPC) are added to
the leader trie. Single-key bindings override defaults. For other modes
(insert, visual, operator_pending, command), bindings are stored in
per-mode tries.
Returns :ok on success or {:error, reason} on failure.
Examples
bind(:normal, "SPC g s", :git_status, "Git status")
bind(:normal, "Q", :replay_macro_q, "Replay macro q")
bind(:insert, "C-j", :next_line, "Next line")
bind(:visual, "SPC x", :custom_delete, "Custom delete")
@spec bind( GenServer.server(), atom() | {atom(), atom()}, String.t(), atom(), String.t() ) :: :ok | {:error, String.t()}
@spec bind(atom(), String.t(), atom(), String.t(), keyword()) :: :ok | {:error, String.t()}
Binds a key sequence to a command with options.
Supports the filetype: option for filetype-scoped bindings under SPC m.
Examples
bind(:normal, "SPC m t", :mix_test, "Run tests", filetype: :elixir)
bind(:normal, "SPC m p", :markdown_preview, "Preview", filetype: :markdown)
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec filetype_mode_trie(atom(), atom()) :: Minga.Keymap.Bindings.node_t()
Returns the filetype-scoped binding trie for a specific mode.
Used by insert and visual modes to check for filetype-specific key overrides before the global mode trie. Returns an empty trie if no bindings exist for the combination.
@spec filetype_mode_trie(GenServer.server(), atom(), atom()) :: Minga.Keymap.Bindings.node_t()
@spec filetype_trie(atom()) :: Minga.Keymap.Bindings.node_t()
Returns the filetype-scoped binding trie for SPC m.
Returns an empty trie if no bindings have been defined for the filetype.
@spec filetype_trie(GenServer.server(), atom()) :: Minga.Keymap.Bindings.node_t()
@spec leader_trie() :: Minga.Keymap.Bindings.node_t()
Returns the current leader trie (defaults + user overrides).
@spec leader_trie(GenServer.server()) :: Minga.Keymap.Bindings.node_t()
@spec mode_trie(atom()) :: Minga.Keymap.Bindings.node_t()
Returns the binding trie for a specific mode (insert, visual, etc.).
Returns an empty trie if no user bindings have been defined for that mode.
@spec mode_trie(GenServer.server(), atom()) :: Minga.Keymap.Bindings.node_t()
@spec normal_bindings() :: %{ required(Minga.Keymap.Bindings.key()) => {atom(), String.t()} }
Returns the merged normal-mode bindings (defaults + user overrides).
@spec normal_bindings(GenServer.server()) :: %{ required(Minga.Keymap.Bindings.key()) => {atom(), String.t()} }
@spec normal_overrides() :: %{ required(Minga.Keymap.Bindings.key()) => {atom(), String.t()} }
Returns normal-mode binding overrides as a map.
These are merged on top of Defaults.normal_bindings() at lookup time.
@spec normal_overrides(GenServer.server()) :: %{ required(Minga.Keymap.Bindings.key()) => {atom(), String.t()} }
@spec reset() :: :ok
Resets all bindings to defaults (removes user overrides).
@spec reset(GenServer.server()) :: :ok
@spec resolve_mode_binding(atom(), atom() | nil, Minga.Keymap.Bindings.key()) :: {:command, atom()} | :not_found
Resolves a key binding for a mode with filetype priority.
Checks the filetype-scoped trie first, then falls back to the global
mode trie. Returns {:command, atom()} on match, or :not_found.
This is the single lookup function mode modules should use instead of
calling mode_trie/1 directly.
@spec resolve_mode_binding( GenServer.server(), atom(), atom() | nil, Minga.Keymap.Bindings.key() ) :: {:command, atom()} | :not_found
@spec scope_overrides() :: scope_overrides()
Returns scope-specific binding overrides from user config.
@spec scope_overrides(GenServer.server()) :: scope_overrides()
@spec scope_trie(Minga.Keymap.Scope.scope_name(), Minga.Keymap.Scope.vim_state()) :: Minga.Keymap.Bindings.node_t()
Returns the override trie for a specific scope and vim state.
Returns an empty trie if no user overrides exist for that combination.
@spec scope_trie( GenServer.server(), Minga.Keymap.Scope.scope_name(), Minga.Keymap.Scope.vim_state() ) :: Minga.Keymap.Bindings.node_t()
@spec start_link(keyword()) :: GenServer.on_start()
Starts the keymap store and creates the backing ETS table.
Removes a key binding from the given mode.
Mirrors the dispatch logic of bind/4: for normal mode, leader
sequences are removed from the leader trie and single-key bindings
are removed from normal overrides. For other modes, the binding is
removed from the per-mode trie.
Returns :ok on success or {:error, reason} on failure.
Examples
unbind(:normal, "SPC g s")
unbind(:insert, "C-j")
@spec unbind(GenServer.server(), atom(), String.t()) :: :ok | {:error, String.t()}
@spec unbind(atom(), String.t(), keyword()) :: :ok | {:error, String.t()}
Removes a filetype-scoped key binding.
Examples
unbind(:normal, "SPC m t", filetype: :org)
@spec unbind(GenServer.server(), atom(), String.t(), keyword()) :: :ok | {:error, String.t()}