use std::time::{Duration, Instant};
use keymap_core::{Key, KeyInput, Modifiers};
use keymap_seq::{Match, SequenceKeymap, Step, TimedPending};
#[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, via TimedPending) ==");
let mut map = SequenceKeymap::new();
map.bind([plain('j'), plain('j')], Action::NormalMode)
.unwrap();
let base = Instant::now();
let stream = [
(plain('j'), 0u64),
(plain('j'), 120), (plain('j'), 900),
(plain('j'), 1700), ];
let mut pending = TimedPending::new();
for (key, ms) in stream {
let now = base + Duration::from_millis(ms);
let result = pending.feed(&map, key, now, WINDOW);
if let Some(expired) = result.expired {
println!(
"{} @ +{ms}ms -> idle timeout, flushed as literals",
render(&expired),
);
}
match result.step {
Step::Fired(action) => println!("{key} @ +{ms}ms -> fire {action:?}"),
Step::Pending => println!("{key} @ +{ms}ms -> prefix, waiting (window {WINDOW:?})"),
Step::PassThrough(keys) => {
println!("{} @ +{ms}ms -> no binding, passing through", render(&keys));
}
}
}
let dangling = pending.flush();
if !dangling.is_empty() {
println!(
"{} -> pending at end; flushed as literals",
render(&dangling)
);
}
}
fn render(keys: &[KeyInput]) -> String {
keys.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(" ")
}