feat(keymaps): Separate the keymapping system into its own crate
This commit is contained in:
parent
51eed5697a
commit
2b39608f85
|
@ -6,3 +6,10 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
log = "0.4.20"
|
||||||
|
thiserror = "1.0.50"
|
||||||
|
pest = "2.7.5"
|
||||||
|
pest_derive = {version = "2.7.5", features = ["grammar-extras"]}
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
pretty_assertions = "1.4.0"
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
use std::{fmt::Display, num::ParseIntError};
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
use crate::key::{key, keys, Keys};
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum TrieInsertError<V: Display> {
|
||||||
|
#[error("The key ('{0}') contains nodes, which already have a value set!")]
|
||||||
|
KeyPathBlocked(Keys),
|
||||||
|
|
||||||
|
#[error("The key ('{key}') already has a value associatet with it, which is: '{value}'")]
|
||||||
|
KeyAlreadySet { key: Keys, value: V },
|
||||||
|
|
||||||
|
#[error("The node accessed by this key ('{0}') already has children! You can not set a value for it")]
|
||||||
|
NodeHasChildren(Keys),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum KeyParseError {
|
||||||
|
#[error("This key expression could not be parsed, see the error message for more: {0}")]
|
||||||
|
RawParseError(#[from] pest::error::Error<key::Rule>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum KeysParseError {
|
||||||
|
#[error(
|
||||||
|
"This chain of key expressions could not be parsed, see the error message for more: {0}"
|
||||||
|
)]
|
||||||
|
RawParseError(#[from] pest::error::Error<keys::Rule>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum KeyValueParseError {
|
||||||
|
#[error("The String ('{0}') is not a correct special key name!")]
|
||||||
|
NoMatch(String),
|
||||||
|
|
||||||
|
#[error("The number associate with the F key ('{0}') can't be parsed as u8!")]
|
||||||
|
CantParseFNumber(#[from] ParseIntError),
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
use crossterm::event::KeyCode;
|
||||||
|
|
||||||
|
impl From<KeyCode> for KeyValue {
|
||||||
|
fn from(value: KeyCode) -> Self {
|
||||||
|
match value {
|
||||||
|
KeyCode::Backspace => Self::Backspace,
|
||||||
|
KeyCode::Enter => Self::Enter,
|
||||||
|
KeyCode::Left => Self::Left,
|
||||||
|
KeyCode::Right => Self::Right,
|
||||||
|
KeyCode::Up => Self::Up,
|
||||||
|
KeyCode::Down => Self::Down,
|
||||||
|
KeyCode::Home => Self::Home,
|
||||||
|
KeyCode::End => Self::End,
|
||||||
|
KeyCode::PageUp => Self::PageUp,
|
||||||
|
KeyCode::PageDown => Self::PageDown,
|
||||||
|
KeyCode::Tab => Self::Tab,
|
||||||
|
KeyCode::BackTab => Self::BackTab,
|
||||||
|
KeyCode::Delete => Self::Delete,
|
||||||
|
KeyCode::Insert => Self::Insert,
|
||||||
|
KeyCode::F(n) => Self::F(n),
|
||||||
|
KeyCode::Char(c) => Self::Char(c),
|
||||||
|
KeyCode::Null => Self::Null,
|
||||||
|
KeyCode::Esc => Self::Esc,
|
||||||
|
KeyCode::CapsLock => Self::CapsLock,
|
||||||
|
KeyCode::ScrollLock => Self::ScrollLock,
|
||||||
|
KeyCode::NumLock => Self::NumLock,
|
||||||
|
KeyCode::PrintScreen => Self::PrintScreen,
|
||||||
|
KeyCode::Pause => Self::Pause,
|
||||||
|
KeyCode::Menu => Self::Menu,
|
||||||
|
KeyCode::KeypadBegin => Self::KeypadBegin,
|
||||||
|
// FIXME(@soispha): This reduces our information, casting a KeyCode to a KeyValue
|
||||||
|
// and back again would not equal the original KeyCode <2023-10-15>
|
||||||
|
KeyCode::Media(_) => Self::Null,
|
||||||
|
KeyCode::Modifier(_) => Self::Null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Into<KeyCode> for KeyValue {
|
||||||
|
fn into(self) -> KeyCode {
|
||||||
|
match self {
|
||||||
|
Self::Backspace => KeyCode::Backspace,
|
||||||
|
Self::Enter => KeyCode::Enter,
|
||||||
|
Self::Left => KeyCode::Left,
|
||||||
|
Self::Right => KeyCode::Right,
|
||||||
|
Self::Up => KeyCode::Up,
|
||||||
|
Self::Down => KeyCode::Down,
|
||||||
|
Self::Home => KeyCode::Home,
|
||||||
|
Self::End => KeyCode::End,
|
||||||
|
Self::PageUp => KeyCode::PageUp,
|
||||||
|
Self::PageDown => KeyCode::PageDown,
|
||||||
|
Self::Tab => KeyCode::Tab,
|
||||||
|
Self::BackTab => KeyCode::BackTab,
|
||||||
|
Self::Delete => KeyCode::Delete,
|
||||||
|
Self::Insert => KeyCode::Insert,
|
||||||
|
Self::F(n) => KeyCode::F(n),
|
||||||
|
Self::Char(c) => KeyCode::Char(c),
|
||||||
|
Self::Null => KeyCode::Null,
|
||||||
|
Self::Esc => KeyCode::Esc,
|
||||||
|
Self::CapsLock => KeyCode::CapsLock,
|
||||||
|
Self::ScrollLock => KeyCode::ScrollLock,
|
||||||
|
Self::NumLock => KeyCode::NumLock,
|
||||||
|
Self::PrintScreen => KeyCode::PrintScreen,
|
||||||
|
Self::Pause => KeyCode::Pause,
|
||||||
|
Self::Menu => KeyCode::Menu,
|
||||||
|
Self::KeypadBegin => KeyCode::KeypadBegin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,6 @@
|
||||||
|
#[cfg(crossterm)]
|
||||||
|
mod crossterm;
|
||||||
|
|
||||||
use std::{collections::VecDeque, fmt::Display};
|
use std::{collections::VecDeque, fmt::Display};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
|
@ -1,357 +0,0 @@
|
||||||
use std::{fmt::Display, str::FromStr};
|
|
||||||
|
|
||||||
use anyhow::{bail, Context};
|
|
||||||
use cli_log::{debug, info};
|
|
||||||
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers};
|
|
||||||
|
|
||||||
use super::{Chars, KeyValue, Keys};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
|
||||||
pub struct Key {
|
|
||||||
// Modifiers
|
|
||||||
pub(super) alt: bool,
|
|
||||||
pub(super) ctrl: bool,
|
|
||||||
pub(super) meta: bool,
|
|
||||||
pub(super) shift: bool,
|
|
||||||
|
|
||||||
pub(super) value: Option<KeyValue>,
|
|
||||||
}
|
|
||||||
impl Key {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Key {
|
|
||||||
alt: false,
|
|
||||||
ctrl: false,
|
|
||||||
meta: false,
|
|
||||||
shift: false,
|
|
||||||
value: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn value(&self) -> Option<&KeyValue> {
|
|
||||||
self.value.as_ref()
|
|
||||||
}
|
|
||||||
pub fn to_string_repr(self) -> String {
|
|
||||||
let mut output = String::new();
|
|
||||||
if self.alt || self.ctrl || self.meta || self.shift {
|
|
||||||
output.push('<')
|
|
||||||
}
|
|
||||||
if self.alt {
|
|
||||||
output.push('A');
|
|
||||||
}
|
|
||||||
if self.ctrl {
|
|
||||||
output.push('C');
|
|
||||||
}
|
|
||||||
if self.meta {
|
|
||||||
output.push('M');
|
|
||||||
}
|
|
||||||
if self.shift {
|
|
||||||
output.push('S');
|
|
||||||
}
|
|
||||||
if self.alt || self.ctrl || self.meta || self.shift {
|
|
||||||
output.push('-')
|
|
||||||
}
|
|
||||||
output.push_str(
|
|
||||||
&self
|
|
||||||
.value
|
|
||||||
.expect("There can be no Nones here, if the Key comes from the public api")
|
|
||||||
.to_string(),
|
|
||||||
);
|
|
||||||
if self.alt || self.ctrl || self.meta || self.shift {
|
|
||||||
output.push('>')
|
|
||||||
}
|
|
||||||
output
|
|
||||||
}
|
|
||||||
fn merge_with(mut self, other: Key) -> Self {
|
|
||||||
// Modifiers
|
|
||||||
self.alt = self.alt || other.alt;
|
|
||||||
self.ctrl = self.ctrl || other.ctrl;
|
|
||||||
self.meta = self.meta || other.meta;
|
|
||||||
self.shift = self.shift || other.shift;
|
|
||||||
|
|
||||||
self.value = Some(self.value.unwrap_or(other.value.unwrap_or(KeyValue::Null)));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub(super) fn parse(chars: &mut Chars) -> anyhow::Result<Self> {
|
|
||||||
assert_eq!(chars.pop().expect("This is a developer error"), '<');
|
|
||||||
let mut parse_buffer: Vec<char> = Vec::new();
|
|
||||||
|
|
||||||
let mut reached_non_modifier = false;
|
|
||||||
let mut output_key_filled = false;
|
|
||||||
let mut output_key = Key::new();
|
|
||||||
while let Some(char) = chars.pop() {
|
|
||||||
if char == '>' {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
if char.is_ascii_uppercase()
|
|
||||||
|| char.is_numeric() && !reached_non_modifier && !output_key_filled
|
|
||||||
{
|
|
||||||
parse_buffer.push(char);
|
|
||||||
} else if char == '-' && !reached_non_modifier && !output_key_filled {
|
|
||||||
// We moved to the modified char
|
|
||||||
reached_non_modifier = true;
|
|
||||||
|
|
||||||
// Our parse_buffer should only contain modifiers:
|
|
||||||
let mut alt = false;
|
|
||||||
let mut ctrl = false;
|
|
||||||
let mut meta = false;
|
|
||||||
let mut shift = false;
|
|
||||||
|
|
||||||
for char in &parse_buffer {
|
|
||||||
match char {
|
|
||||||
'A' => alt = true,
|
|
||||||
'C' => ctrl = true,
|
|
||||||
'M' => meta = true,
|
|
||||||
'S' => shift = true,
|
|
||||||
char => bail!(
|
|
||||||
"The char ('{}') is not a valid descriptor of a modifier",
|
|
||||||
char
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output_key = Key {
|
|
||||||
alt,
|
|
||||||
ctrl,
|
|
||||||
meta,
|
|
||||||
shift,
|
|
||||||
value: None,
|
|
||||||
};
|
|
||||||
} else if reached_non_modifier && !output_key_filled {
|
|
||||||
if char == '<' {
|
|
||||||
chars.prepend('<');
|
|
||||||
let key = Key::parse(chars)?;
|
|
||||||
output_key = output_key.merge_with(key);
|
|
||||||
} else {
|
|
||||||
output_key.value = Some(KeyValue::Char(char));
|
|
||||||
}
|
|
||||||
output_key_filled = true;
|
|
||||||
} else {
|
|
||||||
bail!(
|
|
||||||
"Your can not put a this char here!
|
|
||||||
parse_buffer: '{}';
|
|
||||||
char: '{}';
|
|
||||||
chars: '{:#?}';
|
|
||||||
output_key: '{:#?}' ",
|
|
||||||
&parse_buffer.iter().collect::<String>(),
|
|
||||||
&char,
|
|
||||||
&chars,
|
|
||||||
&output_key
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if output_key_filled {
|
|
||||||
Ok(output_key)
|
|
||||||
} else {
|
|
||||||
let mut parse_buffer = Chars(parse_buffer.into());
|
|
||||||
let get_output = |value: KeyValue| -> Key {
|
|
||||||
let key = Key {
|
|
||||||
alt: false,
|
|
||||||
ctrl: false,
|
|
||||||
meta: false,
|
|
||||||
shift: false,
|
|
||||||
value: Some(value),
|
|
||||||
};
|
|
||||||
|
|
||||||
return key.merge_with(output_key);
|
|
||||||
};
|
|
||||||
if let Some(char) = parse_buffer.peek() {
|
|
||||||
if char == &'F' {
|
|
||||||
let _f = parse_buffer.pop();
|
|
||||||
let number: u8 = parse_buffer.to_string().parse().with_context(|| {
|
|
||||||
format!("Failed to parse buffer ('{}') as u8", &parse_buffer)
|
|
||||||
})?;
|
|
||||||
Ok(get_output(KeyValue::F(number)))
|
|
||||||
} else {
|
|
||||||
match &parse_buffer.to_string()[..] {
|
|
||||||
"BACKSPACE" => Ok(get_output(KeyValue::Backspace)),
|
|
||||||
"ENTER" => Ok(get_output(KeyValue::Enter)),
|
|
||||||
"LEFT" => Ok(get_output(KeyValue::Left)),
|
|
||||||
"RIGHT" => Ok(get_output(KeyValue::Right)),
|
|
||||||
"UP" => Ok(get_output(KeyValue::Up)),
|
|
||||||
"DOWN" => Ok(get_output(KeyValue::Down)),
|
|
||||||
"HOME" => Ok(get_output(KeyValue::Home)),
|
|
||||||
"END" => Ok(get_output(KeyValue::End)),
|
|
||||||
"PAGEUP" => Ok(get_output(KeyValue::PageUp)),
|
|
||||||
"PAGEDOWN" => Ok(get_output(KeyValue::PageDown)),
|
|
||||||
"TAB" => Ok(get_output(KeyValue::Tab)),
|
|
||||||
"BACKTAB" => Ok(get_output(KeyValue::BackTab)),
|
|
||||||
"DELETE" => Ok(get_output(KeyValue::Delete)),
|
|
||||||
"INSERT" => Ok(get_output(KeyValue::Insert)),
|
|
||||||
"ESC" => Ok(get_output(KeyValue::Esc)),
|
|
||||||
"CAPSLOCK" => Ok(get_output(KeyValue::CapsLock)),
|
|
||||||
"SCROLLlOCK" => Ok(get_output(KeyValue::ScrollLock)),
|
|
||||||
"NUMLOCK" => Ok(get_output(KeyValue::NumLock)),
|
|
||||||
"PRINTSCREEN" => Ok(get_output(KeyValue::PrintScreen)),
|
|
||||||
"PAUSE" => Ok(get_output(KeyValue::Pause)),
|
|
||||||
"MENU" => Ok(get_output(KeyValue::Menu)),
|
|
||||||
"KEYPADBEGIN" => Ok(get_output(KeyValue::KeypadBegin)),
|
|
||||||
|
|
||||||
"DASH" => Ok(get_output(KeyValue::Char('-'))),
|
|
||||||
"ANGULAR_BRACKET_OPEN" | "ABO" => Ok(get_output(KeyValue::Char('<'))),
|
|
||||||
"ANGULAR_BRACKET_CLOSE" | "ABC" => Ok(get_output(KeyValue::Char('>'))),
|
|
||||||
other_str => bail!(
|
|
||||||
"The String ('{}') is not a correct special key name!",
|
|
||||||
other_str
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bail!(
|
|
||||||
"You need to put something into the angulare brackets (<>)
|
|
||||||
parse_buffer: '{}';
|
|
||||||
chars: '{:#?}';",
|
|
||||||
&parse_buffer,
|
|
||||||
&chars,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Display for Key {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.write_str(&self.to_string_repr())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Into<Event> for Key {
|
|
||||||
fn into(self) -> Event {
|
|
||||||
let mut modifiers;
|
|
||||||
{
|
|
||||||
modifiers = KeyModifiers::all();
|
|
||||||
if !self.alt {
|
|
||||||
modifiers.remove(KeyModifiers::ALT);
|
|
||||||
}
|
|
||||||
if !self.ctrl {
|
|
||||||
modifiers.remove(KeyModifiers::CONTROL);
|
|
||||||
}
|
|
||||||
if !self.meta {
|
|
||||||
modifiers.remove(KeyModifiers::META);
|
|
||||||
modifiers.remove(KeyModifiers::SUPER);
|
|
||||||
}
|
|
||||||
if !self.shift {
|
|
||||||
modifiers.remove(KeyModifiers::SHIFT);
|
|
||||||
}
|
|
||||||
modifiers.remove(KeyModifiers::HYPER);
|
|
||||||
modifiers.remove(KeyModifiers::META);
|
|
||||||
if self.alt || self.ctrl || self.meta || self.shift {
|
|
||||||
modifiers.remove(KeyModifiers::NONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let output = Event::Key(KeyEvent {
|
|
||||||
code: {
|
|
||||||
// We sorta hit a edge case here, if we have a shifted char we need to tell
|
|
||||||
// crossterm about that. Thus we need to manually apply the shift modifier on the
|
|
||||||
// value.
|
|
||||||
if self.shift {
|
|
||||||
if let Some(KeyValue::Char(char)) = self.value {
|
|
||||||
let upper_case_char: char = {
|
|
||||||
let chars = char.to_uppercase().collect::<Vec<char>>();
|
|
||||||
if chars.len() != 1 {
|
|
||||||
unimplemented!(
|
|
||||||
"
|
|
||||||
I have no idea how we handle this case,
|
|
||||||
we'll just have to hope, that it never comes up.
|
|
||||||
"
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
*chars.first().expect("We checked the length")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
KeyCode::Char(upper_case_char)
|
|
||||||
} else {
|
|
||||||
self.value.unwrap_or(KeyValue::Null).into()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.value.unwrap_or(KeyValue::Null).into()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
modifiers,
|
|
||||||
kind: KeyEventKind::Press,
|
|
||||||
state: KeyEventState::NONE,
|
|
||||||
});
|
|
||||||
output
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&Event> for Key {
|
|
||||||
type Error = anyhow::Error;
|
|
||||||
|
|
||||||
fn try_from(value: &Event) -> std::result::Result<Self, Self::Error> {
|
|
||||||
let mut output_key: Key = Key::new();
|
|
||||||
match value {
|
|
||||||
Event::Key(key_event) => {
|
|
||||||
{
|
|
||||||
let key_mods = key_event.modifiers;
|
|
||||||
|
|
||||||
output_key.alt = KeyModifiers::intersects(&key_mods, KeyModifiers::ALT);
|
|
||||||
output_key.ctrl = KeyModifiers::intersects(&key_mods, KeyModifiers::CONTROL);
|
|
||||||
output_key.meta = KeyModifiers::intersects(&key_mods, KeyModifiers::META)
|
|
||||||
|| KeyModifiers::intersects(&key_mods, KeyModifiers::SUPER);
|
|
||||||
output_key.shift = KeyModifiers::intersects(&key_mods, KeyModifiers::SHIFT);
|
|
||||||
// let hyper = KeyModifiers::intersects(&key_mods, KeyModifiers::HYPER);
|
|
||||||
// let none = KeyModifiers::intersects(&key_mods, KeyModifiers::NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let key_code = key_event.code;
|
|
||||||
if output_key.shift {
|
|
||||||
// We need to deal have an edge case for shift, as the value will be in the
|
|
||||||
// shifted form (for example 'A'). If we left that, we would get "<S-A>" as
|
|
||||||
// Key representation, which can't be parsed. So we simply unshift the 'A'
|
|
||||||
// here, turning the representation into: "<S-a>"
|
|
||||||
if let KeyCode::Char(char) = key_code {
|
|
||||||
let lower_case_char: char = {
|
|
||||||
let chars = char.to_lowercase().collect::<Vec<char>>();
|
|
||||||
if chars.len() != 1 {
|
|
||||||
unimplemented!(
|
|
||||||
"
|
|
||||||
I have no idea how we handle this case,
|
|
||||||
we'll just have to hope, that it never comes up.
|
|
||||||
"
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
*chars.first().expect("We checked the length")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
info!(
|
|
||||||
"Had to translate key ('{}') to it's lowercase variant ('{}')",
|
|
||||||
char, lower_case_char
|
|
||||||
);
|
|
||||||
output_key.value = Some(KeyValue::Char(lower_case_char));
|
|
||||||
} else {
|
|
||||||
debug!(
|
|
||||||
"Key ('{}') is shifted but not a character!",
|
|
||||||
Into::<KeyValue>::into(key_code)
|
|
||||||
);
|
|
||||||
output_key.value = Some(key_code.into());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
output_key.value = Some(key_code.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(output_key)
|
|
||||||
}
|
|
||||||
Event::Mouse(_)
|
|
||||||
| Event::Paste(_)
|
|
||||||
| Event::Resize(_, _)
|
|
||||||
| Event::FocusGained
|
|
||||||
| Event::FocusLost => bail!("Only supports parsing from key event"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl FromStr for Key {
|
|
||||||
type Err = anyhow::Error;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
let mut keys: Keys = s.parse().context("Failed to parse string as keys")?;
|
|
||||||
if keys.0.len() == 1 {
|
|
||||||
let key = keys
|
|
||||||
.0
|
|
||||||
.pop()
|
|
||||||
.expect("The vec should have exactly one element");
|
|
||||||
return Ok(key);
|
|
||||||
} else {
|
|
||||||
bail!("The string ('{}') contains more than one key", &s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
use log::{debug, info};
|
||||||
|
|
||||||
|
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers};
|
||||||
|
|
||||||
|
|
||||||
|
impl Into<Event> for Key {
|
||||||
|
fn into(self) -> Event {
|
||||||
|
let mut modifiers;
|
||||||
|
{
|
||||||
|
modifiers = KeyModifiers::all();
|
||||||
|
if !self.alt {
|
||||||
|
modifiers.remove(KeyModifiers::ALT);
|
||||||
|
}
|
||||||
|
if !self.ctrl {
|
||||||
|
modifiers.remove(KeyModifiers::CONTROL);
|
||||||
|
}
|
||||||
|
if !self.meta {
|
||||||
|
modifiers.remove(KeyModifiers::META);
|
||||||
|
modifiers.remove(KeyModifiers::SUPER);
|
||||||
|
}
|
||||||
|
if !self.shift {
|
||||||
|
modifiers.remove(KeyModifiers::SHIFT);
|
||||||
|
}
|
||||||
|
modifiers.remove(KeyModifiers::HYPER);
|
||||||
|
modifiers.remove(KeyModifiers::META);
|
||||||
|
if self.alt || self.ctrl || self.meta || self.shift {
|
||||||
|
modifiers.remove(KeyModifiers::NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = Event::Key(KeyEvent {
|
||||||
|
code: {
|
||||||
|
// We sorta hit a edge case here, if we have a shifted char we need to tell
|
||||||
|
// crossterm about that. Thus we need to manually apply the shift modifier on the
|
||||||
|
// value.
|
||||||
|
if self.shift {
|
||||||
|
if let Some(KeyValue::Char(char)) = self.value {
|
||||||
|
let upper_case_char: char = {
|
||||||
|
let chars = char.to_uppercase().collect::<Vec<char>>();
|
||||||
|
if chars.len() != 1 {
|
||||||
|
unimplemented!(
|
||||||
|
"
|
||||||
|
I have no idea how we handle this case,
|
||||||
|
we'll just have to hope, that it never comes up.
|
||||||
|
"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
*chars.first().expect("We checked the length")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
KeyCode::Char(upper_case_char)
|
||||||
|
} else {
|
||||||
|
self.value.unwrap_or(KeyValue::Null).into()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.value.unwrap_or(KeyValue::Null).into()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifiers,
|
||||||
|
kind: KeyEventKind::Press,
|
||||||
|
state: KeyEventState::NONE,
|
||||||
|
});
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(crossterm)]
|
||||||
|
impl TryFrom<&Event> for Key {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(value: &Event) -> std::result::Result<Self, Self::Error> {
|
||||||
|
let mut output_key: Key = Key::new();
|
||||||
|
match value {
|
||||||
|
Event::Key(key_event) => {
|
||||||
|
{
|
||||||
|
let key_mods = key_event.modifiers;
|
||||||
|
|
||||||
|
output_key.alt = KeyModifiers::intersects(&key_mods, KeyModifiers::ALT);
|
||||||
|
output_key.ctrl = KeyModifiers::intersects(&key_mods, KeyModifiers::CONTROL);
|
||||||
|
output_key.meta = KeyModifiers::intersects(&key_mods, KeyModifiers::META)
|
||||||
|
|| KeyModifiers::intersects(&key_mods, KeyModifiers::SUPER);
|
||||||
|
output_key.shift = KeyModifiers::intersects(&key_mods, KeyModifiers::SHIFT);
|
||||||
|
// let hyper = KeyModifiers::intersects(&key_mods, KeyModifiers::HYPER);
|
||||||
|
// let none = KeyModifiers::intersects(&key_mods, KeyModifiers::NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let key_code = key_event.code;
|
||||||
|
if output_key.shift {
|
||||||
|
// We need to deal have an edge case for shift, as the value will be in the
|
||||||
|
// shifted form (for example 'A'). If we left that, we would get "<S-A>" as
|
||||||
|
// Key representation, which can't be parsed. So we simply unshift the 'A'
|
||||||
|
// here, turning the representation into: "<S-a>"
|
||||||
|
if let KeyCode::Char(char) = key_code {
|
||||||
|
let lower_case_char: char = {
|
||||||
|
let chars = char.to_lowercase().collect::<Vec<char>>();
|
||||||
|
if chars.len() != 1 {
|
||||||
|
unimplemented!(
|
||||||
|
"
|
||||||
|
I have no idea how we handle this case,
|
||||||
|
we'll just have to hope, that it never comes up.
|
||||||
|
"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
*chars.first().expect("We checked the length")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
info!(
|
||||||
|
"Had to translate key ('{}') to it's lowercase variant ('{}')",
|
||||||
|
char, lower_case_char
|
||||||
|
);
|
||||||
|
output_key.value = Some(KeyValue::Char(lower_case_char));
|
||||||
|
} else {
|
||||||
|
debug!(
|
||||||
|
"Key ('{}') is shifted but not a character!",
|
||||||
|
Into::<KeyValue>::into(key_code)
|
||||||
|
);
|
||||||
|
output_key.value = Some(key_code.into());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output_key.value = Some(key_code.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(output_key)
|
||||||
|
}
|
||||||
|
Event::Mouse(_)
|
||||||
|
| Event::Paste(_)
|
||||||
|
| Event::Resize(_, _)
|
||||||
|
| Event::FocusGained
|
||||||
|
| Event::FocusLost => bail!("Only supports parsing from key event"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,80 @@
|
||||||
|
#[cfg(crossterm)]
|
||||||
|
mod crossterm;
|
||||||
|
mod parsing;
|
||||||
|
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use pest_derive::Parser;
|
||||||
|
|
||||||
|
use super::KeyValue;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default, Parser)]
|
||||||
|
#[grammar = "./key/keys.pest"]
|
||||||
|
pub struct Key {
|
||||||
|
// Modifiers
|
||||||
|
pub(super) alt: bool,
|
||||||
|
pub(super) ctrl: bool,
|
||||||
|
pub(super) meta: bool,
|
||||||
|
pub(super) shift: bool,
|
||||||
|
|
||||||
|
pub(super) value: Option<KeyValue>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Key {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value(&self) -> Option<&KeyValue> {
|
||||||
|
self.value.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string_repr(self) -> String {
|
||||||
|
let mut output = String::new();
|
||||||
|
if self.alt || self.ctrl || self.meta || self.shift {
|
||||||
|
output.push('<')
|
||||||
|
}
|
||||||
|
if self.alt {
|
||||||
|
output.push('A');
|
||||||
|
}
|
||||||
|
if self.ctrl {
|
||||||
|
output.push('C');
|
||||||
|
}
|
||||||
|
if self.meta {
|
||||||
|
output.push('M');
|
||||||
|
}
|
||||||
|
if self.shift {
|
||||||
|
output.push('S');
|
||||||
|
}
|
||||||
|
if self.alt || self.ctrl || self.meta || self.shift {
|
||||||
|
output.push('-')
|
||||||
|
}
|
||||||
|
output.push_str(
|
||||||
|
&self
|
||||||
|
.value
|
||||||
|
.expect("There can be no `None`s here, if the Key comes from the public api")
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
if self.alt || self.ctrl || self.meta || self.shift {
|
||||||
|
output.push('>')
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge_with(mut self, other: Key) -> Self {
|
||||||
|
// Modifiers
|
||||||
|
self.alt = self.alt || other.alt;
|
||||||
|
self.ctrl = self.ctrl || other.ctrl;
|
||||||
|
self.meta = self.meta || other.meta;
|
||||||
|
self.shift = self.shift || other.shift;
|
||||||
|
|
||||||
|
self.value = Some(self.value.unwrap_or(other.value.unwrap_or(KeyValue::Null)));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Key {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str(&self.to_string_repr())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,213 @@
|
||||||
|
use std::{fmt::Debug, hash::Hash, marker, mem, str::FromStr};
|
||||||
|
|
||||||
|
use pest::{iterators::Pair, Parser};
|
||||||
|
|
||||||
|
use crate::{error, key::KeyValue};
|
||||||
|
|
||||||
|
use super::{Key, Rule};
|
||||||
|
|
||||||
|
impl Key {
|
||||||
|
pub fn from_pair<R>(pair: Pair<R>) -> Self
|
||||||
|
where
|
||||||
|
R: marker::Copy + Debug + Hash + Ord,
|
||||||
|
{
|
||||||
|
let pairs = pair.into_inner();
|
||||||
|
let mut output = Self::default();
|
||||||
|
|
||||||
|
let modifiers: Vec<_> = pairs.clone().find_tagged("modifiers").collect();
|
||||||
|
let values: Vec<_> = pairs.clone().find_tagged("value").collect();
|
||||||
|
let special_keys: Vec<_> = pairs.find_tagged("special_key").collect();
|
||||||
|
|
||||||
|
for modifier in modifiers {
|
||||||
|
match modifier.as_span().as_str() {
|
||||||
|
"A" => output.alt = true,
|
||||||
|
"C" => output.ctrl = true,
|
||||||
|
"M" => output.meta = true,
|
||||||
|
"S" => output.shift = true,
|
||||||
|
_ => unreachable!("This modifier should not have been parsed"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if values.is_empty() {
|
||||||
|
// Check that we have parsed *something*
|
||||||
|
debug_assert!(!special_keys.is_empty());
|
||||||
|
// Check, that we have only parsed one special key
|
||||||
|
debug_assert_eq!(special_keys.len(), 1);
|
||||||
|
|
||||||
|
let special_key_value: KeyValue = special_keys
|
||||||
|
.first()
|
||||||
|
.expect("This should exist (and be the only value)")
|
||||||
|
.as_str()
|
||||||
|
.parse()
|
||||||
|
.expect("This was parsed, and should thus be a valid special_key");
|
||||||
|
output.value = Some(special_key_value);
|
||||||
|
} else {
|
||||||
|
// Check, that we have only parsed one key
|
||||||
|
debug_assert_eq!(values.len(), 1);
|
||||||
|
|
||||||
|
let key_str = values.first().expect("This should exist").as_str();
|
||||||
|
// Check that we really only have a one char &str
|
||||||
|
debug_assert_eq!(key_str.chars().count(), 1);
|
||||||
|
|
||||||
|
let key_value: KeyValue = KeyValue::Char(
|
||||||
|
key_str
|
||||||
|
.chars()
|
||||||
|
.nth(0)
|
||||||
|
.expect("This &str should have a length of exactly one char"),
|
||||||
|
);
|
||||||
|
|
||||||
|
output.value = Some(key_value);
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
// pub(super) fn parse_old(chars: &mut Chars) -> anyhow::Result<Self> {
|
||||||
|
// assert_eq!(chars.pop().expect("This is a developer error"), '<');
|
||||||
|
// let mut parse_buffer: Vec<char> = Vec::new();
|
||||||
|
//
|
||||||
|
// let mut reached_non_modifier = false;
|
||||||
|
// let mut output_key_filled = false;
|
||||||
|
// let mut output_key = Key::new();
|
||||||
|
// while let Some(char) = chars.pop() {
|
||||||
|
// if char == '>' {
|
||||||
|
// break;
|
||||||
|
// } else {
|
||||||
|
// if char.is_ascii_uppercase()
|
||||||
|
// || char.is_numeric() && !reached_non_modifier && !output_key_filled
|
||||||
|
// {
|
||||||
|
// parse_buffer.push(char);
|
||||||
|
// } else if char == '-' && !reached_non_modifier && !output_key_filled {
|
||||||
|
// // We moved to the modified char
|
||||||
|
// reached_non_modifier = true;
|
||||||
|
//
|
||||||
|
// // Our parse_buffer should only contain modifiers:
|
||||||
|
// let mut alt = false;
|
||||||
|
// let mut ctrl = false;
|
||||||
|
// let mut meta = false;
|
||||||
|
// let mut shift = false;
|
||||||
|
//
|
||||||
|
// for char in &parse_buffer {
|
||||||
|
// match char {
|
||||||
|
// 'A' => alt = true,
|
||||||
|
// 'C' => ctrl = true,
|
||||||
|
// 'M' => meta = true,
|
||||||
|
// 'S' => shift = true,
|
||||||
|
// char => bail!(
|
||||||
|
// "The char ('{}') is not a valid descriptor of a modifier",
|
||||||
|
// char
|
||||||
|
// ),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// output_key = Key {
|
||||||
|
// alt,
|
||||||
|
// ctrl,
|
||||||
|
// meta,
|
||||||
|
// shift,
|
||||||
|
// value: None,
|
||||||
|
// };
|
||||||
|
// } else if reached_non_modifier && !output_key_filled {
|
||||||
|
// if char == '<' {
|
||||||
|
// chars.prepend('<');
|
||||||
|
// let key = Key::parse(chars)?;
|
||||||
|
// output_key = output_key.merge_with(key);
|
||||||
|
// } else {
|
||||||
|
// output_key.value = Some(KeyValue::Char(char));
|
||||||
|
// }
|
||||||
|
// output_key_filled = true;
|
||||||
|
// } else {
|
||||||
|
// bail!(
|
||||||
|
// "Your can not put a this char here!
|
||||||
|
// parse_buffer: '{}';
|
||||||
|
// char: '{}';
|
||||||
|
// chars: '{:#?}';
|
||||||
|
// output_key: '{:#?}' ",
|
||||||
|
// &parse_buffer.iter().collect::<String>(),
|
||||||
|
// &char,
|
||||||
|
// &chars,
|
||||||
|
// &output_key
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if output_key_filled {
|
||||||
|
// Ok(output_key)
|
||||||
|
// } else {
|
||||||
|
// let mut parse_buffer = Chars(parse_buffer.into());
|
||||||
|
// let get_output = |value: KeyValue| -> Key {
|
||||||
|
// let key = Key {
|
||||||
|
// alt: false,
|
||||||
|
// ctrl: false,
|
||||||
|
// meta: false,
|
||||||
|
// shift: false,
|
||||||
|
// value: Some(value),
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// return key.merge_with(output_key);
|
||||||
|
// };
|
||||||
|
// if let Some(char) = parse_buffer.peek() {
|
||||||
|
// if char == &'F' {
|
||||||
|
// let _f = parse_buffer.pop();
|
||||||
|
// let number: u8 = parse_buffer.to_string().parse().with_context(|| {
|
||||||
|
// format!("Failed to parse buffer ('{}') as u8", &parse_buffer)
|
||||||
|
// })?;
|
||||||
|
// Ok(get_output(KeyValue::F(number)))
|
||||||
|
// } else {
|
||||||
|
// match &parse_buffer.to_string()[..] {
|
||||||
|
// "BACKSPACE" => Ok(get_output(KeyValue::Backspace)),
|
||||||
|
// "ENTER" => Ok(get_output(KeyValue::Enter)),
|
||||||
|
// "LEFT" => Ok(get_output(KeyValue::Left)),
|
||||||
|
// "RIGHT" => Ok(get_output(KeyValue::Right)),
|
||||||
|
// "UP" => Ok(get_output(KeyValue::Up)),
|
||||||
|
// "DOWN" => Ok(get_output(KeyValue::Down)),
|
||||||
|
// "HOME" => Ok(get_output(KeyValue::Home)),
|
||||||
|
// "END" => Ok(get_output(KeyValue::End)),
|
||||||
|
// "PAGEUP" => Ok(get_output(KeyValue::PageUp)),
|
||||||
|
// "PAGEDOWN" => Ok(get_output(KeyValue::PageDown)),
|
||||||
|
// "TAB" => Ok(get_output(KeyValue::Tab)),
|
||||||
|
// "BACKTAB" => Ok(get_output(KeyValue::BackTab)),
|
||||||
|
// "DELETE" => Ok(get_output(KeyValue::Delete)),
|
||||||
|
// "INSERT" => Ok(get_output(KeyValue::Insert)),
|
||||||
|
// "ESC" => Ok(get_output(KeyValue::Esc)),
|
||||||
|
// "CAPSLOCK" => Ok(get_output(KeyValue::CapsLock)),
|
||||||
|
// "SCROLLlOCK" => Ok(get_output(KeyValue::ScrollLock)),
|
||||||
|
// "NUMLOCK" => Ok(get_output(KeyValue::NumLock)),
|
||||||
|
// "PRINTSCREEN" => Ok(get_output(KeyValue::PrintScreen)),
|
||||||
|
// "PAUSE" => Ok(get_output(KeyValue::Pause)),
|
||||||
|
// "MENU" => Ok(get_output(KeyValue::Menu)),
|
||||||
|
// "KEYPADBEGIN" => Ok(get_output(KeyValue::KeypadBegin)),
|
||||||
|
//
|
||||||
|
// "DASH" => Ok(get_output(KeyValue::Char('-'))),
|
||||||
|
// "ANGULAR_BRACKET_OPEN" | "ABO" => Ok(get_output(KeyValue::Char('<'))),
|
||||||
|
// "ANGULAR_BRACKET_CLOSE" | "ABC" => Ok(get_output(KeyValue::Char('>'))),
|
||||||
|
// other_str => bail!(
|
||||||
|
// "The String ('{}') is not a correct special key name!",
|
||||||
|
// other_str
|
||||||
|
// ),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// bail!(
|
||||||
|
// "You need to put something into the angulare brackets (<>)
|
||||||
|
// parse_buffer: '{}';
|
||||||
|
// chars: '{:#?}';",
|
||||||
|
// &parse_buffer,
|
||||||
|
// &chars,
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Key {
|
||||||
|
type Err = error::KeyParseError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut key_pairs: Vec<_> = Self::parse(Rule::key, s)?.find_tagged("key").collect();
|
||||||
|
debug_assert_eq!(key_pairs.len(), 1);
|
||||||
|
let key: Key = Self::from_pair::<Rule>(
|
||||||
|
key_pairs
|
||||||
|
.pop()
|
||||||
|
.expect("This should contain only this first (and last) element"),
|
||||||
|
);
|
||||||
|
Ok(key)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
use std::fmt::{Display, Write};
|
use std::{
|
||||||
|
fmt::{Display, Write},
|
||||||
|
str::FromStr,
|
||||||
|
};
|
||||||
|
|
||||||
use crossterm::event::KeyCode;
|
use crate::error;
|
||||||
|
|
||||||
// taken directly from crossterm
|
// taken directly from crossterm
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||||
|
@ -21,7 +24,7 @@ pub enum KeyValue {
|
||||||
Insert,
|
Insert,
|
||||||
F(u8),
|
F(u8),
|
||||||
Char(char),
|
Char(char),
|
||||||
Null, // TODO(@soispha): What is this key? <2023-10-15>
|
Null, // TODO(@soispha): Should we keep this key (as it's effectively not a key)?<2023-10-15>
|
||||||
Esc,
|
Esc,
|
||||||
CapsLock,
|
CapsLock,
|
||||||
ScrollLock,
|
ScrollLock,
|
||||||
|
@ -34,6 +37,50 @@ pub enum KeyValue {
|
||||||
// Media(MediaKeyCode),
|
// Media(MediaKeyCode),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromStr for KeyValue {
|
||||||
|
type Err = error::KeyValueParseError;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"BACKSPACE" => Ok(Self::Backspace),
|
||||||
|
"ENTER" => Ok(Self::Enter),
|
||||||
|
"LEFT" => Ok(Self::Left),
|
||||||
|
"RIGHT" => Ok(Self::Right),
|
||||||
|
"UP" => Ok(Self::Up),
|
||||||
|
"DOWN" => Ok(Self::Down),
|
||||||
|
"HOME" => Ok(Self::Home),
|
||||||
|
"END" => Ok(Self::End),
|
||||||
|
"PAGEUP" => Ok(Self::PageUp),
|
||||||
|
"PAGEDOWN" => Ok(Self::PageDown),
|
||||||
|
"TAB" => Ok(Self::Tab),
|
||||||
|
"BACKTAB" => Ok(Self::BackTab),
|
||||||
|
"DELETE" => Ok(Self::Delete),
|
||||||
|
"INSERT" => Ok(Self::Insert),
|
||||||
|
"ESC" => Ok(Self::Esc),
|
||||||
|
"CAPSLOCK" => Ok(Self::CapsLock),
|
||||||
|
"SCROLLlOCK" => Ok(Self::ScrollLock),
|
||||||
|
"NUMLOCK" => Ok(Self::NumLock),
|
||||||
|
"PRINTSCREEN" => Ok(Self::PrintScreen),
|
||||||
|
"PAUSE" => Ok(Self::Pause),
|
||||||
|
"MENU" => Ok(Self::Menu),
|
||||||
|
"KEYPADBEGIN" => Ok(Self::KeypadBegin),
|
||||||
|
f_str
|
||||||
|
// FIXME(@soispha): This should check the full string, as something
|
||||||
|
// like 'FA12' would also match this case <2023-11-07>
|
||||||
|
if f_str.starts_with("F")
|
||||||
|
&& f_str.ends_with(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']) =>
|
||||||
|
{
|
||||||
|
Ok(Self::F(f_str.trim_matches('F').parse()?))
|
||||||
|
}
|
||||||
|
|
||||||
|
"DASH" => Ok(Self::Char('-')),
|
||||||
|
"ANGULAR_BRACKET_OPEN" | "ABO" => Ok(Self::Char('<')),
|
||||||
|
"ANGULAR_BRACKET_CLOSE" | "ABC" => Ok(Self::Char('>')),
|
||||||
|
other_str => return Err(Self::Err::NoMatch(other_str.to_owned())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for KeyValue {
|
impl Display for KeyValue {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let mut w = |str| return f.write_str(str);
|
let mut w = |str| return f.write_str(str);
|
||||||
|
@ -53,14 +100,11 @@ impl Display for KeyValue {
|
||||||
KeyValue::Delete => w("<DELETE>"),
|
KeyValue::Delete => w("<DELETE>"),
|
||||||
KeyValue::Insert => w("<INSERT>"),
|
KeyValue::Insert => w("<INSERT>"),
|
||||||
KeyValue::F(n) => f.write_fmt(format_args!("<F{}>", n)),
|
KeyValue::F(n) => f.write_fmt(format_args!("<F{}>", n)),
|
||||||
KeyValue::Char(c) => {
|
KeyValue::Char(c) => match c {
|
||||||
match c {
|
|
||||||
'<' => w("<ANGULAR_BRACKET_OPEN>"),
|
'<' => w("<ANGULAR_BRACKET_OPEN>"),
|
||||||
'>' => w("<ANGULAR_BRACKET_CLOSE>"),
|
'>' => w("<ANGULAR_BRACKET_CLOSE>"),
|
||||||
'-' => w("<DASH>"),
|
'-' => w("<DASH>"),
|
||||||
c => f.write_char(*c),
|
c => f.write_char(*c),
|
||||||
}
|
|
||||||
|
|
||||||
},
|
},
|
||||||
KeyValue::Null => w("<NULL>"),
|
KeyValue::Null => w("<NULL>"),
|
||||||
KeyValue::Esc => w("<ESC>"),
|
KeyValue::Esc => w("<ESC>"),
|
||||||
|
@ -74,70 +118,3 @@ impl Display for KeyValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<KeyCode> for KeyValue {
|
|
||||||
fn from(value: KeyCode) -> Self {
|
|
||||||
match value {
|
|
||||||
KeyCode::Backspace => Self::Backspace,
|
|
||||||
KeyCode::Enter => Self::Enter,
|
|
||||||
KeyCode::Left => Self::Left,
|
|
||||||
KeyCode::Right => Self::Right,
|
|
||||||
KeyCode::Up => Self::Up,
|
|
||||||
KeyCode::Down => Self::Down,
|
|
||||||
KeyCode::Home => Self::Home,
|
|
||||||
KeyCode::End => Self::End,
|
|
||||||
KeyCode::PageUp => Self::PageUp,
|
|
||||||
KeyCode::PageDown => Self::PageDown,
|
|
||||||
KeyCode::Tab => Self::Tab,
|
|
||||||
KeyCode::BackTab => Self::BackTab,
|
|
||||||
KeyCode::Delete => Self::Delete,
|
|
||||||
KeyCode::Insert => Self::Insert,
|
|
||||||
KeyCode::F(n) => Self::F(n),
|
|
||||||
KeyCode::Char(c) => Self::Char(c),
|
|
||||||
KeyCode::Null => Self::Null,
|
|
||||||
KeyCode::Esc => Self::Esc,
|
|
||||||
KeyCode::CapsLock => Self::CapsLock,
|
|
||||||
KeyCode::ScrollLock => Self::ScrollLock,
|
|
||||||
KeyCode::NumLock => Self::NumLock,
|
|
||||||
KeyCode::PrintScreen => Self::PrintScreen,
|
|
||||||
KeyCode::Pause => Self::Pause,
|
|
||||||
KeyCode::Menu => Self::Menu,
|
|
||||||
KeyCode::KeypadBegin => Self::KeypadBegin,
|
|
||||||
// FIXME(@soispha): This reduces our information, casting a KeyCode to a KeyValue
|
|
||||||
// and back again would not equal the original KeyCode <2023-10-15>
|
|
||||||
KeyCode::Media(_) => Self::Null,
|
|
||||||
KeyCode::Modifier(_) => Self::Null,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Into<KeyCode> for KeyValue {
|
|
||||||
fn into(self) -> KeyCode {
|
|
||||||
match self {
|
|
||||||
Self::Backspace => KeyCode::Backspace,
|
|
||||||
Self::Enter => KeyCode::Enter,
|
|
||||||
Self::Left => KeyCode::Left,
|
|
||||||
Self::Right => KeyCode::Right,
|
|
||||||
Self::Up => KeyCode::Up,
|
|
||||||
Self::Down => KeyCode::Down,
|
|
||||||
Self::Home => KeyCode::Home,
|
|
||||||
Self::End => KeyCode::End,
|
|
||||||
Self::PageUp => KeyCode::PageUp,
|
|
||||||
Self::PageDown => KeyCode::PageDown,
|
|
||||||
Self::Tab => KeyCode::Tab,
|
|
||||||
Self::BackTab => KeyCode::BackTab,
|
|
||||||
Self::Delete => KeyCode::Delete,
|
|
||||||
Self::Insert => KeyCode::Insert,
|
|
||||||
Self::F(n) => KeyCode::F(n),
|
|
||||||
Self::Char(c) => KeyCode::Char(c),
|
|
||||||
Self::Null => KeyCode::Null,
|
|
||||||
Self::Esc => KeyCode::Esc,
|
|
||||||
Self::CapsLock => KeyCode::CapsLock,
|
|
||||||
Self::ScrollLock => KeyCode::ScrollLock,
|
|
||||||
Self::NumLock => KeyCode::NumLock,
|
|
||||||
Self::PrintScreen => KeyCode::PrintScreen,
|
|
||||||
Self::Pause => KeyCode::Pause,
|
|
||||||
Self::Menu => KeyCode::Menu,
|
|
||||||
Self::KeypadBegin => KeyCode::KeypadBegin,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
angular_bracket_open = _{ "<" }
|
||||||
|
abo = _{ angular_bracket_open }
|
||||||
|
angular_bracket_close = _{ ">" }
|
||||||
|
abc = _{ angular_bracket_close }
|
||||||
|
|
||||||
|
special_key = {
|
||||||
|
"BACKSPACE"
|
||||||
|
| "ENTER"
|
||||||
|
| "LEFT"
|
||||||
|
| "RIGHT"
|
||||||
|
| "UP"
|
||||||
|
| "DOWN"
|
||||||
|
| "HOME"
|
||||||
|
| "END"
|
||||||
|
| "PAGEUP"
|
||||||
|
| "PAGEDOWN"
|
||||||
|
| "TAB"
|
||||||
|
| "BACKTAB"
|
||||||
|
| "DELETE"
|
||||||
|
| "INSERT"
|
||||||
|
| "ESC"
|
||||||
|
| "CAPSLOCK"
|
||||||
|
| "SCROLLlOCK"
|
||||||
|
| "NUMLOCK"
|
||||||
|
| "PRINTSCREEN"
|
||||||
|
| "PAUSE"
|
||||||
|
| "MENU"
|
||||||
|
| "KEYPADBEGIN"
|
||||||
|
| ("F" ~ ASCII_DIGIT+)
|
||||||
|
|
||||||
|
// Aliases to make using them easier, as they are also used in the notation
|
||||||
|
| "DASH"
|
||||||
|
| "ANGULAR_BRACKET_OPEN"
|
||||||
|
| "ABO"
|
||||||
|
| "ANGULAR_BRACKET_CLOSE"
|
||||||
|
| "ABC"
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier = {
|
||||||
|
"A" | // Alt
|
||||||
|
"C" | // Ctrl
|
||||||
|
"M" | // Meta
|
||||||
|
"S" // Shift
|
||||||
|
}
|
||||||
|
|
||||||
|
value = {
|
||||||
|
!abo ~ ANY
|
||||||
|
}
|
||||||
|
|
||||||
|
raw_key = {
|
||||||
|
((abo ~ (#modifiers = modifier)+ ~ "-" ~ (#special_key = (abo ~ special_key ~ abc) | #value = value) ~ abc) | #special_key = (abo ~ special_key ~ abc) | #value = value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// api
|
||||||
|
key = {
|
||||||
|
SOI ~ #key = raw_key ~ EOI
|
||||||
|
}
|
||||||
|
keys = {
|
||||||
|
SOI ~ (#keys = raw_key)+ ~ EOI
|
||||||
|
}
|
|
@ -1,10 +1,14 @@
|
||||||
use anyhow::Context;
|
use pest::Parser;
|
||||||
|
use pest_derive::Parser;
|
||||||
|
|
||||||
use super::{Chars, Key, KeyValue};
|
use crate::error;
|
||||||
|
|
||||||
|
use super::Key;
|
||||||
|
|
||||||
use std::{fmt::Display, str::FromStr};
|
use std::{fmt::Display, str::FromStr};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default, Parser)]
|
||||||
|
#[grammar = "./key/keys.pest"]
|
||||||
pub struct Keys(pub(super) Vec<Key>);
|
pub struct Keys(pub(super) Vec<Key>);
|
||||||
|
|
||||||
impl Keys {
|
impl Keys {
|
||||||
|
@ -90,28 +94,37 @@ impl Display for Keys {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Keys {
|
impl FromStr for Keys {
|
||||||
type Err = anyhow::Error;
|
type Err = error::KeysParseError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
let mut output: Vec<Key> = vec![];
|
let mut output: Vec<Key> = vec![];
|
||||||
let mut chars = Chars(s.chars().collect());
|
let parsed = Self::parse(Rule::keys, s)?;
|
||||||
while let Some(char) = chars.pop() {
|
|
||||||
match char {
|
let keys: Vec<_> = parsed.find_tagged("keys").collect();
|
||||||
'<' => {
|
|
||||||
chars.prepend('<');
|
for key in keys {
|
||||||
let key = Key::parse(&mut chars)
|
let output_key: Key = Key::from_pair(key);
|
||||||
.with_context(|| format!("Failed to parse keymapping ('{}')", &chars))?;
|
output.push(output_key)
|
||||||
output.push(key)
|
|
||||||
}
|
|
||||||
other_char => output.push(Key {
|
|
||||||
alt: false,
|
|
||||||
ctrl: false,
|
|
||||||
meta: false,
|
|
||||||
shift: false,
|
|
||||||
value: Some(KeyValue::Char(other_char)),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// let mut chars = Chars(s.chars().collect());
|
||||||
|
// while let Some(char) = chars.pop() {
|
||||||
|
// match char {
|
||||||
|
// '<' => {
|
||||||
|
// chars.prepend('<');
|
||||||
|
// let key = Key::parse(&mut chars)
|
||||||
|
// .with_context(|| format!("Failed to parse keymapping ('{}')", &chars))?;
|
||||||
|
// output.push(key)
|
||||||
|
// }
|
||||||
|
// other_char => output.push(Key {
|
||||||
|
// alt: false,
|
||||||
|
// ctrl: false,
|
||||||
|
// meta: false,
|
||||||
|
// shift: false,
|
||||||
|
// value: Some(KeyValue::Char(other_char)),
|
||||||
|
// }),
|
||||||
|
// }
|
||||||
|
// }
|
||||||
Ok(Keys(output))
|
Ok(Keys(output))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
pub mod key;
|
|
||||||
pub mod keys;
|
|
||||||
pub mod chars;
|
pub mod chars;
|
||||||
|
pub mod key;
|
||||||
pub mod key_value;
|
pub mod key_value;
|
||||||
|
pub mod keys;
|
||||||
|
|
||||||
pub use key::*;
|
pub use key::Key;
|
||||||
pub use keys::*;
|
|
||||||
pub(self) use chars::*;
|
|
||||||
pub use key_value::*;
|
pub use key_value::*;
|
||||||
|
pub use keys::Keys;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::app::keymappings::key::{Key, KeyValue};
|
use crate::key::{Key, KeyValue};
|
||||||
use anyhow::Error;
|
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use super::Keys;
|
use super::Keys;
|
||||||
|
@ -80,7 +77,10 @@ mod test {
|
||||||
shift: true,
|
shift: true,
|
||||||
value: Some(KeyValue::Char('<')),
|
value: Some(KeyValue::Char('<')),
|
||||||
};
|
};
|
||||||
assert_eq!("<AMS-<ANGULAR_BRACKET_OPEN>>".to_owned(), key.to_string_repr());
|
assert_eq!(
|
||||||
|
"<AMS-<ANGULAR_BRACKET_OPEN>>".to_owned(),
|
||||||
|
key.to_string_repr()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -124,13 +124,28 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_false_pattern() {
|
fn test_false_pattern() {
|
||||||
let keys: Result<Keys, Error> = "<c->".parse();
|
let keys: Result<Keys, _> = "<c->".parse();
|
||||||
assert!(keys.is_err())
|
assert!(keys.is_err())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_f_keys() {
|
||||||
|
let key: Key = "<C-<F255>>".parse().unwrap();
|
||||||
|
let expected = Key {
|
||||||
|
alt: false,
|
||||||
|
ctrl: true,
|
||||||
|
meta: false,
|
||||||
|
shift: false,
|
||||||
|
value: Some(KeyValue::F(255)),
|
||||||
|
};
|
||||||
|
assert_eq!(key, expected);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_complex() {
|
fn test_complex() {
|
||||||
let keys: Keys = "<C-a><ACMS-<ESC>>a 🙂".parse().unwrap();
|
let keys: Keys = "<C-a><ACMS-<ESC>>a 🙂"
|
||||||
|
.parse()
|
||||||
|
.unwrap_or_else(|e| panic!("{}", e));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
keys,
|
keys,
|
||||||
Keys(vec![
|
Keys(vec![
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod key;
|
pub mod key;
|
||||||
pub mod trie;
|
pub mod trie;
|
||||||
|
pub mod error;
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::bail;
|
use log::info;
|
||||||
use cli_log::info;
|
|
||||||
|
use crate::error;
|
||||||
|
|
||||||
use super::key::{Key, Keys};
|
use super::key::{Key, Keys};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct Node<V: std::fmt::Display> {
|
pub struct Node<V: std::fmt::Display + ToOwned<Owned = V>> {
|
||||||
children: HashMap<Key, Box<Node<V>>>,
|
children: HashMap<Key, Box<Node<V>>>,
|
||||||
value: Option<V>,
|
value: Option<V>,
|
||||||
is_terminal: bool,
|
is_terminal: bool,
|
||||||
is_child: bool,
|
is_child: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: std::fmt::Display> Node<V> {
|
impl<V: std::fmt::Display + ToOwned<Owned = V>> Node<V> {
|
||||||
pub fn new() -> Node<V> {
|
pub fn new() -> Node<V> {
|
||||||
Node {
|
Node {
|
||||||
children: HashMap::new(),
|
children: HashMap::new(),
|
||||||
|
@ -87,7 +88,7 @@ impl<V: std::fmt::Display> Node<V> {
|
||||||
|
|
||||||
/// Insert a key value pair into the trie. The key is supplied as a string to facilitate the
|
/// Insert a key value pair into the trie. The key is supplied as a string to facilitate the
|
||||||
/// creation of nested nodes.
|
/// creation of nested nodes.
|
||||||
pub fn insert(&mut self, keys: &Keys, value: V) -> anyhow::Result<()> {
|
pub fn insert(&mut self, keys: &Keys, value: V) -> Result<(), error::TrieInsertError<V>> {
|
||||||
let mut current_node = self;
|
let mut current_node = self;
|
||||||
for char in keys {
|
for char in keys {
|
||||||
let child_node = current_node
|
let child_node = current_node
|
||||||
|
@ -95,25 +96,22 @@ impl<V: std::fmt::Display> Node<V> {
|
||||||
.entry(char.to_owned())
|
.entry(char.to_owned())
|
||||||
.or_insert(Box::new(Node::new_child()));
|
.or_insert(Box::new(Node::new_child()));
|
||||||
if current_node.value.is_some() {
|
if current_node.value.is_some() {
|
||||||
bail!(
|
return Err(error::TrieInsertError::KeyPathBlocked(keys.to_owned()));
|
||||||
"The key ('{}') contains nodes, which already have a value set!",
|
|
||||||
keys
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
current_node.is_terminal = false;
|
current_node.is_terminal = false;
|
||||||
current_node = child_node
|
current_node = child_node
|
||||||
}
|
}
|
||||||
if current_node.value.is_some() {
|
if current_node.value.is_some() {
|
||||||
bail!(
|
return Err(error::TrieInsertError::KeyAlreadySet {
|
||||||
"The key ('{}') is already set! The value is: '{}'",
|
key: keys.to_owned(),
|
||||||
keys,
|
value: current_node
|
||||||
current_node.value.as_ref().expect("This should be set")
|
.value
|
||||||
)
|
.as_ref()
|
||||||
|
.expect("This should be set")
|
||||||
|
.to_owned(),
|
||||||
|
});
|
||||||
} else if current_node.children.len() > 0 {
|
} else if current_node.children.len() > 0 {
|
||||||
bail!(
|
return Err(error::TrieInsertError::NodeHasChildren(keys.to_owned()));
|
||||||
"The node accessed by this key ('{}') has children! You can't set a value for it",
|
|
||||||
keys
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
current_node.value = Some(value);
|
current_node.value = Some(value);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -145,7 +143,7 @@ impl<V: std::fmt::Display> Node<V> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::app::keymappings::key::{Key, Keys};
|
use crate::key::{Key, Keys};
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use super::Node;
|
use super::Node;
|
||||||
|
@ -156,7 +154,9 @@ mod test {
|
||||||
fn k(str: &str) -> Key {
|
fn k(str: &str) -> Key {
|
||||||
str.parse::<Key>().unwrap()
|
str.parse::<Key>().unwrap()
|
||||||
}
|
}
|
||||||
fn collect<V: std::fmt::Display>(nodes: Option<(Vec<&Box<Node<V>>>, bool)>) -> Vec<&V> {
|
fn collect<V: std::fmt::Display + ToOwned<Owned = V>>(
|
||||||
|
nodes: Option<(Vec<&Box<Node<V>>>, bool)>,
|
||||||
|
) -> Vec<&V> {
|
||||||
let (nodes, _should_call) = nodes.unwrap();
|
let (nodes, _should_call) = nodes.unwrap();
|
||||||
nodes
|
nodes
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -218,7 +218,7 @@ mod test {
|
||||||
output.insert(&i("ace"), true).unwrap();
|
output.insert(&i("ace"), true).unwrap();
|
||||||
|
|
||||||
let a_children = &output.children.get(&k("a")).unwrap();
|
let a_children = &output.children.get(&k("a")).unwrap();
|
||||||
let output_nodes = vec![&a_children.children[&k("a")], &a_children.children[&k("b")]];
|
let output_nodes = vec![&a_children.children[&k("b")], &a_children.children[&k("a")]];
|
||||||
|
|
||||||
let (nodes, _should_call) = trie.get(&i("a")).unwrap();
|
let (nodes, _should_call) = trie.get(&i("a")).unwrap();
|
||||||
assert_eq!(nodes, output_nodes);
|
assert_eq!(nodes, output_nodes);
|
||||||
|
|
Reference in New Issue