Skip to main content

tms/
keymap.rs

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