use keymap_core::KeyInput;
use crate::{Match, SequenceKeymap};
#[derive(Debug, Clone, Default)]
pub struct PendingSequence {
pending: Vec<KeyInput>,
}
impl PendingSequence {
#[must_use]
pub fn new() -> Self {
PendingSequence::default()
}
pub fn feed<'a, A>(&mut self, map: &'a SequenceKeymap<A>, key: KeyInput) -> Step<'a, A> {
self.pending.push(key);
match map.lookup(&self.pending) {
Match::Exact(action) => {
self.pending.clear();
Step::Fired(action)
}
Match::Prefix => Step::Pending,
Match::NoMatch => Step::PassThrough(std::mem::take(&mut self.pending)),
}
}
pub fn flush(&mut self) -> Vec<KeyInput> {
std::mem::take(&mut self.pending)
}
#[must_use]
pub fn pending(&self) -> &[KeyInput] {
&self.pending
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.pending.is_empty()
}
}
#[derive(Debug, PartialEq, Eq)]
#[must_use]
pub enum Step<'a, A> {
Fired(&'a A),
Pending,
PassThrough(Vec<KeyInput>),
}
#[cfg(test)]
mod tests {
use super::*;
use keymap_core::{Key, Modifiers};
#[derive(Debug, Clone, PartialEq)]
enum Action {
Save,
Quit,
Top,
Solo,
}
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 sample() -> SequenceKeymap<Action> {
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::Top).unwrap();
map.bind([plain('q')], Action::Solo).unwrap();
map
}
#[test]
fn prefix_then_exact_fires_and_clears() {
let map = sample();
let mut p = PendingSequence::new();
assert_eq!(p.feed(&map, ctrl('x')), Step::Pending);
assert_eq!(p.pending(), &[ctrl('x')]);
assert_eq!(p.feed(&map, ctrl('s')), Step::Fired(&Action::Save));
assert!(p.is_empty());
}
#[test]
fn clear_after_exact_lets_the_next_sequence_start_fresh() {
let map = sample();
let mut p = PendingSequence::new();
assert_eq!(p.feed(&map, plain('g')), Step::Pending);
assert_eq!(p.feed(&map, plain('g')), Step::Fired(&Action::Top));
assert_eq!(p.feed(&map, plain('g')), Step::Pending);
assert_eq!(p.pending(), &[plain('g')]);
}
#[test]
fn passthrough_returns_the_whole_buffer_not_just_the_last_key() {
let map = sample();
let mut p = PendingSequence::new();
assert_eq!(p.feed(&map, ctrl('x')), Step::Pending);
assert_eq!(
p.feed(&map, plain('z')),
Step::PassThrough(vec![ctrl('x'), plain('z')])
);
assert!(p.is_empty());
}
#[test]
fn length_one_sequence_fires_on_the_first_key() {
let map = sample();
let mut p = PendingSequence::new();
assert_eq!(p.feed(&map, plain('q')), Step::Fired(&Action::Solo));
assert!(p.is_empty());
}
#[test]
fn empty_map_passes_every_key_through() {
let map: SequenceKeymap<Action> = SequenceKeymap::new();
let mut p = PendingSequence::new();
assert_eq!(
p.feed(&map, plain('a')),
Step::PassThrough(vec![plain('a')])
);
assert!(p.is_empty());
}
#[test]
fn abandoned_prefix_does_not_contaminate_a_fresh_sequence() {
let map = sample();
let mut p = PendingSequence::new();
assert_eq!(p.feed(&map, ctrl('x')), Step::Pending);
assert_eq!(
p.feed(&map, plain('z')),
Step::PassThrough(vec![ctrl('x'), plain('z')])
);
assert_eq!(p.feed(&map, plain('g')), Step::Pending);
assert_eq!(p.feed(&map, plain('g')), Step::Fired(&Action::Top));
assert!(p.is_empty());
}
#[test]
fn a_key_that_could_start_a_sequence_is_passed_through_mid_prefix() {
let mut map = SequenceKeymap::new();
map.bind([plain('a'), plain('b')], Action::Save).unwrap();
map.bind([plain('c')], Action::Solo).unwrap();
let mut p = PendingSequence::new();
assert_eq!(p.feed(&map, plain('a')), Step::Pending);
assert_eq!(
p.feed(&map, plain('c')),
Step::PassThrough(vec![plain('a'), plain('c')])
);
}
#[test]
fn branching_prefix_reaches_both_leaves() {
let map = sample();
let mut p = PendingSequence::new();
assert_eq!(p.feed(&map, ctrl('x')), Step::Pending);
assert_eq!(p.feed(&map, ctrl('c')), Step::Fired(&Action::Quit));
assert_eq!(p.feed(&map, ctrl('x')), Step::Pending);
assert_eq!(p.feed(&map, ctrl('s')), Step::Fired(&Action::Save));
}
#[test]
fn deep_prefix_is_pending_at_every_step_then_fires() {
let mut map = SequenceKeymap::new();
map.bind([plain('a'), plain('b'), plain('c')], Action::Top)
.unwrap();
let mut p = PendingSequence::new();
assert_eq!(p.feed(&map, plain('a')), Step::Pending);
assert_eq!(p.feed(&map, plain('b')), Step::Pending);
assert_eq!(p.feed(&map, plain('c')), Step::Fired(&Action::Top));
}
#[test]
fn flush_drains_a_pending_prefix_as_literals_exactly_once() {
let mut map = SequenceKeymap::new();
map.bind([plain('j'), plain('j')], Action::Top).unwrap();
let mut p = PendingSequence::new();
assert_eq!(p.feed(&map, plain('j')), Step::Pending);
assert_eq!(p.pending(), &[plain('j')]);
assert_eq!(p.flush(), vec![plain('j')]);
assert!(p.is_empty());
assert_eq!(p.flush(), Vec::<KeyInput>::new());
}
#[test]
fn flush_on_empty_buffer_is_a_no_op() {
let mut p = PendingSequence::new();
assert_eq!(p.flush(), Vec::<KeyInput>::new());
assert!(p.is_empty());
}
#[test]
fn flush_after_fired_or_passthrough_is_empty() {
let map = sample();
let mut p = PendingSequence::new();
assert_eq!(p.feed(&map, plain('q')), Step::Fired(&Action::Solo)); assert_eq!(p.flush(), Vec::<KeyInput>::new());
assert_eq!(
p.feed(&map, plain('z')),
Step::PassThrough(vec![plain('z')])
); assert_eq!(p.flush(), Vec::<KeyInput>::new());
}
#[test]
fn no_key_is_dropped_or_duplicated_across_a_stream() {
let map = sample();
let stream = [
ctrl('x'),
ctrl('s'), ctrl('x'),
plain('z'), plain('g'), ];
let mut p = PendingSequence::new();
let mut seen: Vec<KeyInput> = Vec::new();
let mut buf: Vec<KeyInput> = Vec::new();
for key in stream {
buf.push(key);
match p.feed(&map, key) {
Step::Fired(_) => {
seen.append(&mut buf);
}
Step::Pending => {}
Step::PassThrough(keys) => {
assert_eq!(keys, buf);
seen.append(&mut buf);
}
}
}
let mut flushed = p.flush();
seen.append(&mut flushed);
buf.clear();
assert_eq!(seen, stream);
}
}