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_prompt_line",
715        desc_key: "cmd.toggle_prompt_line_desc",
716        action: || Action::TogglePromptLine,
717        contexts: &[Normal, FileExplorer, Terminal],
718        custom_contexts: &[],
719    },
720    CommandDef {
721        name_key: "cmd.toggle_vertical_scrollbar",
722        desc_key: "cmd.toggle_vertical_scrollbar_desc",
723        action: || Action::ToggleVerticalScrollbar,
724        contexts: &[Normal, FileExplorer, Terminal],
725        custom_contexts: &[],
726    },
727    CommandDef {
728        name_key: "cmd.toggle_horizontal_scrollbar",
729        desc_key: "cmd.toggle_horizontal_scrollbar_desc",
730        action: || Action::ToggleHorizontalScrollbar,
731        contexts: &[Normal, FileExplorer, Terminal],
732        custom_contexts: &[],
733    },
734    CommandDef {
735        name_key: "cmd.focus_file_explorer",
736        desc_key: "cmd.focus_file_explorer_desc",
737        action: || Action::FocusFileExplorer,
738        contexts: &[Normal, Terminal],
739        custom_contexts: &[],
740    },
741    CommandDef {
742        name_key: "cmd.focus_editor",
743        desc_key: "cmd.focus_editor_desc",
744        action: || Action::FocusEditor,
745        contexts: &[FileExplorer],
746        custom_contexts: &[],
747    },
748    CommandDef {
749        name_key: "cmd.explorer_refresh",
750        desc_key: "cmd.explorer_refresh_desc",
751        action: || Action::FileExplorerRefresh,
752        contexts: &[FileExplorer],
753        custom_contexts: &[],
754    },
755    CommandDef {
756        name_key: "cmd.explorer_new_file",
757        desc_key: "cmd.explorer_new_file_desc",
758        action: || Action::FileExplorerNewFile,
759        contexts: &[FileExplorer],
760        custom_contexts: &[],
761    },
762    CommandDef {
763        name_key: "cmd.explorer_new_directory",
764        desc_key: "cmd.explorer_new_directory_desc",
765        action: || Action::FileExplorerNewDirectory,
766        contexts: &[FileExplorer],
767        custom_contexts: &[],
768    },
769    CommandDef {
770        name_key: "cmd.explorer_delete",
771        desc_key: "cmd.explorer_delete_desc",
772        action: || Action::FileExplorerDelete,
773        contexts: &[FileExplorer],
774        custom_contexts: &[],
775    },
776    CommandDef {
777        name_key: "cmd.explorer_rename",
778        desc_key: "cmd.explorer_rename_desc",
779        action: || Action::FileExplorerRename,
780        contexts: &[FileExplorer],
781        custom_contexts: &[],
782    },
783    CommandDef {
784        name_key: "cmd.toggle_hidden_files",
785        desc_key: "cmd.toggle_hidden_files_desc",
786        action: || Action::FileExplorerToggleHidden,
787        contexts: &[FileExplorer],
788        custom_contexts: &[],
789    },
790    CommandDef {
791        name_key: "cmd.toggle_gitignored_files",
792        desc_key: "cmd.toggle_gitignored_files_desc",
793        action: || Action::FileExplorerToggleGitignored,
794        contexts: &[FileExplorer],
795        custom_contexts: &[],
796    },
797    // View
798    CommandDef {
799        name_key: "cmd.toggle_line_wrap",
800        desc_key: "cmd.toggle_line_wrap_desc",
801        action: || Action::ToggleLineWrap,
802        contexts: &[Normal],
803        custom_contexts: &[],
804    },
805    CommandDef {
806        name_key: "cmd.toggle_current_line_highlight",
807        desc_key: "cmd.toggle_current_line_highlight_desc",
808        action: || Action::ToggleCurrentLineHighlight,
809        contexts: &[Normal],
810        custom_contexts: &[],
811    },
812    CommandDef {
813        name_key: "cmd.toggle_page_view",
814        desc_key: "cmd.toggle_page_view_desc",
815        action: || Action::TogglePageView,
816        contexts: &[Normal],
817        custom_contexts: &[],
818    },
819    CommandDef {
820        name_key: "cmd.set_page_width",
821        desc_key: "cmd.set_page_width_desc",
822        action: || Action::SetPageWidth,
823        contexts: &[Normal],
824        custom_contexts: &[],
825    },
826    CommandDef {
827        name_key: "cmd.toggle_read_only",
828        desc_key: "cmd.toggle_read_only_desc",
829        action: || Action::ToggleReadOnly,
830        contexts: &[Normal],
831        custom_contexts: &[],
832    },
833    CommandDef {
834        name_key: "cmd.set_background",
835        desc_key: "cmd.set_background_desc",
836        action: || Action::SetBackground,
837        contexts: &[Normal],
838        custom_contexts: &[],
839    },
840    CommandDef {
841        name_key: "cmd.set_background_blend",
842        desc_key: "cmd.set_background_blend_desc",
843        action: || Action::SetBackgroundBlend,
844        contexts: &[Normal],
845        custom_contexts: &[],
846    },
847    // Search and replace
848    CommandDef {
849        name_key: "cmd.search",
850        desc_key: "cmd.search_desc",
851        action: || Action::Search,
852        contexts: &[Normal],
853        custom_contexts: &[],
854    },
855    CommandDef {
856        name_key: "cmd.find_in_selection",
857        desc_key: "cmd.find_in_selection_desc",
858        action: || Action::FindInSelection,
859        contexts: &[Normal],
860        custom_contexts: &[],
861    },
862    CommandDef {
863        name_key: "cmd.find_next",
864        desc_key: "cmd.find_next_desc",
865        action: || Action::FindNext,
866        contexts: &[Normal],
867        custom_contexts: &[],
868    },
869    CommandDef {
870        name_key: "cmd.find_previous",
871        desc_key: "cmd.find_previous_desc",
872        action: || Action::FindPrevious,
873        contexts: &[Normal],
874        custom_contexts: &[],
875    },
876    CommandDef {
877        name_key: "cmd.find_selection_next",
878        desc_key: "cmd.find_selection_next_desc",
879        action: || Action::FindSelectionNext,
880        contexts: &[Normal],
881        custom_contexts: &[],
882    },
883    CommandDef {
884        name_key: "cmd.find_selection_previous",
885        desc_key: "cmd.find_selection_previous_desc",
886        action: || Action::FindSelectionPrevious,
887        contexts: &[Normal],
888        custom_contexts: &[],
889    },
890    CommandDef {
891        name_key: "cmd.replace",
892        desc_key: "cmd.replace_desc",
893        action: || Action::Replace,
894        contexts: &[Normal],
895        custom_contexts: &[],
896    },
897    CommandDef {
898        name_key: "cmd.query_replace",
899        desc_key: "cmd.query_replace_desc",
900        action: || Action::QueryReplace,
901        contexts: &[Normal],
902        custom_contexts: &[],
903    },
904    // Navigation
905    CommandDef {
906        name_key: "cmd.goto_line",
907        desc_key: "cmd.goto_line_desc",
908        action: || Action::GotoLine,
909        contexts: &[Normal],
910        custom_contexts: &[],
911    },
912    CommandDef {
913        name_key: "cmd.scan_line_index",
914        desc_key: "cmd.scan_line_index_desc",
915        action: || Action::ScanLineIndex,
916        contexts: &[Normal],
917        custom_contexts: &[],
918    },
919    CommandDef {
920        name_key: "cmd.smart_home",
921        desc_key: "cmd.smart_home_desc",
922        action: || Action::SmartHome,
923        contexts: &[Normal],
924        custom_contexts: &[],
925    },
926    CommandDef {
927        name_key: "cmd.show_completions",
928        desc_key: "cmd.show_completions_desc",
929        action: || Action::LspCompletion,
930        contexts: &[Normal],
931        custom_contexts: &[],
932    },
933    CommandDef {
934        name_key: "cmd.goto_definition",
935        desc_key: "cmd.goto_definition_desc",
936        action: || Action::LspGotoDefinition,
937        contexts: &[Normal],
938        custom_contexts: &[],
939    },
940    CommandDef {
941        name_key: "cmd.show_hover_info",
942        desc_key: "cmd.show_hover_info_desc",
943        action: || Action::LspHover,
944        contexts: &[Normal],
945        custom_contexts: &[],
946    },
947    CommandDef {
948        name_key: "cmd.find_references",
949        desc_key: "cmd.find_references_desc",
950        action: || Action::LspReferences,
951        contexts: &[Normal],
952        custom_contexts: &[],
953    },
954    CommandDef {
955        name_key: "cmd.show_signature_help",
956        desc_key: "cmd.show_signature_help_desc",
957        action: || Action::LspSignatureHelp,
958        contexts: &[Normal],
959        custom_contexts: &[],
960    },
961    CommandDef {
962        name_key: "cmd.code_actions",
963        desc_key: "cmd.code_actions_desc",
964        action: || Action::LspCodeActions,
965        contexts: &[Normal],
966        custom_contexts: &[],
967    },
968    CommandDef {
969        name_key: "cmd.start_restart_lsp",
970        desc_key: "cmd.start_restart_lsp_desc",
971        action: || Action::LspRestart,
972        contexts: &[Normal],
973        custom_contexts: &[],
974    },
975    CommandDef {
976        name_key: "cmd.stop_lsp",
977        desc_key: "cmd.stop_lsp_desc",
978        action: || Action::LspStop,
979        contexts: &[Normal],
980        custom_contexts: &[],
981    },
982    CommandDef {
983        name_key: "cmd.toggle_lsp_for_buffer",
984        desc_key: "cmd.toggle_lsp_for_buffer_desc",
985        action: || Action::LspToggleForBuffer,
986        contexts: &[Normal],
987        custom_contexts: &[],
988    },
989    CommandDef {
990        name_key: "cmd.toggle_mouse_hover",
991        desc_key: "cmd.toggle_mouse_hover_desc",
992        action: || Action::ToggleMouseHover,
993        contexts: &[],
994        custom_contexts: &[],
995    },
996    CommandDef {
997        name_key: "cmd.navigate_back",
998        desc_key: "cmd.navigate_back_desc",
999        action: || Action::NavigateBack,
1000        contexts: &[Normal],
1001        custom_contexts: &[],
1002    },
1003    CommandDef {
1004        name_key: "cmd.navigate_forward",
1005        desc_key: "cmd.navigate_forward_desc",
1006        action: || Action::NavigateForward,
1007        contexts: &[Normal],
1008        custom_contexts: &[],
1009    },
1010    // Smart editing
1011    CommandDef {
1012        name_key: "cmd.toggle_comment",
1013        desc_key: "cmd.toggle_comment_desc",
1014        action: || Action::ToggleComment,
1015        contexts: &[Normal],
1016        custom_contexts: &[],
1017    },
1018    CommandDef {
1019        name_key: "cmd.dedent_selection",
1020        desc_key: "cmd.dedent_selection_desc",
1021        action: || Action::DedentSelection,
1022        contexts: &[Normal],
1023        custom_contexts: &[],
1024    },
1025    CommandDef {
1026        name_key: "cmd.goto_matching_bracket",
1027        desc_key: "cmd.goto_matching_bracket_desc",
1028        action: || Action::GoToMatchingBracket,
1029        contexts: &[Normal],
1030        custom_contexts: &[],
1031    },
1032    // Error navigation
1033    CommandDef {
1034        name_key: "cmd.jump_to_next_error",
1035        desc_key: "cmd.jump_to_next_error_desc",
1036        action: || Action::JumpToNextError,
1037        contexts: &[Normal],
1038        custom_contexts: &[],
1039    },
1040    CommandDef {
1041        name_key: "cmd.jump_to_previous_error",
1042        desc_key: "cmd.jump_to_previous_error_desc",
1043        action: || Action::JumpToPreviousError,
1044        contexts: &[Normal],
1045        custom_contexts: &[],
1046    },
1047    // LSP
1048    CommandDef {
1049        name_key: "cmd.rename_symbol",
1050        desc_key: "cmd.rename_symbol_desc",
1051        action: || Action::LspRename,
1052        contexts: &[Normal],
1053        custom_contexts: &[],
1054    },
1055    // Bookmarks and Macros
1056    CommandDef {
1057        name_key: "cmd.list_bookmarks",
1058        desc_key: "cmd.list_bookmarks_desc",
1059        action: || Action::ListBookmarks,
1060        contexts: &[Normal],
1061        custom_contexts: &[],
1062    },
1063    CommandDef {
1064        name_key: "cmd.list_macros",
1065        desc_key: "cmd.list_macros_desc",
1066        action: || Action::ListMacros,
1067        contexts: &[Normal],
1068        custom_contexts: &[],
1069    },
1070    CommandDef {
1071        name_key: "cmd.record_macro",
1072        desc_key: "cmd.record_macro_desc",
1073        action: || Action::PromptRecordMacro,
1074        contexts: &[Normal],
1075        custom_contexts: &[],
1076    },
1077    CommandDef {
1078        name_key: "cmd.stop_recording_macro",
1079        desc_key: "cmd.stop_recording_macro_desc",
1080        action: || Action::StopMacroRecording,
1081        contexts: &[Normal],
1082        custom_contexts: &[],
1083    },
1084    CommandDef {
1085        name_key: "cmd.play_macro",
1086        desc_key: "cmd.play_macro_desc",
1087        action: || Action::PromptPlayMacro,
1088        contexts: &[Normal],
1089        custom_contexts: &[],
1090    },
1091    CommandDef {
1092        name_key: "cmd.play_last_macro",
1093        desc_key: "cmd.play_last_macro_desc",
1094        action: || Action::PlayLastMacro,
1095        contexts: &[Normal],
1096        custom_contexts: &[],
1097    },
1098    CommandDef {
1099        name_key: "cmd.set_bookmark",
1100        desc_key: "cmd.set_bookmark_desc",
1101        action: || Action::PromptSetBookmark,
1102        contexts: &[Normal],
1103        custom_contexts: &[],
1104    },
1105    CommandDef {
1106        name_key: "cmd.jump_to_bookmark",
1107        desc_key: "cmd.jump_to_bookmark_desc",
1108        action: || Action::PromptJumpToBookmark,
1109        contexts: &[Normal],
1110        custom_contexts: &[],
1111    },
1112    // Help
1113    CommandDef {
1114        name_key: "cmd.show_manual",
1115        desc_key: "cmd.show_manual_desc",
1116        action: || Action::ShowHelp,
1117        contexts: &[],
1118        custom_contexts: &[],
1119    },
1120    CommandDef {
1121        name_key: "cmd.show_keyboard_shortcuts",
1122        desc_key: "cmd.show_keyboard_shortcuts_desc",
1123        action: || Action::ShowKeyboardShortcuts,
1124        contexts: &[],
1125        custom_contexts: &[],
1126    },
1127    CommandDef {
1128        name_key: "cmd.show_warnings",
1129        desc_key: "cmd.show_warnings_desc",
1130        action: || Action::ShowWarnings,
1131        contexts: &[],
1132        custom_contexts: &[],
1133    },
1134    CommandDef {
1135        name_key: "cmd.show_lsp_status",
1136        desc_key: "cmd.show_lsp_status_desc",
1137        action: || Action::ShowLspStatus,
1138        contexts: &[],
1139        custom_contexts: &[],
1140    },
1141    CommandDef {
1142        name_key: "cmd.clear_warnings",
1143        desc_key: "cmd.clear_warnings_desc",
1144        action: || Action::ClearWarnings,
1145        contexts: &[],
1146        custom_contexts: &[],
1147    },
1148    // Config
1149    CommandDef {
1150        name_key: "cmd.dump_config",
1151        desc_key: "cmd.dump_config_desc",
1152        action: || Action::DumpConfig,
1153        contexts: &[],
1154        custom_contexts: &[],
1155    },
1156    CommandDef {
1157        name_key: "cmd.toggle_inlay_hints",
1158        desc_key: "cmd.toggle_inlay_hints_desc",
1159        action: || Action::ToggleInlayHints,
1160        contexts: &[Normal],
1161        custom_contexts: &[],
1162    },
1163    // Theme selection
1164    CommandDef {
1165        name_key: "cmd.select_theme",
1166        desc_key: "cmd.select_theme_desc",
1167        action: || Action::SelectTheme,
1168        contexts: &[],
1169        custom_contexts: &[],
1170    },
1171    // Theme inspection
1172    CommandDef {
1173        name_key: "cmd.inspect_theme_at_cursor",
1174        desc_key: "cmd.inspect_theme_at_cursor_desc",
1175        action: || Action::InspectThemeAtCursor,
1176        contexts: &[Normal],
1177        custom_contexts: &[],
1178    },
1179    // Keybinding map selection
1180    CommandDef {
1181        name_key: "cmd.select_keybinding_map",
1182        desc_key: "cmd.select_keybinding_map_desc",
1183        action: || Action::SelectKeybindingMap,
1184        contexts: &[],
1185        custom_contexts: &[],
1186    },
1187    // Cursor style selection
1188    CommandDef {
1189        name_key: "cmd.select_cursor_style",
1190        desc_key: "cmd.select_cursor_style_desc",
1191        action: || Action::SelectCursorStyle,
1192        contexts: &[],
1193        custom_contexts: &[],
1194    },
1195    // Locale selection
1196    CommandDef {
1197        name_key: "cmd.select_locale",
1198        desc_key: "cmd.select_locale_desc",
1199        action: || Action::SelectLocale,
1200        contexts: &[],
1201        custom_contexts: &[],
1202    },
1203    // Settings
1204    CommandDef {
1205        name_key: "cmd.open_settings",
1206        desc_key: "cmd.open_settings_desc",
1207        action: || Action::OpenSettings,
1208        contexts: &[],
1209        custom_contexts: &[],
1210    },
1211    // Keybinding editor
1212    CommandDef {
1213        name_key: "cmd.open_keybinding_editor",
1214        desc_key: "cmd.open_keybinding_editor_desc",
1215        action: || Action::OpenKeybindingEditor,
1216        contexts: &[],
1217        custom_contexts: &[],
1218    },
1219    // Input calibration
1220    CommandDef {
1221        name_key: "cmd.calibrate_input",
1222        desc_key: "cmd.calibrate_input_desc",
1223        action: || Action::CalibrateInput,
1224        contexts: &[],
1225        custom_contexts: &[],
1226    },
1227    // Terminal commands
1228    CommandDef {
1229        name_key: "cmd.open_terminal",
1230        desc_key: "cmd.open_terminal_desc",
1231        action: || Action::OpenTerminal,
1232        contexts: &[],
1233        custom_contexts: &[],
1234    },
1235    CommandDef {
1236        name_key: "cmd.focus_terminal",
1237        desc_key: "cmd.focus_terminal_desc",
1238        action: || Action::FocusTerminal,
1239        contexts: &[Normal],
1240        custom_contexts: &[],
1241    },
1242    CommandDef {
1243        name_key: "cmd.exit_terminal_mode",
1244        desc_key: "cmd.exit_terminal_mode_desc",
1245        action: || Action::TerminalEscape,
1246        contexts: &[Terminal],
1247        custom_contexts: &[],
1248    },
1249    CommandDef {
1250        name_key: "cmd.toggle_keyboard_capture",
1251        desc_key: "cmd.toggle_keyboard_capture_desc",
1252        action: || Action::ToggleKeyboardCapture,
1253        contexts: &[Terminal],
1254        custom_contexts: &[],
1255    },
1256    // Shell command operations
1257    CommandDef {
1258        name_key: "cmd.shell_command",
1259        desc_key: "cmd.shell_command_desc",
1260        action: || Action::ShellCommand,
1261        contexts: &[Normal],
1262        custom_contexts: &[],
1263    },
1264    CommandDef {
1265        name_key: "cmd.shell_command_replace",
1266        desc_key: "cmd.shell_command_replace_desc",
1267        action: || Action::ShellCommandReplace,
1268        contexts: &[Normal],
1269        custom_contexts: &[],
1270    },
1271    // Debugging
1272    CommandDef {
1273        name_key: "cmd.event_debug",
1274        desc_key: "cmd.event_debug_desc",
1275        action: || Action::EventDebug,
1276        contexts: &[],
1277        custom_contexts: &[],
1278    },
1279    // Plugin development
1280    CommandDef {
1281        name_key: "cmd.load_plugin_from_buffer",
1282        desc_key: "cmd.load_plugin_from_buffer_desc",
1283        action: || Action::LoadPluginFromBuffer,
1284        contexts: &[Normal],
1285        custom_contexts: &[],
1286    },
1287];
1288
1289/// Get all available commands for the command palette
1290pub fn get_all_commands() -> Vec<Command> {
1291    COMMAND_DEFS
1292        .iter()
1293        .map(|def| Command {
1294            name: t!(def.name_key).to_string(),
1295            description: t!(def.desc_key).to_string(),
1296            action: (def.action)(),
1297            contexts: def.contexts.to_vec(),
1298            custom_contexts: def.custom_contexts.iter().map(|s| s.to_string()).collect(),
1299            source: CommandSource::Builtin,
1300        })
1301        .collect()
1302}
1303
1304/// Filter commands by fuzzy matching the query, with context awareness
1305pub fn filter_commands(
1306    query: &str,
1307    current_context: KeyContext,
1308    keybinding_resolver: &crate::input::keybindings::KeybindingResolver,
1309) -> Vec<Suggestion> {
1310    let query_lower = query.to_lowercase();
1311    let commands = get_all_commands();
1312
1313    // Helper function to check if command is available in current context
1314    let is_available = |cmd: &Command| -> bool {
1315        // Empty contexts means available in all contexts
1316        cmd.contexts.is_empty() || cmd.contexts.contains(&current_context)
1317    };
1318
1319    // Helper function for fuzzy matching
1320    let matches_query = |cmd: &Command| -> bool {
1321        if query.is_empty() {
1322            return true;
1323        }
1324
1325        let name_lower = cmd.name.to_lowercase();
1326        let mut query_chars = query_lower.chars();
1327        let mut current_char = query_chars.next();
1328
1329        for name_char in name_lower.chars() {
1330            if let Some(qc) = current_char {
1331                if qc == name_char {
1332                    current_char = query_chars.next();
1333                }
1334            } else {
1335                break;
1336            }
1337        }
1338
1339        current_char.is_none() // All query characters matched
1340    };
1341
1342    // Filter and convert to suggestions
1343    let current_context_ref = &current_context;
1344    let mut suggestions: Vec<Suggestion> = commands
1345        .into_iter()
1346        .filter(|cmd| matches_query(cmd))
1347        .map(|cmd| {
1348            let available = is_available(&cmd);
1349            let keybinding = keybinding_resolver
1350                .get_keybinding_for_action(&cmd.action, current_context_ref.clone());
1351            Suggestion::with_all(
1352                cmd.name.clone(),
1353                Some(cmd.description),
1354                !available,
1355                keybinding,
1356            )
1357        })
1358        .collect();
1359
1360    // Sort: available commands first, then disabled ones
1361    suggestions.sort_by_key(|s| s.disabled);
1362
1363    suggestions
1364}