Skip to main content

PendingSequence

Struct PendingSequence 

Source
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

Source

pub fn new() -> Self

Creates an empty pending buffer.

Examples found in repository?
examples/leader_sequence.rs (line 113)
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}
Source

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:

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?
examples/leader_sequence.rs (line 133)
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}
Source

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?
examples/leader_sequence.rs (line 121)
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}
Source

pub fn pending(&self) -> &[KeyInput]

The keys buffered so far (a proper prefix of some binding), in press order.

Source

pub fn is_empty(&self) -> bool

Returns true if no keys are buffered.

Examples found in repository?
examples/leader_sequence.rs (line 120)
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

Source§

fn clone(&self) -> PendingSequence

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PendingSequence

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for PendingSequence

Source§

fn default() -> PendingSequence

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.