Skip to main content

TimedPending

Struct TimedPending 

Source
pub struct TimedPending { /* private fields */ }
Expand description

A PendingSequence with a time-window expiry gate.

Each call to feed performs three things in order:

  1. If the pending buffer is non-empty and the gap since the last accepted key exceeds window, the old buffer is drained, and the result’s expired field carries those keys (guaranteed non-empty when Some).
  2. The buffer (now empty if it just expired) accepts the new key.
  3. The new-key Step is returned in TimedStep::step.

The library does not call Instant::now(); now is caller data (see the crate docs and PendingSequence).

§Layout

TimedPending is designed to live beside the map it reads:

use keymap_seq::{SequenceKeymap, TimedPending};

struct App<A> {
    map: SequenceKeymap<A>,
    pending: TimedPending,
}

§Example

use std::time::{Duration, Instant};
use keymap_core::{Key, KeyInput, Modifiers};
use keymap_seq::{SequenceKeymap, Step, TimedPending, TimedStep};

#[derive(Debug, PartialEq)]
enum Action { NormalMode }

let j = KeyInput::new(Key::Char('j'), Modifiers::NONE);
let mut map = SequenceKeymap::new();
map.bind([j, j], Action::NormalMode).unwrap();

const WINDOW: Duration = Duration::from_millis(500);
let base = Instant::now();
let mut pending = TimedPending::new();

// First j: still a prefix, no expiry yet.
let r1 = pending.feed(&map, j, base, WINDOW);
assert!(r1.expired.is_none());
assert!(matches!(r1.step, Step::Pending));

// Second j arrives within the window: fires.
let r2 = pending.feed(&map, j, base + Duration::from_millis(100), WINDOW);
assert!(r2.expired.is_none());
assert!(matches!(r2.step, Step::Fired(&Action::NormalMode)));

Implementations§

Source§

impl TimedPending

Source

pub fn new() -> Self

Creates an empty timed pending buffer.

Examples found in repository?
examples/leader_sequence.rs (line 103)
86fn timed() {
87    const WINDOW: Duration = Duration::from_millis(500);
88
89    println!("== timed (jj, via TimedPending) ==");
90    let mut map = SequenceKeymap::new();
91    map.bind([plain('j'), plain('j')], Action::NormalMode)
92        .unwrap();
93
94    // Deterministic timestamps: base + offset (no real sleep / Instant::now).
95    let base = Instant::now();
96    let stream = [
97        (plain('j'), 0u64),
98        (plain('j'), 120), // 120ms gap: quick, completes `jj`
99        (plain('j'), 900),
100        (plain('j'), 1700), // 800ms gap: too slow, expiry fires
101    ];
102
103    let mut pending = TimedPending::new();
104    for (key, ms) in stream {
105        let now = base + Duration::from_millis(ms);
106        let result = pending.feed(&map, key, now, WINDOW);
107
108        // If a previous prefix expired, report its keys as literals first.
109        if let Some(expired) = result.expired {
110            println!(
111                "{} @ +{ms}ms -> idle timeout, flushed as literals",
112                render(&expired),
113            );
114        }
115
116        match result.step {
117            Step::Fired(action) => println!("{key} @ +{ms}ms -> fire {action:?}"),
118            Step::Pending => println!("{key} @ +{ms}ms -> prefix, waiting (window {WINDOW:?})"),
119            Step::PassThrough(keys) => {
120                println!("{} @ +{ms}ms -> no binding, passing through", render(&keys));
121            }
122        }
123    }
124
125    // The stream ended mid-prefix. Drain as literals (what an idle timer does).
126    let dangling = pending.flush();
127    if !dangling.is_empty() {
128        println!(
129            "{} -> pending at end; flushed as literals",
130            render(&dangling)
131        );
132    }
133}
Source

pub fn feed<'a, A>( &mut self, map: &'a SequenceKeymap<A>, key: KeyInput, now: Instant, window: Duration, ) -> TimedStep<'a, A>

Processes one key with expiry checking, returning the combined result.

The call sequence is:

  1. Expiry check: if the pending buffer is non-empty and now - last_key_at > window, the buffer is flushed. The flushed keys appear in TimedStep::expired (always non-empty when Some).
  2. Key processing: key is fed into the (possibly just-reset) buffer via PendingSequence::feed, and the result becomes TimedStep::step.
  3. Clock update: last_key_at is set to now when the step is Step::Pending (the buffer grew), or cleared otherwise.

The library never calls Instant::now(). Pass the event timestamp from your event loop.

Examples found in repository?
examples/leader_sequence.rs (line 106)
86fn timed() {
87    const WINDOW: Duration = Duration::from_millis(500);
88
89    println!("== timed (jj, via TimedPending) ==");
90    let mut map = SequenceKeymap::new();
91    map.bind([plain('j'), plain('j')], Action::NormalMode)
92        .unwrap();
93
94    // Deterministic timestamps: base + offset (no real sleep / Instant::now).
95    let base = Instant::now();
96    let stream = [
97        (plain('j'), 0u64),
98        (plain('j'), 120), // 120ms gap: quick, completes `jj`
99        (plain('j'), 900),
100        (plain('j'), 1700), // 800ms gap: too slow, expiry fires
101    ];
102
103    let mut pending = TimedPending::new();
104    for (key, ms) in stream {
105        let now = base + Duration::from_millis(ms);
106        let result = pending.feed(&map, key, now, WINDOW);
107
108        // If a previous prefix expired, report its keys as literals first.
109        if let Some(expired) = result.expired {
110            println!(
111                "{} @ +{ms}ms -> idle timeout, flushed as literals",
112                render(&expired),
113            );
114        }
115
116        match result.step {
117            Step::Fired(action) => println!("{key} @ +{ms}ms -> fire {action:?}"),
118            Step::Pending => println!("{key} @ +{ms}ms -> prefix, waiting (window {WINDOW:?})"),
119            Step::PassThrough(keys) => {
120                println!("{} @ +{ms}ms -> no binding, passing through", render(&keys));
121            }
122        }
123    }
124
125    // The stream ended mid-prefix. Drain as literals (what an idle timer does).
126    let dangling = pending.flush();
127    if !dangling.is_empty() {
128        println!(
129            "{} -> pending at end; flushed as literals",
130            render(&dangling)
131        );
132    }
133}
Source

pub fn deadline(&self, window: Duration) -> Option<Instant>

The deadline at which a pending prefix expires, or None if no keys are buffered.

Use this as your event loop’s poll timeout: if no key arrives before deadline, call flush and treat the result as pass-through literals.

use std::time::{Duration, Instant};
use keymap_core::{Key, KeyInput, Modifiers};
use keymap_seq::{SequenceKeymap, TimedPending};

#[derive(Debug)] enum Action { Jump }
let j = KeyInput::new(Key::Char('j'), Modifiers::NONE);
let mut map = SequenceKeymap::new();
map.bind([j, j], Action::Jump).unwrap();

const WINDOW: Duration = Duration::from_millis(500);
let mut pending = TimedPending::new();

// Empty buffer → no deadline.
assert!(pending.deadline(WINDOW).is_none());

let base = Instant::now();
pending.feed(&map, j, base, WINDOW);
// After accepting a prefix key, deadline = base + window.
assert_eq!(pending.deadline(WINDOW), Some(base + WINDOW));
Source

pub fn flush(&mut self) -> Vec<KeyInput>

Drains the pending buffer, returning the keys held in it, and leaves the buffer empty.

This is the idle flush: call it when deadline has passed with no further key. The returned keys are literals the caller now owns (the dangling j of a half-typed jj). Flushing an empty buffer returns an empty Vec.

Examples found in repository?
examples/leader_sequence.rs (line 126)
86fn timed() {
87    const WINDOW: Duration = Duration::from_millis(500);
88
89    println!("== timed (jj, via TimedPending) ==");
90    let mut map = SequenceKeymap::new();
91    map.bind([plain('j'), plain('j')], Action::NormalMode)
92        .unwrap();
93
94    // Deterministic timestamps: base + offset (no real sleep / Instant::now).
95    let base = Instant::now();
96    let stream = [
97        (plain('j'), 0u64),
98        (plain('j'), 120), // 120ms gap: quick, completes `jj`
99        (plain('j'), 900),
100        (plain('j'), 1700), // 800ms gap: too slow, expiry fires
101    ];
102
103    let mut pending = TimedPending::new();
104    for (key, ms) in stream {
105        let now = base + Duration::from_millis(ms);
106        let result = pending.feed(&map, key, now, WINDOW);
107
108        // If a previous prefix expired, report its keys as literals first.
109        if let Some(expired) = result.expired {
110            println!(
111                "{} @ +{ms}ms -> idle timeout, flushed as literals",
112                render(&expired),
113            );
114        }
115
116        match result.step {
117            Step::Fired(action) => println!("{key} @ +{ms}ms -> fire {action:?}"),
118            Step::Pending => println!("{key} @ +{ms}ms -> prefix, waiting (window {WINDOW:?})"),
119            Step::PassThrough(keys) => {
120                println!("{} @ +{ms}ms -> no binding, passing through", render(&keys));
121            }
122        }
123    }
124
125    // The stream ended mid-prefix. Drain as literals (what an idle timer does).
126    let dangling = pending.flush();
127    if !dangling.is_empty() {
128        println!(
129            "{} -> pending at end; flushed as literals",
130            render(&dangling)
131        );
132    }
133}
Source

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

The keys buffered so far, in press order.

Source

pub fn is_empty(&self) -> bool

Returns true if no keys are buffered.

Trait Implementations§

Source§

impl Clone for TimedPending

Source§

fn clone(&self) -> TimedPending

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 TimedPending

Source§

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

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

impl Default for TimedPending

Source§

fn default() -> TimedPending

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.