Skip to main content

fresh/input/
commands.rs

1//! Command palette system for executing editor actions by name
2
3use crate::input::keybindings::{Action, KeyContext};
4use crate::types::context_keys;
5use rust_i18n::t;
6
7/// Source of a command (builtin or from a plugin)
8#[derive(Debug, Clone, PartialEq)]
9pub enum CommandSource {
10    /// Built-in editor command
11    Builtin,
12    /// Command registered by a plugin (contains plugin filename without extension)
13    Plugin(String),
14}
15
16/// A command that can be executed from the command palette
17#[derive(Debug, Clone)]
18pub struct Command {
19    /// Command name (e.g., "Open File")
20    pub name: String,
21    /// Command description
22    pub description: String,
23    /// The action to trigger
24    pub action: Action,
25    /// Contexts where this command is available (empty = available in all contexts)
26    pub contexts: Vec<KeyContext>,
27    /// Custom contexts required for this command (plugin-defined contexts like "config-editor")
28    /// If non-empty, all custom contexts must be active for the command to be available
29    pub custom_contexts: Vec<String>,
30    /// Source of the command (builtin or plugin)
31    pub source: CommandSource,
32}
33
34impl Command {
35    /// Get the localized name of the command
36    pub fn get_localized_name(&self) -> String {
37        if self.name.starts_with('%') {
38            if let CommandSource::Plugin(ref plugin_name) = self.source {
39                return crate::i18n::translate_plugin_string(
40                    plugin_name,
41                    &self.name[1..],
42                    &std::collections::HashMap::new(),
43                );
44            }
45        }
46        self.name.clone()
47    }
48
49    /// Get the localized description of the command
50    pub fn get_localized_description(&self) -> String {
51        if self.description.starts_with('%') {
52            if let CommandSource::Plugin(ref plugin_name) = self.source {
53                return crate::i18n::translate_plugin_string(
54                    plugin_name,
55                    &self.description[1..],
56                    &std::collections::HashMap::new(),
57                );
58            }
59        }
60        self.description.clone()
61    }
62}
63
64/// A single suggestion item for autocomplete
65#[derive(Debug, Clone, PartialEq)]
66pub struct Suggestion {
67    /// The text to display
68    pub text: String,
69    /// Optional description
70    pub description: Option<String>,
71    /// The value to use when selected (defaults to text if None)
72    pub value: Option<String>,
73    /// Whether this suggestion is disabled (greyed out)
74    pub disabled: bool,
75    /// Optional keyboard shortcut
76    pub keybinding: Option<String>,
77    /// Source of the command (for command palette)
78    pub source: Option<CommandSource>,
79}
80
81impl Suggestion {
82    /// Create an active (selectable) suggestion
83    pub fn new(text: String) -> Self {
84        Self {
85            text,
86            description: None,
87            value: None,
88            disabled: false,
89            keybinding: None,
90            source: None,
91        }
92    }
93
94    /// Create a disabled (greyed-out) suggestion used for hints or errors
95    pub fn disabled(text: String) -> Self {
96        Self {
97            text,
98            description: None,
99            value: None,
100            disabled: true,
101            keybinding: None,
102            source: None,
103        }
104    }
105
106    pub fn with_description(mut self, description: String) -> Self {
107        self.description = Some(description);
108        self
109    }
110
111    pub fn with_value(mut self, value: String) -> Self {
112        self.value = Some(value);
113        self
114    }
115
116    pub fn with_keybinding(mut self, keybinding: Option<String>) -> Self {
117        self.keybinding = keybinding;
118        self
119    }
120
121    pub fn with_source(mut self, source: Option<CommandSource>) -> Self {
122        self.source = source;
123        self
124    }
125
126    pub fn set_disabled(mut self, disabled: bool) -> Self {
127        self.disabled = disabled;
128        self
129    }
130
131    pub fn get_value(&self) -> &str {
132        self.value.as_ref().unwrap_or(&self.text)
133    }
134}
135
136/// Static definition of a builtin command (all data except translated strings)
137struct CommandDef {
138    name_key: &'static str,
139    desc_key: &'static str,
140    action: fn() -> Action,
141    contexts: &'static [KeyContext],
142    custom_contexts: &'static [&'static str],
143}
144
145use KeyContext::{FileExplorer, Normal, Terminal};
146
147/// All builtin command definitions as static data.
148/// Translation happens at runtime via the loop in get_all_commands().
149static COMMAND_DEFS: &[CommandDef] = &[
150    // File operations
151    CommandDef {
152        name_key: "cmd.open_file",
153        desc_key: "cmd.open_file_desc",
154        action: || Action::Open,
155        contexts: &[],
156        custom_contexts: &[],
157    },
158    CommandDef {
159        name_key: "cmd.switch_project",
160        desc_key: "cmd.switch_project_desc",
161        action: || Action::SwitchProject,
162        contexts: &[],
163        custom_contexts: &[],
164    },
165    CommandDef {
166        name_key: "cmd.save_file",
167        desc_key: "cmd.save_file_desc",
168        action: || Action::Save,
169        contexts: &[Normal],
170        custom_contexts: &[],
171    },
172    CommandDef {
173        name_key: "cmd.save_file_as",
174        desc_key: "cmd.save_file_as_desc",
175        action: || Action::SaveAs,
176        contexts: &[Normal],
177        custom_contexts: &[],
178    },
179    CommandDef {
180        name_key: "cmd.new_file",
181        desc_key: "cmd.new_file_desc",
182        action: || Action::New,
183        contexts: &[],
184        custom_contexts: &[],
185    },
186    CommandDef {
187        name_key: "cmd.close_buffer",
188        desc_key: "cmd.close_buffer_desc",
189        action: || Action::Close,
190        contexts: &[Normal, Terminal],
191        custom_contexts: &[],
192    },
193    CommandDef {
194        name_key: "cmd.close_tab",
195        desc_key: "cmd.close_tab_desc",
196        action: || Action::CloseTab,
197        contexts: &[Normal, Terminal],
198        custom_contexts: &[],
199    },
200    CommandDef {
201        name_key: "cmd.revert_file",
202        desc_key: "cmd.revert_file_desc",
203        action: || Action::Revert,
204        contexts: &[Normal],
205        custom_contexts: &[],
206    },
207    CommandDef {
208        name_key: "cmd.toggle_auto_revert",
209        desc_key: "cmd.toggle_auto_revert_desc",
210        action: || Action::ToggleAutoRevert,
211        contexts: &[],
212        custom_contexts: &[],
213    },
214    CommandDef {
215        name_key: "cmd.format_buffer",
216        desc_key: "cmd.format_buffer_desc",
217        action: || Action::FormatBuffer,
218        contexts: &[Normal],
219        custom_contexts: &[],
220    },
221    CommandDef {
222        name_key: "cmd.trim_trailing_whitespace",
223        desc_key: "cmd.trim_trailing_whitespace_desc",
224        action: || Action::TrimTrailingWhitespace,
225        contexts: &[Normal],
226        custom_contexts: &[],
227    },
228    CommandDef {
229        name_key: "cmd.ensure_final_newline",
230        desc_key: "cmd.ensure_final_newline_desc",
231        action: || Action::EnsureFinalNewline,
232        contexts: &[Normal],
233        custom_contexts: &[],
234    },
235    CommandDef {
236        name_key: "cmd.quit",
237        desc_key: "cmd.quit_desc",
238        action: || Action::Quit,
239        contexts: &[],
240        custom_contexts: &[],
241    },
242    CommandDef {
243        name_key: "cmd.detach",
244        desc_key: "cmd.detach_desc",
245        action: || Action::Detach,
246        contexts: &[],
247        custom_contexts: &[context_keys::SESSION_MODE],
248    },
249    // Edit operations
250    CommandDef {
251        name_key: "cmd.undo",
252        desc_key: "cmd.undo_desc",
253        action: || Action::Undo,
254        contexts: &[Normal],
255        custom_contexts: &[],
256    },
257    CommandDef {
258        name_key: "cmd.redo",
259        desc_key: "cmd.redo_desc",
260        action: || Action::Redo,
261        contexts: &[Normal],
262        custom_contexts: &[],
263    },
264    CommandDef {
265        name_key: "cmd.copy",
266        desc_key: "cmd.copy_desc",
267        action: || Action::Copy,
268        contexts: &[Normal],
269        custom_contexts: &[],
270    },
271    CommandDef {
272        name_key: "cmd.copy_with_formatting",
273        desc_key: "cmd.copy_with_formatting_desc",
274        action: || Action::CopyWithTheme(String::new()),
275        contexts: &[Normal],
276        custom_contexts: &[],
277    },
278    CommandDef {
279        name_key: "cmd.cut",
280        desc_key: "cmd.cut_desc",
281        action: || Action::Cut,
282        contexts: &[Normal],
283        custom_contexts: &[],
284    },
285    CommandDef {
286        name_key: "cmd.paste",
287        desc_key: "cmd.paste_desc",
288        action: || Action::Paste,
289        contexts: &[Normal],
290        custom_contexts: &[],
291    },
292    CommandDef {
293        name_key: "cmd.delete_line",
294        desc_key: "cmd.delete_line_desc",
295        action: || Action::DeleteLine,
296        contexts: &[Normal],
297        custom_contexts: &[],
298    },
299    CommandDef {
300        name_key: "cmd.delete_word_backward",
301        desc_key: "cmd.delete_word_backward_desc",
302        action: || Action::DeleteWordBackward,
303        contexts: &[Normal],
304        custom_contexts: &[],
305    },
306    CommandDef {
307        name_key: "cmd.delete_word_forward",
308        desc_key: "cmd.delete_word_forward_desc",
309        action: || Action::DeleteWordForward,
310        contexts: &[Normal],
311        custom_contexts: &[],
312    },
313    CommandDef {
314        name_key: "cmd.delete_to_end_of_line",
315        desc_key: "cmd.delete_to_end_of_line_desc",
316        action: || Action::DeleteToLineEnd,
317        contexts: &[Normal],
318        custom_contexts: &[],
319    },
320    CommandDef {
321        name_key: "cmd.transpose_characters",
322        desc_key: "cmd.transpose_characters_desc",
323        action: || Action::TransposeChars,
324        contexts: &[Normal],
325        custom_contexts: &[],
326    },
327    CommandDef {
328        name_key: "cmd.transform_uppercase",
329        desc_key: "cmd.transform_uppercase_desc",
330        action: || Action::ToUpperCase,
331        contexts: &[Normal],
332        custom_contexts: &[],
333    },
334    CommandDef {
335        name_key: "cmd.transform_lowercase",
336        desc_key: "cmd.transform_lowercase_desc",
337        action: || Action::ToLowerCase,
338        contexts: &[Normal],
339        custom_contexts: &[],
340    },
341    CommandDef {
342        name_key: "cmd.sort_lines",
343        desc_key: "cmd.sort_lines_desc",
344        action: || Action::SortLines,
345        contexts: &[Normal],
346        custom_contexts: &[],
347    },
348    CommandDef {
349        name_key: "cmd.open_line",
350        desc_key: "cmd.open_line_desc",
351        action: || Action::OpenLine,
352        contexts: &[Normal],
353        custom_contexts: &[],
354    },
355    CommandDef {
356        name_key: "cmd.duplicate_line",
357        desc_key: "cmd.duplicate_line_desc",
358        action: || Action::DuplicateLine,
359        contexts: &[Normal],
360        custom_contexts: &[],
361    },
362    CommandDef {
363        name_key: "cmd.recenter",
364        desc_key: "cmd.recenter_desc",
365        action: || Action::Recenter,
366        contexts: &[Normal],
367        custom_contexts: &[],
368    },
369    CommandDef {
370        name_key: "cmd.set_mark",
371        desc_key: "cmd.set_mark_desc",
372        action: || Action::SetMark,
373        contexts: &[Normal],
374        custom_contexts: &[],
375    },
376    // Selection
377    CommandDef {
378        name_key: "cmd.select_all",
379        desc_key: "cmd.select_all_desc",
380        action: || Action::SelectAll,
381        contexts: &[Normal],
382        custom_contexts: &[],
383    },
384    CommandDef {
385        name_key: "cmd.select_word",
386        desc_key: "cmd.select_word_desc",
387        action: || Action::SelectWord,
388        contexts: &[Normal],
389        custom_contexts: &[],
390    },
391    CommandDef {
392        name_key: "cmd.select_line",
393        desc_key: "cmd.select_line_desc",
394        action: || Action::SelectLine,
395        contexts: &[Normal],
396        custom_contexts: &[],
397    },
398    CommandDef {
399        name_key: "cmd.expand_selection",
400        desc_key: "cmd.expand_selection_desc",
401        action: || Action::ExpandSelection,
402        contexts: &[Normal],
403        custom_contexts: &[],
404    },
405    // Multi-cursor
406    CommandDef {
407        name_key: "cmd.add_cursor_above",
408        desc_key: "cmd.add_cursor_above_desc",
409        action: || Action::AddCursorAbove,
410        contexts: &[Normal],
411        custom_contexts: &[],
412    },
413    CommandDef {
414        name_key: "cmd.add_cursor_below",
415        desc_key: "cmd.add_cursor_below_desc",
416        action: || Action::AddCursorBelow,
417        contexts: &[Normal],
418        custom_contexts: &[],
419    },
420    CommandDef {
421        name_key: "cmd.add_cursor_next_match",
422        desc_key: "cmd.add_cursor_next_match_desc",
423        action: || Action::AddCursorNextMatch,
424        contexts: &[Normal],
425        custom_contexts: &[],
426    },
427    CommandDef {
428        name_key: "cmd.remove_secondary_cursors",
429        desc_key: "cmd.remove_secondary_cursors_desc",
430        action: || Action::RemoveSecondaryCursors,
431        contexts: &[Normal],
432        custom_contexts: &[],
433    },
434    // Buffer navigation
435    CommandDef {
436        name_key: "cmd.next_buffer",
437        desc_key: "cmd.next_buffer_desc",
438        action: || Action::NextBuffer,
439        contexts: &[Normal, Terminal],
440        custom_contexts: &[],
441    },
442    CommandDef {
443        name_key: "cmd.previous_buffer",
444        desc_key: "cmd.previous_buffer_desc",
445        action: || Action::PrevBuffer,
446        contexts: &[Normal, Terminal],
447        custom_contexts: &[],
448    },
449    CommandDef {
450        name_key: "cmd.switch_to_previous_tab",
451        desc_key: "cmd.switch_to_previous_tab_desc",
452        action: || Action::SwitchToPreviousTab,
453        contexts: &[Normal, Terminal],
454        custom_contexts: &[],
455    },
456    CommandDef {
457        name_key: "cmd.switch_to_tab_by_name",
458        desc_key: "cmd.switch_to_tab_by_name_desc",
459        action: || Action::SwitchToTabByName,
460        contexts: &[Normal, Terminal],
461        custom_contexts: &[],
462    },
463    // Split operations
464    CommandDef {
465        name_key: "cmd.split_horizontal",
466        desc_key: "cmd.split_horizontal_desc",
467        action: || Action::SplitHorizontal,
468        contexts: &[Normal, Terminal],
469        custom_contexts: &[],
470    },
471    CommandDef {
472        name_key: "cmd.split_vertical",
473        desc_key: "cmd.split_vertical_desc",
474        action: || Action::SplitVertical,
475        contexts: &[Normal, Terminal],
476        custom_contexts: &[],
477    },
478    CommandDef {
479        name_key: "cmd.close_split",
480        desc_key: "cmd.close_split_desc",
481        action: || Action::CloseSplit,
482        contexts: &[Normal, Terminal],
483        custom_contexts: &[],
484    },
485    CommandDef {
486        name_key: "cmd.next_split",
487        desc_key: "cmd.next_split_desc",
488        action: || Action::NextSplit,
489        contexts: &[Normal, Terminal],
490        custom_contexts: &[],
491    },
492    CommandDef {
493        name_key: "cmd.previous_split",
494        desc_key: "cmd.previous_split_desc",
495        action: || Action::PrevSplit,
496        contexts: &[Normal, Terminal],
497        custom_contexts: &[],
498    },
499    CommandDef {
500        name_key: "cmd.increase_split_size",
501        desc_key: "cmd.increase_split_size_desc",
502        action: || Action::IncreaseSplitSize,
503        contexts: &[Normal, Terminal],
504        custom_contexts: &[],
505    },
506    CommandDef {
507        name_key: "cmd.decrease_split_size",
508        desc_key: "cmd.decrease_split_size_desc",
509        action: || Action::DecreaseSplitSize,
510        contexts: &[Normal, Terminal],
511        custom_contexts: &[],
512    },
513    CommandDef {
514        name_key: "cmd.toggle_maximize_split",
515        desc_key: "cmd.toggle_maximize_split_desc",
516        action: || Action::ToggleMaximizeSplit,
517        contexts: &[Normal, Terminal],
518        custom_contexts: &[],
519    },
520    // View toggles
521    CommandDef {
522        name_key: "cmd.toggle_line_numbers",
523        desc_key: "cmd.toggle_line_numbers_desc",
524        action: || Action::ToggleLineNumbers,
525        contexts: &[Normal],
526        custom_contexts: &[],
527    },
528    CommandDef {
529        name_key: "cmd.toggle_scroll_sync",
530        desc_key: "cmd.toggle_scroll_sync_desc",
531        action: || Action::ToggleScrollSync,
532        contexts: &[Normal],
533        custom_contexts: &[],
534    },
535    CommandDef {
536        name_key: "cmd.toggle_fold",
537        desc_key: "cmd.toggle_fold_desc",
538        action: || Action::ToggleFold,
539        contexts: &[Normal],
540        custom_contexts: &[],
541    },
542    CommandDef {
543        name_key: "cmd.debug_toggle_highlight",
544        desc_key: "cmd.debug_toggle_highlight_desc",
545        action: || Action::ToggleDebugHighlights,
546        contexts: &[Normal],
547        custom_contexts: &[],
548    },
549    // Rulers
550    CommandDef {
551        name_key: "cmd.add_ruler",
552        desc_key: "cmd.add_ruler_desc",
553        action: || Action::AddRuler,
554        contexts: &[Normal],
555        custom_contexts: &[],
556    },
557    CommandDef {
558        name_key: "cmd.remove_ruler",
559        desc_key: "cmd.remove_ruler_desc",
560        action: || Action::RemoveRuler,
561        contexts: &[Normal],
562        custom_contexts: &[],
563    },
564    // Buffer settings
565    CommandDef {
566        name_key: "cmd.set_tab_size",
567        desc_key: "cmd.set_tab_size_desc",
568        action: || Action::SetTabSize,
569        contexts: &[Normal],
570        custom_contexts: &[],
571    },
572    CommandDef {
573        name_key: "cmd.set_line_ending",
574        desc_key: "cmd.set_line_ending_desc",
575        action: || Action::SetLineEnding,
576        contexts: &[Normal],
577        custom_contexts: &[],
578    },
579    CommandDef {
580        name_key: "cmd.set_encoding",
581        desc_key: "cmd.set_encoding_desc",
582        action: || Action::SetEncoding,
583        contexts: &[Normal],
584        custom_contexts: &[],
585    },
586    CommandDef {
587        name_key: "cmd.reload_with_encoding",
588        desc_key: "cmd.reload_with_encoding_desc",
589        action: || Action::ReloadWithEncoding,
590        contexts: &[Normal],
591        custom_contexts: &[],
592    },
593    CommandDef {
594        name_key: "cmd.set_language",
595        desc_key: "cmd.set_language_desc",
596        action: || Action::SetLanguage,
597        contexts: &[Normal],
598        custom_contexts: &[],
599    },
600    CommandDef {
601        name_key: "cmd.toggle_indentation",
602        desc_key: "cmd.toggle_indentation_desc",
603        action: || Action::ToggleIndentationStyle,
604        contexts: &[Normal],
605        custom_contexts: &[],
606    },
607    CommandDef {
608        name_key: "cmd.toggle_tab_indicators",
609        desc_key: "cmd.toggle_tab_indicators_desc",
610        action: || Action::ToggleTabIndicators,
611        contexts: &[Normal],
612        custom_contexts: &[],
613    },
614    CommandDef {
615        name_key: "cmd.toggle_whitespace_indicators",
616        desc_key: "cmd.toggle_whitespace_indicators_desc",
617        action: || Action::ToggleWhitespaceIndicators,
618        contexts: &[Normal],
619        custom_contexts: &[],
620    },
621    CommandDef {
622        name_key: "cmd.reset_buffer_settings",
623        desc_key: "cmd.reset_buffer_settings_desc",
624        action: || Action::ResetBufferSettings,
625        contexts: &[Normal],
626        custom_contexts: &[],
627    },
628    CommandDef {
629        name_key: "cmd.scroll_up",
630        desc_key: "cmd.scroll_up_desc",
631        action: || Action::ScrollUp,
632        contexts: &[Normal],
633        custom_contexts: &[],
634    },
635    CommandDef {
636        name_key: "cmd.scroll_down",
637        desc_key: "cmd.scroll_down_desc",
638        action: || Action::ScrollDown,
639        contexts: &[Normal],
640        custom_contexts: &[],
641    },
642    CommandDef {
643        name_key: "cmd.scroll_tabs_left",
644        desc_key: "cmd.scroll_tabs_left_desc",
645        action: || Action::ScrollTabsLeft,
646        contexts: &[Normal, Terminal],
647        custom_contexts: &[],
648    },
649    CommandDef {
650        name_key: "cmd.scroll_tabs_right",
651        desc_key: "cmd.scroll_tabs_right_desc",
652        action: || Action::ScrollTabsRight,
653        contexts: &[Normal, Terminal],
654        custom_contexts: &[],
655    },
656    CommandDef {
657        name_key: "cmd.toggle_mouse_support",
658        desc_key: "cmd.toggle_mouse_support_desc",
659        action: || Action::ToggleMouseCapture,
660        contexts: &[Normal, Terminal],
661        custom_contexts: &[],
662    },
663    // File explorer
664    CommandDef {
665        name_key: "cmd.toggle_file_explorer",
666        desc_key: "cmd.toggle_file_explorer_desc",
667        action: || Action::ToggleFileExplorer,
668        contexts: &[Normal, FileExplorer, Terminal],
669        custom_contexts: &[],
670    },
671    CommandDef {
672        name_key: "cmd.toggle_menu_bar",
673        desc_key: "cmd.toggle_menu_bar_desc",
674        action: || Action::ToggleMenuBar,
675        contexts: &[Normal, FileExplorer, Terminal],
676        custom_contexts: &[],
677    },
678    CommandDef {
679        name_key: "cmd.toggle_tab_bar",
680        desc_key: "cmd.toggle_tab_bar_desc",
681        action: || Action::ToggleTabBar,
682        contexts: &[Normal, FileExplorer, Terminal],
683        custom_contexts: &[],
684    },
685    CommandDef {
686        name_key: "cmd.toggle_status_bar",
687        desc_key: "cmd.toggle_status_bar_desc",
688        action: || Action::ToggleStatusBar,
689        contexts: &[Normal, FileExplorer, Terminal],
690        custom_contexts: &[],
691    },
692    CommandDef {
693        name_key: "cmd.toggle_prompt_line",
694        desc_key: "cmd.toggle_prompt_line_desc",
695        action: || Action::TogglePromptLine,
696        contexts: &[Normal, FileExplorer, Terminal],
697        custom_contexts: &[],
698    },
699    CommandDef {
700        name_key: "cmd.toggle_vertical_scrollbar",
701        desc_key: "cmd.toggle_vertical_scrollbar_desc",
702        action: || Action::ToggleVerticalScrollbar,
703        contexts: &[Normal, FileExplorer, Terminal],
704        custom_contexts: &[],
705    },
706    CommandDef {
707        name_key: "cmd.toggle_horizontal_scrollbar",
708        desc_key: "cmd.toggle_horizontal_scrollbar_desc",
709        action: || Action::ToggleHorizontalScrollbar,
710        contexts: &[Normal, FileExplorer, Terminal],
711        custom_contexts: &[],
712    },
713    CommandDef {
714        name_key: "cmd.focus_file_explorer",
715        desc_key: "cmd.focus_file_explorer_desc",
716        action: || Action::FocusFileExplorer,
717        contexts: &[Normal, Terminal],
718        custom_contexts: &[],
719    },
720    CommandDef {
721        name_key: "cmd.focus_editor",
722        desc_key: "cmd.focus_editor_desc",
723        action: || Action::FocusEditor,
724        contexts: &[FileExplorer],
725        custom_contexts: &[],
726    },
727    CommandDef {
728        name_key: "cmd.explorer_refresh",
729        desc_key: "cmd.explorer_refresh_desc",
730        action: || Action::FileExplorerRefresh,
731        contexts: &[FileExplorer],
732        custom_contexts: &[],
733    },
734    CommandDef {
735        name_key: "cmd.explorer_new_file",
736        desc_key: "cmd.explorer_new_file_desc",
737        action: || Action::FileExplorerNewFile,
738        contexts: &[FileExplorer],
739        custom_contexts: &[],
740    },
741    CommandDef {
742        name_key: "cmd.explorer_new_directory",
743        desc_key: "cmd.explorer_new_directory_desc",
744        action: || Action::FileExplorerNewDirectory,
745        contexts: &[FileExplorer],
746        custom_contexts: &[],
747    },
748    CommandDef {
749        name_key: "cmd.explorer_delete",
750        desc_key: "cmd.explorer_delete_desc",
751        action: || Action::FileExplorerDelete,
752        contexts: &[FileExplorer],
753        custom_contexts: &[],
754    },
755    CommandDef {
756        name_key: "cmd.explorer_rename",
757        desc_key: "cmd.explorer_rename_desc",
758        action: || Action::FileExplorerRename,
759        contexts: &[FileExplorer],
760        custom_contexts: &[],
761    },
762    CommandDef {
763        name_key: "cmd.toggle_hidden_files",
764        desc_key: "cmd.toggle_hidden_files_desc",
765        action: || Action::FileExplorerToggleHidden,
766        contexts: &[FileExplorer],
767        custom_contexts: &[],
768    },
769    CommandDef {
770        name_key: "cmd.toggle_gitignored_files",
771        desc_key: "cmd.toggle_gitignored_files_desc",
772        action: || Action::FileExplorerToggleGitignored,
773        contexts: &[FileExplorer],
774        custom_contexts: &[],
775    },
776    // View
777    CommandDef {
778        name_key: "cmd.toggle_line_wrap",
779        desc_key: "cmd.toggle_line_wrap_desc",
780        action: || Action::ToggleLineWrap,
781        contexts: &[Normal],
782        custom_contexts: &[],
783    },
784    CommandDef {
785        name_key: "cmd.toggle_current_line_highlight",
786        desc_key: "cmd.toggle_current_line_highlight_desc",
787        action: || Action::ToggleCurrentLineHighlight,
788        contexts: &[Normal],
789        custom_contexts: &[],
790    },
791    CommandDef {
792        name_key: "cmd.toggle_page_view",
793        desc_key: "cmd.toggle_page_view_desc",
794        action: || Action::TogglePageView,
795        contexts: &[Normal],
796        custom_contexts: &[],
797    },
798    CommandDef {
799        name_key: "cmd.set_page_width",
800        desc_key: "cmd.set_page_width_desc",
801        action: || Action::SetPageWidth,
802        contexts: &[Normal],
803        custom_contexts: &[],
804    },
805    CommandDef {
806        name_key: "cmd.toggle_read_only",
807        desc_key: "cmd.toggle_read_only_desc",
808        action: || Action::ToggleReadOnly,
809        contexts: &[Normal],
810        custom_contexts: &[],
811    },
812    CommandDef {
813        name_key: "cmd.set_background",
814        desc_key: "cmd.set_background_desc",
815        action: || Action::SetBackground,
816        contexts: &[Normal],
817        custom_contexts: &[],
818    },
819    CommandDef {
820        name_key: "cmd.set_background_blend",
821        desc_key: "cmd.set_background_blend_desc",
822        action: || Action::SetBackgroundBlend,
823        contexts: &[Normal],
824        custom_contexts: &[],
825    },
826    // Search and replace
827    CommandDef {
828        name_key: "cmd.search",
829        desc_key: "cmd.search_desc",
830        action: || Action::Search,
831        contexts: &[Normal],
832        custom_contexts: &[],
833    },
834    CommandDef {
835        name_key: "cmd.find_in_selection",
836        desc_key: "cmd.find_in_selection_desc",
837        action: || Action::FindInSelection,
838        contexts: &[Normal],
839        custom_contexts: &[],
840    },
841    CommandDef {
842        name_key: "cmd.find_next",
843        desc_key: "cmd.find_next_desc",
844        action: || Action::FindNext,
845        contexts: &[Normal],
846        custom_contexts: &[],
847    },
848    CommandDef {
849        name_key: "cmd.find_previous",
850        desc_key: "cmd.find_previous_desc",
851        action: || Action::FindPrevious,
852        contexts: &[Normal],
853        custom_contexts: &[],
854    },
855    CommandDef {
856        name_key: "cmd.find_selection_next",
857        desc_key: "cmd.find_selection_next_desc",
858        action: || Action::FindSelectionNext,
859        contexts: &[Normal],
860        custom_contexts: &[],
861    },
862    CommandDef {
863        name_key: "cmd.find_selection_previous",
864        desc_key: "cmd.find_selection_previous_desc",
865        action: || Action::FindSelectionPrevious,
866        contexts: &[Normal],
867        custom_contexts: &[],
868    },
869    CommandDef {
870        name_key: "cmd.replace",
871        desc_key: "cmd.replace_desc",
872        action: || Action::Replace,
873        contexts: &[Normal],
874        custom_contexts: &[],
875    },
876    CommandDef {
877        name_key: "cmd.query_replace",
878        desc_key: "cmd.query_replace_desc",
879        action: || Action::QueryReplace,
880        contexts: &[Normal],
881        custom_contexts: &[],
882    },
883    // Navigation
884    CommandDef {
885        name_key: "cmd.goto_line",
886        desc_key: "cmd.goto_line_desc",
887        action: || Action::GotoLine,
888        contexts: &[Normal],
889        custom_contexts: &[],
890    },
891    CommandDef {
892        name_key: "cmd.scan_line_index",
893        desc_key: "cmd.scan_line_index_desc",
894        action: || Action::ScanLineIndex,
895        contexts: &[Normal],
896        custom_contexts: &[],
897    },
898    CommandDef {
899        name_key: "cmd.smart_home",
900        desc_key: "cmd.smart_home_desc",
901        action: || Action::SmartHome,
902        contexts: &[Normal],
903        custom_contexts: &[],
904    },
905    CommandDef {
906        name_key: "cmd.show_completions",
907        desc_key: "cmd.show_completions_desc",
908        action: || Action::LspCompletion,
909        contexts: &[Normal],
910        custom_contexts: &[],
911    },
912    CommandDef {
913        name_key: "cmd.goto_definition",
914        desc_key: "cmd.goto_definition_desc",
915        action: || Action::LspGotoDefinition,
916        contexts: &[Normal],
917        custom_contexts: &[],
918    },
919    CommandDef {
920        name_key: "cmd.show_hover_info",
921        desc_key: "cmd.show_hover_info_desc",
922        action: || Action::LspHover,
923        contexts: &[Normal],
924        custom_contexts: &[],
925    },
926    CommandDef {
927        name_key: "cmd.find_references",
928        desc_key: "cmd.find_references_desc",
929        action: || Action::LspReferences,
930        contexts: &[Normal],
931        custom_contexts: &[],
932    },
933    CommandDef {
934        name_key: "cmd.show_signature_help",
935        desc_key: "cmd.show_signature_help_desc",
936        action: || Action::LspSignatureHelp,
937        contexts: &[Normal],
938        custom_contexts: &[],
939    },
940    CommandDef {
941        name_key: "cmd.code_actions",
942        desc_key: "cmd.code_actions_desc",
943        action: || Action::LspCodeActions,
944        contexts: &[Normal],
945        custom_contexts: &[],
946    },
947    CommandDef {
948        name_key: "cmd.start_restart_lsp",
949        desc_key: "cmd.start_restart_lsp_desc",
950        action: || Action::LspRestart,
951        contexts: &[Normal],
952        custom_contexts: &[],
953    },
954    CommandDef {
955        name_key: "cmd.stop_lsp",
956        desc_key: "cmd.stop_lsp_desc",
957        action: || Action::LspStop,
958        contexts: &[Normal],
959        custom_contexts: &[],
960    },
961    CommandDef {
962        name_key: "cmd.toggle_lsp_for_buffer",
963        desc_key: "cmd.toggle_lsp_for_buffer_desc",
964        action: || Action::LspToggleForBuffer,
965        contexts: &[Normal],
966        custom_contexts: &[],
967    },
968    CommandDef {
969        name_key: "cmd.toggle_mouse_hover",
970        desc_key: "cmd.toggle_mouse_hover_desc",
971        action: || Action::ToggleMouseHover,
972        contexts: &[],
973        custom_contexts: &[],
974    },
975    CommandDef {
976        name_key: "cmd.navigate_back",
977        desc_key: "cmd.navigate_back_desc",
978        action: || Action::NavigateBack,
979        contexts: &[Normal],
980        custom_contexts: &[],
981    },
982    CommandDef {
983        name_key: "cmd.navigate_forward",
984        desc_key: "cmd.navigate_forward_desc",
985        action: || Action::NavigateForward,
986        contexts: &[Normal],
987        custom_contexts: &[],
988    },
989    // Smart editing
990    CommandDef {
991        name_key: "cmd.toggle_comment",
992        desc_key: "cmd.toggle_comment_desc",
993        action: || Action::ToggleComment,
994        contexts: &[Normal],
995        custom_contexts: &[],
996    },
997    CommandDef {
998        name_key: "cmd.dedent_selection",
999        desc_key: "cmd.dedent_selection_desc",
1000        action: || Action::DedentSelection,
1001        contexts: &[Normal],
1002        custom_contexts: &[],
1003    },
1004    CommandDef {
1005        name_key: "cmd.goto_matching_bracket",
1006        desc_key: "cmd.goto_matching_bracket_desc",
1007        action: || Action::GoToMatchingBracket,
1008        contexts: &[Normal],
1009        custom_contexts: &[],
1010    },
1011    // Error navigation
1012    CommandDef {
1013        name_key: "cmd.jump_to_next_error",
1014        desc_key: "cmd.jump_to_next_error_desc",
1015        action: || Action::JumpToNextError,
1016        contexts: &[Normal],
1017        custom_contexts: &[],
1018    },
1019    CommandDef {
1020        name_key: "cmd.jump_to_previous_error",
1021        desc_key: "cmd.jump_to_previous_error_desc",
1022        action: || Action::JumpToPreviousError,
1023        contexts: &[Normal],
1024        custom_contexts: &[],
1025    },
1026    // LSP
1027    CommandDef {
1028        name_key: "cmd.rename_symbol",
1029        desc_key: "cmd.rename_symbol_desc",
1030        action: || Action::LspRename,
1031        contexts: &[Normal],
1032        custom_contexts: &[],
1033    },
1034    // Bookmarks and Macros
1035    CommandDef {
1036        name_key: "cmd.list_bookmarks",
1037        desc_key: "cmd.list_bookmarks_desc",
1038        action: || Action::ListBookmarks,
1039        contexts: &[Normal],
1040        custom_contexts: &[],
1041    },
1042    CommandDef {
1043        name_key: "cmd.list_macros",
1044        desc_key: "cmd.list_macros_desc",
1045        action: || Action::ListMacros,
1046        contexts: &[Normal],
1047        custom_contexts: &[],
1048    },
1049    CommandDef {
1050        name_key: "cmd.record_macro",
1051        desc_key: "cmd.record_macro_desc",
1052        action: || Action::PromptRecordMacro,
1053        contexts: &[Normal],
1054        custom_contexts: &[],
1055    },
1056    CommandDef {
1057        name_key: "cmd.stop_recording_macro",
1058        desc_key: "cmd.stop_recording_macro_desc",
1059        action: || Action::StopMacroRecording,
1060        contexts: &[Normal],
1061        custom_contexts: &[],
1062    },
1063    CommandDef {
1064        name_key: "cmd.play_macro",
1065        desc_key: "cmd.play_macro_desc",
1066        action: || Action::PromptPlayMacro,
1067        contexts: &[Normal],
1068        custom_contexts: &[],
1069    },
1070    CommandDef {
1071        name_key: "cmd.play_last_macro",
1072        desc_key: "cmd.play_last_macro_desc",
1073        action: || Action::PlayLastMacro,
1074        contexts: &[Normal],
1075        custom_contexts: &[],
1076    },
1077    CommandDef {
1078        name_key: "cmd.set_bookmark",
1079        desc_key: "cmd.set_bookmark_desc",
1080        action: || Action::PromptSetBookmark,
1081        contexts: &[Normal],
1082        custom_contexts: &[],
1083    },
1084    CommandDef {
1085        name_key: "cmd.jump_to_bookmark",
1086        desc_key: "cmd.jump_to_bookmark_desc",
1087        action: || Action::PromptJumpToBookmark,
1088        contexts: &[Normal],
1089        custom_contexts: &[],
1090    },
1091    // Help
1092    CommandDef {
1093        name_key: "cmd.show_manual",
1094        desc_key: "cmd.show_manual_desc",
1095        action: || Action::ShowHelp,
1096        contexts: &[],
1097        custom_contexts: &[],
1098    },
1099    CommandDef {
1100        name_key: "cmd.show_keyboard_shortcuts",
1101        desc_key: "cmd.show_keyboard_shortcuts_desc",
1102        action: || Action::ShowKeyboardShortcuts,
1103        contexts: &[],
1104        custom_contexts: &[],
1105    },
1106    CommandDef {
1107        name_key: "cmd.show_warnings",
1108        desc_key: "cmd.show_warnings_desc",
1109        action: || Action::ShowWarnings,
1110        contexts: &[],
1111        custom_contexts: &[],
1112    },
1113    CommandDef {
1114        name_key: "cmd.show_lsp_status",
1115        desc_key: "cmd.show_lsp_status_desc",
1116        action: || Action::ShowLspStatus,
1117        contexts: &[],
1118        custom_contexts: &[],
1119    },
1120    CommandDef {
1121        name_key: "cmd.clear_warnings",
1122        desc_key: "cmd.clear_warnings_desc",
1123        action: || Action::ClearWarnings,
1124        contexts: &[],
1125        custom_contexts: &[],
1126    },
1127    // Config
1128    CommandDef {
1129        name_key: "cmd.dump_config",
1130        desc_key: "cmd.dump_config_desc",
1131        action: || Action::DumpConfig,
1132        contexts: &[],
1133        custom_contexts: &[],
1134    },
1135    CommandDef {
1136        name_key: "cmd.toggle_inlay_hints",
1137        desc_key: "cmd.toggle_inlay_hints_desc",
1138        action: || Action::ToggleInlayHints,
1139        contexts: &[Normal],
1140        custom_contexts: &[],
1141    },
1142    // Theme selection
1143    CommandDef {
1144        name_key: "cmd.select_theme",
1145        desc_key: "cmd.select_theme_desc",
1146        action: || Action::SelectTheme,
1147        contexts: &[],
1148        custom_contexts: &[],
1149    },
1150    // Theme inspection
1151    CommandDef {
1152        name_key: "cmd.inspect_theme_at_cursor",
1153        desc_key: "cmd.inspect_theme_at_cursor_desc",
1154        action: || Action::InspectThemeAtCursor,
1155        contexts: &[Normal],
1156        custom_contexts: &[],
1157    },
1158    // Keybinding map selection
1159    CommandDef {
1160        name_key: "cmd.select_keybinding_map",
1161        desc_key: "cmd.select_keybinding_map_desc",
1162        action: || Action::SelectKeybindingMap,
1163        contexts: &[],
1164        custom_contexts: &[],
1165    },
1166    // Cursor style selection
1167    CommandDef {
1168        name_key: "cmd.select_cursor_style",
1169        desc_key: "cmd.select_cursor_style_desc",
1170        action: || Action::SelectCursorStyle,
1171        contexts: &[],
1172        custom_contexts: &[],
1173    },
1174    // Locale selection
1175    CommandDef {
1176        name_key: "cmd.select_locale",
1177        desc_key: "cmd.select_locale_desc",
1178        action: || Action::SelectLocale,
1179        contexts: &[],
1180        custom_contexts: &[],
1181    },
1182    // Settings
1183    CommandDef {
1184        name_key: "cmd.open_settings",
1185        desc_key: "cmd.open_settings_desc",
1186        action: || Action::OpenSettings,
1187        contexts: &[],
1188        custom_contexts: &[],
1189    },
1190    // Keybinding editor
1191    CommandDef {
1192        name_key: "cmd.open_keybinding_editor",
1193        desc_key: "cmd.open_keybinding_editor_desc",
1194        action: || Action::OpenKeybindingEditor,
1195        contexts: &[],
1196        custom_contexts: &[],
1197    },
1198    // Input calibration
1199    CommandDef {
1200        name_key: "cmd.calibrate_input",
1201        desc_key: "cmd.calibrate_input_desc",
1202        action: || Action::CalibrateInput,
1203        contexts: &[],
1204        custom_contexts: &[],
1205    },
1206    // Terminal commands
1207    CommandDef {
1208        name_key: "cmd.open_terminal",
1209        desc_key: "cmd.open_terminal_desc",
1210        action: || Action::OpenTerminal,
1211        contexts: &[],
1212        custom_contexts: &[],
1213    },
1214    CommandDef {
1215        name_key: "cmd.focus_terminal",
1216        desc_key: "cmd.focus_terminal_desc",
1217        action: || Action::FocusTerminal,
1218        contexts: &[Normal],
1219        custom_contexts: &[],
1220    },
1221    CommandDef {
1222        name_key: "cmd.exit_terminal_mode",
1223        desc_key: "cmd.exit_terminal_mode_desc",
1224        action: || Action::TerminalEscape,
1225        contexts: &[Terminal],
1226        custom_contexts: &[],
1227    },
1228    CommandDef {
1229        name_key: "cmd.toggle_keyboard_capture",
1230        desc_key: "cmd.toggle_keyboard_capture_desc",
1231        action: || Action::ToggleKeyboardCapture,
1232        contexts: &[Terminal],
1233        custom_contexts: &[],
1234    },
1235    // Shell command operations
1236    CommandDef {
1237        name_key: "cmd.shell_command",
1238        desc_key: "cmd.shell_command_desc",
1239        action: || Action::ShellCommand,
1240        contexts: &[Normal],
1241        custom_contexts: &[],
1242    },
1243    CommandDef {
1244        name_key: "cmd.shell_command_replace",
1245        desc_key: "cmd.shell_command_replace_desc",
1246        action: || Action::ShellCommandReplace,
1247        contexts: &[Normal],
1248        custom_contexts: &[],
1249    },
1250    // Debugging
1251    CommandDef {
1252        name_key: "cmd.event_debug",
1253        desc_key: "cmd.event_debug_desc",
1254        action: || Action::EventDebug,
1255        contexts: &[],
1256        custom_contexts: &[],
1257    },
1258    // Plugin development
1259    CommandDef {
1260        name_key: "cmd.load_plugin_from_buffer",
1261        desc_key: "cmd.load_plugin_from_buffer_desc",
1262        action: || Action::LoadPluginFromBuffer,
1263        contexts: &[Normal],
1264        custom_contexts: &[],
1265    },
1266];
1267
1268/// Get all available commands for the command palette
1269pub fn get_all_commands() -> Vec<Command> {
1270    COMMAND_DEFS
1271        .iter()
1272        .map(|def| Command {
1273            name: t!(def.name_key).to_string(),
1274            description: t!(def.desc_key).to_string(),
1275            action: (def.action)(),
1276            contexts: def.contexts.to_vec(),
1277            custom_contexts: def.custom_contexts.iter().map(|s| s.to_string()).collect(),
1278            source: CommandSource::Builtin,
1279        })
1280        .collect()
1281}
1282
1283/// Filter commands by fuzzy matching the query, with context awareness
1284pub fn filter_commands(
1285    query: &str,
1286    current_context: KeyContext,
1287    keybinding_resolver: &crate::input::keybindings::KeybindingResolver,
1288) -> Vec<Suggestion> {
1289    let query_lower = query.to_lowercase();
1290    let commands = get_all_commands();
1291
1292    // Helper function to check if command is available in current context
1293    let is_available = |cmd: &Command| -> bool {
1294        // Empty contexts means available in all contexts
1295        cmd.contexts.is_empty() || cmd.contexts.contains(&current_context)
1296    };
1297
1298    // Helper function for fuzzy matching
1299    let matches_query = |cmd: &Command| -> bool {
1300        if query.is_empty() {
1301            return true;
1302        }
1303
1304        let name_lower = cmd.name.to_lowercase();
1305        let mut query_chars = query_lower.chars();
1306        let mut current_char = query_chars.next();
1307
1308        for name_char in name_lower.chars() {
1309            if let Some(qc) = current_char {
1310                if qc == name_char {
1311                    current_char = query_chars.next();
1312                }
1313            } else {
1314                break;
1315            }
1316        }
1317
1318        current_char.is_none() // All query characters matched
1319    };
1320
1321    // Filter and convert to suggestions
1322    let current_context_ref = &current_context;
1323    let mut suggestions: Vec<Suggestion> = commands
1324        .into_iter()
1325        .filter(|cmd| matches_query(cmd))
1326        .map(|cmd| {
1327            let available = is_available(&cmd);
1328            let keybinding = keybinding_resolver
1329                .get_keybinding_for_action(&cmd.action, current_context_ref.clone());
1330            Suggestion::new(cmd.name.clone())
1331                .with_description(cmd.description)
1332                .set_disabled(!available)
1333                .with_keybinding(keybinding)
1334        })
1335        .collect();
1336
1337    // Sort: available commands first, then disabled ones
1338    suggestions.sort_by_key(|s| s.disabled);
1339
1340    suggestions
1341}