Skip to main content

oo_ide/input/
key_event.rs

1use crate::prelude::*;
2
3use input::{Key, Modifiers};
4
5#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
6pub struct KeyEvent {
7    pub modifiers: Modifiers,
8    pub key: Key,
9}
10
11impl TryFrom<crossterm::event::KeyEvent> for KeyEvent {
12    type Error = ();
13
14    fn try_from(event: crossterm::event::KeyEvent) -> Result<Self, Self::Error> {
15        Ok(KeyEvent {
16            modifiers: event.modifiers.into(),
17            key: Key::try_from(event.code)?,
18        })
19    }
20}
21
22impl std::fmt::Display for KeyEvent {
23    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24        if self.modifiers.is_empty() {
25            write!(f, "{}", self.key)
26        } else {
27            write!(f, "{}+{}", self.modifiers, self.key)
28        }
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35    use crossterm::event::{KeyCode, KeyModifiers};
36
37    fn ct_key(
38        code: KeyCode,
39        mods: KeyModifiers,
40    ) -> crossterm::event::KeyEvent {
41        crossterm::event::KeyEvent::new(code, mods)
42    }
43
44    // --- TryFrom ---
45    #[test]
46    fn try_from_char_no_mods() {
47        let ev = KeyEvent::try_from(ct_key(KeyCode::Char('a'), KeyModifiers::empty()))
48            .unwrap();
49        assert_eq!(ev.key, Key::Char('a'));
50        assert!(ev.modifiers.is_empty());
51    }
52
53    #[test]
54    fn try_from_char_with_ctrl() {
55        let ev =
56            KeyEvent::try_from(ct_key(KeyCode::Char('s'), KeyModifiers::CONTROL)).unwrap();
57        assert_eq!(ev.key, Key::Char('s'));
58        assert!(ev.modifiers.contains(Modifiers::CTRL));
59    }
60
61    #[test]
62    fn try_from_enter_with_shift() {
63        let ev = KeyEvent::try_from(ct_key(KeyCode::Enter, KeyModifiers::SHIFT)).unwrap();
64        assert_eq!(ev.key, Key::Enter);
65        assert!(ev.modifiers.contains(Modifiers::SHIFT));
66    }
67
68    #[test]
69    fn try_from_unknown_key_is_err() {
70        assert!(
71            KeyEvent::try_from(ct_key(KeyCode::Null, KeyModifiers::empty())).is_err()
72        );
73    }
74
75    // --- Display ---
76    #[test]
77    fn display_no_modifiers() {
78        let ev = KeyEvent {
79            modifiers: Modifiers::empty(),
80            key: Key::Enter,
81        };
82        assert_eq!(ev.to_string(), "Enter");
83    }
84
85    #[test]
86    fn display_ctrl_modifier() {
87        let ev = KeyEvent {
88            modifiers: Modifiers::CTRL,
89            key: Key::Char('s'),
90        };
91        assert_eq!(ev.to_string(), "Ctrl+s");
92    }
93
94    #[test]
95    fn display_ctrl_shift_modifier() {
96        let ev = KeyEvent {
97            modifiers: Modifiers::CTRL | Modifiers::SHIFT,
98            key: Key::ArrowUp,
99        };
100        assert_eq!(ev.to_string(), "Ctrl+Shift+ArrowUp");
101    }
102
103    #[test]
104    fn display_alt_modifier() {
105        let ev = KeyEvent {
106            modifiers: Modifiers::ALT,
107            key: Key::F(4),
108        };
109        assert_eq!(ev.to_string(), "Alt+F4");
110    }
111
112    // --- Derived trait smoke tests ---
113    #[test]
114    fn key_event_clone_eq() {
115        let ev = KeyEvent {
116            modifiers: Modifiers::CTRL,
117            key: Key::Char('c'),
118        };
119        assert_eq!(ev.clone(), ev);
120    }
121
122    #[test]
123    fn key_event_hash() {
124        use std::collections::HashSet;
125        let ev = KeyEvent {
126            modifiers: Modifiers::CTRL,
127            key: Key::Char('c'),
128        };
129        let mut s = HashSet::new();
130        s.insert(ev);
131        assert!(s.contains(&ev));
132    }
133}