use std::time::Duration;
use keymap_core::{Key, KeyInput, Modifiers};
use keymap_seq::{Match, PendingSequence, SequenceKeymap, Step};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Action {
Save,
Quit,
GotoTop,
NormalMode,
}
fn ctrl(c: char) -> KeyInput {
KeyInput::new(Key::Char(c), Modifiers::CTRL)
}
fn plain(c: char) -> KeyInput {
KeyInput::new(Key::Char(c), Modifiers::NONE)
}
fn main() {
untimed();
println!();
timed();
}
fn untimed() {
println!("== untimed ==");
let mut map = SequenceKeymap::new();
map.bind([ctrl('x'), ctrl('s')], Action::Save).unwrap();
map.bind([ctrl('x'), ctrl('c')], Action::Quit).unwrap();
map.bind([plain('g'), plain('g')], Action::GotoTop).unwrap();
let stream = [
ctrl('x'),
ctrl('s'),
ctrl('x'),
plain('z'),
plain('g'),
plain('g'),
];
let mut pending: Vec<KeyInput> = Vec::new();
for key in stream {
pending.push(key);
match map.lookup(&pending) {
Match::Exact(action) => {
println!("{} -> fire {action:?}", render(&pending));
pending.clear();
}
Match::Prefix => {
println!("{} -> prefix, waiting", render(&pending));
}
Match::NoMatch => {
println!("{} -> no binding, passing through", render(&pending));
pending.clear();
}
}
}
}
fn timed() {
const WINDOW: Duration = Duration::from_millis(500);
println!("== timed (jj) ==");
let mut map = SequenceKeymap::new();
map.bind([plain('j'), plain('j')], Action::NormalMode)
.unwrap();
let stream = [
(plain('j'), Duration::from_millis(0)),
(plain('j'), Duration::from_millis(120)), (plain('j'), Duration::from_millis(900)),
(plain('j'), Duration::from_millis(1700)), ];
let mut pending = PendingSequence::new();
let mut last: Option<Duration> = None;
for (key, now) in stream {
if let Some(prev) = last {
if now.saturating_sub(prev) > WINDOW && !pending.is_empty() {
let dropped = pending.flush();
println!(
"{} @ {now:?} -> idle ({:?} gap > {WINDOW:?}), flushed as literals",
render(&dropped),
now.saturating_sub(prev),
);
}
}
last = match pending.feed(&map, key) {
Step::Fired(action) => {
println!("{key} @ {now:?} -> fire {action:?}");
None
}
Step::Pending => {
println!("{key} @ {now:?} -> prefix, waiting (window {WINDOW:?})");
Some(now)
}
Step::PassThrough(keys) => {
println!("{} @ {now:?} -> no binding, passing through", render(&keys));
None
}
};
}
let dangling = pending.flush();
if !dangling.is_empty() {
println!(
"{} -> still pending at end; idle timer flushes it as a literal",
render(&dangling)
);
}
}
fn render(keys: &[KeyInput]) -> String {
keys.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(" ")
}