1use std::{fmt, slice};
2
3use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
4
5use serde::{
6 de::{self, Visitor},
7 Deserialize, Deserializer,
8};
9
10use crate::{command::Command, config::ConfigError};
11
12#[derive(Clone, Debug, PartialEq, Deserialize)]
13pub(crate) struct KeyBinding {
14 pub key: KeySpec,
15 pub command: Command,
16}
17
18impl KeyBinding {
19 pub const fn new(key: KeySpec, command: Command) -> Self {
20 Self { key, command }
21 }
22}
23
24#[derive(Clone, Debug, Eq, Hash, PartialEq)]
25pub struct Keystroke {
26 pub code: KeyCode,
27 pub modifiers: KeyModifiers,
28}
29
30impl Keystroke {
31 pub const fn new(code: KeyCode, modifiers: KeyModifiers) -> Self {
32 Self { code, modifiers }
33 }
34}
35
36impl From<KeyEvent> for Keystroke {
37 fn from(value: KeyEvent) -> Self {
38 Self::from((value.code, value.modifiers))
39 }
40}
41
42impl From<KeyCode> for Keystroke {
43 fn from(code: KeyCode) -> Self {
44 Keystroke::from((code, KeyModifiers::NONE))
45 }
46}
47
48impl From<(KeyCode, KeyModifiers)> for Keystroke {
49 fn from((code, mut modifiers): (KeyCode, KeyModifiers)) -> Self {
50 let code = match code {
51 KeyCode::Char(ch) if ch.is_uppercase() => {
52 modifiers.insert(KeyModifiers::SHIFT);
53 code
54 }
55 KeyCode::Char(ch)
56 if modifiers.contains(KeyModifiers::SHIFT) && ch.is_ascii_lowercase() =>
57 {
58 KeyCode::Char(ch.to_ascii_uppercase())
60 }
61 _ => code,
62 };
63 Self { code, modifiers }
64 }
65}
66
67impl From<(char, KeyModifiers)> for Keystroke {
68 fn from((c, modifiers): (char, KeyModifiers)) -> Self {
69 Keystroke::from((KeyCode::Char(c), modifiers))
70 }
71}
72
73impl From<&KeyEvent> for Keystroke {
74 fn from(event: &KeyEvent) -> Self {
75 Self::from((event.code, event.modifiers))
76 }
77}
78
79impl fmt::Display for Keystroke {
80 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81 let code = self.code.to_string().replace(" ", "_");
82
83 let modifiers = match self.code {
86 KeyCode::Char(ch) if ch.is_uppercase() => self.modifiers - KeyModifiers::SHIFT,
87 _ => self.modifiers,
88 };
89
90 if modifiers.is_empty() {
91 write!(f, "{code}")
92 } else {
93 write!(f, "{}-{code}", modifiers.to_string().to_ascii_lowercase())
94 }
95 }
96}
97
98#[derive(Clone, Debug, Eq, Hash, PartialEq)]
99pub enum Key {
100 Single(Keystroke),
101 Chord(Vec<Keystroke>),
102}
103
104impl fmt::Display for Key {
105 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106 match self {
107 Key::Single(key) => key.fmt(f),
108 Key::Chord(keys) => keys.iter().try_for_each(|key| key.fmt(f)),
109 }
110 }
111}
112
113impl Key {
114 pub const CTRL_C: Key = Key::new(KeyCode::Char('c'), KeyModifiers::CONTROL);
115
116 pub const fn new(code: KeyCode, modifiers: KeyModifiers) -> Self {
117 Key::Single(Keystroke::new(code, modifiers))
118 }
119
120 pub fn chord(iter: impl IntoIterator<Item = Keystroke>) -> Self {
121 let mut keystrokes: Vec<Keystroke> = iter.into_iter().collect();
122 match keystrokes.len() {
123 1 => Key::Single(keystrokes.remove(0)),
124 _ => Key::Chord(keystrokes),
125 }
126 }
127
128 fn keystrokes(&self) -> &[Keystroke] {
129 match self {
130 Key::Single(keystroke) => slice::from_ref(keystroke),
131 Key::Chord(keystrokes) => keystrokes,
132 }
133 }
134}
135
136#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize)]
138#[serde(transparent)]
139pub struct Leader(Key);
140
141impl Default for Leader {
142 fn default() -> Self {
143 Self(Key::from(' '))
144 }
145}
146
147impl From<Key> for Leader {
148 fn from(key: Key) -> Self {
149 Self(key)
150 }
151}
152
153#[derive(Clone, Debug, Eq, Hash, PartialEq)]
157enum Keys {
158 Keystroke(Keystroke),
159 Leader,
160}
161
162#[derive(Clone, Debug, Eq, Hash, PartialEq)]
165pub struct KeySpec(Vec<Keys>);
166
167impl KeySpec {
168 pub fn resolve(&self, leader: &Leader) -> Key {
170 self.0
171 .iter()
172 .flat_map(|keys| match keys {
173 Keys::Keystroke(keystroke) => slice::from_ref(keystroke),
174 Keys::Leader => leader.0.keystrokes(),
175 })
176 .cloned()
177 .collect()
178 }
179
180 fn literal(&self) -> Option<Key> {
182 self.0
183 .iter()
184 .map(|keys| match keys {
185 Keys::Keystroke(keystroke) => Some(keystroke.clone()),
186 Keys::Leader => None,
187 })
188 .collect()
189 }
190}
191
192impl From<Key> for KeySpec {
193 fn from(key: Key) -> Self {
194 Self(
195 key.keystrokes()
196 .iter()
197 .cloned()
198 .map(Keys::Keystroke)
199 .collect(),
200 )
201 }
202}
203
204impl From<KeyEvent> for Key {
205 fn from(value: KeyEvent) -> Self {
206 Self::Single(Keystroke::from(value))
207 }
208}
209
210impl From<KeyCode> for Key {
211 fn from(value: KeyCode) -> Self {
212 Self::Single(Keystroke::from(value))
213 }
214}
215
216impl From<(KeyCode, KeyModifiers)> for Key {
217 fn from(value: (KeyCode, KeyModifiers)) -> Self {
218 Self::Single(Keystroke::from(value))
219 }
220}
221
222impl From<char> for Key {
223 fn from(value: char) -> Self {
224 Self::from(KeyCode::Char(value))
225 }
226}
227
228impl From<(char, KeyModifiers)> for Key {
229 fn from(value: (char, KeyModifiers)) -> Self {
230 Self::Single(Keystroke::from(value))
231 }
232}
233
234impl From<Keystroke> for Key {
235 fn from(value: Keystroke) -> Self {
236 Self::Single(value)
237 }
238}
239
240impl FromIterator<Keystroke> for Key {
241 fn from_iter<T: IntoIterator<Item = Keystroke>>(iter: T) -> Self {
242 Key::chord(iter)
243 }
244}
245
246impl From<Vec<Keystroke>> for Key {
247 fn from(value: Vec<Keystroke>) -> Self {
248 Key::from_iter(value)
249 }
250}
251
252impl<'de> Deserialize<'de> for KeySpec {
253 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
254 where
255 D: Deserializer<'de>,
256 {
257 deserializer.deserialize_str(KeySpecVisitor)
258 }
259}
260
261impl<'de> Deserialize<'de> for Key {
264 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
265 where
266 D: Deserializer<'de>,
267 {
268 KeySpec::deserialize(deserializer)?
269 .literal()
270 .ok_or_else(|| de::Error::custom("`<leader>` is not allowed here"))
271 }
272}
273
274struct KeySpecVisitor;
275
276impl Visitor<'_> for KeySpecVisitor {
277 type Value = KeySpec;
278
279 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
280 formatter.write_str("a single key (\"a\"), named key (\"esc\"), modified key (\"ctrl+x\"), or key sequence (\"gg\", \"<leader>f\")")
281 }
282
283 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
284 where
285 E: de::Error,
286 {
287 parse_keys(value).map(KeySpec).map_err(de::Error::custom)
288 }
289}
290
291fn parse_keys(input: &str) -> Result<Vec<Keys>, ConfigError> {
296 let mut keys = Vec::new();
297 let mut rest = input;
298
299 while !rest.is_empty() {
300 match rest.strip_prefix('<') {
301 Some(after) => {
303 let (name, tail) = after.split_once('>').ok_or_else(|| {
304 ConfigError::InvalidKeybinding(format!("unterminated `<` in {input:?}"))
305 })?;
306 match name {
307 "leader" => keys.push(Keys::Leader),
308 _ => match parse_segment(name)?.as_slice() {
309 [single] if !name.is_empty() => keys.push(Keys::Keystroke(single.clone())),
310 _ => {
311 return Err(ConfigError::InvalidKeybinding(format!(
312 "`<{name}>` is not a single key"
313 )))
314 }
315 },
316 }
317 rest = tail;
318 }
319 None => {
320 let bare = rest.split('<').next().unwrap_or(rest);
321 keys.extend(parse_segment(bare)?.into_iter().map(Keys::Keystroke));
322 rest = &rest[bare.len()..];
323 }
324 }
325 }
326
327 Ok(keys)
328}
329
330fn parse_segment(segment: &str) -> Result<Vec<Keystroke>, ConfigError> {
333 let mut parts = segment.split('+');
334 let code = parts
335 .next_back()
336 .ok_or_else(|| ConfigError::UnknownKeyCode(segment.to_string()))?;
337
338 let modifiers = parts.try_fold(KeyModifiers::NONE, |modifiers, part| {
339 parse_modifiers(&part.to_lowercase()).map(|parsed| modifiers | parsed)
340 })?;
341
342 Ok(parse_key(code, modifiers)?.keystrokes().to_vec())
343}
344
345fn parse_key(code: &str, modifiers: KeyModifiers) -> Result<Key, ConfigError> {
346 if code.is_empty() {
347 return Ok(Key::from((KeyCode::Null, modifiers)));
348 }
349
350 let key_code = match code {
351 "esc" => KeyCode::Esc,
352 "space" => KeyCode::Char(' '),
353 "backspace" => KeyCode::Backspace,
354 "backtab" => KeyCode::BackTab,
355 "delete" => KeyCode::Delete,
356 "down" => KeyCode::Down,
357 "end" => KeyCode::End,
358 "enter" => KeyCode::Enter,
359 "home" => KeyCode::Home,
360 "insert" => KeyCode::Insert,
361 "lt" => KeyCode::Char('<'),
363 "gt" => KeyCode::Char('>'),
364 "left" => KeyCode::Left,
365 "page_down" => KeyCode::PageDown,
366 "page_up" => KeyCode::PageUp,
367 "right" => KeyCode::Right,
368 "tab" => KeyCode::Tab,
369 "up" => KeyCode::Up,
370 c if c.chars().count() == 1 => c
372 .chars()
373 .next()
374 .map(KeyCode::Char)
375 .ok_or_else(|| ConfigError::UnknownKeyCode(c.to_string()))?,
376 c if c.starts_with('f') => c[1..]
378 .parse::<u8>()
379 .map(KeyCode::F)
380 .map_err(|_| ConfigError::UnknownKeyCode(c.to_string()))?,
381 c => {
383 return Ok(Key::chord(
384 c.chars().map(KeyCode::Char).map(Keystroke::from),
385 ))
386 }
387 };
388
389 Ok(Key::from((key_code, modifiers)))
390}
391
392fn parse_modifiers(modifiers: &str) -> Result<KeyModifiers, ConfigError> {
393 if modifiers.is_empty() {
394 return Ok(KeyModifiers::NONE);
395 }
396
397 match modifiers {
398 "alt" => Ok(KeyModifiers::ALT),
399 "ctrl" | "control" => Ok(KeyModifiers::CONTROL),
400 "hyper" => Ok(KeyModifiers::HYPER),
401 "meta" => Ok(KeyModifiers::META),
402 "shift" => Ok(KeyModifiers::SHIFT),
403 "super" => Ok(KeyModifiers::SUPER),
404 _ => Err(ConfigError::UnknownKeyModifiers(modifiers.to_string())),
405 }
406}
407
408impl de::Error for ConfigError {
409 fn custom<T>(msg: T) -> Self
410 where
411 T: fmt::Display,
412 {
413 ConfigError::InvalidKeybinding(msg.to_string())
414 }
415}
416
417#[cfg(test)]
418mod tests {
419 use ratatui::crossterm::event::{KeyCode, KeyModifiers};
420 use serde::de::IntoDeserializer;
421
422 use super::*;
423
424 fn key_from_str(s: &str) -> Result<Key, ConfigError> {
425 Key::deserialize(s.into_deserializer())
426 }
427
428 fn spec_from_str(s: &str) -> Result<KeySpec, ConfigError> {
429 KeySpec::deserialize(s.into_deserializer())
430 }
431
432 #[test]
433 fn test_named_keys() {
434 let cases = [
435 ("esc", Key::from(KeyCode::Esc)),
436 ("enter", Key::from(KeyCode::Enter)),
437 ("space", Key::from(KeyCode::Char(' '))),
438 ("backspace", Key::from(KeyCode::Backspace)),
439 ("backtab", Key::from(KeyCode::BackTab)),
440 ("delete", Key::from(KeyCode::Delete)),
441 ("tab", Key::from(KeyCode::Tab)),
442 ("up", Key::from(KeyCode::Up)),
443 ("down", Key::from(KeyCode::Down)),
444 ("left", Key::from(KeyCode::Left)),
445 ("right", Key::from(KeyCode::Right)),
446 ("home", Key::from(KeyCode::Home)),
447 ("end", Key::from(KeyCode::End)),
448 ("page_up", Key::from(KeyCode::PageUp)),
449 ("page_down", Key::from(KeyCode::PageDown)),
450 ("insert", Key::from(KeyCode::Insert)),
451 ];
452
453 cases.into_iter().for_each(|(input, expected)| {
454 assert_eq!(key_from_str(input).unwrap(), expected, "input: {input:?}");
455 });
456 }
457
458 #[test]
459 fn test_single_char_keys() {
460 let cases = [
461 ("a", Key::from('a')),
462 ("z", Key::from('z')),
463 ("A", Key::from('A')),
464 ("0", Key::from('0')),
465 ("?", Key::from('?')),
466 ("/", Key::from('/')),
467 (":", Key::from(':')),
468 ];
469
470 cases.into_iter().for_each(|(input, expected)| {
471 assert_eq!(key_from_str(input).unwrap(), expected, "input: {input:?}");
472 });
473 }
474
475 #[test]
476 fn test_function_keys() {
477 let cases = [
478 ("f1", Key::from(KeyCode::F(1))),
479 ("f5", Key::from(KeyCode::F(5))),
480 ("f12", Key::from(KeyCode::F(12))),
481 ];
482
483 cases.into_iter().for_each(|(input, expected)| {
484 assert_eq!(key_from_str(input).unwrap(), expected, "input: {input:?}");
485 });
486 }
487
488 #[test]
489 fn test_modified_keys() {
490 let cases = [
491 ("ctrl+c", Key::from(('c', KeyModifiers::CONTROL))),
492 ("control+c", Key::from(('c', KeyModifiers::CONTROL))),
493 ("alt+x", Key::from(('x', KeyModifiers::ALT))),
494 ("shift+a", Key::from(('a', KeyModifiers::SHIFT))),
495 (
496 "ctrl+shift+k",
497 Key::from((
498 KeyCode::Char('k'),
499 KeyModifiers::CONTROL | KeyModifiers::SHIFT,
500 )),
501 ),
502 (
503 "ctrl+enter",
504 Key::from((KeyCode::Enter, KeyModifiers::CONTROL)),
505 ),
506 ("alt+esc", Key::from((KeyCode::Esc, KeyModifiers::ALT))),
507 ("ctrl+f5", Key::from((KeyCode::F(5), KeyModifiers::CONTROL))),
508 ];
509
510 cases.into_iter().for_each(|(input, expected)| {
511 assert_eq!(key_from_str(input).unwrap(), expected, "input: {input:?}");
512 });
513 }
514
515 #[test]
516 fn test_key_sequences() {
517 let cases: &[(&str, &[Keystroke])] = &[
518 (
519 "gg",
520 &[
521 Keystroke::from(KeyCode::Char('g')),
522 Keystroke::from(KeyCode::Char('g')),
523 ],
524 ),
525 (
526 "gG",
527 &[
528 Keystroke::from(KeyCode::Char('g')),
529 Keystroke::from(KeyCode::Char('G')),
530 ],
531 ),
532 (
533 "crn",
534 &[
535 Keystroke::from(KeyCode::Char('c')),
536 Keystroke::from(KeyCode::Char('r')),
537 Keystroke::from(KeyCode::Char('n')),
538 ],
539 ),
540 ];
541
542 cases.iter().for_each(|(input, expected_keys)| {
543 let key = key_from_str(input).unwrap();
544 match key {
545 Key::Chord(keys) => assert_eq!(keys, *expected_keys, "input: {input:?}"),
546 Key::Single(_) => panic!("Expected sequence for {input:?}, got plain key"),
547 }
548 });
549 }
550
551 #[test]
552 fn test_named_key_sequences() {
553 let space = Keystroke::from(KeyCode::Char(' '));
554 let cases: &[(&str, &[Keystroke])] = &[
555 (
556 "<space>f",
557 &[space.clone(), Keystroke::from(KeyCode::Char('f'))],
558 ),
559 ("<space><space>", &[space.clone(), space.clone()]),
560 (
561 "<enter>x",
562 &[
563 Keystroke::from(KeyCode::Enter),
564 Keystroke::from(KeyCode::Char('x')),
565 ],
566 ),
567 (
568 "g<space>",
569 &[Keystroke::from(KeyCode::Char('g')), space.clone()],
570 ),
571 (
572 "<ctrl+space>f",
573 &[
574 Keystroke::from((KeyCode::Char(' '), KeyModifiers::CONTROL)),
575 Keystroke::from(KeyCode::Char('f')),
576 ],
577 ),
578 ];
579
580 cases.iter().for_each(
581 |(input, expected_keys)| match key_from_str(input).unwrap() {
582 Key::Chord(keys) => assert_eq!(keys, *expected_keys, "input: {input:?}"),
583 Key::Single(_) => panic!("Expected sequence for {input:?}, got plain key"),
584 },
585 );
586 }
587
588 #[test]
589 fn test_named_group_single_key() {
590 assert_eq!(
592 key_from_str("<space>").unwrap(),
593 key_from_str("space").unwrap()
594 );
595 assert_eq!(key_from_str("<esc>").unwrap(), key_from_str("esc").unwrap());
596 assert_eq!(key_from_str("<lt>").unwrap(), Key::from('<'));
598 assert_eq!(key_from_str("<gt>").unwrap(), Key::from('>'));
599 }
600
601 #[test]
602 fn test_invalid_keys() {
603 let cases = [
604 "unknown_modifier+c",
605 "badmod+x",
606 "f999",
607 "<space",
608 "<>",
609 "<unknown_key>",
610 ];
611
612 cases.into_iter().for_each(|input| {
613 assert!(key_from_str(input).is_err(), "Expected error for {input:?}");
614 });
615 }
616
617 #[test]
618 fn test_keystroke_display() {
619 let cases = [
620 (Keystroke::new(KeyCode::Char('a'), KeyModifiers::NONE), "a"),
621 (
622 Keystroke::new(KeyCode::Char('c'), KeyModifiers::CONTROL),
623 "control-c",
624 ),
625 (Keystroke::from(KeyCode::Char('G')), "G"),
627 (
629 Keystroke::from((KeyCode::Char('G'), KeyModifiers::CONTROL)),
630 "control-G",
631 ),
632 ];
633
634 cases.into_iter().for_each(|(key, expected)| {
635 assert_eq!(key.to_string(), expected, "key: {key:?}");
636 });
637 }
638
639 #[test]
640 fn test_key_sequence_display() {
641 let keys = [
642 Keystroke::from(KeyCode::Char('g')),
643 Keystroke::from(KeyCode::Char('G')),
644 ];
645
646 assert_eq!(Key::chord(keys).to_string(), "gG");
647 }
648
649 #[test]
650 fn test_leader_expands_to_the_configured_key() {
651 let leader = Leader::from(Key::from(','));
652 let cases = [
653 ("<leader>", ","),
654 ("<leader>f", ",f"),
655 ("<leader><leader>", ",,"),
656 ("g<leader>", "g,"),
657 ("<leader><space>", ",<space>"),
658 ];
659
660 cases.into_iter().for_each(|(input, expected)| {
661 assert_eq!(
662 spec_from_str(input).unwrap().resolve(&leader),
663 key_from_str(expected).unwrap(),
664 "input: {input:?}"
665 );
666 });
667 }
668
669 #[test]
670 fn test_leader_defaults_to_space() {
671 assert_eq!(
672 spec_from_str("<leader>f")
673 .unwrap()
674 .resolve(&Leader::default()),
675 key_from_str("<space>f").unwrap()
676 );
677 }
678
679 #[test]
680 fn test_leader_can_be_a_sequence() {
681 let leader = Leader::from(key_from_str("gs").unwrap());
682
683 assert_eq!(
684 spec_from_str("<leader>f").unwrap().resolve(&leader),
685 key_from_str("gsf").unwrap()
686 );
687 }
688
689 #[test]
690 fn test_leader_is_not_a_literal_key() {
691 assert!(key_from_str("<leader>").is_err());
693 assert!(key_from_str("<leader>f").is_err());
694 }
695
696 #[test]
697 fn test_uppercase_implies_shift() {
698 let upper = key_from_str("G").unwrap();
700 assert_eq!(
701 upper,
702 Key::Single(Keystroke::new(KeyCode::Char('G'), KeyModifiers::SHIFT))
703 );
704
705 let seq = key_from_str("gG").unwrap();
707 assert_eq!(
708 seq,
709 Key::chord([
710 Keystroke::new(KeyCode::Char('g'), KeyModifiers::NONE),
711 Keystroke::new(KeyCode::Char('G'), KeyModifiers::SHIFT),
712 ])
713 );
714 }
715}