1use crate::{Event, KeyCode, KeyEvent, KeyModifiers};
2
3#[derive(Debug)]
5#[non_exhaustive]
6pub struct KeyBindings<CustomKeybinding: crate::CustomKeybinding, CustomEvent: crate::CustomEvent> {
7 pub redo: Vec<Event<CustomEvent>>,
9 pub undo: Vec<Event<CustomEvent>>,
11
12 pub scroll_down: Vec<Event<CustomEvent>>,
14 pub scroll_end: Vec<Event<CustomEvent>>,
16 pub scroll_home: Vec<Event<CustomEvent>>,
18 pub scroll_left: Vec<Event<CustomEvent>>,
20 pub scroll_right: Vec<Event<CustomEvent>>,
22 pub scroll_up: Vec<Event<CustomEvent>>,
24 pub scroll_step_down: Vec<Event<CustomEvent>>,
26 pub scroll_step_up: Vec<Event<CustomEvent>>,
28
29 pub help: Vec<Event<CustomEvent>>,
31
32 pub search_start: Vec<Event<CustomEvent>>,
34 pub search_next: Vec<Event<CustomEvent>>,
36 pub search_previous: Vec<Event<CustomEvent>>,
38
39 pub custom: CustomKeybinding,
41}
42
43#[must_use]
45#[inline]
46#[allow(clippy::string_slice, clippy::missing_panics_doc)]
47pub fn map_keybindings<CustomEvent: crate::CustomEvent>(bindings: &[String]) -> Vec<Event<CustomEvent>> {
48 bindings
49 .iter()
50 .map(|b| {
51 let mut key = String::from(b);
52 let mut modifiers = KeyModifiers::empty();
53 if key.contains("Control") {
54 key = key.replace("Control", "");
55 modifiers.insert(KeyModifiers::CONTROL);
56 }
57 if key.contains("Alt") {
58 key = key.replace("Alt", "");
59 modifiers.insert(KeyModifiers::ALT);
60 }
61 if key.contains("Shift") {
62 key = key.replace("Shift", "");
63 modifiers.insert(KeyModifiers::SHIFT);
64 }
65
66 let code = match key.as_str() {
67 "Backspace" => KeyCode::Backspace,
68 "BackTab" => KeyCode::BackTab,
69 "Delete" => KeyCode::Delete,
70 "Down" => KeyCode::Down,
71 "End" => KeyCode::End,
72 "Enter" => KeyCode::Enter,
73 "Esc" => KeyCode::Esc,
74 "Home" => KeyCode::Home,
75 "Insert" => KeyCode::Insert,
76 "Left" => KeyCode::Left,
77 "PageDown" => KeyCode::PageDown,
78 "PageUp" => KeyCode::PageUp,
79 "Right" => KeyCode::Right,
80 "Tab" => KeyCode::Tab,
81 "Up" => KeyCode::Up,
82 k if k.len() > 1 => {
84 let key_number = k[1..].parse::<u8>().unwrap_or(1);
85 KeyCode::F(key_number)
86 },
87 k => KeyCode::Char(k.chars().next().expect("Expected only one character from Char KeyCode")),
88 };
89 Event::Key(KeyEvent::new(code, modifiers))
90 })
91 .collect()
92}
93
94impl<CustomKeybinding: crate::CustomKeybinding, CustomEvent: crate::CustomEvent>
95 KeyBindings<CustomKeybinding, CustomEvent>
96{
97 #[inline]
99 #[must_use]
100 pub fn new(key_bindings: &config::KeyBindings) -> Self {
101 Self {
102 redo: map_keybindings(&key_bindings.redo),
103 undo: map_keybindings(&key_bindings.undo),
104 scroll_down: map_keybindings(&key_bindings.scroll_down),
105 scroll_end: map_keybindings(&key_bindings.scroll_end),
106 scroll_home: map_keybindings(&key_bindings.scroll_home),
107 scroll_left: map_keybindings(&key_bindings.scroll_left),
108 scroll_right: map_keybindings(&key_bindings.scroll_right),
109 scroll_up: map_keybindings(&key_bindings.scroll_up),
110 scroll_step_down: map_keybindings(&key_bindings.scroll_step_down),
111 scroll_step_up: map_keybindings(&key_bindings.scroll_step_up),
112 help: map_keybindings(&key_bindings.help),
113 search_start: map_keybindings(&key_bindings.search_start),
114 search_next: map_keybindings(&key_bindings.search_next),
115 search_previous: map_keybindings(&key_bindings.search_previous),
116 custom: CustomKeybinding::new(key_bindings),
117 }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use rstest::rstest;
124
125 use super::*;
126 use crate::testutil::local::{TestEvent, TestKeybinding};
127
128 #[test]
129 fn new() {
130 let _key_bindings = KeyBindings::<TestKeybinding, TestEvent>::new(&config::KeyBindings::new());
131 }
132
133 #[test]
134 fn map_keybindings_with_modifiers() {
135 assert_eq!(
136 map_keybindings::<TestEvent>(&[String::from("ControlAltShiftUp")]),
137 vec![Event::Key(KeyEvent::new(
138 KeyCode::Up,
139 KeyModifiers::CONTROL | KeyModifiers::ALT | KeyModifiers::SHIFT
140 ))]
141 );
142 }
143
144 #[rstest]
145 #[case::backspace("Backspace", KeyCode::Backspace)]
146 #[case::back_tab("BackTab", KeyCode::BackTab)]
147 #[case::delete("Delete", KeyCode::Delete)]
148 #[case::down("Down", KeyCode::Down)]
149 #[case::end("End", KeyCode::End)]
150 #[case::enter("Enter", KeyCode::Enter)]
151 #[case::esc("Esc", KeyCode::Esc)]
152 #[case::home("Home", KeyCode::Home)]
153 #[case::insert("Insert", KeyCode::Insert)]
154 #[case::left("Left", KeyCode::Left)]
155 #[case::page_down("PageDown", KeyCode::PageDown)]
156 #[case::page_up("PageUp", KeyCode::PageUp)]
157 #[case::right("Right", KeyCode::Right)]
158 #[case::tab("Tab", KeyCode::Tab)]
159 #[case::up("Up", KeyCode::Up)]
160 #[case::function_in_range("F10", KeyCode::F(10))]
161 #[case::function_out_of_range("F10000", KeyCode::F(1))]
162 #[case::char("a", KeyCode::Char('a'))]
163 fn map_keybindings_key_code(#[case] binding: &str, #[case] key_code: KeyCode) {
164 assert_eq!(map_keybindings::<TestEvent>(&[String::from(binding)]), vec![
165 Event::from(key_code)
166 ]);
167 }
168}