tms/
keymap.rs

1use std::{collections::HashMap, fmt::Debug};
2
3use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
4use serde::de::Error as DeError;
5use serde::ser::Error as SerError;
6use serde::{Deserialize, Serialize};
7
8use crate::error::TmsError;
9
10#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd)]
11pub struct Key {
12    code: KeyCode,
13    modifiers: KeyModifiers,
14}
15
16impl Serialize for Key {
17    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
18    where
19        S: serde::Serializer,
20    {
21        let modifiers = self
22            .modifiers
23            .iter()
24            .filter_map(modifier_to_string)
25            .collect::<Vec<&str>>()
26            .join("-");
27        let code = keycode_to_string(self.code)
28            .ok_or(TmsError::ConfigError)
29            .map_err(S::Error::custom)?;
30        let formatted = if modifiers.is_empty() {
31            code
32        } else {
33            format!("{}-{}", modifiers, code)
34        };
35        serializer.serialize_str(&formatted)
36    }
37}
38
39fn modifier_to_string<'a>(modifier: KeyModifiers) -> Option<&'a str> {
40    match modifier {
41        KeyModifiers::SHIFT => Some("shift"),
42        KeyModifiers::CONTROL => Some("ctrl"),
43        KeyModifiers::ALT => Some("alt"),
44        KeyModifiers::SUPER => Some("super"),
45        KeyModifiers::HYPER => Some("hyper"),
46        KeyModifiers::META => Some("meta"),
47        _ => None,
48    }
49}
50
51fn keycode_to_string(code: KeyCode) -> Option<String> {
52    match code {
53        KeyCode::Esc => Some("esc".to_owned()),
54        KeyCode::Enter => Some("enter".to_owned()),
55        KeyCode::Left => Some("left".to_owned()),
56        KeyCode::Right => Some("right".to_owned()),
57        KeyCode::Up => Some("up".to_owned()),
58        KeyCode::Down => Some("down".to_owned()),
59        KeyCode::Home => Some("home".to_owned()),
60        KeyCode::End => Some("end".to_owned()),
61        KeyCode::PageUp => Some("pageup".to_owned()),
62        KeyCode::PageDown => Some("pagedown".to_owned()),
63        KeyCode::BackTab => Some("backtab".to_owned()),
64        KeyCode::Backspace => Some("backspace".to_owned()),
65        KeyCode::Delete => Some("delete".to_owned()),
66        KeyCode::Insert => Some("insert".to_owned()),
67        KeyCode::F(1) => Some("f1".to_owned()),
68        KeyCode::F(2) => Some("f2".to_owned()),
69        KeyCode::F(3) => Some("f3".to_owned()),
70        KeyCode::F(4) => Some("f4".to_owned()),
71        KeyCode::F(5) => Some("f5".to_owned()),
72        KeyCode::F(6) => Some("f6".to_owned()),
73        KeyCode::F(7) => Some("f7".to_owned()),
74        KeyCode::F(8) => Some("f8".to_owned()),
75        KeyCode::F(9) => Some("f9".to_owned()),
76        KeyCode::F(10) => Some("f10".to_owned()),
77        KeyCode::F(11) => Some("f11".to_owned()),
78        KeyCode::F(12) => Some("f12".to_owned()),
79        KeyCode::Char(' ') => Some("space".to_owned()),
80        KeyCode::Tab => Some("tab".to_owned()),
81        KeyCode::Char(c) => Some(String::from(c)),
82        _ => None,
83    }
84}
85
86impl<'de> Deserialize<'de> for Key {
87    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
88    where
89        D: serde::Deserializer<'de>,
90    {
91        let value: String = Deserialize::deserialize(deserializer)?;
92        let tokens = value.split('-').collect::<Vec<&str>>();
93
94        let mut modifiers = KeyModifiers::empty();
95
96        for modifier in tokens.iter().take(tokens.len() - 1) {
97            match modifier.to_ascii_lowercase().as_ref() {
98                "shift" => modifiers.insert(KeyModifiers::SHIFT),
99                "ctrl" => modifiers.insert(KeyModifiers::CONTROL),
100                "alt" => modifiers.insert(KeyModifiers::ALT),
101                "super" => modifiers.insert(KeyModifiers::SUPER),
102                "hyper" => modifiers.insert(KeyModifiers::HYPER),
103                "meta" => modifiers.insert(KeyModifiers::META),
104                _ => {}
105            };
106        }
107
108        let last = tokens
109            .last()
110            .ok_or(TmsError::ConfigError)
111            .map_err(D::Error::custom)?;
112
113        let code = match last.to_ascii_lowercase().as_ref() {
114            "esc" => KeyCode::Esc,
115            "enter" => KeyCode::Enter,
116            "left" => KeyCode::Left,
117            "right" => KeyCode::Right,
118            "up" => KeyCode::Up,
119            "down" => KeyCode::Down,
120            "home" => KeyCode::Home,
121            "end" => KeyCode::End,
122            "pageup" => KeyCode::PageUp,
123            "pagedown" => KeyCode::PageDown,
124            "backtab" => KeyCode::BackTab,
125            "backspace" => KeyCode::Backspace,
126            "del" => KeyCode::Delete,
127            "delete" => KeyCode::Delete,
128            "insert" => KeyCode::Insert,
129            "ins" => KeyCode::Insert,
130            "f1" => KeyCode::F(1),
131            "f2" => KeyCode::F(2),
132            "f3" => KeyCode::F(3),
133            "f4" => KeyCode::F(4),
134            "f5" => KeyCode::F(5),
135            "f6" => KeyCode::F(6),
136            "f7" => KeyCode::F(7),
137            "f8" => KeyCode::F(8),
138            "f9" => KeyCode::F(9),
139            "f10" => KeyCode::F(10),
140            "f11" => KeyCode::F(11),
141            "f12" => KeyCode::F(12),
142            "space" => KeyCode::Char(' '),
143            "tab" => KeyCode::Tab,
144            c if c.len() == 1 => KeyCode::Char(c.chars().next().unwrap()),
145            _ => {
146                return Err(D::Error::custom(TmsError::ConfigError));
147            }
148        };
149        Ok(Key { code, modifiers })
150    }
151}
152
153impl From<KeyEvent> for Key {
154    fn from(value: KeyEvent) -> Self {
155        Self {
156            code: value.code,
157            modifiers: value.modifiers,
158        }
159    }
160}
161
162pub type Keymap = HashMap<Key, PickerAction>;
163
164pub fn default_keymap() -> Keymap {
165    HashMap::from([
166        (
167            Key {
168                code: KeyCode::Char('c'),
169                modifiers: KeyModifiers::CONTROL,
170            },
171            PickerAction::Cancel,
172        ),
173        (
174            Key {
175                code: KeyCode::Esc,
176                modifiers: KeyModifiers::empty(),
177            },
178            PickerAction::Cancel,
179        ),
180        (
181            Key {
182                code: KeyCode::Enter,
183                modifiers: KeyModifiers::empty(),
184            },
185            PickerAction::Confirm,
186        ),
187        (
188            Key {
189                code: KeyCode::Delete,
190                modifiers: KeyModifiers::empty(),
191            },
192            PickerAction::Delete,
193        ),
194        (
195            Key {
196                code: KeyCode::Char('d'),
197                modifiers: KeyModifiers::CONTROL,
198            },
199            PickerAction::Delete,
200        ),
201        (
202            Key {
203                code: KeyCode::Backspace,
204                modifiers: KeyModifiers::empty(),
205            },
206            PickerAction::Backspace,
207        ),
208        (
209            Key {
210                code: KeyCode::Down,
211                modifiers: KeyModifiers::empty(),
212            },
213            PickerAction::MoveDown,
214        ),
215        (
216            Key {
217                code: KeyCode::Char('j'),
218                modifiers: KeyModifiers::CONTROL,
219            },
220            PickerAction::MoveDown,
221        ),
222        (
223            Key {
224                code: KeyCode::Char('n'),
225                modifiers: KeyModifiers::CONTROL,
226            },
227            PickerAction::MoveDown,
228        ),
229        (
230            Key {
231                code: KeyCode::Up,
232                modifiers: KeyModifiers::empty(),
233            },
234            PickerAction::MoveUp,
235        ),
236        (
237            Key {
238                code: KeyCode::Char('k'),
239                modifiers: KeyModifiers::CONTROL,
240            },
241            PickerAction::MoveUp,
242        ),
243        (
244            Key {
245                code: KeyCode::Char('p'),
246                modifiers: KeyModifiers::CONTROL,
247            },
248            PickerAction::MoveUp,
249        ),
250        (
251            Key {
252                code: KeyCode::Left,
253                modifiers: KeyModifiers::empty(),
254            },
255            PickerAction::CursorLeft,
256        ),
257        (
258            Key {
259                code: KeyCode::Right,
260                modifiers: KeyModifiers::empty(),
261            },
262            PickerAction::CursorRight,
263        ),
264        (
265            Key {
266                code: KeyCode::Char('w'),
267                modifiers: KeyModifiers::CONTROL,
268            },
269            PickerAction::DeleteWord,
270        ),
271        (
272            Key {
273                code: KeyCode::Char('u'),
274                modifiers: KeyModifiers::CONTROL,
275            },
276            PickerAction::DeleteToLineStart,
277        ),
278        (
279            Key {
280                code: KeyCode::Char('a'),
281                modifiers: KeyModifiers::CONTROL,
282            },
283            PickerAction::MoveToLineStart,
284        ),
285        (
286            Key {
287                code: KeyCode::Char('e'),
288                modifiers: KeyModifiers::CONTROL,
289            },
290            PickerAction::MoveToLineEnd,
291        ),
292    ])
293}
294
295#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
296pub enum PickerAction {
297    #[serde(rename = "")]
298    Noop,
299    #[serde(rename = "cancel")]
300    Cancel,
301    #[serde(rename = "confirm")]
302    Confirm,
303    #[serde(rename = "backspace")]
304    Backspace,
305    #[serde(rename = "delete")]
306    Delete,
307    #[serde(rename = "move_up")]
308    MoveUp,
309    #[serde(rename = "move_down")]
310    MoveDown,
311    #[serde(rename = "cursor_left")]
312    CursorLeft,
313    #[serde(rename = "cursor_right")]
314    CursorRight,
315    #[serde(rename = "delete_word")]
316    DeleteWord,
317    #[serde(rename = "delete_to_line_start")]
318    DeleteToLineStart,
319    #[serde(rename = "delete_to_line_end")]
320    DeleteToLineEnd,
321    #[serde(rename = "move_to_line_start")]
322    MoveToLineStart,
323    #[serde(rename = "move_to_line_end")]
324    MoveToLineEnd,
325}