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