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