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    // Quick Open variants
250    CommandDef {
251        name_key: "cmd.quick_open_buffers",
252        desc_key: "cmd.quick_open_buffers_desc",
253        action: || Action::QuickOpenBuffers,
254        contexts: &[],
255        custom_contexts: &[],
256    },
257    CommandDef {
258        name_key: "cmd.quick_open_files",
259        desc_key: "cmd.quick_open_files_desc",
260        action: || Action::QuickOpenFiles,
261        contexts: &[],
262        custom_contexts: &[],
263    },
264    // Edit operations
265    CommandDef {
266        name_key: "cmd.undo",
267        desc_key: "cmd.undo_desc",
268        action: || Action::Undo,
269        contexts: &[Normal],
270        custom_contexts: &[],
271    },
272    CommandDef {
273        name_key: "cmd.redo",
274        desc_key: "cmd.redo_desc",
275        action: || Action::Redo,
276        contexts: &[Normal],
277        custom_contexts: &[],
278    },
279    CommandDef {
280        name_key: "cmd.copy",
281        desc_key: "cmd.copy_desc",
282        action: || Action::Copy,
283        contexts: &[Normal],
284        custom_contexts: &[],
285    },
286    CommandDef {
287        name_key: "cmd.copy_with_formatting",
288        desc_key: "cmd.copy_with_formatting_desc",
289        action: || Action::CopyWithTheme(String::new()),
290        contexts: &[Normal],
291        custom_contexts: &[],
292    },
293    CommandDef {
294        name_key: "cmd.cut",
295        desc_key: "cmd.cut_desc",
296        action: || Action::Cut,
297        contexts: &[Normal],
298        custom_contexts: &[],
299    },
300    CommandDef {
301        name_key: "cmd.paste",
302        desc_key: "cmd.paste_desc",
303        action: || Action::Paste,
304        contexts: &[Normal],
305        custom_contexts: &[],
306    },
307    CommandDef {
308        name_key: "cmd.delete_line",
309        desc_key: "cmd.delete_line_desc",
310        action: || Action::DeleteLine,
311        contexts: &[Normal],
312        custom_contexts: &[],
313    },
314    CommandDef {
315        name_key: "cmd.delete_word_backward",
316        desc_key: "cmd.delete_word_backward_desc",
317        action: || Action::DeleteWordBackward,
318        contexts: &[Normal],
319        custom_contexts: &[],
320    },
321    CommandDef {
322        name_key: "cmd.delete_word_forward",
323        desc_key: "cmd.delete_word_forward_desc",
324        action: || Action::DeleteWordForward,
325        contexts: &[Normal],
326        custom_contexts: &[],
327    },
328    CommandDef {
329        name_key: "cmd.delete_to_end_of_line",
330        desc_key: "cmd.delete_to_end_of_line_desc",
331        action: || Action::DeleteToLineEnd,
332        contexts: &[Normal],
333        custom_contexts: &[],
334    },
335    CommandDef {
336        name_key: "cmd.transpose_characters",
337        desc_key: "cmd.transpose_characters_desc",
338        action: || Action::TransposeChars,
339        contexts: &[Normal],
340        custom_contexts: &[],
341    },
342    CommandDef {
343        name_key: "cmd.transform_uppercase",
344        desc_key: "cmd.transform_uppercase_desc",
345        action: || Action::ToUpperCase,
346        contexts: &[Normal],
347        custom_contexts: &[],
348    },
349    CommandDef {
350        name_key: "cmd.transform_lowercase",
351        desc_key: "cmd.transform_lowercase_desc",
352        action: || Action::ToLowerCase,
353        contexts: &[Normal],
354        custom_contexts: &[],
355    },
356    CommandDef {
357        name_key: "cmd.sort_lines",
358        desc_key: "cmd.sort_lines_desc",
359        action: || Action::SortLines,
360        contexts: &[Normal],
361        custom_contexts: &[],
362    },
363    CommandDef {
364        name_key: "cmd.open_line",
365        desc_key: "cmd.open_line_desc",
366        action: || Action::OpenLine,
367        contexts: &[Normal],
368        custom_contexts: &[],
369    },
370    CommandDef {
371        name_key: "cmd.duplicate_line",
372        desc_key: "cmd.duplicate_line_desc",
373        action: || Action::DuplicateLine,
374        contexts: &[Normal],
375        custom_contexts: &[],
376    },
377    CommandDef {
378        name_key: "cmd.recenter",
379        desc_key: "cmd.recenter_desc",
380        action: || Action::Recenter,
381        contexts: &[Normal],
382        custom_contexts: &[],
383    },
384    CommandDef {
385        name_key: "cmd.set_mark",
386        desc_key: "cmd.set_mark_desc",
387        action: || Action::SetMark,
388        contexts: &[Normal],
389        custom_contexts: &[],
390    },
391    // Selection
392    CommandDef {
393        name_key: "cmd.select_all",
394        desc_key: "cmd.select_all_desc",
395        action: || Action::SelectAll,
396        contexts: &[Normal],
397        custom_contexts: &[],
398    },
399    CommandDef {
400        name_key: "cmd.select_word",
401        desc_key: "cmd.select_word_desc",
402        action: || Action::SelectWord,
403        contexts: &[Normal],
404        custom_contexts: &[],
405    },
406    CommandDef {
407        name_key: "cmd.select_line",
408        desc_key: "cmd.select_line_desc",
409        action: || Action::SelectLine,
410        contexts: &[Normal],
411        custom_contexts: &[],
412    },
413    CommandDef {
414        name_key: "cmd.expand_selection",
415        desc_key: "cmd.expand_selection_desc",
416        action: || Action::ExpandSelection,
417        contexts: &[Normal],
418        custom_contexts: &[],
419    },
420    // Multi-cursor
421    CommandDef {
422        name_key: "cmd.add_cursor_above",
423        desc_key: "cmd.add_cursor_above_desc",
424        action: || Action::AddCursorAbove,
425        contexts: &[Normal],
426        custom_contexts: &[],
427    },
428    CommandDef {
429        name_key: "cmd.add_cursor_below",
430        desc_key: "cmd.add_cursor_below_desc",
431        action: || Action::AddCursorBelow,
432        contexts: &[Normal],
433        custom_contexts: &[],
434    },
435    CommandDef {
436        name_key: "cmd.add_cursor_next_match",
437        desc_key: "cmd.add_cursor_next_match_desc",
438        action: || Action::AddCursorNextMatch,
439        contexts: &[Normal],
440        custom_contexts: &[],
441    },
442    CommandDef {
443        name_key: "cmd.remove_secondary_cursors",
444        desc_key: "cmd.remove_secondary_cursors_desc",
445        action: || Action::RemoveSecondaryCursors,
446        contexts: &[Normal],
447        custom_contexts: &[],
448    },
449    // Buffer navigation
450    CommandDef {
451        name_key: "cmd.next_buffer",
452        desc_key: "cmd.next_buffer_desc",
453        action: || Action::NextBuffer,
454        contexts: &[Normal, Terminal],
455        custom_contexts: &[],
456    },
457    CommandDef {
458        name_key: "cmd.previous_buffer",
459        desc_key: "cmd.previous_buffer_desc",
460        action: || Action::PrevBuffer,
461        contexts: &[Normal, Terminal],
462        custom_contexts: &[],
463    },
464    CommandDef {
465        name_key: "cmd.switch_to_previous_tab",
466        desc_key: "cmd.switch_to_previous_tab_desc",
467        action: || Action::SwitchToPreviousTab,
468        contexts: &[Normal, Terminal],
469        custom_contexts: &[],
470    },
471    CommandDef {
472        name_key: "cmd.switch_to_tab_by_name",
473        desc_key: "cmd.switch_to_tab_by_name_desc",
474        action: || Action::SwitchToTabByName,
475        contexts: &[Normal, Terminal],
476        custom_contexts: &[],
477    },
478    // Split operations
479    CommandDef {
480        name_key: "cmd.split_horizontal",
481        desc_key: "cmd.split_horizontal_desc",
482        action: || Action::SplitHorizontal,
483        contexts: &[Normal, Terminal],
484        custom_contexts: &[],
485    },
486    CommandDef {
487        name_key: "cmd.split_vertical",
488        desc_key: "cmd.split_vertical_desc",
489        action: || Action::SplitVertical,
490        contexts: &[Normal, Terminal],
491        custom_contexts: &[],
492    },
493    CommandDef {
494        name_key: "cmd.close_split",
495        desc_key: "cmd.close_split_desc",
496        action: || Action::CloseSplit,
497        contexts: &[Normal, Terminal],
498        custom_contexts: &[],
499    },
500    CommandDef {
501        name_key: "cmd.next_split",
502        desc_key: "cmd.next_split_desc",
503        action: || Action::NextSplit,
504        contexts: &[Normal, Terminal],
505        custom_contexts: &[],
506    },
507    CommandDef {
508        name_key: "cmd.previous_split",
509        desc_key: "cmd.previous_split_desc",
510        action: || Action::PrevSplit,
511        contexts: &[Normal, Terminal],
512        custom_contexts: &[],
513    },
514    CommandDef {
515        name_key: "cmd.increase_split_size",
516        desc_key: "cmd.increase_split_size_desc",
517        action: || Action::IncreaseSplitSize,
518        contexts: &[Normal, Terminal],
519        custom_contexts: &[],
520    },
521    CommandDef {
522        name_key: "cmd.decrease_split_size",
523        desc_key: "cmd.decrease_split_size_desc",
524        action: || Action::DecreaseSplitSize,
525        contexts: &[Normal, Terminal],
526        custom_contexts: &[],
527    },
528    CommandDef {
529        name_key: "cmd.toggle_maximize_split",
530        desc_key: "cmd.toggle_maximize_split_desc",
531        action: || Action::ToggleMaximizeSplit,
532        contexts: &[Normal, Terminal],
533        custom_contexts: &[],
534    },
535    // View toggles
536    CommandDef {
537        name_key: "cmd.toggle_line_numbers",
538        desc_key: "cmd.toggle_line_numbers_desc",
539        action: || Action::ToggleLineNumbers,
540        contexts: &[Normal],
541        custom_contexts: &[],
542    },
543    CommandDef {
544        name_key: "cmd.toggle_scroll_sync",
545        desc_key: "cmd.toggle_scroll_sync_desc",
546        action: || Action::ToggleScrollSync,
547        contexts: &[Normal],
548        custom_contexts: &[],
549    },
550    CommandDef {
551        name_key: "cmd.toggle_fold",
552        desc_key: "cmd.toggle_fold_desc",
553        action: || Action::ToggleFold,
554        contexts: &[Normal],
555        custom_contexts: &[],
556    },
557    CommandDef {
558        name_key: "cmd.debug_toggle_highlight",
559        desc_key: "cmd.debug_toggle_highlight_desc",
560        action: || Action::ToggleDebugHighlights,
561        contexts: &[Normal],
562        custom_contexts: &[],
563    },
564    // Rulers
565    CommandDef {
566        name_key: "cmd.add_ruler",
567        desc_key: "cmd.add_ruler_desc",
568        action: || Action::AddRuler,
569        contexts: &[Normal],
570        custom_contexts: &[],
571    },
572    CommandDef {
573        name_key: "cmd.remove_ruler",
574        desc_key: "cmd.remove_ruler_desc",
575        action: || Action::RemoveRuler,
576        contexts: &[Normal],
577        custom_contexts: &[],
578    },
579    // Buffer settings
580    CommandDef {
581        name_key: "cmd.set_tab_size",
582        desc_key: "cmd.set_tab_size_desc",
583        action: || Action::SetTabSize,
584        contexts: &[Normal],
585        custom_contexts: &[],
586    },
587    CommandDef {
588        name_key: "cmd.set_line_ending",
589        desc_key: "cmd.set_line_ending_desc",
590        action: || Action::SetLineEnding,
591        contexts: &[Normal],
592        custom_contexts: &[],
593    },
594    CommandDef {
595        name_key: "cmd.set_encoding",
596        desc_key: "cmd.set_encoding_desc",
597        action: || Action::SetEncoding,
598        contexts: &[Normal],
599        custom_contexts: &[],
600    },
601    CommandDef {
602        name_key: "cmd.reload_with_encoding",
603        desc_key: "cmd.reload_with_encoding_desc",
604        action: || Action::ReloadWithEncoding,
605        contexts: &[Normal],
606        custom_contexts: &[],
607    },
608    CommandDef {
609        name_key: "cmd.set_language",
610        desc_key: "cmd.set_language_desc",
611        action: || Action::SetLanguage,
612        contexts: &[Normal],
613        custom_contexts: &[],
614    },
615    CommandDef {
616        name_key: "cmd.toggle_indentation",
617        desc_key: "cmd.toggle_indentation_desc",
618        action: || Action::ToggleIndentationStyle,
619        contexts: &[Normal],
620        custom_contexts: &[],
621    },
622    CommandDef {
623        name_key: "cmd.toggle_tab_indicators",
624        desc_key: "cmd.toggle_tab_indicators_desc",
625        action: || Action::ToggleTabIndicators,
626        contexts: &[Normal],
627        custom_contexts: &[],
628    },
629    CommandDef {
630        name_key: "cmd.toggle_whitespace_indicators",
631        desc_key: "cmd.toggle_whitespace_indicators_desc",
632        action: || Action::ToggleWhitespaceIndicators,
633        contexts: &[Normal],
634        custom_contexts: &[],
635    },
636    CommandDef {
637        name_key: "cmd.reset_buffer_settings",
638        desc_key: "cmd.reset_buffer_settings_desc",
639        action: || Action::ResetBufferSettings,
640        contexts: &[Normal],
641        custom_contexts: &[],
642    },
643    CommandDef {
644        name_key: "cmd.scroll_up",
645        desc_key: "cmd.scroll_up_desc",
646        action: || Action::ScrollUp,
647        contexts: &[Normal],
648        custom_contexts: &[],
649    },
650    CommandDef {
651        name_key: "cmd.scroll_down",
652        desc_key: "cmd.scroll_down_desc",
653        action: || Action::ScrollDown,
654        contexts: &[Normal],
655        custom_contexts: &[],
656    },
657    CommandDef {
658        name_key: "cmd.scroll_tabs_left",
659        desc_key: "cmd.scroll_tabs_left_desc",
660        action: || Action::ScrollTabsLeft,
661        contexts: &[Normal, Terminal],
662        custom_contexts: &[],
663    },
664    CommandDef {
665        name_key: "cmd.scroll_tabs_right",
666        desc_key: "cmd.scroll_tabs_right_desc",
667        action: || Action::ScrollTabsRight,
668        contexts: &[Normal, Terminal],
669        custom_contexts: &[],
670    },
671    CommandDef {
672        name_key: "cmd.toggle_mouse_support",
673        desc_key: "cmd.toggle_mouse_support_desc",
674        action: || Action::ToggleMouseCapture,
675        contexts: &[Normal, Terminal],
676        custom_contexts: &[],
677    },
678    // File explorer
679    CommandDef {
680        name_key: "cmd.toggle_file_explorer",
681        desc_key: "cmd.toggle_file_explorer_desc",
682        action: || Action::ToggleFileExplorer,
683        contexts: &[Normal, FileExplorer, Terminal],
684        custom_contexts: &[],
685    },
686    CommandDef {
687        name_key: "cmd.toggle_menu_bar",
688        desc_key: "cmd.toggle_menu_bar_desc",
689        action: || Action::ToggleMenuBar,
690        contexts: &[Normal, FileExplorer, Terminal],
691        custom_contexts: &[],
692    },
693    CommandDef {
694        name_key: "cmd.toggle_tab_bar",
695        desc_key: "cmd.toggle_tab_bar_desc",
696        action: || Action::ToggleTabBar,
697        contexts: &[Normal, FileExplorer, Terminal],
698        custom_contexts: &[],
699    },
700    CommandDef {
701        name_key: "cmd.toggle_status_bar",
702        desc_key: "cmd.toggle_status_bar_desc",
703        action: || Action::ToggleStatusBar,
704        contexts: &[Normal, FileExplorer, Terminal],
705        custom_contexts: &[],
706    },
707    CommandDef {
708        name_key: "cmd.toggle_prompt_line",
709        desc_key: "cmd.toggle_prompt_line_desc",
710        action: || Action::TogglePromptLine,
711        contexts: &[Normal, FileExplorer, Terminal],
712        custom_contexts: &[],
713    },
714    CommandDef {
715        name_key: "cmd.toggle_vertical_scrollbar",
716        desc_key: "cmd.toggle_vertical_scrollbar_desc",
717        action: || Action::ToggleVerticalScrollbar,
718        contexts: &[Normal, FileExplorer, Terminal],
719        custom_contexts: &[],
720    },
721    CommandDef {
722        name_key: "cmd.toggle_horizontal_scrollbar",
723        desc_key: "cmd.toggle_horizontal_scrollbar_desc",
724        action: || Action::ToggleHorizontalScrollbar,
725        contexts: &[Normal, FileExplorer, Terminal],
726        custom_contexts: &[],
727    },
728    CommandDef {
729        name_key: "cmd.focus_file_explorer",
730        desc_key: "cmd.focus_file_explorer_desc",
731        action: || Action::FocusFileExplorer,
732        contexts: &[Normal, Terminal],
733        custom_contexts: &[],
734    },
735    CommandDef {
736        name_key: "cmd.focus_editor",
737        desc_key: "cmd.focus_editor_desc",
738        action: || Action::FocusEditor,
739        contexts: &[FileExplorer],
740        custom_contexts: &[],
741    },
742    CommandDef {
743        name_key: "cmd.explorer_refresh",
744        desc_key: "cmd.explorer_refresh_desc",
745        action: || Action::FileExplorerRefresh,
746        contexts: &[FileExplorer],
747        custom_contexts: &[],
748    },
749    CommandDef {
750        name_key: "cmd.explorer_new_file",
751        desc_key: "cmd.explorer_new_file_desc",
752        action: || Action::FileExplorerNewFile,
753        contexts: &[FileExplorer],
754        custom_contexts: &[],
755    },
756    CommandDef {
757        name_key: "cmd.explorer_new_directory",
758        desc_key: "cmd.explorer_new_directory_desc",
759        action: || Action::FileExplorerNewDirectory,
760        contexts: &[FileExplorer],
761        custom_contexts: &[],
762    },
763    CommandDef {
764        name_key: "cmd.explorer_delete",
765        desc_key: "cmd.explorer_delete_desc",
766        action: || Action::FileExplorerDelete,
767        contexts: &[FileExplorer],
768        custom_contexts: &[],
769    },
770    CommandDef {
771        name_key: "cmd.explorer_rename",
772        desc_key: "cmd.explorer_rename_desc",
773        action: || Action::FileExplorerRename,
774        contexts: &[FileExplorer],
775        custom_contexts: &[],
776    },
777    CommandDef {
778        name_key: "cmd.toggle_hidden_files",
779        desc_key: "cmd.toggle_hidden_files_desc",
780        action: || Action::FileExplorerToggleHidden,
781        contexts: &[FileExplorer],
782        custom_contexts: &[],
783    },
784    CommandDef {
785        name_key: "cmd.toggle_gitignored_files",
786        desc_key: "cmd.toggle_gitignored_files_desc",
787        action: || Action::FileExplorerToggleGitignored,
788        contexts: &[FileExplorer],
789        custom_contexts: &[],
790    },
791    // View
792    CommandDef {
793        name_key: "cmd.toggle_line_wrap",
794        desc_key: "cmd.toggle_line_wrap_desc",
795        action: || Action::ToggleLineWrap,
796        contexts: &[Normal],
797        custom_contexts: &[],
798    },
799    CommandDef {
800        name_key: "cmd.toggle_current_line_highlight",
801        desc_key: "cmd.toggle_current_line_highlight_desc",
802        action: || Action::ToggleCurrentLineHighlight,
803        contexts: &[Normal],
804        custom_contexts: &[],
805    },
806    CommandDef {
807        name_key: "cmd.toggle_page_view",
808        desc_key: "cmd.toggle_page_view_desc",
809        action: || Action::TogglePageView,
810        contexts: &[Normal],
811        custom_contexts: &[],
812    },
813    CommandDef {
814        name_key: "cmd.set_page_width",
815        desc_key: "cmd.set_page_width_desc",
816        action: || Action::SetPageWidth,
817        contexts: &[Normal],
818        custom_contexts: &[],
819    },
820    CommandDef {
821        name_key: "cmd.toggle_read_only",
822        desc_key: "cmd.toggle_read_only_desc",
823        action: || Action::ToggleReadOnly,
824        contexts: &[Normal],
825        custom_contexts: &[],
826    },
827    CommandDef {
828        name_key: "cmd.set_background",
829        desc_key: "cmd.set_background_desc",
830        action: || Action::SetBackground,
831        contexts: &[Normal],
832        custom_contexts: &[],
833    },
834    CommandDef {
835        name_key: "cmd.set_background_blend",
836        desc_key: "cmd.set_background_blend_desc",
837        action: || Action::SetBackgroundBlend,
838        contexts: &[Normal],
839        custom_contexts: &[],
840    },
841    // Search and replace
842    CommandDef {
843        name_key: "cmd.search",
844        desc_key: "cmd.search_desc",
845        action: || Action::Search,
846        contexts: &[Normal],
847        custom_contexts: &[],
848    },
849    CommandDef {
850        name_key: "cmd.find_in_selection",
851        desc_key: "cmd.find_in_selection_desc",
852        action: || Action::FindInSelection,
853        contexts: &[Normal],
854        custom_contexts: &[],
855    },
856    CommandDef {
857        name_key: "cmd.find_next",
858        desc_key: "cmd.find_next_desc",
859        action: || Action::FindNext,
860        contexts: &[Normal],
861        custom_contexts: &[],
862    },
863    CommandDef {
864        name_key: "cmd.find_previous",
865        desc_key: "cmd.find_previous_desc",
866        action: || Action::FindPrevious,
867        contexts: &[Normal],
868        custom_contexts: &[],
869    },
870    CommandDef {
871        name_key: "cmd.find_selection_next",
872        desc_key: "cmd.find_selection_next_desc",
873        action: || Action::FindSelectionNext,
874        contexts: &[Normal],
875        custom_contexts: &[],
876    },
877    CommandDef {
878        name_key: "cmd.find_selection_previous",
879        desc_key: "cmd.find_selection_previous_desc",
880        action: || Action::FindSelectionPrevious,
881        contexts: &[Normal],
882        custom_contexts: &[],
883    },
884    CommandDef {
885        name_key: "cmd.replace",
886        desc_key: "cmd.replace_desc",
887        action: || Action::Replace,
888        contexts: &[Normal],
889        custom_contexts: &[],
890    },
891    CommandDef {
892        name_key: "cmd.query_replace",
893        desc_key: "cmd.query_replace_desc",
894        action: || Action::QueryReplace,
895        contexts: &[Normal],
896        custom_contexts: &[],
897    },
898    // Navigation
899    CommandDef {
900        name_key: "cmd.goto_line",
901        desc_key: "cmd.goto_line_desc",
902        action: || Action::GotoLine,
903        contexts: &[Normal],
904        custom_contexts: &[],
905    },
906    CommandDef {
907        name_key: "cmd.scan_line_index",
908        desc_key: "cmd.scan_line_index_desc",
909        action: || Action::ScanLineIndex,
910        contexts: &[Normal],
911        custom_contexts: &[],
912    },
913    CommandDef {
914        name_key: "cmd.smart_home",
915        desc_key: "cmd.smart_home_desc",
916        action: || Action::SmartHome,
917        contexts: &[Normal],
918        custom_contexts: &[],
919    },
920    CommandDef {
921        name_key: "cmd.show_completions",
922        desc_key: "cmd.show_completions_desc",
923        action: || Action::LspCompletion,
924        contexts: &[Normal],
925        custom_contexts: &[],
926    },
927    CommandDef {
928        name_key: "cmd.goto_definition",
929        desc_key: "cmd.goto_definition_desc",
930        action: || Action::LspGotoDefinition,
931        contexts: &[Normal],
932        custom_contexts: &[],
933    },
934    CommandDef {
935        name_key: "cmd.show_hover_info",
936        desc_key: "cmd.show_hover_info_desc",
937        action: || Action::LspHover,
938        contexts: &[Normal],
939        custom_contexts: &[],
940    },
941    CommandDef {
942        name_key: "cmd.find_references",
943        desc_key: "cmd.find_references_desc",
944        action: || Action::LspReferences,
945        contexts: &[Normal],
946        custom_contexts: &[],
947    },
948    CommandDef {
949        name_key: "cmd.show_signature_help",
950        desc_key: "cmd.show_signature_help_desc",
951        action: || Action::LspSignatureHelp,
952        contexts: &[Normal],
953        custom_contexts: &[],
954    },
955    CommandDef {
956        name_key: "cmd.code_actions",
957        desc_key: "cmd.code_actions_desc",
958        action: || Action::LspCodeActions,
959        contexts: &[Normal],
960        custom_contexts: &[],
961    },
962    CommandDef {
963        name_key: "cmd.start_restart_lsp",
964        desc_key: "cmd.start_restart_lsp_desc",
965        action: || Action::LspRestart,
966        contexts: &[Normal],
967        custom_contexts: &[],
968    },
969    CommandDef {
970        name_key: "cmd.stop_lsp",
971        desc_key: "cmd.stop_lsp_desc",
972        action: || Action::LspStop,
973        contexts: &[Normal],
974        custom_contexts: &[],
975    },
976    CommandDef {
977        name_key: "cmd.toggle_lsp_for_buffer",
978        desc_key: "cmd.toggle_lsp_for_buffer_desc",
979        action: || Action::LspToggleForBuffer,
980        contexts: &[Normal],
981        custom_contexts: &[],
982    },
983    CommandDef {
984        name_key: "cmd.toggle_mouse_hover",
985        desc_key: "cmd.toggle_mouse_hover_desc",
986        action: || Action::ToggleMouseHover,
987        contexts: &[],
988        custom_contexts: &[],
989    },
990    CommandDef {
991        name_key: "cmd.navigate_back",
992        desc_key: "cmd.navigate_back_desc",
993        action: || Action::NavigateBack,
994        contexts: &[Normal],
995        custom_contexts: &[],
996    },
997    CommandDef {
998        name_key: "cmd.navigate_forward",
999        desc_key: "cmd.navigate_forward_desc",
1000        action: || Action::NavigateForward,
1001        contexts: &[Normal],
1002        custom_contexts: &[],
1003    },
1004    // Smart editing
1005    CommandDef {
1006        name_key: "cmd.toggle_comment",
1007        desc_key: "cmd.toggle_comment_desc",
1008        action: || Action::ToggleComment,
1009        contexts: &[Normal],
1010        custom_contexts: &[],
1011    },
1012    CommandDef {
1013        name_key: "cmd.dedent_selection",
1014        desc_key: "cmd.dedent_selection_desc",
1015        action: || Action::DedentSelection,
1016        contexts: &[Normal],
1017        custom_contexts: &[],
1018    },
1019    CommandDef {
1020        name_key: "cmd.goto_matching_bracket",
1021        desc_key: "cmd.goto_matching_bracket_desc",
1022        action: || Action::GoToMatchingBracket,
1023        contexts: &[Normal],
1024        custom_contexts: &[],
1025    },
1026    // Error navigation
1027    CommandDef {
1028        name_key: "cmd.jump_to_next_error",
1029        desc_key: "cmd.jump_to_next_error_desc",
1030        action: || Action::JumpToNextError,
1031        contexts: &[Normal],
1032        custom_contexts: &[],
1033    },
1034    CommandDef {
1035        name_key: "cmd.jump_to_previous_error",
1036        desc_key: "cmd.jump_to_previous_error_desc",
1037        action: || Action::JumpToPreviousError,
1038        contexts: &[Normal],
1039        custom_contexts: &[],
1040    },
1041    // LSP
1042    CommandDef {
1043        name_key: "cmd.rename_symbol",
1044        desc_key: "cmd.rename_symbol_desc",
1045        action: || Action::LspRename,
1046        contexts: &[Normal],
1047        custom_contexts: &[],
1048    },
1049    // Bookmarks and Macros
1050    CommandDef {
1051        name_key: "cmd.list_bookmarks",
1052        desc_key: "cmd.list_bookmarks_desc",
1053        action: || Action::ListBookmarks,
1054        contexts: &[Normal],
1055        custom_contexts: &[],
1056    },
1057    CommandDef {
1058        name_key: "cmd.list_macros",
1059        desc_key: "cmd.list_macros_desc",
1060        action: || Action::ListMacros,
1061        contexts: &[Normal],
1062        custom_contexts: &[],
1063    },
1064    CommandDef {
1065        name_key: "cmd.record_macro",
1066        desc_key: "cmd.record_macro_desc",
1067        action: || Action::PromptRecordMacro,
1068        contexts: &[Normal],
1069        custom_contexts: &[],
1070    },
1071    CommandDef {
1072        name_key: "cmd.stop_recording_macro",
1073        desc_key: "cmd.stop_recording_macro_desc",
1074        action: || Action::StopMacroRecording,
1075        contexts: &[Normal],
1076        custom_contexts: &[],
1077    },
1078    CommandDef {
1079        name_key: "cmd.play_macro",
1080        desc_key: "cmd.play_macro_desc",
1081        action: || Action::PromptPlayMacro,
1082        contexts: &[Normal],
1083        custom_contexts: &[],
1084    },
1085    CommandDef {
1086        name_key: "cmd.play_last_macro",
1087        desc_key: "cmd.play_last_macro_desc",
1088        action: || Action::PlayLastMacro,
1089        contexts: &[Normal],
1090        custom_contexts: &[],
1091    },
1092    CommandDef {
1093        name_key: "cmd.set_bookmark",
1094        desc_key: "cmd.set_bookmark_desc",
1095        action: || Action::PromptSetBookmark,
1096        contexts: &[Normal],
1097        custom_contexts: &[],
1098    },
1099    CommandDef {
1100        name_key: "cmd.jump_to_bookmark",
1101        desc_key: "cmd.jump_to_bookmark_desc",
1102        action: || Action::PromptJumpToBookmark,
1103        contexts: &[Normal],
1104        custom_contexts: &[],
1105    },
1106    // Help
1107    CommandDef {
1108        name_key: "cmd.show_manual",
1109        desc_key: "cmd.show_manual_desc",
1110        action: || Action::ShowHelp,
1111        contexts: &[],
1112        custom_contexts: &[],
1113    },
1114    CommandDef {
1115        name_key: "cmd.show_keyboard_shortcuts",
1116        desc_key: "cmd.show_keyboard_shortcuts_desc",
1117        action: || Action::ShowKeyboardShortcuts,
1118        contexts: &[],
1119        custom_contexts: &[],
1120    },
1121    CommandDef {
1122        name_key: "cmd.show_warnings",
1123        desc_key: "cmd.show_warnings_desc",
1124        action: || Action::ShowWarnings,
1125        contexts: &[],
1126        custom_contexts: &[],
1127    },
1128    CommandDef {
1129        name_key: "cmd.show_lsp_status",
1130        desc_key: "cmd.show_lsp_status_desc",
1131        action: || Action::ShowLspStatus,
1132        contexts: &[],
1133        custom_contexts: &[],
1134    },
1135    CommandDef {
1136        name_key: "cmd.show_remote_indicator_menu",
1137        desc_key: "cmd.show_remote_indicator_menu_desc",
1138        action: || Action::ShowRemoteIndicatorMenu,
1139        contexts: &[],
1140        custom_contexts: &[],
1141    },
1142    CommandDef {
1143        name_key: "cmd.clear_warnings",
1144        desc_key: "cmd.clear_warnings_desc",
1145        action: || Action::ClearWarnings,
1146        contexts: &[],
1147        custom_contexts: &[],
1148    },
1149    // Config
1150    CommandDef {
1151        name_key: "cmd.dump_config",
1152        desc_key: "cmd.dump_config_desc",
1153        action: || Action::DumpConfig,
1154        contexts: &[],
1155        custom_contexts: &[],
1156    },
1157    CommandDef {
1158        name_key: "cmd.redraw_screen",
1159        desc_key: "cmd.redraw_screen_desc",
1160        action: || Action::RedrawScreen,
1161        contexts: &[],
1162        custom_contexts: &[],
1163    },
1164    CommandDef {
1165        name_key: "cmd.toggle_inlay_hints",
1166        desc_key: "cmd.toggle_inlay_hints_desc",
1167        action: || Action::ToggleInlayHints,
1168        contexts: &[Normal],
1169        custom_contexts: &[],
1170    },
1171    // Theme selection
1172    CommandDef {
1173        name_key: "cmd.select_theme",
1174        desc_key: "cmd.select_theme_desc",
1175        action: || Action::SelectTheme,
1176        contexts: &[],
1177        custom_contexts: &[],
1178    },
1179    // Theme inspection
1180    CommandDef {
1181        name_key: "cmd.inspect_theme_at_cursor",
1182        desc_key: "cmd.inspect_theme_at_cursor_desc",
1183        action: || Action::InspectThemeAtCursor,
1184        contexts: &[Normal],
1185        custom_contexts: &[],
1186    },
1187    // Keybinding map selection
1188    CommandDef {
1189        name_key: "cmd.select_keybinding_map",
1190        desc_key: "cmd.select_keybinding_map_desc",
1191        action: || Action::SelectKeybindingMap,
1192        contexts: &[],
1193        custom_contexts: &[],
1194    },
1195    // Cursor style selection
1196    CommandDef {
1197        name_key: "cmd.select_cursor_style",
1198        desc_key: "cmd.select_cursor_style_desc",
1199        action: || Action::SelectCursorStyle,
1200        contexts: &[],
1201        custom_contexts: &[],
1202    },
1203    // Locale selection
1204    CommandDef {
1205        name_key: "cmd.select_locale",
1206        desc_key: "cmd.select_locale_desc",
1207        action: || Action::SelectLocale,
1208        contexts: &[],
1209        custom_contexts: &[],
1210    },
1211    // Settings
1212    CommandDef {
1213        name_key: "cmd.open_settings",
1214        desc_key: "cmd.open_settings_desc",
1215        action: || Action::OpenSettings,
1216        contexts: &[],
1217        custom_contexts: &[],
1218    },
1219    // Keybinding editor
1220    CommandDef {
1221        name_key: "cmd.open_keybinding_editor",
1222        desc_key: "cmd.open_keybinding_editor_desc",
1223        action: || Action::OpenKeybindingEditor,
1224        contexts: &[],
1225        custom_contexts: &[],
1226    },
1227    // Input calibration
1228    CommandDef {
1229        name_key: "cmd.calibrate_input",
1230        desc_key: "cmd.calibrate_input_desc",
1231        action: || Action::CalibrateInput,
1232        contexts: &[],
1233        custom_contexts: &[],
1234    },
1235    // Terminal commands
1236    CommandDef {
1237        name_key: "cmd.open_terminal",
1238        desc_key: "cmd.open_terminal_desc",
1239        action: || Action::OpenTerminal,
1240        contexts: &[],
1241        custom_contexts: &[],
1242    },
1243    CommandDef {
1244        name_key: "cmd.focus_terminal",
1245        desc_key: "cmd.focus_terminal_desc",
1246        action: || Action::FocusTerminal,
1247        contexts: &[Normal],
1248        custom_contexts: &[],
1249    },
1250    CommandDef {
1251        name_key: "cmd.exit_terminal_mode",
1252        desc_key: "cmd.exit_terminal_mode_desc",
1253        action: || Action::TerminalEscape,
1254        contexts: &[Terminal],
1255        custom_contexts: &[],
1256    },
1257    CommandDef {
1258        name_key: "cmd.toggle_keyboard_capture",
1259        desc_key: "cmd.toggle_keyboard_capture_desc",
1260        action: || Action::ToggleKeyboardCapture,
1261        contexts: &[Terminal],
1262        custom_contexts: &[],
1263    },
1264    // Shell command operations
1265    CommandDef {
1266        name_key: "cmd.shell_command",
1267        desc_key: "cmd.shell_command_desc",
1268        action: || Action::ShellCommand,
1269        contexts: &[Normal],
1270        custom_contexts: &[],
1271    },
1272    CommandDef {
1273        name_key: "cmd.shell_command_replace",
1274        desc_key: "cmd.shell_command_replace_desc",
1275        action: || Action::ShellCommandReplace,
1276        contexts: &[Normal],
1277        custom_contexts: &[],
1278    },
1279    // Debugging
1280    CommandDef {
1281        name_key: "cmd.event_debug",
1282        desc_key: "cmd.event_debug_desc",
1283        action: || Action::EventDebug,
1284        contexts: &[],
1285        custom_contexts: &[],
1286    },
1287    // Process control (Unix job-control suspend)
1288    CommandDef {
1289        name_key: "cmd.suspend_process",
1290        desc_key: "cmd.suspend_process_desc",
1291        action: || Action::SuspendProcess,
1292        contexts: &[Normal, FileExplorer, Terminal],
1293        custom_contexts: &[],
1294    },
1295    // Plugin development
1296    CommandDef {
1297        name_key: "cmd.load_plugin_from_buffer",
1298        desc_key: "cmd.load_plugin_from_buffer_desc",
1299        action: || Action::LoadPluginFromBuffer,
1300        contexts: &[Normal],
1301        custom_contexts: &[],
1302    },
1303    // User init.ts
1304    CommandDef {
1305        name_key: "cmd.init_reload",
1306        desc_key: "cmd.init_reload_desc",
1307        action: || Action::InitReload,
1308        contexts: &[Normal],
1309        custom_contexts: &[],
1310    },
1311    CommandDef {
1312        name_key: "cmd.init_edit",
1313        desc_key: "cmd.init_edit_desc",
1314        action: || Action::InitEdit,
1315        contexts: &[Normal],
1316        custom_contexts: &[],
1317    },
1318    CommandDef {
1319        name_key: "cmd.init_check",
1320        desc_key: "cmd.init_check_desc",
1321        action: || Action::InitCheck,
1322        contexts: &[Normal],
1323        custom_contexts: &[],
1324    },
1325];
1326
1327/// Get all available commands for the command palette
1328pub fn get_all_commands() -> Vec<Command> {
1329    COMMAND_DEFS
1330        .iter()
1331        .map(|def| Command {
1332            name: t!(def.name_key).to_string(),
1333            description: t!(def.desc_key).to_string(),
1334            action: (def.action)(),
1335            contexts: def.contexts.to_vec(),
1336            custom_contexts: def.custom_contexts.iter().map(|s| s.to_string()).collect(),
1337            source: CommandSource::Builtin,
1338        })
1339        .collect()
1340}
1341
1342/// Filter commands by fuzzy matching the query, with context awareness
1343pub fn filter_commands(
1344    query: &str,
1345    current_context: KeyContext,
1346    keybinding_resolver: &crate::input::keybindings::KeybindingResolver,
1347) -> Vec<Suggestion> {
1348    let query_lower = query.to_lowercase();
1349    let commands = get_all_commands();
1350
1351    // Helper function to check if command is available in current context
1352    let is_available = |cmd: &Command| -> bool {
1353        // Empty contexts means available in all contexts
1354        cmd.contexts.is_empty() || cmd.contexts.contains(&current_context)
1355    };
1356
1357    // Helper function for fuzzy matching
1358    let matches_query = |cmd: &Command| -> bool {
1359        if query.is_empty() {
1360            return true;
1361        }
1362
1363        let name_lower = cmd.name.to_lowercase();
1364        let mut query_chars = query_lower.chars();
1365        let mut current_char = query_chars.next();
1366
1367        for name_char in name_lower.chars() {
1368            if let Some(qc) = current_char {
1369                if qc == name_char {
1370                    current_char = query_chars.next();
1371                }
1372            } else {
1373                break;
1374            }
1375        }
1376
1377        current_char.is_none() // All query characters matched
1378    };
1379
1380    // Filter and convert to suggestions
1381    let current_context_ref = &current_context;
1382    let mut suggestions: Vec<Suggestion> = commands
1383        .into_iter()
1384        .filter(|cmd| matches_query(cmd))
1385        .map(|cmd| {
1386            let available = is_available(&cmd);
1387            let keybinding = keybinding_resolver
1388                .get_keybinding_for_action(&cmd.action, current_context_ref.clone());
1389            Suggestion::new(cmd.name.clone())
1390                .with_description(cmd.description)
1391                .set_disabled(!available)
1392                .with_keybinding(keybinding)
1393        })
1394        .collect();
1395
1396    // Sort: available commands first, then disabled ones
1397    suggestions.sort_by_key(|s| s.disabled);
1398
1399    suggestions
1400}