pub struct PendingSequence { /* private fields */ }Expand description
A caller-owned pending buffer for resolving multi-key sequences, with the clear/retain bookkeeping folded in.
SequenceKeymap::lookup answers “exact / prefix / miss” for the keys so
far, but leaves the caller to push each key, clear the buffer on a hit or a
miss, and keep it on a prefix — the mechanical, easy-to-get-wrong half of
keymap-seq’s “the caller owns the buffer” contract (see the crate docs and
the leader_sequence example). PendingSequence owns exactly that buffer and
that bookkeeping, and nothing else: like the rest of keymap-rs it holds no
clock.
The idle window a jj-style binding needs — “abandon a half-typed prefix if
the next key is too slow” — stays the caller’s policy. The library cannot see
time, so it cannot decide a prefix was abandoned; the caller runs its own
timer and calls flush when that timer fires. Making the idle
flush an explicit method (rather than a buffer the caller forgets to drain) is
the point: it is the one step the old example could only describe in a comment.
It is generic over no lifetime — the SequenceKeymap is passed to
feed per call rather than borrowed for the buffer’s lifetime —
so a PendingSequence can live as a field beside the map it reads. Holding a
borrow would make that a self-referential struct, which is exactly the layout
a modal-UI caller wants (struct App { map: SequenceKeymap<A>, pending: PendingSequence }).
use keymap_core::{Key, KeyInput, Modifiers};
use keymap_seq::{PendingSequence, SequenceKeymap, Step};
#[derive(Debug, PartialEq)]
enum Action {
Save,
}
let cx = KeyInput::new(Key::Char('x'), Modifiers::CTRL);
let cs = KeyInput::new(Key::Char('s'), Modifiers::CTRL);
let mut map = SequenceKeymap::new();
map.bind([cx, cs], Action::Save).unwrap();
let mut pending = PendingSequence::new();
assert!(matches!(pending.feed(&map, cx), Step::Pending));
assert!(matches!(pending.feed(&map, cs), Step::Fired(&Action::Save)));
// Fired cleared the buffer, so the next key starts a fresh sequence.
assert!(pending.is_empty());Implementations§
Source§impl PendingSequence
impl PendingSequence
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates an empty pending buffer.
Examples found in repository?
94fn timed() {
95 const WINDOW: Duration = Duration::from_millis(500);
96
97 println!("== timed (jj) ==");
98 let mut map = SequenceKeymap::new();
99 map.bind([plain('j'), plain('j')], Action::NormalMode)
100 .unwrap();
101
102 // `(key, timestamp-since-start)`: a deterministic stand-in for an event
103 // loop's clock (no real `Instant`/`sleep`, so the demo can't be flaky). We
104 // compare the *inter-key* gap to the window — the gap since the last key the
105 // pending prefix accepted, which is what "pressed twice quickly" means.
106 let stream = [
107 (plain('j'), Duration::from_millis(0)),
108 (plain('j'), Duration::from_millis(120)), // quick: completes `jj`
109 (plain('j'), Duration::from_millis(900)),
110 (plain('j'), Duration::from_millis(1700)), // 800ms gap: too slow
111 ];
112
113 let mut pending = PendingSequence::new();
114 let mut last: Option<Duration> = None;
115 for (key, now) in stream {
116 // Timeout check lives here, in the caller, before the new key is judged:
117 // a too-slow key means the held prefix was abandoned, so flush it (pass
118 // its keys through as literals) and let this key start fresh.
119 if let Some(prev) = last {
120 if now.saturating_sub(prev) > WINDOW && !pending.is_empty() {
121 let dropped = pending.flush();
122 println!(
123 "{} @ {now:?} -> idle ({:?} gap > {WINDOW:?}), flushed as literals",
124 render(&dropped),
125 now.saturating_sub(prev),
126 );
127 }
128 }
129
130 // `last` tracks the time of the key that last extended a live prefix, so
131 // it is set solely by this resolution: only `Pending` leaves a prefix
132 // waiting on the clock.
133 last = match pending.feed(&map, key) {
134 Step::Fired(action) => {
135 println!("{key} @ {now:?} -> fire {action:?}");
136 None
137 }
138 Step::Pending => {
139 println!("{key} @ {now:?} -> prefix, waiting (window {WINDOW:?})");
140 Some(now)
141 }
142 Step::PassThrough(keys) => {
143 println!("{} @ {now:?} -> no binding, passing through", render(&keys));
144 None
145 }
146 };
147 }
148
149 // The stream ended mid-prefix. A real caller's idle timer would fire after the
150 // window with no further key; here we flush that dangling `j` as a literal —
151 // the step the old version of this example could only describe in a comment.
152 let dangling = pending.flush();
153 if !dangling.is_empty() {
154 println!(
155 "{} -> still pending at end; idle timer flushes it as a literal",
156 render(&dangling)
157 );
158 }
159}Sourcepub fn feed<'a, A>(
&mut self,
map: &'a SequenceKeymap<A>,
key: KeyInput,
) -> Step<'a, A>
pub fn feed<'a, A>( &mut self, map: &'a SequenceKeymap<A>, key: KeyInput, ) -> Step<'a, A>
Pushes key onto the buffer, resolves the buffer against map, and
returns what the caller should do — applying the clear/retain bookkeeping
itself.
This is the SequenceKeymap::lookup trichotomy turned into an action:
Match::Exact→Step::Fired, and the buffer is cleared (the sequence completed; the nextfeedstarts fresh).Match::Prefix→Step::Pending, and the buffer is kept (more keys may follow; (re)start your idle timer).Match::NoMatch→Step::PassThroughcarrying the whole buffer (every key buffered so far, in order), and the buffer is cleared.
The whole buffer — not just key — is returned on a miss because the
earlier keys were held on the promise of a longer binding that did not
materialise, so they are now the caller’s to handle (replay, pass to the
PTY, …). A key that would have started its own sequence but arrived
mid-prefix is passed through with the rest, not re-fed: “is this key a
fresh start or the tail of an abandoned prefix?” is lookahead policy, which
stays the caller’s (the same reason the library has no clock).
Examples found in repository?
94fn timed() {
95 const WINDOW: Duration = Duration::from_millis(500);
96
97 println!("== timed (jj) ==");
98 let mut map = SequenceKeymap::new();
99 map.bind([plain('j'), plain('j')], Action::NormalMode)
100 .unwrap();
101
102 // `(key, timestamp-since-start)`: a deterministic stand-in for an event
103 // loop's clock (no real `Instant`/`sleep`, so the demo can't be flaky). We
104 // compare the *inter-key* gap to the window — the gap since the last key the
105 // pending prefix accepted, which is what "pressed twice quickly" means.
106 let stream = [
107 (plain('j'), Duration::from_millis(0)),
108 (plain('j'), Duration::from_millis(120)), // quick: completes `jj`
109 (plain('j'), Duration::from_millis(900)),
110 (plain('j'), Duration::from_millis(1700)), // 800ms gap: too slow
111 ];
112
113 let mut pending = PendingSequence::new();
114 let mut last: Option<Duration> = None;
115 for (key, now) in stream {
116 // Timeout check lives here, in the caller, before the new key is judged:
117 // a too-slow key means the held prefix was abandoned, so flush it (pass
118 // its keys through as literals) and let this key start fresh.
119 if let Some(prev) = last {
120 if now.saturating_sub(prev) > WINDOW && !pending.is_empty() {
121 let dropped = pending.flush();
122 println!(
123 "{} @ {now:?} -> idle ({:?} gap > {WINDOW:?}), flushed as literals",
124 render(&dropped),
125 now.saturating_sub(prev),
126 );
127 }
128 }
129
130 // `last` tracks the time of the key that last extended a live prefix, so
131 // it is set solely by this resolution: only `Pending` leaves a prefix
132 // waiting on the clock.
133 last = match pending.feed(&map, key) {
134 Step::Fired(action) => {
135 println!("{key} @ {now:?} -> fire {action:?}");
136 None
137 }
138 Step::Pending => {
139 println!("{key} @ {now:?} -> prefix, waiting (window {WINDOW:?})");
140 Some(now)
141 }
142 Step::PassThrough(keys) => {
143 println!("{} @ {now:?} -> no binding, passing through", render(&keys));
144 None
145 }
146 };
147 }
148
149 // The stream ended mid-prefix. A real caller's idle timer would fire after the
150 // window with no further key; here we flush that dangling `j` as a literal —
151 // the step the old version of this example could only describe in a comment.
152 let dangling = pending.flush();
153 if !dangling.is_empty() {
154 println!(
155 "{} -> still pending at end; idle timer flushes it as a literal",
156 render(&dangling)
157 );
158 }
159}Sourcepub fn flush(&mut self) -> Vec<KeyInput>
pub fn flush(&mut self) -> Vec<KeyInput>
Drains the pending buffer, returning the keys held in it (empty if none), and leaves the buffer empty.
This is the idle flush: call it when your timer says a pending prefix
was abandoned (no further key arrived in time). The returned keys are
literals the caller now owns — the dangling j of a half-typed jj that
must still reach the application as a plain j. It takes no time argument
and consults no window: the decision that the prefix is stale is the
caller’s; flush only performs the drain the caller decided on. Flushing
an empty buffer is a no-op that returns an empty Vec.
Examples found in repository?
94fn timed() {
95 const WINDOW: Duration = Duration::from_millis(500);
96
97 println!("== timed (jj) ==");
98 let mut map = SequenceKeymap::new();
99 map.bind([plain('j'), plain('j')], Action::NormalMode)
100 .unwrap();
101
102 // `(key, timestamp-since-start)`: a deterministic stand-in for an event
103 // loop's clock (no real `Instant`/`sleep`, so the demo can't be flaky). We
104 // compare the *inter-key* gap to the window — the gap since the last key the
105 // pending prefix accepted, which is what "pressed twice quickly" means.
106 let stream = [
107 (plain('j'), Duration::from_millis(0)),
108 (plain('j'), Duration::from_millis(120)), // quick: completes `jj`
109 (plain('j'), Duration::from_millis(900)),
110 (plain('j'), Duration::from_millis(1700)), // 800ms gap: too slow
111 ];
112
113 let mut pending = PendingSequence::new();
114 let mut last: Option<Duration> = None;
115 for (key, now) in stream {
116 // Timeout check lives here, in the caller, before the new key is judged:
117 // a too-slow key means the held prefix was abandoned, so flush it (pass
118 // its keys through as literals) and let this key start fresh.
119 if let Some(prev) = last {
120 if now.saturating_sub(prev) > WINDOW && !pending.is_empty() {
121 let dropped = pending.flush();
122 println!(
123 "{} @ {now:?} -> idle ({:?} gap > {WINDOW:?}), flushed as literals",
124 render(&dropped),
125 now.saturating_sub(prev),
126 );
127 }
128 }
129
130 // `last` tracks the time of the key that last extended a live prefix, so
131 // it is set solely by this resolution: only `Pending` leaves a prefix
132 // waiting on the clock.
133 last = match pending.feed(&map, key) {
134 Step::Fired(action) => {
135 println!("{key} @ {now:?} -> fire {action:?}");
136 None
137 }
138 Step::Pending => {
139 println!("{key} @ {now:?} -> prefix, waiting (window {WINDOW:?})");
140 Some(now)
141 }
142 Step::PassThrough(keys) => {
143 println!("{} @ {now:?} -> no binding, passing through", render(&keys));
144 None
145 }
146 };
147 }
148
149 // The stream ended mid-prefix. A real caller's idle timer would fire after the
150 // window with no further key; here we flush that dangling `j` as a literal —
151 // the step the old version of this example could only describe in a comment.
152 let dangling = pending.flush();
153 if !dangling.is_empty() {
154 println!(
155 "{} -> still pending at end; idle timer flushes it as a literal",
156 render(&dangling)
157 );
158 }
159}Sourcepub fn pending(&self) -> &[KeyInput]
pub fn pending(&self) -> &[KeyInput]
The keys buffered so far (a proper prefix of some binding), in press order.
Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Returns true if no keys are buffered.
Examples found in repository?
94fn timed() {
95 const WINDOW: Duration = Duration::from_millis(500);
96
97 println!("== timed (jj) ==");
98 let mut map = SequenceKeymap::new();
99 map.bind([plain('j'), plain('j')], Action::NormalMode)
100 .unwrap();
101
102 // `(key, timestamp-since-start)`: a deterministic stand-in for an event
103 // loop's clock (no real `Instant`/`sleep`, so the demo can't be flaky). We
104 // compare the *inter-key* gap to the window — the gap since the last key the
105 // pending prefix accepted, which is what "pressed twice quickly" means.
106 let stream = [
107 (plain('j'), Duration::from_millis(0)),
108 (plain('j'), Duration::from_millis(120)), // quick: completes `jj`
109 (plain('j'), Duration::from_millis(900)),
110 (plain('j'), Duration::from_millis(1700)), // 800ms gap: too slow
111 ];
112
113 let mut pending = PendingSequence::new();
114 let mut last: Option<Duration> = None;
115 for (key, now) in stream {
116 // Timeout check lives here, in the caller, before the new key is judged:
117 // a too-slow key means the held prefix was abandoned, so flush it (pass
118 // its keys through as literals) and let this key start fresh.
119 if let Some(prev) = last {
120 if now.saturating_sub(prev) > WINDOW && !pending.is_empty() {
121 let dropped = pending.flush();
122 println!(
123 "{} @ {now:?} -> idle ({:?} gap > {WINDOW:?}), flushed as literals",
124 render(&dropped),
125 now.saturating_sub(prev),
126 );
127 }
128 }
129
130 // `last` tracks the time of the key that last extended a live prefix, so
131 // it is set solely by this resolution: only `Pending` leaves a prefix
132 // waiting on the clock.
133 last = match pending.feed(&map, key) {
134 Step::Fired(action) => {
135 println!("{key} @ {now:?} -> fire {action:?}");
136 None
137 }
138 Step::Pending => {
139 println!("{key} @ {now:?} -> prefix, waiting (window {WINDOW:?})");
140 Some(now)
141 }
142 Step::PassThrough(keys) => {
143 println!("{} @ {now:?} -> no binding, passing through", render(&keys));
144 None
145 }
146 };
147 }
148
149 // The stream ended mid-prefix. A real caller's idle timer would fire after the
150 // window with no further key; here we flush that dangling `j` as a literal —
151 // the step the old version of this example could only describe in a comment.
152 let dangling = pending.flush();
153 if !dangling.is_empty() {
154 println!(
155 "{} -> still pending at end; idle timer flushes it as a literal",
156 render(&dangling)
157 );
158 }
159}Trait Implementations§
Source§impl Clone for PendingSequence
impl Clone for PendingSequence
Source§fn clone(&self) -> PendingSequence
fn clone(&self) -> PendingSequence
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more