Skip to main content

keymap_seq/
lib.rs

1//! # keymap-seq
2//!
3//! Multi-key *sequence* resolution layered on top of [`keymap-core`]'s
4//! single-chord vocabulary: leader trees (`space w`), emacs-style prefix chords
5//! (`ctrl+x ctrl+s`), and the like.
6//!
7//! Like [`keymap-core`], this crate is **state-free**. A [`SequenceKeymap`]
8//! answers "given the keys pressed so far, is this an exact binding, a prefix of
9//! a longer one, or neither?" via [`SequenceKeymap::lookup`], and "what keys may
10//! follow this prefix?" via [`SequenceKeymap::continuations`] (for which-key-style
11//! menus). The *pending key buffer* — the keys typed since the last resolution —
12//! lives in the caller,
13//! exactly as `keymap_core::resolve_layered` keeps the active-layer choice
14//! caller-side. The library holds no clock and no mutable session state.
15//!
16//! ## The prefix-free invariant
17//!
18//! A binding may not be a proper prefix of another binding. With timeouts out of
19//! scope (see below), `j` bound *and* `j j` bound would make the press of `j`
20//! undecidable — fire `j` now, or wait for the second `j`? Only a clock or a
21//! lookahead could break that tie, and this crate has neither. So
22//! [`SequenceKeymap::bind`] rejects such a pair with [`SeqBindError::PrefixShadow`],
23//! which keeps `lookup` a *total, deterministic, time-free* function: a prefix is
24//! always [`Match::Prefix`], a leaf is always [`Match::Exact`], anything off the
25//! tree is [`Match::NoMatch`].
26//!
27//! ## Timeouts and the caller loop
28//!
29//! `jj`-style "press twice quickly" bindings need a time window, which is a
30//! property of the caller's event loop (when to read the clock, blocking vs
31//! `poll`-with-deadline), not of this table. The intended pattern keeps the
32//! library time-free. [`PendingSequence`] folds the buffer bookkeeping below into
33//! a small caller-owned helper (`feed` to push a key, `flush` for the idle
34//! timeout); the raw `lookup` loop it wraps is:
35//!
36//! ```
37//! use keymap_core::{Key, KeyInput, Modifiers};
38//! use keymap_seq::{Match, SequenceKeymap};
39//!
40//! # #[derive(Debug, PartialEq)]
41//! enum Action { Save }
42//!
43//! let cx = KeyInput::new(Key::Char('x'), Modifiers::CTRL);
44//! let cs = KeyInput::new(Key::Char('s'), Modifiers::CTRL);
45//! let mut map = SequenceKeymap::new();
46//! map.bind([cx, cs], Action::Save).unwrap();
47//!
48//! // The caller owns the pending buffer.
49//! let mut pending: Vec<KeyInput> = Vec::new();
50//! for key in [cx, cs] {
51//!     pending.push(key);
52//!     match map.lookup(&pending) {
53//!         Match::Exact(action) => {
54//!             assert_eq!(action, &Action::Save);
55//!             pending.clear(); // fired — reset for the next sequence
56//!         }
57//!         Match::Prefix => { /* keep waiting (and, for `jj`, start a timer) */ }
58//!         Match::NoMatch => { pending.clear(); /* replay/pass through */ }
59//!     }
60//! }
61//! ```
62//!
63//! ## Runnable example
64//!
65//! `cargo run -p keymap-seq --example leader_sequence` runs the
66//! buffer-and-flush loop above, including a timed `jj`-style window demo
67//! using [`TimedPending`]
68//! ([source](https://github.com/S-Nakamur-a/keymap-rs/tree/main/crates/keymap-seq/examples/leader_sequence.rs)).
69//!
70//! [`keymap-core`]: keymap_core
71
72mod map;
73mod progress;
74mod timed;
75
76pub use map::{Continuation, Match, SeqBindError, SequenceKeymap};
77pub use progress::{PendingSequence, Step};
78pub use timed::{TimedPending, TimedStep};