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}