1use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, PartialEq, Eq, Hash)]
6pub struct KeyBinding {
7 pub code: KeyCode,
8 pub modifiers: KeyModifiers,
9}
10
11impl KeyBinding {
12 #[must_use]
13 pub fn new(code: KeyCode) -> Self {
14 Self {
15 code,
16 modifiers: KeyModifiers::empty(),
17 }
18 }
19
20 #[must_use]
21 pub fn with_ctrl(code: KeyCode) -> Self {
22 Self {
23 code,
24 modifiers: KeyModifiers::CONTROL,
25 }
26 }
27
28 #[must_use]
29 pub fn with_alt(code: KeyCode) -> Self {
30 Self {
31 code,
32 modifiers: KeyModifiers::ALT,
33 }
34 }
35
36 #[must_use]
37 pub fn with_shift(code: KeyCode) -> Self {
38 Self {
39 code,
40 modifiers: KeyModifiers::SHIFT,
41 }
42 }
43
44 #[must_use]
45 pub fn from_event(event: &KeyEvent) -> Self {
46 Self {
47 code: event.code,
48 modifiers: event.modifiers,
49 }
50 }
51}
52
53#[derive(Clone)]
55pub struct KeyDispatcher {
56 command_map: HashMap<KeyBinding, String>,
58 results_map: HashMap<KeyBinding, String>,
59 search_map: HashMap<KeyBinding, String>,
60 filter_map: HashMap<KeyBinding, String>,
61 help_map: HashMap<KeyBinding, String>,
62 debug_map: HashMap<KeyBinding, String>,
63}
64
65impl Default for KeyDispatcher {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71impl KeyDispatcher {
72 #[must_use]
73 pub fn new() -> Self {
74 let mut dispatcher = Self {
75 command_map: HashMap::new(),
76 results_map: HashMap::new(),
77 search_map: HashMap::new(),
78 filter_map: HashMap::new(),
79 help_map: HashMap::new(),
80 debug_map: HashMap::new(),
81 };
82 dispatcher.setup_default_bindings();
83 dispatcher
84 }
85
86 fn setup_default_bindings(&mut self) {
87 self.setup_command_bindings();
89
90 self.setup_results_bindings();
92
93 self.setup_search_bindings();
95 self.setup_filter_bindings();
96
97 self.setup_help_bindings();
99 self.setup_debug_bindings();
100 }
101
102 fn setup_command_bindings(&mut self) {
103 self.command_map
105 .insert(KeyBinding::new(KeyCode::Enter), "execute_query".into());
106 self.command_map
107 .insert(KeyBinding::new(KeyCode::Tab), "handle_completion".into());
108 self.command_map.insert(
109 KeyBinding::new(KeyCode::Backspace),
110 "delete_char_backward".into(),
111 );
112 self.command_map.insert(
113 KeyBinding::new(KeyCode::Delete),
114 "delete_char_forward".into(),
115 );
116 self.command_map
117 .insert(KeyBinding::new(KeyCode::Left), "move_cursor_left".into());
118 self.command_map
119 .insert(KeyBinding::new(KeyCode::Right), "move_cursor_right".into());
120 self.command_map
121 .insert(KeyBinding::new(KeyCode::Home), "move_to_line_start".into());
122 self.command_map
123 .insert(KeyBinding::new(KeyCode::End), "move_to_line_end".into());
124
125 self.command_map
127 .insert(KeyBinding::with_ctrl(KeyCode::Char('c')), "quit".into());
128 self.command_map
129 .insert(KeyBinding::with_ctrl(KeyCode::Char('d')), "quit".into());
130 self.command_map.insert(
131 KeyBinding::with_ctrl(KeyCode::Char('x')),
132 "expand_asterisk".into(),
133 );
134 self.command_map.insert(
135 KeyBinding::with_alt(KeyCode::Char('x')),
136 "expand_asterisk_visible".into(),
137 );
138 self.command_map.insert(
139 KeyBinding::with_ctrl(KeyCode::Char('r')),
140 "search_history".into(),
141 );
142 self.command_map.insert(
143 KeyBinding::with_ctrl(KeyCode::Char('p')),
144 "previous_history".into(),
145 );
146 self.command_map.insert(
147 KeyBinding::with_ctrl(KeyCode::Char('n')),
148 "next_history".into(),
149 );
150 self.command_map.insert(
151 KeyBinding::with_ctrl(KeyCode::Char('a')),
152 "move_to_line_start".into(),
153 );
154 self.command_map.insert(
155 KeyBinding::with_ctrl(KeyCode::Char('e')),
156 "move_to_line_end".into(),
157 );
158 self.command_map.insert(
159 KeyBinding::with_ctrl(KeyCode::Char('w')),
160 "delete_word_backward".into(),
161 );
162 self.command_map.insert(
163 KeyBinding::with_ctrl(KeyCode::Char('k')),
164 "kill_line".into(),
165 );
166 self.command_map.insert(
167 KeyBinding::with_ctrl(KeyCode::Char('u')),
168 "kill_line_backward".into(),
169 );
170 self.command_map
171 .insert(KeyBinding::with_ctrl(KeyCode::Char('y')), "yank".into());
172 self.command_map
173 .insert(KeyBinding::with_ctrl(KeyCode::Char('z')), "undo".into());
174 self.command_map.insert(
175 KeyBinding::with_ctrl(KeyCode::Char('v')),
176 "paste_from_clipboard".into(),
177 );
178 self.command_map.insert(
179 KeyBinding::with_ctrl(KeyCode::Char('6')),
180 "quick_switch_buffer".into(),
181 );
182
183 self.command_map.insert(
185 KeyBinding::with_ctrl(KeyCode::Left),
186 "move_word_backward".into(),
187 );
188 self.command_map.insert(
189 KeyBinding::with_ctrl(KeyCode::Right),
190 "move_word_forward".into(),
191 );
192
193 for i in 1..=9 {
195 let digit_char = char::from_digit(i, 10).unwrap();
196 self.command_map.insert(
197 KeyBinding::with_alt(KeyCode::Char(digit_char)),
198 format!("switch_to_buffer_{i}"),
199 );
200 }
201
202 self.command_map.insert(
204 KeyBinding::with_alt(KeyCode::Char('d')),
205 "delete_word_forward".into(),
206 );
207 self.command_map.insert(
208 KeyBinding::with_alt(KeyCode::Char('b')),
209 "list_buffers".into(), );
211 self.command_map.insert(
212 KeyBinding::with_alt(KeyCode::Char('f')),
213 "move_word_forward".into(),
214 );
215 self.command_map.insert(
216 KeyBinding::with_alt(KeyCode::Char('n')),
217 "new_buffer".into(),
218 );
219 self.command_map.insert(
220 KeyBinding::with_alt(KeyCode::Char('w')),
221 "close_buffer".into(),
222 );
223 self.command_map
224 .insert(KeyBinding::with_alt(KeyCode::Tab), "next_buffer".into());
225 self.command_map.insert(
226 KeyBinding::with_alt(KeyCode::Char('[')),
227 "jump_to_prev_token".into(),
228 );
229 self.command_map.insert(
230 KeyBinding::with_alt(KeyCode::Char(']')),
231 "jump_to_next_token".into(),
232 );
233
234 self.command_map
236 .insert(KeyBinding::new(KeyCode::F(1)), "toggle_help".into());
237 self.command_map
238 .insert(KeyBinding::new(KeyCode::F(5)), "toggle_debug".into());
239 self.command_map
240 .insert(KeyBinding::new(KeyCode::F(6)), "show_pretty_query".into());
241 self.command_map.insert(
242 KeyBinding::new(KeyCode::F(8)),
243 "toggle_case_insensitive".into(),
244 );
245 self.command_map
246 .insert(KeyBinding::new(KeyCode::F(9)), "kill_line".into());
247 self.command_map
248 .insert(KeyBinding::new(KeyCode::F(10)), "kill_line_backward".into());
249 self.command_map
250 .insert(KeyBinding::new(KeyCode::F(11)), "previous_buffer".into());
251 self.command_map
252 .insert(KeyBinding::new(KeyCode::F(12)), "next_buffer".into());
253
254 self.command_map
256 .insert(KeyBinding::new(KeyCode::Down), "enter_results_mode".into());
257 self.command_map.insert(
258 KeyBinding::new(KeyCode::PageDown),
259 "enter_results_mode".into(),
260 );
261 }
262
263 fn setup_results_bindings(&mut self) {
264 self.results_map
266 .insert(KeyBinding::new(KeyCode::Esc), "exit_results_mode".into());
267 self.results_map
268 .insert(KeyBinding::new(KeyCode::Up), "exit_results_mode".into());
269 self.results_map
270 .insert(KeyBinding::with_ctrl(KeyCode::Char('c')), "quit".into());
271 self.results_map
272 .insert(KeyBinding::new(KeyCode::Char('q')), "quit".into());
273
274 self.results_map
276 .insert(KeyBinding::new(KeyCode::Char('j')), "next_row".into());
277 self.results_map
278 .insert(KeyBinding::new(KeyCode::Down), "next_row".into());
279 self.results_map
280 .insert(KeyBinding::new(KeyCode::Char('k')), "previous_row".into());
281
282 self.results_map.insert(
284 KeyBinding::new(KeyCode::Char('h')),
285 "move_column_left".into(),
286 );
287 self.results_map
288 .insert(KeyBinding::new(KeyCode::Left), "move_column_left".into());
289 self.results_map.insert(
290 KeyBinding::new(KeyCode::Char('l')),
291 "move_column_right".into(),
292 );
293 self.results_map
294 .insert(KeyBinding::new(KeyCode::Right), "move_column_right".into());
295
296 self.results_map
298 .insert(KeyBinding::new(KeyCode::Char('g')), "goto_first_row".into());
299 self.results_map.insert(
301 KeyBinding::with_shift(KeyCode::Char('G')),
302 "goto_last_row".into(),
303 );
304
305 self.results_map.insert(
307 KeyBinding::with_shift(KeyCode::Char('H')),
308 "goto_viewport_top".into(),
309 );
310 self.results_map.insert(
311 KeyBinding::with_shift(KeyCode::Char('M')),
312 "goto_viewport_middle".into(),
313 );
314 self.results_map.insert(
315 KeyBinding::with_shift(KeyCode::Char('L')),
316 "goto_viewport_bottom".into(),
317 );
318
319 self.results_map.insert(
320 KeyBinding::new(KeyCode::Char('^')),
321 "goto_first_column".into(),
322 );
323 self.results_map.insert(
324 KeyBinding::new(KeyCode::Char('0')),
325 "goto_first_column".into(),
326 );
327 self.results_map.insert(
328 KeyBinding::new(KeyCode::Char('$')),
329 "goto_last_column".into(),
330 );
331 self.results_map
332 .insert(KeyBinding::new(KeyCode::PageUp), "page_up".into());
333 self.results_map
334 .insert(KeyBinding::new(KeyCode::PageDown), "page_down".into());
335
336 self.results_map
338 .insert(KeyBinding::new(KeyCode::Char('/')), "start_search".into());
339 self.results_map.insert(
340 KeyBinding::new(KeyCode::Char('\\')),
341 "start_column_search".into(),
342 );
343 self.results_map.insert(
344 KeyBinding::with_shift(KeyCode::Char('F')),
345 "start_filter".into(),
346 );
347 self.results_map.insert(
348 KeyBinding::new(KeyCode::Char('f')),
349 "start_fuzzy_filter".into(),
350 );
351 self.results_map
352 .insert(KeyBinding::new(KeyCode::Char('s')), "sort_by_column".into());
353 self.results_map.insert(
354 KeyBinding::with_shift(KeyCode::Char('S')),
355 "show_column_stats".into(),
356 );
357 self.results_map.insert(
358 KeyBinding::new(KeyCode::Char('n')),
359 "next_search_match".into(),
360 );
361 self.results_map.insert(
362 KeyBinding::with_shift(KeyCode::Char('N')),
363 "previous_search_match".into(),
364 );
365
366 self.results_map.insert(
368 KeyBinding::with_shift(KeyCode::Char('C')),
369 "toggle_compact_mode".into(),
370 );
371 self.results_map.insert(
372 KeyBinding::with_shift(KeyCode::Char('1')),
373 "toggle_row_numbers".into(),
374 );
375 self.results_map
376 .insert(KeyBinding::new(KeyCode::Char(':')), "jump_to_row".into());
377 self.results_map
378 .insert(KeyBinding::new(KeyCode::Char('p')), "pin_column".into());
379 self.results_map.insert(
380 KeyBinding::with_shift(KeyCode::Char('P')),
381 "clear_pins".into(),
382 );
383
384 self.results_map.insert(
386 KeyBinding::new(KeyCode::Char('v')),
387 "toggle_selection_mode".into(),
388 );
389 self.results_map.insert(
395 KeyBinding::with_ctrl(KeyCode::Char('e')),
396 "export_to_csv".into(),
397 );
398 self.results_map.insert(
399 KeyBinding::with_ctrl(KeyCode::Char('j')),
400 "export_to_json".into(),
401 );
402
403 self.results_map
405 .insert(KeyBinding::new(KeyCode::F(1)), "toggle_help".into());
406 self.results_map
407 .insert(KeyBinding::new(KeyCode::Char('?')), "toggle_help".into());
408 self.results_map
409 .insert(KeyBinding::new(KeyCode::F(5)), "toggle_debug".into());
410 self.results_map.insert(
411 KeyBinding::new(KeyCode::F(8)),
412 "toggle_case_insensitive".into(),
413 );
414 }
415
416 fn setup_search_bindings(&mut self) {
417 self.search_map
418 .insert(KeyBinding::new(KeyCode::Enter), "apply_search".into());
419 self.search_map
420 .insert(KeyBinding::new(KeyCode::Esc), "cancel_search".into());
421 self.search_map.insert(
422 KeyBinding::new(KeyCode::Backspace),
423 "delete_char_backward".into(),
424 );
425 self.search_map.insert(
426 KeyBinding::new(KeyCode::Delete),
427 "delete_char_forward".into(),
428 );
429 self.search_map
430 .insert(KeyBinding::new(KeyCode::Left), "move_cursor_left".into());
431 self.search_map
432 .insert(KeyBinding::new(KeyCode::Right), "move_cursor_right".into());
433 self.search_map.insert(
434 KeyBinding::with_ctrl(KeyCode::Char('u')),
435 "clear_input".into(),
436 );
437 }
438
439 fn setup_filter_bindings(&mut self) {
440 self.filter_map
441 .insert(KeyBinding::new(KeyCode::Enter), "apply_filter".into());
442 self.filter_map
443 .insert(KeyBinding::new(KeyCode::Esc), "cancel_filter".into());
444 self.filter_map.insert(
445 KeyBinding::new(KeyCode::Backspace),
446 "delete_char_backward".into(),
447 );
448 self.filter_map.insert(
449 KeyBinding::new(KeyCode::Delete),
450 "delete_char_forward".into(),
451 );
452 self.filter_map
453 .insert(KeyBinding::new(KeyCode::Left), "move_cursor_left".into());
454 self.filter_map
455 .insert(KeyBinding::new(KeyCode::Right), "move_cursor_right".into());
456 self.filter_map.insert(
457 KeyBinding::with_ctrl(KeyCode::Char('u')),
458 "clear_input".into(),
459 );
460 }
461
462 fn setup_help_bindings(&mut self) {
463 self.help_map
464 .insert(KeyBinding::new(KeyCode::Esc), "exit_help".into());
465 self.help_map
466 .insert(KeyBinding::new(KeyCode::Char('q')), "exit_help".into());
467 self.help_map
468 .insert(KeyBinding::new(KeyCode::Down), "scroll_help_down".into());
469 self.help_map
470 .insert(KeyBinding::new(KeyCode::Up), "scroll_help_up".into());
471 self.help_map
472 .insert(KeyBinding::new(KeyCode::PageDown), "help_page_down".into());
473 self.help_map
474 .insert(KeyBinding::new(KeyCode::PageUp), "help_page_up".into());
475 }
476
477 fn setup_debug_bindings(&mut self) {
478 self.debug_map
479 .insert(KeyBinding::new(KeyCode::Esc), "exit_debug".into());
480 self.debug_map
481 .insert(KeyBinding::new(KeyCode::Enter), "exit_debug".into());
482 self.debug_map
483 .insert(KeyBinding::new(KeyCode::Char('q')), "exit_debug".into());
484 self.debug_map
485 .insert(KeyBinding::new(KeyCode::Down), "scroll_debug_down".into());
486 self.debug_map
487 .insert(KeyBinding::new(KeyCode::Up), "scroll_debug_up".into());
488 self.debug_map
489 .insert(KeyBinding::new(KeyCode::PageDown), "debug_page_down".into());
490 self.debug_map
491 .insert(KeyBinding::new(KeyCode::PageUp), "debug_page_up".into());
492 self.debug_map
493 .insert(KeyBinding::new(KeyCode::Home), "debug_go_to_top".into());
494 self.debug_map
495 .insert(KeyBinding::new(KeyCode::End), "debug_go_to_bottom".into());
496 self.debug_map.insert(
498 KeyBinding::new(KeyCode::Char('j')),
499 "scroll_debug_down".into(),
500 );
501 self.debug_map.insert(
502 KeyBinding::new(KeyCode::Char('k')),
503 "scroll_debug_up".into(),
504 );
505 self.debug_map.insert(
506 KeyBinding::new(KeyCode::Char('g')),
507 "debug_go_to_top".into(),
508 );
509 self.debug_map.insert(
511 KeyBinding::with_shift(KeyCode::Char('G')),
512 "debug_go_to_bottom".into(),
513 );
514
515 self.debug_map.insert(
517 KeyBinding::with_ctrl(KeyCode::Char('t')),
518 "yank_as_test_case".into(),
519 );
520 self.debug_map.insert(
521 KeyBinding::with_shift(KeyCode::Char('Y')),
522 "yank_debug_context".into(),
523 );
524 }
525
526 #[must_use]
528 pub fn get_command_action(&self, key: &KeyEvent) -> Option<&str> {
529 let binding = KeyBinding::from_event(key);
530 self.command_map
531 .get(&binding)
532 .map(std::string::String::as_str)
533 }
534
535 #[must_use]
537 pub fn get_results_action(&self, key: &KeyEvent) -> Option<&str> {
538 let binding = KeyBinding::from_event(key);
539 self.results_map
540 .get(&binding)
541 .map(std::string::String::as_str)
542 }
543
544 #[must_use]
546 pub fn get_search_action(&self, key: &KeyEvent) -> Option<&str> {
547 let binding = KeyBinding::from_event(key);
548 self.search_map
549 .get(&binding)
550 .map(std::string::String::as_str)
551 }
552
553 #[must_use]
555 pub fn get_filter_action(&self, key: &KeyEvent) -> Option<&str> {
556 let binding = KeyBinding::from_event(key);
557 self.filter_map
558 .get(&binding)
559 .map(std::string::String::as_str)
560 }
561
562 #[must_use]
564 pub fn get_help_action(&self, key: &KeyEvent) -> Option<&str> {
565 let binding = KeyBinding::from_event(key);
566 self.help_map.get(&binding).map(std::string::String::as_str)
567 }
568
569 #[must_use]
571 pub fn get_debug_action(&self, key: &KeyEvent) -> Option<&str> {
572 let binding = KeyBinding::from_event(key);
573 self.debug_map
574 .get(&binding)
575 .map(std::string::String::as_str)
576 }
577
578 pub fn load_custom_bindings(&mut self, mode: &str, _bindings: HashMap<String, String>) {
580 let _map = match mode {
581 "command" => &mut self.command_map,
582 "results" => &mut self.results_map,
583 "search" => &mut self.search_map,
584 "filter" => &mut self.filter_map,
585 "help" => &mut self.help_map,
586 "debug" => &mut self.debug_map,
587 _ => return,
588 };
589
590 }
594}