pub struct Keymap<A, M: Mode> { /* private fields */ }Expand description
A modal keymap that maps chord sequences to user-defined actions.
Generic over both the action type A and the mode discriminator M.
M can be any Copy + Eq + Hash + Debug type — typically a consumer-defined
enum such as VimMode or HelixMode. Chords are stored per-M in
separate tries. Call Keymap::feed once per key event; it manages an
internal per-mode buffer and returns a KeyResolve indicating what happened.
Implementations§
Source§impl<A: Clone, M: Mode> Keymap<A, M>
impl<A: Clone, M: Mode> Keymap<A, M>
Sourcepub fn set_leader(&mut self, c: char)
pub fn set_leader(&mut self, c: char)
Update the leader character (re-parses are not needed; leader is
applied at add/feed time through Chord::parse).
Sourcepub fn set_timeout(&mut self, t: Duration)
pub fn set_timeout(&mut self, t: Duration)
Override the ambiguity-resolution timeout.
Sourcepub fn timeout_duration(&self) -> Duration
pub fn timeout_duration(&self) -> Duration
The current timeout duration.
Sourcepub fn add(
&mut self,
mode: M,
chord_str: &str,
action: A,
desc: &str,
) -> Result<(), KeymapError>
pub fn add( &mut self, mode: M, chord_str: &str, action: A, desc: &str, ) -> Result<(), KeymapError>
Parse chord_str (vim notation, <leader> expanded) and register
action for mode unconditionally.
Sourcepub fn add_if(
&mut self,
mode: M,
chord_str: &str,
action: A,
desc: &str,
predicate: impl Fn() -> bool + Send + Sync + 'static,
) -> Result<(), KeymapError>
pub fn add_if( &mut self, mode: M, chord_str: &str, action: A, desc: &str, predicate: impl Fn() -> bool + Send + Sync + 'static, ) -> Result<(), KeymapError>
Parse chord_str and register action for mode gated behind a
runtime predicate.
When the predicate returns false at resolve time the binding is
treated as if it does not exist: the key falls through to the next
dispatch layer (engine FSM, tmux fallback, etc.).
predicate must be Fn() -> bool + Send + Sync + 'static. Capture
runtime state via Arc<Mutex<…>> or Arc<AtomicBool> as needed.
§When to use this over an always-bound action
Prefer this when the gate has no fall-back behaviour — the
binding should simply “not exist” when the predicate is false, and
the key should reach whatever handler runs after the keymap (engine,
tmux fallback, etc.). Action-variant gating (an always-bound action
that checks state at dispatch time and shows a toast on miss) is
fine when the user benefits from feedback; use add_if when silent
fall-through is the desired UX.
Intentionally retained for future consumers — not yet called by
apps/hjkl. Concrete planned callers (issue #120 review):
- kryptic-sh/hjkl#39 — scripting (lua / vimscript) layer:
user-defined conditional bindings (
if filetype == 'rust' then bind(...)) need a host-side predicate primitive. - kryptic-sh/hjkl#113 — extension API: third-party plugins registering bindings gated on runtime state (debugger attached, project type detected, etc.).
- kryptic-sh/hjkl#115 — git hunk actions:
<leader>hs/<leader>hronly meaningful inside a git repo; silent fall-through is cleaner than anot-in-git-repotoast.
Sourcepub fn add_chord(&mut self, mode: M, chord: Chord, binding: Binding<A>)
pub fn add_chord(&mut self, mode: M, chord: Chord, binding: Binding<A>)
Register a pre-parsed chord + binding.
Sourcepub fn remove(&mut self, mode: M, chord_str: &str) -> Result<bool, KeymapError>
pub fn remove(&mut self, mode: M, chord_str: &str) -> Result<bool, KeymapError>
Remove the binding for chord_str in mode. Returns Ok(true) if
something was actually removed.
Sourcepub fn children(&self, mode: M, prefix: &Chord) -> Vec<(KeyEvent, Binding<A>)>
pub fn children(&self, mode: M, prefix: &Chord) -> Vec<(KeyEvent, Binding<A>)>
Return the direct-child terminal bindings reachable from prefix in
mode. Used by which-key to list available completions.
Sourcepub fn children_all(
&self,
mode: M,
prefix: &Chord,
) -> Vec<(KeyEvent, Option<Binding<A>>)>
pub fn children_all( &self, mode: M, prefix: &Chord, ) -> Vec<(KeyEvent, Option<Binding<A>>)>
Return all direct children reachable from prefix in mode —
both terminal bindings and pure-prefix (submenu) entries.
Terminal entries carry Some(Binding); prefix-only entries carry None.
Callers (e.g. which-key) should render prefix-only entries with a
synthetic description such as "…".
Sourcepub fn feed(&mut self, mode: M, ev: KeyEvent, _now: Instant) -> KeyResolve<A>
pub fn feed(&mut self, mode: M, ev: KeyEvent, _now: Instant) -> KeyResolve<A>
Feed a single key event for mode and return what happened.
now is used to drive timeout logic — pass Instant::now() in
production; use a fake Instant in tests if needed.
Sourcepub fn timeout_resolve(&mut self, mode: M) -> KeyResolve<A>
pub fn timeout_resolve(&mut self, mode: M) -> KeyResolve<A>
Force-resolve any pending chord state (called when the timeout fires).
Three outcomes:
- Buffer matches a terminal binding →
Match(binding)and the buffer is drained. This is the Ambiguous resolution case (e.g. bothgandgdbound: pressinggand waiting fires thegbinding). - Buffer is a pure prefix (no terminal at this depth but deeper
bindings exist) →
Unbound(vec![])and the buffer is left in place. The user is mid-chord; the timeout fired for which-key purposes but no chord-level action is required. - Buffer is a dead-end (no terminal, no descendants) →
Unbound(buf)with the drained events. This shouldn’t normally occur given thatfeedonly buffers keys that extend a valid prefix.