use std::collections::HashMap;
use std::time::{Duration, Instant};
use thiserror::Error;
use crate::chord::{Chord, ChordParseError};
use crate::key::KeyEvent;
use crate::trie::{Binding, TrieNode};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Mode {
Normal,
Insert,
Visual,
OpPending,
CommandLine,
}
#[derive(Debug, Error)]
pub enum KeymapError {
#[error("chord parse error: {0}")]
Parse(#[from] ChordParseError),
#[error("chord is empty")]
EmptyChord,
}
#[derive(Debug)]
pub enum KeyResolve<A> {
Pending,
Match(Binding<A>),
Ambiguous,
Unbound(Vec<KeyEvent>),
}
#[derive(Default)]
struct ModeState {
buffer: Vec<KeyEvent>,
}
pub struct Keymap<A> {
trees: HashMap<Mode, TrieNode<A>>,
leader: char,
timeout: Duration,
state: HashMap<Mode, ModeState>,
}
impl<A: Clone> Keymap<A> {
pub fn new(leader: char) -> Self {
Self {
trees: HashMap::new(),
leader,
timeout: Duration::from_millis(500),
state: HashMap::new(),
}
}
pub fn set_leader(&mut self, c: char) {
self.leader = c;
}
pub fn set_timeout(&mut self, t: Duration) {
self.timeout = t;
}
pub fn leader(&self) -> char {
self.leader
}
pub fn timeout_duration(&self) -> Duration {
self.timeout
}
pub fn add(
&mut self,
mode: Mode,
chord_str: &str,
action: A,
desc: &str,
) -> Result<(), KeymapError> {
let chord = Chord::parse(chord_str, self.leader)?;
if chord.is_empty() {
return Err(KeymapError::EmptyChord);
}
let binding = Binding {
action,
desc: desc.to_string(),
recursive: false,
};
self.add_chord(mode, chord, binding);
Ok(())
}
pub fn add_chord(&mut self, mode: Mode, chord: Chord, binding: Binding<A>) {
self.trees
.entry(mode)
.or_default()
.insert(&chord.0, binding);
}
pub fn remove(&mut self, mode: Mode, chord_str: &str) -> Result<bool, KeymapError> {
let chord = Chord::parse(chord_str, self.leader)?;
if chord.is_empty() {
return Err(KeymapError::EmptyChord);
}
let removed = self
.trees
.get_mut(&mode)
.map(|t| t.remove(&chord.0))
.unwrap_or(false);
Ok(removed)
}
pub fn children(&self, mode: Mode, prefix: &Chord) -> Vec<(KeyEvent, Binding<A>)> {
let Some(tree) = self.trees.get(&mode) else {
return vec![];
};
tree.children_of(&prefix.0)
.into_iter()
.map(|(k, b)| (*k, b.clone()))
.collect()
}
pub fn children_all(&self, mode: Mode, prefix: &Chord) -> Vec<(KeyEvent, Option<Binding<A>>)> {
let Some(tree) = self.trees.get(&mode) else {
return vec![];
};
tree.all_children_of(&prefix.0)
.into_iter()
.map(|(k, b)| (*k, b.cloned()))
.collect()
}
pub fn feed(&mut self, mode: Mode, ev: KeyEvent, _now: Instant) -> KeyResolve<A> {
let state = self.state.entry(mode).or_default();
state.buffer.push(ev);
let buf = state.buffer.clone();
let Some(tree) = self.trees.get(&mode) else {
let drained: Vec<KeyEvent> = self
.state
.entry(mode)
.or_default()
.buffer
.drain(..)
.collect();
return KeyResolve::Unbound(drained);
};
let exact = tree.lookup(&buf);
let has_longer = tree.has_prefix(&buf);
match (exact, has_longer) {
(Some(_binding), true) => {
KeyResolve::Ambiguous
}
(Some(binding), false) => {
let binding = binding.clone();
self.state.entry(mode).or_default().buffer.clear();
KeyResolve::Match(binding)
}
(None, true) => {
KeyResolve::Pending
}
(None, false) => {
let drained: Vec<KeyEvent> = self
.state
.entry(mode)
.or_default()
.buffer
.drain(..)
.collect();
KeyResolve::Unbound(drained)
}
}
}
pub fn timeout_resolve(&mut self, mode: Mode) -> KeyResolve<A> {
let buf = match self.state.get(&mode) {
Some(s) if !s.buffer.is_empty() => s.buffer.clone(),
_ => return KeyResolve::Unbound(vec![]),
};
let Some(tree) = self.trees.get(&mode) else {
let drained: Vec<KeyEvent> = self
.state
.entry(mode)
.or_default()
.buffer
.drain(..)
.collect();
return KeyResolve::Unbound(drained);
};
if let Some(binding) = tree.lookup(&buf) {
let binding = binding.clone();
self.state.entry(mode).or_default().buffer.clear();
KeyResolve::Match(binding)
} else {
let drained: Vec<KeyEvent> = self
.state
.entry(mode)
.or_default()
.buffer
.drain(..)
.collect();
KeyResolve::Unbound(drained)
}
}
pub fn pending(&self, mode: Mode) -> &[KeyEvent] {
self.state
.get(&mode)
.map(|s| s.buffer.as_slice())
.unwrap_or(&[])
}
pub fn reset(&mut self, mode: Mode) {
if let Some(state) = self.state.get_mut(&mode) {
state.buffer.clear();
}
}
pub fn pop(&mut self, mode: Mode) -> Option<KeyEvent> {
self.state.get_mut(&mode)?.buffer.pop()
}
}