a random KeyMatcher appears

This commit is contained in:
🪞👃🪞 2025-01-14 19:44:50 +01:00
parent 12faadef44
commit 44201ebf76
6 changed files with 152 additions and 80 deletions

View file

@ -2,50 +2,134 @@ use crate::*;
impl EdnInput for TuiIn {
fn matches_edn (&self, token: &str) -> bool {
if let Some(event) = parse_key_spec(token.to_string(), KeyModifiers::NONE) {
if let Some(event) = KeyMatcher::new(token).build() {
&event == self.event()
} else {
false
}
}
fn get_event <S: AsRef<str>> (item: &EdnItem<S>) -> Option<Event> {
match item { EdnItem::Sym(s) => parse_key_spec(s.as_ref().to_string(), KeyModifiers::NONE), _ => None }
match item { EdnItem::Sym(s) => KeyMatcher::new(s).build(), _ => None }
}
}
fn parse_key_spec (spec: String, mut mods: KeyModifiers) -> Option<Event> {
Some(Event::Key(match spec.as_str() {
":enter" | ":return" => KeyEvent::new(KeyCode::Enter, mods),
":delete" | ":del" => KeyEvent::new(KeyCode::Enter, mods),
":comma" => KeyEvent::new(KeyCode::Char(','), mods),
":period" => KeyEvent::new(KeyCode::Char('.'), mods),
":plus" => KeyEvent::new(KeyCode::Char('+'), mods),
":underscore" => KeyEvent::new(KeyCode::Char('_'), mods),
":up" => KeyEvent::new(KeyCode::Up, mods),
":down" => KeyEvent::new(KeyCode::Down, mods),
":left" => KeyEvent::new(KeyCode::Left, mods),
":right" => KeyEvent::new(KeyCode::Right, mods),
_ => {
let chars = spec.chars().collect::<Vec<_>>();
match chars.as_slice() {
[':', c] => KeyEvent::new(KeyCode::Char(*c), mods),
[':', c @ ..] => {
let mut splits = c.split(|c|*c=='-');
while let Some(split) = splits.next() {
match split {
['c','t','r','l'] => mods |= KeyModifiers::CONTROL,
['a','l','t'] => mods |= KeyModifiers::ALT,
['s','h','i','f','t'] => mods |= KeyModifiers::SHIFT,
_ => return parse_key_spec(split.iter().collect(), mods)
}
}
panic!("invalid key event {spec:?}")
struct KeyMatcher {
valid: bool,
key: Option<KeyCode>,
mods: KeyModifiers,
}
impl KeyMatcher {
fn new (token: impl AsRef<str>) -> Self {
let token = token.as_ref();
if token.len() < 2 {
Self { valid: false, key: None, mods: KeyModifiers::NONE }
} else if token.chars().next() != Some(':') {
Self { valid: false, key: None, mods: KeyModifiers::NONE }
} else {
Self { valid: true, key: None, mods: KeyModifiers::NONE }.next(&token[1..])
}
}
fn next (mut self, token: &str) -> Self {
let mut tokens = token.split('-').peekable();
while let Some(token) = tokens.next() {
if tokens.peek().is_some() {
match token {
"ctrl" | "Ctrl" | "c" | "C" => self.mods |= KeyModifiers::CONTROL,
"alt" | "Alt" | "m" | "M" => self.mods |= KeyModifiers::ALT,
"shift" | "Shift" | "s" | "S" => {
self.mods |= KeyModifiers::SHIFT;
// + TODO normalize character case, BackTab, etc.
},
_ => panic!("unknown modifier {token}"),
}
} else {
self.key = if token.len() == 1 {
Some(KeyCode::Char(token.chars().next().unwrap()))
} else {
Some(Self::named_key(token).unwrap_or_else(||panic!("unknown character {token}")))
}
_ => panic!("invalid key event {spec:?}")
}
}
}))
self
}
fn named_key (token: &str) -> Option<KeyCode> {
use KeyCode::*;
Some(match token {
"up" => Up,
"down" => Down,
"left" => Left,
"right" => Right,
"enter" | "return" => Enter,
"delete" | "del" => Delete,
"tab" => Tab,
"space" => Char(' '),
"comma" => Char(','),
"period" => Char('.'),
"plus" => Char('+'),
"minus" | "dash" => Char('-'),
"equal" | "equals" => Char('='),
"underscore" => Char('_'),
"backtick" => Char('`'),
_ => return None,
})
}
fn build (self) -> Option<Event> {
if self.valid && self.key.is_some() {
Some(Event::Key(KeyEvent::new(self.key.unwrap(), self.mods)))
} else {
None
}
}
}
#[cfg(test)] #[test] fn test_parse_key () {
use KeyModifiers as Mods;
let test = |x: &str, y|assert_eq!(KeyMatcher::new(x).build(), Some(Event::Key(y)));
test(":x",
KeyEvent::new(KeyCode::Char('x'), Mods::NONE));
test(":ctrl-x",
KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL));
test(":alt-x",
KeyEvent::new(KeyCode::Char('x'), Mods::ALT));
test(":shift-x",
KeyEvent::new(KeyCode::Char('x'), Mods::SHIFT));
test(":ctrl-alt-shift-x",
KeyEvent::new(KeyCode::Char('x'), Mods::CONTROL | Mods::ALT | Mods::SHIFT ));
}
//fn parse_key (spec: std::str::Chars<'_>, mut mods: KeyModifiers) -> Option<Event> {
//Some(Event::Key(match spec.chars() {
//":enter" | ":return" => KeyEvent::new(KeyCode::Enter, mods),
//":delete" | ":del" => KeyEvent::new(KeyCode::Enter, mods),
//":comma" => KeyEvent::new(KeyCode::Char(','), mods),
//":period" => KeyEvent::new(KeyCode::Char('.'), mods),
//":plus" => KeyEvent::new(KeyCode::Char('+'), mods),
//":underscore" => KeyEvent::new(KeyCode::Char('_'), mods),
//":up" => KeyEvent::new(KeyCode::Up, mods),
//":down" => KeyEvent::new(KeyCode::Down, mods),
//":left" => KeyEvent::new(KeyCode::Left, mods),
//":right" => KeyEvent::new(KeyCode::Right, mods),
//_ => {
//let chars = spec.chars().collect::<Vec<_>>();
//match chars.as_slice() {
//[':', c] => KeyEvent::new(KeyCode::Char(*c), mods),
//[':', c @ ..] => {
//let mut splits = c.split(|c|*c=='-');
//while let Some(split) = splits.next() {
//match split {
//['c','t','r','l'] => mods |= KeyModifiers::CONTROL,
//['a','l','t'] => mods |= KeyModifiers::ALT,
//['s','h','i','f','t'] => mods |= KeyModifiers::SHIFT,
//_ => return parse_key(split.iter().collect(), mods)
//}
//}
//panic!("invalid key event {spec:?}")
//}
//_ => panic!("invalid key event {spec:?}")
//}
//}
//}))
//}