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