1use {
2 crate::{enums::ReedlineEvent, EditCommand},
3 crossterm::event::{KeyCode, KeyModifiers},
4 serde::{Deserialize, Serialize},
5 std::collections::HashMap,
6};
7
8#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Debug)]
9pub struct KeyCombination {
10 pub modifier: KeyModifiers,
11 pub key_code: KeyCode,
12}
13
14#[derive(Serialize, Deserialize, Clone, Debug)]
16pub struct Keybindings {
17 pub bindings: HashMap<KeyCombination, ReedlineEvent>,
19}
20
21impl Default for Keybindings {
22 fn default() -> Self {
23 Self::new()
24 }
25}
26
27impl Keybindings {
28 pub fn new() -> Self {
30 Self {
31 bindings: HashMap::new(),
32 }
33 }
34
35 pub fn empty() -> Self {
37 Self::new()
38 }
39
40 pub fn add_binding(
46 &mut self,
47 modifier: KeyModifiers,
48 key_code: KeyCode,
49 command: ReedlineEvent,
50 ) {
51 if let ReedlineEvent::UntilFound(subcommands) = &command {
52 assert!(
53 !subcommands.is_empty(),
54 "UntilFound should contain a series of potential events to handle"
55 );
56 }
57
58 let key_combo = KeyCombination { modifier, key_code };
59 self.bindings.insert(key_combo, command);
60 }
61
62 pub fn find_binding(&self, modifier: KeyModifiers, key_code: KeyCode) -> Option<ReedlineEvent> {
64 let key_combo = KeyCombination { modifier, key_code };
65 self.bindings.get(&key_combo).cloned()
66 }
67
68 pub fn remove_binding(
72 &mut self,
73 modifier: KeyModifiers,
74 key_code: KeyCode,
75 ) -> Option<ReedlineEvent> {
76 let key_combo = KeyCombination { modifier, key_code };
77 self.bindings.remove(&key_combo)
78 }
79
80 pub fn get_keybindings(&self) -> &HashMap<KeyCombination, ReedlineEvent> {
82 &self.bindings
83 }
84}
85
86pub fn edit_bind(command: EditCommand) -> ReedlineEvent {
87 ReedlineEvent::Edit(vec![command])
88}
89
90pub fn add_common_control_bindings(kb: &mut Keybindings) {
96 use KeyCode as KC;
97 use KeyModifiers as KM;
98
99 kb.add_binding(KM::NONE, KC::Esc, ReedlineEvent::Esc);
100 kb.add_binding(KM::CONTROL, KC::Char('c'), ReedlineEvent::CtrlC);
101 kb.add_binding(KM::CONTROL, KC::Char('d'), ReedlineEvent::CtrlD);
102 kb.add_binding(KM::CONTROL, KC::Char('l'), ReedlineEvent::ClearScreen);
103 kb.add_binding(KM::CONTROL, KC::Char('r'), ReedlineEvent::SearchHistory);
104 kb.add_binding(KM::CONTROL, KC::Char('o'), ReedlineEvent::OpenEditor);
105}
106pub fn add_common_navigation_bindings(kb: &mut Keybindings) {
108 use EditCommand as EC;
109 use KeyCode as KC;
110 use KeyModifiers as KM;
111
112 kb.add_binding(
114 KM::NONE,
115 KC::Up,
116 ReedlineEvent::UntilFound(vec![ReedlineEvent::MenuUp, ReedlineEvent::Up]),
117 );
118 kb.add_binding(
119 KM::NONE,
120 KC::Down,
121 ReedlineEvent::UntilFound(vec![ReedlineEvent::MenuDown, ReedlineEvent::Down]),
122 );
123 kb.add_binding(
124 KM::NONE,
125 KC::Left,
126 ReedlineEvent::UntilFound(vec![ReedlineEvent::MenuLeft, ReedlineEvent::Left]),
127 );
128 kb.add_binding(
129 KM::NONE,
130 KC::Right,
131 ReedlineEvent::UntilFound(vec![
132 ReedlineEvent::HistoryHintComplete,
133 ReedlineEvent::MenuRight,
134 ReedlineEvent::Right,
135 ]),
136 );
137
138 kb.add_binding(KM::CONTROL, KC::Left, edit_bind(EC::MoveWordLeft));
140 kb.add_binding(
141 KM::CONTROL,
142 KC::Right,
143 ReedlineEvent::UntilFound(vec![
144 ReedlineEvent::HistoryHintWordComplete,
145 edit_bind(EC::MoveWordRight),
146 ]),
147 );
148 kb.add_binding(KM::NONE, KC::Home, edit_bind(EC::MoveToLineStart));
150 kb.add_binding(KM::CONTROL, KC::Char('a'), edit_bind(EC::MoveToLineStart));
151 kb.add_binding(
152 KM::NONE,
153 KC::End,
154 ReedlineEvent::UntilFound(vec![
155 ReedlineEvent::HistoryHintComplete,
156 edit_bind(EC::MoveToLineEnd),
157 ]),
158 );
159 kb.add_binding(
160 KM::CONTROL,
161 KC::Char('e'),
162 ReedlineEvent::UntilFound(vec![
163 ReedlineEvent::HistoryHintComplete,
164 edit_bind(EC::MoveToLineEnd),
165 ]),
166 );
167 kb.add_binding(KM::CONTROL, KC::Home, edit_bind(EC::MoveToStart));
169 kb.add_binding(KM::CONTROL, KC::End, edit_bind(EC::MoveToEnd));
170 kb.add_binding(
172 KM::CONTROL,
173 KC::Char('p'),
174 ReedlineEvent::UntilFound(vec![ReedlineEvent::MenuUp, ReedlineEvent::Up]),
175 );
176 kb.add_binding(
177 KM::CONTROL,
178 KC::Char('n'),
179 ReedlineEvent::UntilFound(vec![ReedlineEvent::MenuDown, ReedlineEvent::Down]),
180 );
181}
182
183pub fn add_common_edit_bindings(kb: &mut Keybindings) {
187 use EditCommand as EC;
188 use KeyCode as KC;
189 use KeyModifiers as KM;
190 kb.add_binding(KM::NONE, KC::Backspace, edit_bind(EC::Backspace));
191 kb.add_binding(KM::NONE, KC::Delete, edit_bind(EC::Delete));
192 kb.add_binding(KM::CONTROL, KC::Backspace, edit_bind(EC::BackspaceWord));
193 kb.add_binding(KM::CONTROL, KC::Delete, edit_bind(EC::DeleteWord));
194 kb.add_binding(KM::CONTROL, KC::Char('h'), edit_bind(EC::Backspace));
196 kb.add_binding(KM::CONTROL, KC::Char('w'), edit_bind(EC::BackspaceWord));
197}