Skip to main content

typ_core/
key.rs

1use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
2
3/// A key press in both raw and canonical form.
4///
5/// `raw` is used for text insertion and PTY passthrough, where the exact event
6/// matters. `canonical` is used for keybinding lookup, where a stable string
7/// form matters. Keeping both avoids the bug where a binding table and a
8/// text-input path disagree about what was pressed.
9#[derive(Debug, Clone)]
10pub struct KeyChord {
11    pub raw: KeyEvent,
12    pub canonical: String,
13}
14
15impl KeyChord {
16    pub fn from_event(raw: KeyEvent) -> Self {
17        let mut s = String::new();
18        // Fixed order so a binding table never has to guess.
19        if raw.modifiers.contains(KeyModifiers::CONTROL) {
20            s.push_str("ctrl+");
21        }
22        if raw.modifiers.contains(KeyModifiers::ALT) {
23            s.push_str("alt+");
24        }
25        if raw.modifiers.contains(KeyModifiers::SHIFT) {
26            s.push_str("shift+");
27        }
28        s.push_str(&key_name(raw.code));
29        Self { raw, canonical: s }
30    }
31}
32
33fn key_name(code: KeyCode) -> String {
34    match code {
35        KeyCode::Char(c) => c.to_lowercase().to_string(),
36        KeyCode::F(n) => format!("f{n}"),
37        KeyCode::Enter => "enter".into(),
38        KeyCode::Esc => "esc".into(),
39        KeyCode::Tab => "tab".into(),
40        KeyCode::BackTab => "backtab".into(),
41        KeyCode::Backspace => "backspace".into(),
42        KeyCode::Delete => "delete".into(),
43        KeyCode::Insert => "insert".into(),
44        KeyCode::Home => "home".into(),
45        KeyCode::End => "end".into(),
46        KeyCode::PageUp => "pageup".into(),
47        KeyCode::PageDown => "pagedown".into(),
48        KeyCode::Up => "up".into(),
49        KeyCode::Down => "down".into(),
50        KeyCode::Left => "left".into(),
51        KeyCode::Right => "right".into(),
52        other => format!("{other:?}").to_lowercase(),
53    }
54}