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 pub fn new(code: KeyCode) -> Self {
95 Self {
96 code,
97 modifiers: KeyModifiers::empty(),
98 }
99 }
100
101 pub fn with_ctrl(code: KeyCode) -> Self {
102 Self {
103 code,
104 modifiers: KeyModifiers::CONTROL,
105 }
106 }
107
108 pub fn with_alt(code: KeyCode) -> Self {
109 Self {
110 code,
111 modifiers: KeyModifiers::ALT,
112 }
113 }
114
115 pub fn with_shift(code: KeyCode) -> Self {
116 Self {
117 code,
118 modifiers: KeyModifiers::SHIFT,
119 }
120 }
121
122 pub fn with_modifiers(code: KeyCode, modifiers: KeyModifiers) -> Self {
123 Self { code, modifiers }
124 }
125
126 pub fn from_event(event: &KeyEvent) -> Self {
127 Self {
128 code: event.code,
129 modifiers: event.modifiers,
130 }
131 }
132}
133
134pub struct KeyBindingManager {
136 command_bindings: HashMap<KeyBinding, String>,
137 results_bindings: HashMap<KeyBinding, String>,
138 search_bindings: HashMap<KeyBinding, String>,
139}
140
141impl KeyBindingManager {
142 pub fn new() -> Self {
143 let mut manager = Self {
144 command_bindings: HashMap::new(),
145 results_bindings: HashMap::new(),
146 search_bindings: HashMap::new(),
147 };
148 manager.setup_default_bindings();
149 manager
150 }
151
152 fn setup_default_bindings(&mut self) {
153 self.command_bindings
155 .insert(KeyBinding::new(KeyCode::Enter), "execute_query".to_string());
156 self.command_bindings
157 .insert(KeyBinding::new(KeyCode::Tab), "autocomplete".to_string());
158 self.command_bindings
159 .insert(KeyBinding::new(KeyCode::F(1)), "toggle_help".to_string());
160 self.command_bindings.insert(
161 KeyBinding::new(KeyCode::Char('?')),
162 "toggle_help".to_string(),
163 );
164 self.command_bindings.insert(
165 KeyBinding::new(KeyCode::F(3)),
166 "toggle_multiline".to_string(),
167 );
168 self.command_bindings
169 .insert(KeyBinding::new(KeyCode::F(5)), "toggle_debug".to_string());
170 self.command_bindings.insert(
171 KeyBinding::new(KeyCode::F(8)),
172 "toggle_case_insensitive".to_string(),
173 );
174 self.command_bindings.insert(
175 KeyBinding::new(KeyCode::Down),
176 "enter_results_mode".to_string(),
177 );
178
179 self.command_bindings.insert(
181 KeyBinding::with_ctrl(KeyCode::Char('c')),
182 "exit_app".to_string(),
183 );
184 self.command_bindings.insert(
185 KeyBinding::with_ctrl(KeyCode::Char('d')),
186 "exit_app".to_string(),
187 );
188 self.command_bindings.insert(
189 KeyBinding::with_ctrl(KeyCode::Char('x')),
190 "expand_select_star".to_string(),
191 );
192 self.command_bindings.insert(
193 KeyBinding::with_ctrl(KeyCode::Char('r')),
194 "search_history".to_string(),
195 );
196 self.command_bindings.insert(
197 KeyBinding::with_ctrl(KeyCode::Char('p')),
198 "previous_history".to_string(),
199 );
200 self.command_bindings.insert(
201 KeyBinding::with_ctrl(KeyCode::Char('n')),
202 "next_history".to_string(),
203 );
204 self.command_bindings.insert(
205 KeyBinding::with_ctrl(KeyCode::Char('a')),
206 "move_to_line_start".to_string(),
207 );
208 self.command_bindings.insert(
209 KeyBinding::with_ctrl(KeyCode::Char('e')),
210 "move_to_line_end".to_string(),
211 );
212 self.command_bindings.insert(
213 KeyBinding::with_ctrl(KeyCode::Char('w')),
214 "delete_word_backward".to_string(),
215 );
216 self.command_bindings.insert(
217 KeyBinding::with_ctrl(KeyCode::Char('y')),
218 "yank".to_string(),
219 );
220 self.command_bindings.insert(
221 KeyBinding::with_ctrl(KeyCode::Char('z')),
222 "undo".to_string(),
223 );
224 self.command_bindings.insert(
225 KeyBinding::with_ctrl(KeyCode::Char('6')),
226 "quick_switch_buffer".to_string(),
227 );
228
229 self.command_bindings.insert(
231 KeyBinding::with_alt(KeyCode::Char('d')),
232 "delete_word_forward".to_string(),
233 );
234 self.command_bindings.insert(
235 KeyBinding::with_alt(KeyCode::Char('n')),
236 "new_buffer".to_string(),
237 );
238 self.command_bindings.insert(
239 KeyBinding::with_alt(KeyCode::Char('w')),
240 "close_buffer".to_string(),
241 );
242 self.command_bindings.insert(
243 KeyBinding::with_alt(KeyCode::Char('b')),
244 "list_buffers".to_string(),
245 );
246 self.command_bindings.insert(
247 KeyBinding::with_alt(KeyCode::Up),
248 "previous_history".to_string(),
249 );
250 self.command_bindings.insert(
251 KeyBinding::with_alt(KeyCode::Down),
252 "next_history".to_string(),
253 );
254
255 self.command_bindings.insert(
257 KeyBinding::new(KeyCode::F(11)),
258 "previous_buffer".to_string(),
259 );
260 self.command_bindings
261 .insert(KeyBinding::new(KeyCode::F(12)), "next_buffer".to_string());
262 self.command_bindings.insert(
263 KeyBinding::with_ctrl(KeyCode::PageUp),
264 "previous_buffer".to_string(),
265 );
266 self.command_bindings.insert(
267 KeyBinding::with_ctrl(KeyCode::PageDown),
268 "next_buffer".to_string(),
269 );
270
271 self.results_bindings.insert(
273 KeyBinding::new(KeyCode::Char('j')),
274 "move_row_down".to_string(),
275 );
276 self.results_bindings.insert(
277 KeyBinding::new(KeyCode::Char('k')),
278 "move_row_up".to_string(),
279 );
280 self.results_bindings.insert(
281 KeyBinding::new(KeyCode::Char('h')),
282 "move_column_left".to_string(),
283 );
284 self.results_bindings.insert(
285 KeyBinding::new(KeyCode::Char('l')),
286 "move_column_right".to_string(),
287 );
288 self.results_bindings
289 .insert(KeyBinding::new(KeyCode::Down), "move_row_down".to_string());
290 self.results_bindings
291 .insert(KeyBinding::new(KeyCode::Up), "move_row_up".to_string());
292 self.results_bindings.insert(
293 KeyBinding::new(KeyCode::Left),
294 "move_column_left".to_string(),
295 );
296 self.results_bindings.insert(
297 KeyBinding::new(KeyCode::Right),
298 "move_column_right".to_string(),
299 );
300 self.results_bindings
301 .insert(KeyBinding::new(KeyCode::PageDown), "page_down".to_string());
302 self.results_bindings
303 .insert(KeyBinding::new(KeyCode::PageUp), "page_up".to_string());
304 self.results_bindings.insert(
305 KeyBinding::new(KeyCode::Char('g')),
306 "go_to_first_row".to_string(),
307 );
308 self.results_bindings.insert(
309 KeyBinding::new(KeyCode::Char('G')),
310 "go_to_last_row".to_string(),
311 );
312 self.results_bindings.insert(
313 KeyBinding::new(KeyCode::Char('0')),
314 "go_to_first_column".to_string(),
315 );
316 self.results_bindings.insert(
317 KeyBinding::new(KeyCode::Char('^')),
318 "go_to_first_column".to_string(),
319 );
320 self.results_bindings.insert(
321 KeyBinding::new(KeyCode::Char('$')),
322 "go_to_last_column".to_string(),
323 );
324 self.results_bindings.insert(
325 KeyBinding::new(KeyCode::Char('C')),
326 "toggle_compact_mode".to_string(),
327 );
328 self.results_bindings.insert(
329 KeyBinding::new(KeyCode::Char('N')),
330 "toggle_row_numbers".to_string(),
331 );
332 self.results_bindings.insert(
333 KeyBinding::new(KeyCode::Char(':')),
334 "jump_to_row".to_string(),
335 );
336 self.results_bindings.insert(
337 KeyBinding::new(KeyCode::Char('p')),
338 "pin_column".to_string(),
339 );
340 self.results_bindings.insert(
341 KeyBinding::new(KeyCode::Char('P')),
342 "clear_pins".to_string(),
343 );
344 self.results_bindings.insert(
345 KeyBinding::new(KeyCode::Char('/')),
346 "start_search".to_string(),
347 );
348 self.results_bindings.insert(
349 KeyBinding::new(KeyCode::Char('\\')),
350 "start_column_search".to_string(),
351 );
352 self.results_bindings.insert(
353 KeyBinding::new(KeyCode::Char('F')),
354 "start_filter".to_string(),
355 );
356 self.results_bindings.insert(
357 KeyBinding::new(KeyCode::Char('f')),
358 "start_fuzzy_filter".to_string(),
359 );
360 self.results_bindings.insert(
361 KeyBinding::new(KeyCode::Char('n')),
362 "next_search_result".to_string(),
363 );
364 self.results_bindings.insert(
365 KeyBinding::new(KeyCode::Char('N')),
366 "previous_search_result".to_string(),
367 );
368 self.results_bindings.insert(
369 KeyBinding::new(KeyCode::Char('s')),
370 "sort_by_column".to_string(),
371 );
372 self.results_bindings.insert(
373 KeyBinding::new(KeyCode::Char('S')),
374 "show_column_stats".to_string(),
375 );
376 self.results_bindings.insert(
377 KeyBinding::new(KeyCode::Char('v')),
378 "toggle_selection_mode".to_string(),
379 );
380 self.results_bindings.insert(
381 KeyBinding::new(KeyCode::Char('y')),
382 "yank_selection".to_string(),
383 );
384 self.results_bindings
385 .insert(KeyBinding::new(KeyCode::Char('q')), "exit_app".to_string());
386 self.results_bindings
387 .insert(KeyBinding::new(KeyCode::Esc), "back_to_command".to_string());
388 self.results_bindings.insert(
389 KeyBinding::with_ctrl(KeyCode::Char('e')),
390 "export_csv".to_string(),
391 );
392 self.results_bindings.insert(
393 KeyBinding::with_ctrl(KeyCode::Char('j')),
394 "export_json".to_string(),
395 );
396
397 self.search_bindings
399 .insert(KeyBinding::new(KeyCode::Enter), "apply_search".to_string());
400 self.search_bindings
401 .insert(KeyBinding::new(KeyCode::Esc), "cancel_search".to_string());
402 }
403
404 pub fn get_command_action(&self, key: &KeyEvent) -> Option<&String> {
406 let binding = KeyBinding::from_event(key);
407 self.command_bindings.get(&binding)
408 }
409
410 pub fn get_results_action(&self, key: &KeyEvent) -> Option<&String> {
412 let binding = KeyBinding::from_event(key);
413 self.results_bindings.get(&binding)
414 }
415
416 pub fn get_search_action(&self, key: &KeyEvent) -> Option<&String> {
418 let binding = KeyBinding::from_event(key);
419 self.search_bindings.get(&binding)
420 }
421
422 pub fn set_binding(&mut self, mode: &str, binding: KeyBinding, action: String) {
424 match mode {
425 "command" => {
426 self.command_bindings.insert(binding, action);
427 }
428 "results" => {
429 self.results_bindings.insert(binding, action);
430 }
431 "search" => {
432 self.search_bindings.insert(binding, action);
433 }
434 _ => {}
435 }
436 }
437
438 pub fn remove_binding(&mut self, mode: &str, binding: &KeyBinding) {
440 match mode {
441 "command" => {
442 self.command_bindings.remove(binding);
443 }
444 "results" => {
445 self.results_bindings.remove(binding);
446 }
447 "search" => {
448 self.search_bindings.remove(binding);
449 }
450 _ => {}
451 }
452 }
453
454 pub fn get_bindings(&self, mode: &str) -> Vec<(KeyBinding, String)> {
456 let map = match mode {
457 "command" => &self.command_bindings,
458 "results" => &self.results_bindings,
459 "search" => &self.search_bindings,
460 _ => return Vec::new(),
461 };
462
463 let mut bindings: Vec<_> = map.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
464 bindings.sort_by_key(|(k, _)| format!("{:?}", k));
465 bindings
466 }
467}