Struct Keymap
pub struct Keymap {
pub bindings: Vec<KeymapBinding>,
pub entries: Vec<&'static KeymapEntry>,
}Expand description
A mode’s full keymap contribution.
Keymap::default() is the empty contribution – modes that
don’t ship bindings rely on the lattice_mode::Mode::keymap trait
default.
Two declaration paths share the same contribution shape:
- Chain form —
Keymap::new().bind_chord(...)/.bind(...). Terse; ergonomic for 1-5 bindings; populatesKeymap::bindingswith fully-typedKeymapBindings. Source-location auto-captured via#[track_caller]. No docstring per binding (use.bind(KeymapBinding::new(...) .with_doc(...))if needed). - Table form —
Keymap::from_entries(&MY_TABLE)/.extend_with_entries(&...). Static-catalog-style; ergonomic for 5-20+ bindings; references a&'static [KeymapEntry]built with thekeymap_entry!macro. Each entry carries a docstring; the host translation pass (K.2.4.A.0.3) resolves the entry’s canonical command-name string against theCommandRegistryat registration time, building oneKeymapBindingper resolvable entry. Mode authors declare entries in astaticslice next to the impl; macro-capturedfile!()+line!()give per-row provenance.
The two paths compose:
use lattice_grammar::{CommandId, CommandInvocation};
use lattice_keymap::{BindingMode, Keymap, KeymapEntry, keymap_entry};
use std::sync::LazyLock;
// A static table (`KeymapEntry` embeds a `SourceLocation`, so the
// slice is built lazily rather than as a `const`).
static MY_KEYMAP: LazyLock<Vec<KeymapEntry>> = LazyLock::new(|| vec![
keymap_entry! { mode: Normal, chord: "]e", doc: "Next excerpt", cmd: "my:excerpt-next" },
keymap_entry! { mode: [Normal, Visual], chord: "q", doc: "Close", cmd: "my:close" },
]);
let refresh = CommandInvocation::of(CommandId::new(7));
let km = Keymap::from_entries(MY_KEYMAP.as_slice())
.bind_chord(BindingMode::Normal, "<C-r>", refresh.clone());
assert_eq!(km.entries.len(), 2); // resolved against the CommandRegistry later
assert_eq!(km.bindings.len(), 1); // already typed
assert_eq!(km.bindings[0].command, refresh);
assert_eq!(km.bindings[0].doc, None); // the chain form carries no docLayer placement is implicit at translation time: every
binding / entry contributed by Mode X lands at
KeymapLayer::MinorMode(x.id()) per K.1.b convention.
Per-binding layer is not exposed here – letting a mode
inject into another layer would break the layer-priority
contract (a “minor mode” silently shadowing a builtin would
be invisible to :describe-key).
Fields§
§bindings: Vec<KeymapBinding>Declarative list of bindings this mode contributes.
Populated by the chain form (bind / bind_chord) and
by the host translation pass when it resolves entries.
entries: Vec<&'static KeymapEntry>Static-catalog-style entries this mode contributes.
Populated by Self::from_entries /
Self::extend_with_entries. The host translation
pass (K.2.4.A.0.3) walks both bindings and entries;
entries get name→CommandId resolved via the
CommandRegistry and the resulting KeymapBindings
(carrying the entry’s doc) flow into the trie
alongside the explicit bindings.
Implementations§
§impl Keymap
impl Keymap
pub fn new() -> Keymap
pub fn new() -> Keymap
Empty keymap. Equivalent to Keymap::default(); kept
for symmetry with builder-style construction.
pub fn from_entries(entries: &'static [KeymapEntry]) -> Keymap
pub fn from_entries(entries: &'static [KeymapEntry]) -> Keymap
Build a keymap from a static slice of keymap_entry!-
constructed entries. The host translation pass resolves
each entry’s canonical command-name string against the
CommandRegistry at registration time; unresolvable
names log a tracing::warn! and skip the binding
(matches the existing catalog-drift convention).
Returns a keymap with Self::entries populated and
Self::bindings empty. Compose with the chain form
(.bind_chord(...)) to add typed bindings on top.
pub fn extend_with_entries(self, entries: &'static [KeymapEntry]) -> Keymap
pub fn extend_with_entries(self, entries: &'static [KeymapEntry]) -> Keymap
Append a static slice of keymap_entry!-constructed
entries to an existing keymap. Returns self so call
sites can chain
Keymap::new().bind_chord(...).extend_with_entries(&TBL)
or
Keymap::from_entries(&BASE).extend_with_entries(&MORE).
pub fn bind(self, binding: KeymapBinding) -> Keymap
pub fn bind(self, binding: KeymapBinding) -> Keymap
Append one binding. Returns self so call sites can
chain Keymap::new().bind(...).bind(...).
pub fn bind_chord(
self,
mode: BindingMode,
chord: &str,
command: CommandInvocation,
) -> Keymap
pub fn bind_chord( self, mode: BindingMode, chord: &str, command: CommandInvocation, ) -> Keymap
Append one binding parsed from a chord-sequence string.
The recommended idiom for mode-contributed keymaps:
use lattice_grammar::{CommandId, CommandInvocation};
use lattice_keymap::{BindingMode, ChordPattern, Keymap};
use lattice_protocol::KeyChord;
let next = CommandInvocation::of(CommandId::new(1));
let prev = CommandInvocation::of(CommandId::new(2));
let km = Keymap::new()
.bind_chord(BindingMode::Normal, "]e", next)
.bind_chord(BindingMode::Normal, "<C-w>j", prev);
assert_eq!(
km.bindings[1].chords,
vec![
ChordPattern::Literal(KeyChord::ctrl('w')),
ChordPattern::Literal(KeyChord::char('j')),
],
);
// Provenance is this call site, via `#[track_caller]`.
assert!(matches!(
&km.bindings[0].source.kind,
lattice_grammar::SourceKind::File { path, .. } if path == std::path::Path::new(file!()),
));#[track_caller] propagates the binding row’s own
file:line into the resulting [SourceLocation]; no
SourceLocation::builtin_file(file!(), line!())
boilerplate per row. :describe-key shows the chord’s
declaration site directly.
The chord string is parsed via
[lattice_protocol::parse_chord_sequence] – accepts
the same notation the host’s keymap_entry! macro
catalog uses ("j", "gd", "]e", "<C-w>j",
"<Esc>", "<C-S-x>", …). Wildcards ('a, "a,
fX) are not expressible here; the rare mode that
needs ChordPattern::CharLiteral calls Keymap::bind
directly with an explicit chords vector.
§Panics
On a chord-string parse error. Mode bindings are declared at compile-time-static call sites with constant chord strings; a malformed string is a bug in the mode impl, not a runtime condition. The panic message names the chord string + the caller location so the fix is obvious. Same shape as host-side catalog drift: the editor refuses to boot rather than silently dropping the binding.
pub fn bind_chord_modes(
self,
modes: &[BindingMode],
chord: &str,
command: CommandInvocation,
) -> Keymap
pub fn bind_chord_modes( self, modes: &[BindingMode], chord: &str, command: CommandInvocation, ) -> Keymap
Bind one chord across SEVERAL modes — the declarative multi-mode
peer of Self::bind_chord. Pushes one KeymapBinding per
mode (the registry trie is per-mode), so :describe-key sees the
chord in each named mode. modes must be non-empty.
The imperative peer is [crate::KeymapHandle::bind_modes]; the
keymap_entry! mode: [..] catalog form is the static-table peer.
Same parse-or-panic discipline + caller-location capture as
Self::bind_chord.