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