Skip to content

Commit

Permalink
feat: Convert from Backend::Key to Node (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
rezigned authored Oct 11, 2023
1 parent d109777 commit 2b46c7f
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 25 deletions.
2 changes: 1 addition & 1 deletion examples/termion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn main() {
let evt = c.unwrap();

if let Event::Key(key) = evt {
if let Some((k, action)) = bindings.keys.get_key_value(&KeyMap::from(key)) {
if let Some((k, action)) = bindings.0.get_key_value(&KeyMap::from(key)) {
if *action == Action::Quit {
break;
}
Expand Down
99 changes: 80 additions & 19 deletions src/backend/crossterm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use serde::{de, Deserialize, Deserializer};

use crate::parser::{self, Key as Keys, Modifier, Node};
use super::Key;
use super::{Key, NodeModifiers};

pub type KeyMap = Key<KeyEvent>;

Expand All @@ -12,7 +12,7 @@ pub fn parse(s: &str) -> Result<KeyMap, pom::Error> {

impl From<KeyEvent> for KeyMap {
fn from(value: KeyEvent) -> Self {
Self { event: value, node: None }
Self { event: value, node: Some(Node::from(value)) }
}
}

Expand All @@ -25,6 +25,38 @@ impl From<Node> for KeyMap {
}
}

impl From<KeyEvent> for Node {
fn from(value: KeyEvent) -> Self {
match value {
KeyEvent { code, modifiers, .. } => {
let key = match code {
KeyCode::BackTab => Keys::BackTab,
KeyCode::Backspace => Keys::Backspace,
KeyCode::Char(' ') => Keys::Space,
KeyCode::Char(c) => Keys::Char(c),
KeyCode::Delete => Keys::Delete,
KeyCode::Down => Keys::Down,
KeyCode::End => Keys::End,
KeyCode::Enter => Keys::Enter,
KeyCode::Esc => Keys::Esc,
KeyCode::F(n) => Keys::F(n),
KeyCode::Home => Keys::Home,
KeyCode::Insert => Keys::Insert,
KeyCode::Left => Keys::Left,
KeyCode::PageDown => Keys::PageDown,
KeyCode::PageUp => Keys::PageUp,
KeyCode::Right => Keys::Right,
KeyCode::Tab => Keys::Tab,
KeyCode::Up => Keys::Up,
code => panic!("Unsupport KeyEvent {code:?}"),
};

Self { key, modifiers: NodeModifiers::from(modifiers).into() }
}
}
}
}

impl<'a> From<&'a Node> for KeyEvent {
fn from(node: &'a Node) -> Self {
let key = match node.key {
Expand All @@ -48,26 +80,37 @@ impl<'a> From<&'a Node> for KeyEvent {
Keys::Up => KeyCode::Up.into(),
};

Self::new(key, modifiers(node.modifiers))
Self::new(key, NodeModifiers::from(node.modifiers).into())
}
}

fn modifiers(m: u8) -> KeyModifiers {
let mut mods = KeyModifiers::NONE;
if m & Modifier::Alt as u8 != 0 {
mods |= KeyModifiers::ALT
}
if m & Modifier::Cmd as u8 != 0 {
mods |= KeyModifiers::META
}
if m & Modifier::Ctrl as u8 != 0 {
mods |= KeyModifiers::CONTROL
}
if m & Modifier::Shift as u8 != 0 {
mods |= KeyModifiers::SHIFT
const MODIFIERS: [(KeyModifiers, parser::Modifier); 4] = [
(KeyModifiers::ALT, Modifier::Alt),
(KeyModifiers::CONTROL, Modifier::Ctrl),
(KeyModifiers::META, Modifier::Cmd),
(KeyModifiers::SHIFT, Modifier::Shift),
];

impl From<KeyModifiers> for NodeModifiers {
fn from(value: KeyModifiers) -> Self {
Self(MODIFIERS.into_iter().fold(0, |mut m, (m1, m2)| {
if value.contains(m1) {
m |= m2 as u8;
}
m
}))
}
}

mods
impl From<NodeModifiers> for KeyModifiers {
fn from(value: NodeModifiers) -> Self {
MODIFIERS.into_iter().fold(KeyModifiers::NONE, |mut m, (m1, m2)| {
if value.0 & m2 as u8 != 0 {
m|= m1
}
m
})
}
}

/// Deserializes into Key
Expand All @@ -86,10 +129,10 @@ mod tests {
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use serde::Deserialize;

use crate::backend::{
use crate::{backend::{
crossterm::{parse, KeyMap},
Key
};
}, parser::{Node, self}};

#[test]
fn test_parse() {
Expand All @@ -110,6 +153,24 @@ mod tests {
});
}

#[test]
fn test_from_key_to_node() {
let alt_a = KeyEvent::new(
KeyCode::Char('a'),
KeyModifiers::ALT | KeyModifiers::CONTROL | KeyModifiers::SHIFT,
);

[
(KeyEvent::from(KeyCode::Char('[')), "["),
(KeyEvent::from(KeyCode::Delete), "del"),
(alt_a, "alt-ctrl-shift-a"),
]
.map(|(key, code)| {
let node = parser::parse(code).unwrap();
assert_eq!(Node::from(key), node);
});
}

#[test]
fn test_deserialize() {
use std::collections::HashMap;
Expand Down
18 changes: 17 additions & 1 deletion src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ mod termion;
#[cfg(feature = "termion")]
pub use self::termion::{KeyMap, parse};

use crate::parser::Node;
use crate::parser::{Node, Modifiers};

#[derive(Debug, Eq)]
pub struct Key<T> {
Expand Down Expand Up @@ -41,3 +41,19 @@ impl Display for KeyMap {
}
}
}

/// A wrapper that allows conversion between backend's modifier
/// and Node's modifier.
struct NodeModifiers(Modifiers);

impl From<NodeModifiers> for Modifiers {
fn from(value: NodeModifiers) -> Self {
value.0
}
}

impl From<Modifiers> for NodeModifiers {
fn from(value: Modifiers) -> Self {
Self(value)
}
}
51 changes: 48 additions & 3 deletions src/backend/termion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,41 @@ impl From<KeyEvent> for KeyMap {
fn from(value: KeyEvent) -> Self {
Self {
event: value,
node: None,
node: Some(Node::from(value)),
}
}
}

impl From<KeyEvent> for Node {
fn from(value: KeyEvent) -> Self {
let (key, modifiers) = match value {
KeyEvent::BackTab => (Keys::BackTab, 0),
KeyEvent::Backspace => (Keys::Backspace, 0),
KeyEvent::Delete => (Keys::Delete, 0),
KeyEvent::Down => (Keys::Down, 0),
KeyEvent::End => (Keys::End, 0),
KeyEvent::Esc => (Keys::Esc, 0),
KeyEvent::Home => (Keys::Home, 0),
KeyEvent::F(n) => (Keys::F(n), 0),
KeyEvent::Insert => (Keys::Insert, 0),
KeyEvent::Left => (Keys::Left, 0),
KeyEvent::PageDown => (Keys::PageDown, 0),
KeyEvent::PageUp => (Keys::PageUp, 0),
KeyEvent::Right => (Keys::Right, 0),
KeyEvent::Up => (Keys::Up, 0),
KeyEvent::Char(' ') => (Keys::Space, 0),
KeyEvent::Char('\n') => (Keys::Enter, 0),
KeyEvent::Char('\t') => (Keys::Tab, 0),
KeyEvent::Char(c) => (Keys::Char(c), 0),
KeyEvent::Alt(c) => (Keys::Char(c), Modifier::Alt as u8),
KeyEvent::Ctrl(c) => (Keys::Char(c), Modifier::Ctrl as u8),
key => panic!("Unsupport Key {key:?}"),
};

Self { key, modifiers }
}
}

impl From<Node> for KeyMap {
fn from(node: Node) -> Self {
let key = match node.key {
Expand Down Expand Up @@ -81,7 +111,7 @@ impl<'s> Deserialize<'s> for KeyMap {

#[cfg(test)]
mod tests {
use crate::{backend::termion::parse, Key};
use crate::{backend::termion::parse, Key, parser::{self, Node}};
use termion::event::Key as KeyEvent;

#[test]
Expand All @@ -91,12 +121,27 @@ mod tests {
("del", KeyEvent::Delete),
("alt-a", KeyEvent::Alt('a')),
("shift-a", KeyEvent::Char('A')),
("shift-=", KeyEvent::Char('+')),
("A", KeyEvent::Char('A')),
("enter", KeyEvent::Char('\n')),
("ctrl-a", KeyEvent::Ctrl('a')),
]
.map(|(s, node)| {
assert_eq!(Key::from(node), parse(s).unwrap());
});
}

#[test]
fn test_from_key_to_node() {
let alt_a = KeyEvent::Alt('a');

[
(KeyEvent::Char('['), "["),
(KeyEvent::Delete, "del"),
(alt_a, "alt-a"),
]
.map(|(key, code)| {
let node = parser::parse(code).unwrap();
assert_eq!(Node::from(key), node);
});
}
}
3 changes: 2 additions & 1 deletion src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ pub(crate) enum Modifier {
Shift = 0b1000,
}

type Modifiers = u8;
pub(crate) type Modifiers = u8;

const MODIFIERS: [Modifier; 4] = [
Modifier::Alt,
Modifier::Cmd,
Expand Down

0 comments on commit 2b46c7f

Please sign in to comment.