Skip to main content

lattice_keymap/
lib.rs

1//! The editor's keymap engine: the chord trie, the layered runtime
2//! registry every keystroke resolves against, the built-in vim keymap
3//! catalog, and the introspection models (`:describe-key`, which-key)
4//! derived from them.
5//!
6//! ## What it owns
7//!
8//! - **Matching** — [`KeymapTrie`]: one layer's bindings, `O(prefix)`
9//!   lookup to [`LookupResult::Bound`] / `Partial` / `Unbound`, with a
10//!   `{char}` wildcard ([`ChordPattern::CharLiteral`]) for marks,
11//!   registers and find-char.
12//! - **Layering** — [`KeymapRegistry`] behind [`KeymapHandle`]: five
13//!   [`KeymapLayer`]s (`Builtin < MajorMode < MinorMode < User < Buffer`),
14//!   one trie per [`BindingMode`] per layer, wait-free reads and
15//!   capability-gated writes ([`KeymapCapability`]). Mode layers are
16//!   gated by the active buffer's [`ModeId`]s
17//!   ([`KeymapHandle::lookup_with_context`]).
18//! - **Declaration** — [`Keymap`] / [`KeymapBinding`], a mode's
19//!   declarative contribution, and the [`keymap_entry!`] static-table form
20//!   ([`KeymapEntry`]) with the built-in catalog in [`default_keymap`].
21//! - **Introspection** — [`KeymapResolution`] / [`Continuation`] for
22//!   `:describe-key`; [`which_key`]'s [`WhichKeyModel`] and layout; the
23//!   [`PartialChordPending`] event which-key subscribes to.
24//!
25//! ## What it must not depend on
26//!
27//! Dependency position in the workspace:
28//!   lattice-protocol → lattice-grammar → lattice-keymap
29//!     → lattice-mode → lattice-host
30//!
31//! Nothing in this crate may import from `lattice-mode` or `lattice-host`.
32//! It is its own crate so the trie, the layer enum and the binding types
33//! can be named by `lattice-mode` (whose `Mode::keymap` returns a
34//! [`Keymap`]) without a cycle, and so the keystroke-path matcher carries
35//! no editor state, renderer or I/O — everything here is testable with a
36//! hand-built trie and no host.
37//!
38//! # Examples
39//!
40//! Bind a builtin chord and a mode override, then resolve a keystroke the
41//! way the dispatcher does:
42//!
43//! ```
44//! use lattice_grammar::{CommandId, CommandInvocation, SourceLocation};
45//! use lattice_keymap::{
46//!     BindingMode, KeymapCapability, KeymapHandle, KeymapLayer, LookupResult, ModeId,
47//! };
48//! use lattice_protocol::parse_chord_sequence;
49//!
50//! let keymap = KeymapHandle::new();
51//! let bind = |layer, chord, id| {
52//!     keymap.try_bind_chord_string(KeymapCapability::Full, layer, BindingMode::Normal, chord,
53//!         CommandInvocation::of(CommandId::new(id)), SourceLocation::synthetic("doc")).unwrap()
54//! };
55//! bind(KeymapLayer::Builtin, "<C-w>v", 1);
56//! bind(KeymapLayer::MinorMode(ModeId::new("magit-mode")), "<C-w>v", 2);
57//!
58//! let keys = parse_chord_sequence("<C-w>v").unwrap();
59//! let resolve = |active: &[ModeId]| match keymap.lookup_with_context(BindingMode::Normal, &keys, active) {
60//!     LookupResult::Bound { command, .. } => command.command.command,
61//!     other => panic!("{other:?}"),
62//! };
63//! assert_eq!(resolve(&[]), CommandId::new(1));
64//! assert_eq!(resolve(&[ModeId::new("magit-mode")]), CommandId::new(2));
65//!
66//! // `:describe-key` sees both layers, and which one fires here.
67//! let trace = keymap.resolve_trace(BindingMode::Normal, &keys, &[]);
68//! assert_eq!(trace.hits.len(), 2);
69//! assert_eq!(trace.winner().unwrap().layer, KeymapLayer::Builtin);
70//! ```
71//!
72//! ## Design
73//!
74//! - `docs/dev/architecture/keymap-architecture.md` — layers, merge on
75//!   write, capabilities, the motion mirror.
76//! - `docs/dev/architecture/which-key.md` — the [`which_key`] model.
77//! - `docs/dev/architecture/design.md` §5.2.3 — the five-layer model.
78
79#![warn(missing_docs)]
80
81pub mod binding_mode;
82pub mod contribution;
83pub mod keymap_entry;
84pub mod mode_id;
85
86pub use binding_mode::BindingMode;
87pub use contribution::{Keymap, KeymapBinding};
88pub use keymap_entry::{KeymapEntry, default_keymap, entries, lookup};
89pub use mode_id::ModeId;
90
91pub mod trie;
92pub use lattice_protocol::ChordPattern;
93pub use trie::{BoundCommand, KeymapLayer, KeymapTrie, LookupResult};
94
95pub mod registry;
96pub use registry::{
97    DEFAULT_LEADER, KeymapCapability, KeymapError, KeymapHandle, KeymapRegistry, LayerId,
98    PushLayerKind, expand_leader, overtypes_in_select,
99};
100
101pub mod resolution;
102pub use resolution::{Continuation, KeymapResolution, LayerHit, parse_describe_key_arg};
103
104pub mod events;
105pub use events::PartialChordPending;
106
107pub mod which_key;
108pub use trie::{ChildView, NodeView};
109pub use which_key::{Entry, EntryKind, Sort, WhichKeyModel, build_model};