1use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
2use std::collections::HashMap;
3
4pub type ActionCallback = Box<dyn Fn(&mut dyn ActionHandler) -> bool>;
6
7pub trait ActionHandler {
9 fn execute_query(&mut self) -> bool;
11 fn toggle_multiline(&mut self) -> bool;
12 fn expand_select_star(&mut self) -> bool;
13 fn search_history(&mut self) -> bool;
14 fn previous_history(&mut self) -> bool;
15 fn next_history(&mut self) -> bool;
16 fn autocomplete(&mut self) -> bool;
17 fn toggle_help(&mut self) -> bool;
18 fn toggle_debug(&mut self) -> bool;
19 fn toggle_case_insensitive(&mut self) -> bool;
20 fn enter_results_mode(&mut self) -> bool;
21 fn exit_app(&mut self) -> bool;
22
23 fn next_buffer(&mut self) -> bool;
25 fn previous_buffer(&mut self) -> bool;
26 fn quick_switch_buffer(&mut self) -> bool;
27 fn new_buffer(&mut self) -> bool;
28 fn close_buffer(&mut self) -> bool;
29 fn list_buffers(&mut self) -> bool;
30
31 fn move_cursor_left(&mut self) -> bool;
33 fn move_cursor_right(&mut self) -> bool;
34 fn move_cursor_up(&mut self) -> bool;
35 fn move_cursor_down(&mut self) -> bool;
36 fn move_to_line_start(&mut self) -> bool;
37 fn move_to_line_end(&mut self) -> bool;
38 fn move_word_backward(&mut self) -> bool;
39 fn move_word_forward(&mut self) -> bool;
40 fn delete_char_backward(&mut self) -> bool;
41 fn delete_char_forward(&mut self) -> bool;
42 fn delete_word_backward(&mut self) -> bool;
43 fn delete_word_forward(&mut self) -> bool;
44 fn kill_line_forward(&mut self) -> bool;
45 fn kill_line_backward(&mut self) -> bool;
46 fn yank(&mut self) -> bool;
47 fn undo(&mut self) -> bool;
48 fn redo(&mut self) -> bool;
49 fn insert_char(&mut self, c: char) -> bool;
50
51 fn move_row_up(&mut self) -> bool;
53 fn move_row_down(&mut self) -> bool;
54 fn move_column_left(&mut self) -> bool;
55 fn move_column_right(&mut self) -> bool;
56 fn page_up(&mut self) -> bool;
57 fn page_down(&mut self) -> bool;
58 fn go_to_first_row(&mut self) -> bool;
59 fn go_to_last_row(&mut self) -> bool;
60 fn go_to_first_column(&mut self) -> bool;
61 fn go_to_last_column(&mut self) -> bool;
62 fn toggle_compact_mode(&mut self) -> bool;
63 fn toggle_row_numbers(&mut self) -> bool;
64 fn jump_to_row(&mut self) -> bool;
65 fn pin_column(&mut self) -> bool;
66 fn clear_pins(&mut self) -> bool;
67 fn start_search(&mut self) -> bool;
68 fn start_column_search(&mut self) -> bool;
69 fn start_filter(&mut self) -> bool;
70 fn start_fuzzy_filter(&mut self) -> bool;
71 fn next_search_result(&mut self) -> bool;
72 fn previous_search_result(&mut self) -> bool;
73 fn sort_by_column(&mut self) -> bool;
74 fn show_column_stats(&mut self) -> bool;
75 fn toggle_selection_mode(&mut self) -> bool;
76 fn yank_selection(&mut self) -> bool;
77 fn export_csv(&mut self) -> bool;
78 fn export_json(&mut self) -> bool;
79 fn back_to_command(&mut self) -> bool;
80
81 fn apply_search(&mut self) -> bool;
83 fn cancel_search(&mut self) -> bool;
84}
85
86#[derive(Debug, Clone, PartialEq, Eq, Hash)]
88pub struct KeyBinding {
89 pub code: KeyCode,
90 pub modifiers: KeyModifiers,
91}
92
93impl KeyBinding {
94 #[must_use]
95 pub fn new(code: KeyCode) -> Self {
96 Self {
97 code,
98 modifiers: KeyModifiers::empty(),
99 }
100 }
101
102 #[must_use]
103 pub fn with_ctrl(code: KeyCode) -> Self {
104 Self {
105 code,
106 modifiers: KeyModifiers::CONTROL,
107 }
108 }
109
110 #[must_use]
111 pub fn with_alt(code: KeyCode) -> Self {
112 Self {
113 code,
114 modifiers: KeyModifiers::ALT,
115 }
116 }
117
118 #[must_use]
119 pub fn with_shift(code: KeyCode) -> Self {
120 Self {
121 code,
122 modifiers: KeyModifiers::SHIFT,
123 }
124 }
125
126 #[must_use]
127 pub fn with_modifiers(code: KeyCode, modifiers: KeyModifiers) -> Self {
128 Self { code, modifiers }
129 }
130
131 #[must_use]
132 pub fn from_event(event: &KeyEvent) -> Self {
133 Self {
134 code: event.code,
135 modifiers: event.modifiers,
136 }
137 }
138}
139
140pub struct KeyBindingManager {
142 command_bindings: HashMap<KeyBinding, String>,
143 results_bindings: HashMap<KeyBinding, String>,
144 search_bindings: HashMap<KeyBinding, String>,
145}
146
147impl Default for KeyBindingManager {
148 fn default() -> Self {
149 Self::new()
150 }
151}
152
153impl KeyBindingManager {
154 #[must_use]
155 pub fn new() -> Self {
156 let mut manager = Self {
157 command_bindings: HashMap::new(),
158 results_bindings: HashMap::new(),
159 search_bindings: HashMap::new(),
160 };
161 manager.setup_default_bindings();
162 manager
163 }
164
165 fn setup_default_bindings(&mut self) {
166 self.command_bindings
168 .insert(KeyBinding::new(KeyCode::Enter), "execute_query".to_string());
169 self.command_bindings
170 .insert(KeyBinding::new(KeyCode::Tab), "autocomplete".to_string());
171 self.command_bindings
172 .insert(KeyBinding::new(KeyCode::F(1)), "toggle_help".to_string());
173 self.command_bindings.insert(
174 KeyBinding::new(KeyCode::Char('?')),
175 "toggle_help".to_string(),
176 );
177 self.command_bindings.insert(
178 KeyBinding::new(KeyCode::F(3)),
179 "toggle_multiline".to_string(),
180 );
181 self.command_bindings
182 .insert(KeyBinding::new(KeyCode::F(5)), "toggle_debug".to_string());
183 self.command_bindings.insert(
184 KeyBinding::new(KeyCode::F(8)),
185 "toggle_case_insensitive".to_string(),
186 );
187 self.command_bindings.insert(
188 KeyBinding::new(KeyCode::Down),
189 "enter_results_mode".to_string(),
190 );
191
192 self.command_bindings.insert(
194 KeyBinding::with_ctrl(KeyCode::Char('c')),
195 "exit_app".to_string(),
196 );
197 self.command_bindings.insert(
198 KeyBinding::with_ctrl(KeyCode::Char('d')),
199 "exit_app".to_string(),
200 );
201 self.command_bindings.insert(
202 KeyBinding::with_ctrl(KeyCode::Char('x')),
203 "expand_select_star".to_string(),
204 );
205 self.command_bindings.insert(
206 KeyBinding::with_ctrl(KeyCode::Char('r')),
207 "search_history".to_string(),
208 );
209 self.command_bindings.insert(
210 KeyBinding::with_ctrl(KeyCode::Char('p')),
211 "previous_history".to_string(),
212 );
213 self.command_bindings.insert(
214 KeyBinding::with_ctrl(KeyCode::Char('n')),
215 "next_history".to_string(),
216 );
217 self.command_bindings.insert(
218 KeyBinding::with_ctrl(KeyCode::Char('a')),
219 "move_to_line_start".to_string(),
220 );
221 self.command_bindings.insert(
222 KeyBinding::with_ctrl(KeyCode::Char('e')),
223 "move_to_line_end".to_string(),
224 );
225 self.command_bindings.insert(
226 KeyBinding::with_ctrl(KeyCode::Char('w')),
227 "delete_word_backward".to_string(),
228 );
229 self.command_bindings.insert(
230 KeyBinding::with_ctrl(KeyCode::Char('y')),
231 "yank".to_string(),
232 );
233 self.command_bindings.insert(
234 KeyBinding::with_ctrl(KeyCode::Char('z')),
235 "undo".to_string(),
236 );
237 self.command_bindings.insert(
238 KeyBinding::with_ctrl(KeyCode::Char('6')),
239 "quick_switch_buffer".to_string(),
240 );
241
242 self.command_bindings.insert(
244 KeyBinding::with_alt(KeyCode::Char('d')),
245 "delete_word_forward".to_string(),
246 );
247 self.command_bindings.insert(
248 KeyBinding::with_alt(KeyCode::Char('n')),
249 "new_buffer".to_string(),
250 );
251 self.command_bindings.insert(
252 KeyBinding::with_alt(KeyCode::Char('w')),
253 "close_buffer".to_string(),
254 );
255 self.command_bindings.insert(
256 KeyBinding::with_alt(KeyCode::Char('b')),
257 "list_buffers".to_string(),
258 );
259 self.command_bindings.insert(
260 KeyBinding::with_alt(KeyCode::Up),
261 "previous_history".to_string(),
262 );
263 self.command_bindings.insert(
264 KeyBinding::with_alt(KeyCode::Down),
265 "next_history".to_string(),
266 );
267
268 self.command_bindings.insert(
270 KeyBinding::new(KeyCode::F(11)),
271 "previous_buffer".to_string(),
272 );
273 self.command_bindings
274 .insert(KeyBinding::new(KeyCode::F(12)), "next_buffer".to_string());
275 self.command_bindings.insert(
276 KeyBinding::with_ctrl(KeyCode::PageUp),
277 "previous_buffer".to_string(),
278 );
279 self.command_bindings.insert(
280 KeyBinding::with_ctrl(KeyCode::PageDown),
281 "next_buffer".to_string(),
282 );
283
284 self.results_bindings.insert(
286 KeyBinding::new(KeyCode::Char('j')),
287 "move_row_down".to_string(),
288 );
289 self.results_bindings.insert(
290 KeyBinding::new(KeyCode::Char('k')),
291 "move_row_up".to_string(),
292 );
293 self.results_bindings.insert(
294 KeyBinding::new(KeyCode::Char('h')),
295 "move_column_left".to_string(),
296 );
297 self.results_bindings.insert(
298 KeyBinding::new(KeyCode::Char('l')),
299 "move_column_right".to_string(),
300 );
301 self.results_bindings
302 .insert(KeyBinding::new(KeyCode::Down), "move_row_down".to_string());
303 self.results_bindings
304 .insert(KeyBinding::new(KeyCode::Up), "move_row_up".to_string());
305 self.results_bindings.insert(
306 KeyBinding::new(KeyCode::Left),
307 "move_column_left".to_string(),
308 );
309 self.results_bindings.insert(
310 KeyBinding::new(KeyCode::Right),
311 "move_column_right".to_string(),
312 );
313 self.results_bindings
314 .insert(KeyBinding::new(KeyCode::PageDown), "page_down".to_string());
315 self.results_bindings
316 .insert(KeyBinding::new(KeyCode::PageUp), "page_up".to_string());
317 self.results_bindings.insert(
318 KeyBinding::new(KeyCode::Char('g')),
319 "go_to_first_row".to_string(),
320 );
321 self.results_bindings.insert(
322 KeyBinding::new(KeyCode::Char('G')),
323 "go_to_last_row".to_string(),
324 );
325 self.results_bindings.insert(
326 KeyBinding::new(KeyCode::Char('0')),
327 "go_to_first_column".to_string(),
328 );
329 self.results_bindings.insert(
330 KeyBinding::new(KeyCode::Char('^')),
331 "go_to_first_column".to_string(),
332 );
333 self.results_bindings.insert(
334 KeyBinding::new(KeyCode::Char('$')),
335 "go_to_last_column".to_string(),
336 );
337 self.results_bindings.insert(
338 KeyBinding::new(KeyCode::Char('C')),
339 "toggle_compact_mode".to_string(),
340 );
341 self.results_bindings.insert(
342 KeyBinding::new(KeyCode::Char('N')),
343 "toggle_row_numbers".to_string(),
344 );
345 self.results_bindings.insert(
346 KeyBinding::new(KeyCode::Char(':')),
347 "jump_to_row".to_string(),
348 );
349 self.results_bindings.insert(
350 KeyBinding::new(KeyCode::Char('p')),
351 "pin_column".to_string(),
352 );
353 self.results_bindings.insert(
354 KeyBinding::new(KeyCode::Char('P')),
355 "clear_pins".to_string(),
356 );
357 self.results_bindings.insert(
358 KeyBinding::new(KeyCode::Char('/')),
359 "start_search".to_string(),
360 );
361 self.results_bindings.insert(
362 KeyBinding::new(KeyCode::Char('\\')),
363 "start_column_search".to_string(),
364 );
365 self.results_bindings.insert(
366 KeyBinding::new(KeyCode::Char('F')),
367 "start_filter".to_string(),
368 );
369 self.results_bindings.insert(
370 KeyBinding::new(KeyCode::Char('f')),
371 "start_fuzzy_filter".to_string(),
372 );
373 self.results_bindings.insert(
374 KeyBinding::new(KeyCode::Char('n')),
375 "next_search_result".to_string(),
376 );
377 self.results_bindings.insert(
378 KeyBinding::new(KeyCode::Char('N')),
379 "previous_search_result".to_string(),
380 );
381 self.results_bindings.insert(
382 KeyBinding::new(KeyCode::Char('s')),
383 "sort_by_column".to_string(),
384 );
385 self.results_bindings.insert(
386 KeyBinding::new(KeyCode::Char('S')),
387 "show_column_stats".to_string(),
388 );
389 self.results_bindings.insert(
390 KeyBinding::new(KeyCode::Char('v')),
391 "toggle_selection_mode".to_string(),
392 );
393 self.results_bindings.insert(
394 KeyBinding::new(KeyCode::Char('y')),
395 "yank_selection".to_string(),
396 );
397 self.results_bindings
398 .insert(KeyBinding::new(KeyCode::Char('q')), "exit_app".to_string());
399 self.results_bindings
400 .insert(KeyBinding::new(KeyCode::Esc), "back_to_command".to_string());
401 self.results_bindings.insert(
402 KeyBinding::with_ctrl(KeyCode::Char('e')),
403 "export_csv".to_string(),
404 );
405 self.results_bindings.insert(
406 KeyBinding::with_ctrl(KeyCode::Char('j')),
407 "export_json".to_string(),
408 );
409
410 self.search_bindings
412 .insert(KeyBinding::new(KeyCode::Enter), "apply_search".to_string());
413 self.search_bindings
414 .insert(KeyBinding::new(KeyCode::Esc), "cancel_search".to_string());
415 }
416
417 #[must_use]
419 pub fn get_command_action(&self, key: &KeyEvent) -> Option<&String> {
420 let binding = KeyBinding::from_event(key);
421 self.command_bindings.get(&binding)
422 }
423
424 #[must_use]
426 pub fn get_results_action(&self, key: &KeyEvent) -> Option<&String> {
427 let binding = KeyBinding::from_event(key);
428 self.results_bindings.get(&binding)
429 }
430
431 #[must_use]
433 pub fn get_search_action(&self, key: &KeyEvent) -> Option<&String> {
434 let binding = KeyBinding::from_event(key);
435 self.search_bindings.get(&binding)
436 }
437
438 pub fn set_binding(&mut self, mode: &str, binding: KeyBinding, action: String) {
440 match mode {
441 "command" => {
442 self.command_bindings.insert(binding, action);
443 }
444 "results" => {
445 self.results_bindings.insert(binding, action);
446 }
447 "search" => {
448 self.search_bindings.insert(binding, action);
449 }
450 _ => {}
451 }
452 }
453
454 pub fn remove_binding(&mut self, mode: &str, binding: &KeyBinding) {
456 match mode {
457 "command" => {
458 self.command_bindings.remove(binding);
459 }
460 "results" => {
461 self.results_bindings.remove(binding);
462 }
463 "search" => {
464 self.search_bindings.remove(binding);
465 }
466 _ => {}
467 }
468 }
469
470 #[must_use]
472 pub fn get_bindings(&self, mode: &str) -> Vec<(KeyBinding, String)> {
473 let map = match mode {
474 "command" => &self.command_bindings,
475 "results" => &self.results_bindings,
476 "search" => &self.search_bindings,
477 _ => return Vec::new(),
478 };
479
480 let mut bindings: Vec<_> = map.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
481 bindings.sort_by_key(|(k, _)| format!("{k:?}"));
482 bindings
483 }
484}