pub struct TimedPending { /* private fields */ }Expand description
A PendingSequence with a time-window expiry gate.
Each call to feed performs three things in order:
- 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’sexpiredfield carries those keys (guaranteed non-empty whenSome). - The buffer (now empty if it just expired) accepts the new
key. - The new-key
Stepis returned inTimedStep::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
impl TimedPending
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates an empty timed pending buffer.
Examples found in repository?
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}Sourcepub fn feed<'a, A>(
&mut self,
map: &'a SequenceKeymap<A>,
key: KeyInput,
now: Instant,
window: Duration,
) -> TimedStep<'a, A>
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:
- Expiry check: if the pending buffer is non-empty and
now - last_key_at > window, the buffer is flushed. The flushed keys appear inTimedStep::expired(always non-empty whenSome). - Key processing:
keyis fed into the (possibly just-reset) buffer viaPendingSequence::feed, and the result becomesTimedStep::step. - Clock update:
last_key_atis set tonowwhen the step isStep::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?
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}Sourcepub fn deadline(&self, window: Duration) -> Option<Instant>
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));Sourcepub fn flush(&mut self) -> Vec<KeyInput>
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?
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}Trait Implementations§
Source§impl Clone for TimedPending
impl Clone for TimedPending
Source§fn clone(&self) -> TimedPending
fn clone(&self) -> TimedPending
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more