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    /// When `true`, a key bound to this command bypasses terminal
33    /// keyboard capture: the action fires even while a terminal pane
34    /// owns the keyboard. Plugins set this via
35    /// `editor.registerCommand(..., { terminalBypass: true })` so
36    /// commands the user must always reach (orchestrator picker /
37    /// new-session form / panic-exit) aren't trapped by a focused
38    /// PTY. Built-in editor commands like `CommandPalette` rely on
39    /// `KeybindingResolver::is_terminal_ui_action` instead, so the
40    /// flag stays `false` for them and they still bypass the
41    /// existing way.
42    pub terminal_bypass: bool,
43}
44
45impl Command {
46    /// Get the localized name of the command
47    pub fn get_localized_name(&self) -> String {
48        if self.name.starts_with('%') {
49            if let CommandSource::Plugin(ref plugin_name) = self.source {
50                return crate::i18n::translate_plugin_string(
51                    plugin_name,
52                    &self.name[1..],
53                    &std::collections::HashMap::new(),
54                );
55            }
56        }
57        self.name.clone()
58    }
59
60    /// Get the localized description of the command
61    pub fn get_localized_description(&self) -> String {
62        if self.description.starts_with('%') {
63            if let CommandSource::Plugin(ref plugin_name) = self.source {
64                return crate::i18n::translate_plugin_string(
65                    plugin_name,
66                    &self.description[1..],
67                    &std::collections::HashMap::new(),
68                );
69            }
70        }
71        self.description.clone()
72    }
73}
74
75/// A single suggestion item for autocomplete
76#[derive(Debug, Clone, PartialEq)]
77pub struct Suggestion {
78    /// The text to display
79    pub text: String,
80    /// Optional description
81    pub description: Option<String>,
82    /// Optional styled rendering of `description`. When present, these
83    /// spans are rendered (in order) instead of the plain `description`
84    /// string, letting a suggestion highlight part of its row (e.g. the
85    /// symbol word inside a code-line snippet).
86    pub description_spans: Option<Vec<fresh_core::api::StyledText>>,
87    /// The value to use when selected (defaults to text if None)
88    pub value: Option<String>,
89    /// Whether this suggestion is disabled (greyed out)
90    pub disabled: bool,
91    /// Optional keyboard shortcut
92    pub keybinding: Option<String>,
93    /// Source of the command (for command palette)
94    pub source: Option<CommandSource>,
95}
96
97impl Suggestion {
98    /// Create an active (selectable) suggestion
99    pub fn new(text: String) -> Self {
100        Self {
101            text,
102            description: None,
103            description_spans: None,
104            value: None,
105            disabled: false,
106            keybinding: None,
107            source: None,
108        }
109    }
110
111    /// Create a disabled (greyed-out) suggestion used for hints or errors
112    pub fn disabled(text: String) -> Self {
113        Self {
114            text,
115            description: None,
116            description_spans: None,
117            value: None,
118            disabled: true,
119            keybinding: None,
120            source: None,
121        }
122    }
123
124    pub fn with_description(mut self, description: String) -> Self {
125        self.description = Some(description);
126        self
127    }
128
129    pub fn with_description_spans(
130        mut self,
131        spans: Option<Vec<fresh_core::api::StyledText>>,
132    ) -> Self {
133        self.description_spans = spans;
134        self
135    }
136
137    pub fn with_value(mut self, value: String) -> Self {
138        self.value = Some(value);
139        self
140    }
141
142    pub fn with_keybinding(mut self, keybinding: Option<String>) -> Self {
143        self.keybinding = keybinding;
144        self
145    }
146
147    pub fn with_source(mut self, source: Option<CommandSource>) -> Self {
148        self.source = source;
149        self
150    }
151
152    pub fn set_disabled(mut self, disabled: bool) -> Self {
153        self.disabled = disabled;
154        self
155    }
156
157    pub fn get_value(&self) -> &str {
158        self.value.as_ref().unwrap_or(&self.text)
159    }
160}
161
162/// Static definition of a builtin command (all data except translated strings)
163struct CommandDef {
164    name_key: &'static str,
165    desc_key: &'static str,
166    action: fn() -> Action,
167    contexts: &'static [KeyContext],
168    custom_contexts: &'static [&'static str],
169}
170
171use KeyContext::{CompositeBuffer, Dock, FileExplorer, Normal, Terminal};
172
173/// All builtin command definitions as static data.
174/// Translation happens at runtime via the loop in get_all_commands().
175static COMMAND_DEFS: &[CommandDef] = &[
176    // File operations
177    CommandDef {
178        name_key: "cmd.open_file",
179        desc_key: "cmd.open_file_desc",
180        action: || Action::Open,
181        contexts: &[],
182        custom_contexts: &[],
183    },
184    CommandDef {
185        name_key: "cmd.switch_project",
186        desc_key: "cmd.switch_project_desc",
187        action: || Action::SwitchProject,
188        contexts: &[],
189        custom_contexts: &[],
190    },
191    CommandDef {
192        name_key: "cmd.save_file",
193        desc_key: "cmd.save_file_desc",
194        action: || Action::Save,
195        contexts: &[Normal],
196        custom_contexts: &[],
197    },
198    CommandDef {
199        name_key: "cmd.save_file_as",
200        desc_key: "cmd.save_file_as_desc",
201        action: || Action::SaveAs,
202        contexts: &[Normal],
203        custom_contexts: &[],
204    },
205    CommandDef {
206        name_key: "cmd.new_file",
207        desc_key: "cmd.new_file_desc",
208        action: || Action::New,
209        contexts: &[],
210        custom_contexts: &[],
211    },
212    CommandDef {
213        name_key: "cmd.close_buffer",
214        desc_key: "cmd.close_buffer_desc",
215        action: || Action::Close,
216        contexts: &[Normal, Terminal],
217        custom_contexts: &[],
218    },
219    CommandDef {
220        name_key: "cmd.close_tab",
221        desc_key: "cmd.close_tab_desc",
222        action: || Action::CloseTab,
223        contexts: &[Normal, Terminal],
224        custom_contexts: &[],
225    },
226    CommandDef {
227        name_key: "cmd.revert_file",
228        desc_key: "cmd.revert_file_desc",
229        action: || Action::Revert,
230        contexts: &[Normal],
231        custom_contexts: &[],
232    },
233    CommandDef {
234        name_key: "cmd.toggle_auto_revert",
235        desc_key: "cmd.toggle_auto_revert_desc",
236        action: || Action::ToggleAutoRevert,
237        contexts: &[],
238        custom_contexts: &[],
239    },
240    CommandDef {
241        name_key: "cmd.format_buffer",
242        desc_key: "cmd.format_buffer_desc",
243        action: || Action::FormatBuffer,
244        contexts: &[Normal],
245        custom_contexts: &[],
246    },
247    CommandDef {
248        name_key: "cmd.trim_trailing_whitespace",
249        desc_key: "cmd.trim_trailing_whitespace_desc",
250        action: || Action::TrimTrailingWhitespace,
251        contexts: &[Normal],
252        custom_contexts: &[],
253    },
254    CommandDef {
255        name_key: "cmd.ensure_final_newline",
256        desc_key: "cmd.ensure_final_newline_desc",
257        action: || Action::EnsureFinalNewline,
258        contexts: &[Normal],
259        custom_contexts: &[],
260    },
261    CommandDef {
262        name_key: "cmd.quit",
263        desc_key: "cmd.quit_desc",
264        action: || Action::Quit,
265        contexts: &[],
266        custom_contexts: &[],
267    },
268    CommandDef {
269        name_key: "cmd.detach",
270        desc_key: "cmd.detach_desc",
271        action: || Action::Detach,
272        contexts: &[],
273        custom_contexts: &[context_keys::SESSION_MODE],
274    },
275    // Quick Open variants
276    CommandDef {
277        name_key: "cmd.quick_open_buffers",
278        desc_key: "cmd.quick_open_buffers_desc",
279        action: || Action::QuickOpenBuffers,
280        contexts: &[],
281        custom_contexts: &[],
282    },
283    CommandDef {
284        name_key: "cmd.quick_open_files",
285        desc_key: "cmd.quick_open_files_desc",
286        action: || Action::QuickOpenFiles,
287        contexts: &[],
288        custom_contexts: &[],
289    },
290    // Edit operations
291    CommandDef {
292        name_key: "cmd.undo",
293        desc_key: "cmd.undo_desc",
294        action: || Action::Undo,
295        contexts: &[Normal],
296        custom_contexts: &[],
297    },
298    CommandDef {
299        name_key: "cmd.redo",
300        desc_key: "cmd.redo_desc",
301        action: || Action::Redo,
302        contexts: &[Normal],
303        custom_contexts: &[],
304    },
305    CommandDef {
306        name_key: "cmd.copy",
307        desc_key: "cmd.copy_desc",
308        action: || Action::Copy,
309        contexts: &[Normal],
310        custom_contexts: &[],
311    },
312    CommandDef {
313        name_key: "cmd.copy_with_formatting",
314        desc_key: "cmd.copy_with_formatting_desc",
315        action: || Action::CopyWithTheme(String::new()),
316        contexts: &[Normal],
317        custom_contexts: &[],
318    },
319    CommandDef {
320        name_key: "cmd.copy_file_path",
321        desc_key: "cmd.copy_file_path_desc",
322        action: || Action::CopyFilePath,
323        contexts: &[Normal],
324        custom_contexts: &[],
325    },
326    CommandDef {
327        name_key: "cmd.copy_relative_file_path",
328        desc_key: "cmd.copy_relative_file_path_desc",
329        action: || Action::CopyRelativeFilePath,
330        contexts: &[Normal],
331        custom_contexts: &[],
332    },
333    CommandDef {
334        name_key: "cmd.cut",
335        desc_key: "cmd.cut_desc",
336        action: || Action::Cut,
337        contexts: &[Normal],
338        custom_contexts: &[],
339    },
340    CommandDef {
341        name_key: "cmd.paste",
342        desc_key: "cmd.paste_desc",
343        action: || Action::Paste,
344        contexts: &[Normal],
345        custom_contexts: &[],
346    },
347    CommandDef {
348        name_key: "cmd.delete_line",
349        desc_key: "cmd.delete_line_desc",
350        action: || Action::DeleteLine,
351        contexts: &[Normal],
352        custom_contexts: &[],
353    },
354    CommandDef {
355        name_key: "cmd.delete_word_backward",
356        desc_key: "cmd.delete_word_backward_desc",
357        action: || Action::DeleteWordBackward,
358        contexts: &[Normal],
359        custom_contexts: &[],
360    },
361    CommandDef {
362        name_key: "cmd.delete_word_forward",
363        desc_key: "cmd.delete_word_forward_desc",
364        action: || Action::DeleteWordForward,
365        contexts: &[Normal],
366        custom_contexts: &[],
367    },
368    CommandDef {
369        name_key: "cmd.delete_to_end_of_line",
370        desc_key: "cmd.delete_to_end_of_line_desc",
371        action: || Action::DeleteToLineEnd,
372        contexts: &[Normal],
373        custom_contexts: &[],
374    },
375    CommandDef {
376        name_key: "cmd.transpose_characters",
377        desc_key: "cmd.transpose_characters_desc",
378        action: || Action::TransposeChars,
379        contexts: &[Normal],
380        custom_contexts: &[],
381    },
382    CommandDef {
383        name_key: "cmd.transform_uppercase",
384        desc_key: "cmd.transform_uppercase_desc",
385        action: || Action::ToUpperCase,
386        contexts: &[Normal],
387        custom_contexts: &[],
388    },
389    CommandDef {
390        name_key: "cmd.transform_lowercase",
391        desc_key: "cmd.transform_lowercase_desc",
392        action: || Action::ToLowerCase,
393        contexts: &[Normal],
394        custom_contexts: &[],
395    },
396    CommandDef {
397        name_key: "cmd.sort_lines",
398        desc_key: "cmd.sort_lines_desc",
399        action: || Action::SortLines,
400        contexts: &[Normal],
401        custom_contexts: &[],
402    },
403    CommandDef {
404        name_key: "cmd.open_line",
405        desc_key: "cmd.open_line_desc",
406        action: || Action::OpenLine,
407        contexts: &[Normal],
408        custom_contexts: &[],
409    },
410    CommandDef {
411        name_key: "cmd.duplicate_line",
412        desc_key: "cmd.duplicate_line_desc",
413        action: || Action::DuplicateLine,
414        contexts: &[Normal],
415        custom_contexts: &[],
416    },
417    CommandDef {
418        name_key: "cmd.recenter",
419        desc_key: "cmd.recenter_desc",
420        action: || Action::Recenter,
421        contexts: &[Normal],
422        custom_contexts: &[],
423    },
424    CommandDef {
425        name_key: "cmd.set_mark",
426        desc_key: "cmd.set_mark_desc",
427        action: || Action::SetMark,
428        contexts: &[Normal],
429        custom_contexts: &[],
430    },
431    CommandDef {
432        name_key: "cmd.cancel_mark",
433        desc_key: "cmd.cancel_mark_desc",
434        action: || Action::CancelMark,
435        contexts: &[Normal],
436        custom_contexts: &[],
437    },
438    CommandDef {
439        name_key: "cmd.clear_mark",
440        desc_key: "cmd.clear_mark_desc",
441        action: || Action::ClearMark,
442        contexts: &[Normal],
443        custom_contexts: &[],
444    },
445    // Selection
446    CommandDef {
447        name_key: "cmd.select_all",
448        desc_key: "cmd.select_all_desc",
449        action: || Action::SelectAll,
450        contexts: &[Normal],
451        custom_contexts: &[],
452    },
453    CommandDef {
454        name_key: "cmd.select_word",
455        desc_key: "cmd.select_word_desc",
456        action: || Action::SelectWord,
457        contexts: &[Normal],
458        custom_contexts: &[],
459    },
460    CommandDef {
461        name_key: "cmd.select_line",
462        desc_key: "cmd.select_line_desc",
463        action: || Action::SelectLine,
464        contexts: &[Normal],
465        custom_contexts: &[],
466    },
467    CommandDef {
468        name_key: "cmd.expand_selection",
469        desc_key: "cmd.expand_selection_desc",
470        action: || Action::ExpandSelection,
471        contexts: &[Normal],
472        custom_contexts: &[],
473    },
474    // Multi-cursor
475    CommandDef {
476        name_key: "cmd.add_cursor_above",
477        desc_key: "cmd.add_cursor_above_desc",
478        action: || Action::AddCursorAbove,
479        contexts: &[Normal],
480        custom_contexts: &[],
481    },
482    CommandDef {
483        name_key: "cmd.add_cursor_below",
484        desc_key: "cmd.add_cursor_below_desc",
485        action: || Action::AddCursorBelow,
486        contexts: &[Normal],
487        custom_contexts: &[],
488    },
489    CommandDef {
490        name_key: "cmd.add_cursor_next_match",
491        desc_key: "cmd.add_cursor_next_match_desc",
492        action: || Action::AddCursorNextMatch,
493        contexts: &[Normal],
494        custom_contexts: &[],
495    },
496    CommandDef {
497        name_key: "cmd.add_cursors_to_line_ends",
498        desc_key: "cmd.add_cursors_to_line_ends_desc",
499        action: || Action::AddCursorsToLineEnds,
500        contexts: &[Normal],
501        custom_contexts: &[],
502    },
503    CommandDef {
504        name_key: "cmd.remove_secondary_cursors",
505        desc_key: "cmd.remove_secondary_cursors_desc",
506        action: || Action::RemoveSecondaryCursors,
507        contexts: &[Normal],
508        custom_contexts: &[],
509    },
510    // Buffer navigation
511    CommandDef {
512        name_key: "cmd.next_buffer",
513        desc_key: "cmd.next_buffer_desc",
514        action: || Action::NextBuffer,
515        contexts: &[Normal, Terminal],
516        custom_contexts: &[],
517    },
518    CommandDef {
519        name_key: "cmd.previous_buffer",
520        desc_key: "cmd.previous_buffer_desc",
521        action: || Action::PrevBuffer,
522        contexts: &[Normal, Terminal],
523        custom_contexts: &[],
524    },
525    CommandDef {
526        name_key: "cmd.switch_to_previous_tab",
527        desc_key: "cmd.switch_to_previous_tab_desc",
528        action: || Action::SwitchToPreviousTab,
529        contexts: &[Normal, Terminal],
530        custom_contexts: &[],
531    },
532    CommandDef {
533        name_key: "cmd.switch_to_tab_by_name",
534        desc_key: "cmd.switch_to_tab_by_name_desc",
535        action: || Action::SwitchToTabByName,
536        contexts: &[Normal, Terminal],
537        custom_contexts: &[],
538    },
539    // Split operations
540    CommandDef {
541        name_key: "cmd.split_horizontal",
542        desc_key: "cmd.split_horizontal_desc",
543        action: || Action::SplitHorizontal,
544        contexts: &[Normal, Terminal],
545        custom_contexts: &[],
546    },
547    CommandDef {
548        name_key: "cmd.split_vertical",
549        desc_key: "cmd.split_vertical_desc",
550        action: || Action::SplitVertical,
551        contexts: &[Normal, Terminal],
552        custom_contexts: &[],
553    },
554    CommandDef {
555        name_key: "cmd.close_split",
556        desc_key: "cmd.close_split_desc",
557        action: || Action::CloseSplit,
558        contexts: &[Normal, Terminal],
559        custom_contexts: &[],
560    },
561    CommandDef {
562        name_key: "cmd.next_split",
563        desc_key: "cmd.next_split_desc",
564        action: || Action::NextSplit,
565        contexts: &[Normal, Terminal],
566        custom_contexts: &[],
567    },
568    CommandDef {
569        name_key: "cmd.previous_split",
570        desc_key: "cmd.previous_split_desc",
571        action: || Action::PrevSplit,
572        contexts: &[Normal, Terminal],
573        custom_contexts: &[],
574    },
575    CommandDef {
576        name_key: "cmd.next_window",
577        desc_key: "cmd.next_window_desc",
578        action: || Action::NextWindow,
579        contexts: &[],
580        custom_contexts: &[],
581    },
582    CommandDef {
583        name_key: "cmd.previous_window",
584        desc_key: "cmd.previous_window_desc",
585        action: || Action::PrevWindow,
586        contexts: &[],
587        custom_contexts: &[],
588    },
589    CommandDef {
590        name_key: "cmd.increase_split_size",
591        desc_key: "cmd.increase_split_size_desc",
592        action: || Action::IncreaseSplitSize,
593        contexts: &[Normal, Terminal],
594        custom_contexts: &[],
595    },
596    CommandDef {
597        name_key: "cmd.decrease_split_size",
598        desc_key: "cmd.decrease_split_size_desc",
599        action: || Action::DecreaseSplitSize,
600        contexts: &[Normal, Terminal],
601        custom_contexts: &[],
602    },
603    CommandDef {
604        name_key: "cmd.toggle_maximize_split",
605        desc_key: "cmd.toggle_maximize_split_desc",
606        action: || Action::ToggleMaximizeSplit,
607        contexts: &[Normal, Terminal],
608        custom_contexts: &[],
609    },
610    // View toggles
611    CommandDef {
612        name_key: "cmd.toggle_line_numbers",
613        desc_key: "cmd.toggle_line_numbers_desc",
614        action: || Action::ToggleLineNumbers,
615        contexts: &[Normal],
616        custom_contexts: &[],
617    },
618    CommandDef {
619        name_key: "cmd.toggle_line_numbers_current_buffer",
620        desc_key: "cmd.toggle_line_numbers_current_buffer_desc",
621        action: || Action::ToggleLineNumbersCurrentBuffer,
622        contexts: &[Normal],
623        custom_contexts: &[],
624    },
625    CommandDef {
626        name_key: "cmd.toggle_line_wrap_current_buffer",
627        desc_key: "cmd.toggle_line_wrap_current_buffer_desc",
628        action: || Action::ToggleLineWrapCurrentBuffer,
629        contexts: &[Normal],
630        custom_contexts: &[],
631    },
632    CommandDef {
633        name_key: "cmd.wave_animation",
634        desc_key: "cmd.wave_animation_desc",
635        action: || Action::TriggerWaveAnimation,
636        contexts: &[Normal],
637        custom_contexts: &[],
638    },
639    CommandDef {
640        name_key: "cmd.toggle_scroll_sync",
641        desc_key: "cmd.toggle_scroll_sync_desc",
642        action: || Action::ToggleScrollSync,
643        contexts: &[Normal],
644        custom_contexts: &[],
645    },
646    CommandDef {
647        name_key: "cmd.toggle_fold",
648        desc_key: "cmd.toggle_fold_desc",
649        action: || Action::ToggleFold,
650        contexts: &[Normal],
651        custom_contexts: &[],
652    },
653    CommandDef {
654        name_key: "cmd.debug_toggle_highlight",
655        desc_key: "cmd.debug_toggle_highlight_desc",
656        action: || Action::ToggleDebugHighlights,
657        contexts: &[Normal],
658        custom_contexts: &[],
659    },
660    // Rulers
661    CommandDef {
662        name_key: "cmd.add_ruler",
663        desc_key: "cmd.add_ruler_desc",
664        action: || Action::AddRuler,
665        contexts: &[Normal],
666        custom_contexts: &[],
667    },
668    CommandDef {
669        name_key: "cmd.remove_ruler",
670        desc_key: "cmd.remove_ruler_desc",
671        action: || Action::RemoveRuler,
672        contexts: &[Normal],
673        custom_contexts: &[],
674    },
675    // Buffer settings
676    CommandDef {
677        name_key: "cmd.set_tab_size",
678        desc_key: "cmd.set_tab_size_desc",
679        action: || Action::SetTabSize,
680        contexts: &[Normal],
681        custom_contexts: &[],
682    },
683    CommandDef {
684        name_key: "cmd.set_line_ending",
685        desc_key: "cmd.set_line_ending_desc",
686        action: || Action::SetLineEnding,
687        contexts: &[Normal],
688        custom_contexts: &[],
689    },
690    CommandDef {
691        name_key: "cmd.set_encoding",
692        desc_key: "cmd.set_encoding_desc",
693        action: || Action::SetEncoding,
694        contexts: &[Normal],
695        custom_contexts: &[],
696    },
697    CommandDef {
698        name_key: "cmd.reload_with_encoding",
699        desc_key: "cmd.reload_with_encoding_desc",
700        action: || Action::ReloadWithEncoding,
701        contexts: &[Normal],
702        custom_contexts: &[],
703    },
704    CommandDef {
705        name_key: "cmd.set_language",
706        desc_key: "cmd.set_language_desc",
707        action: || Action::SetLanguage,
708        contexts: &[Normal],
709        custom_contexts: &[],
710    },
711    CommandDef {
712        name_key: "cmd.toggle_indentation",
713        desc_key: "cmd.toggle_indentation_desc",
714        action: || Action::ToggleIndentationStyle,
715        contexts: &[Normal],
716        custom_contexts: &[],
717    },
718    CommandDef {
719        name_key: "cmd.toggle_tab_indicators",
720        desc_key: "cmd.toggle_tab_indicators_desc",
721        action: || Action::ToggleTabIndicators,
722        contexts: &[Normal],
723        custom_contexts: &[],
724    },
725    CommandDef {
726        name_key: "cmd.toggle_whitespace_indicators",
727        desc_key: "cmd.toggle_whitespace_indicators_desc",
728        action: || Action::ToggleWhitespaceIndicators,
729        contexts: &[Normal],
730        custom_contexts: &[],
731    },
732    CommandDef {
733        name_key: "cmd.reset_buffer_settings",
734        desc_key: "cmd.reset_buffer_settings_desc",
735        action: || Action::ResetBufferSettings,
736        contexts: &[Normal],
737        custom_contexts: &[],
738    },
739    CommandDef {
740        name_key: "cmd.scroll_up",
741        desc_key: "cmd.scroll_up_desc",
742        action: || Action::ScrollUp,
743        contexts: &[Normal],
744        custom_contexts: &[],
745    },
746    CommandDef {
747        name_key: "cmd.scroll_down",
748        desc_key: "cmd.scroll_down_desc",
749        action: || Action::ScrollDown,
750        contexts: &[Normal],
751        custom_contexts: &[],
752    },
753    CommandDef {
754        name_key: "cmd.scroll_tabs_left",
755        desc_key: "cmd.scroll_tabs_left_desc",
756        action: || Action::ScrollTabsLeft,
757        contexts: &[Normal, Terminal],
758        custom_contexts: &[],
759    },
760    CommandDef {
761        name_key: "cmd.scroll_tabs_right",
762        desc_key: "cmd.scroll_tabs_right_desc",
763        action: || Action::ScrollTabsRight,
764        contexts: &[Normal, Terminal],
765        custom_contexts: &[],
766    },
767    CommandDef {
768        name_key: "cmd.toggle_mouse_support",
769        desc_key: "cmd.toggle_mouse_support_desc",
770        action: || Action::ToggleMouseCapture,
771        contexts: &[Normal, Terminal],
772        custom_contexts: &[],
773    },
774    // File explorer
775    CommandDef {
776        name_key: "cmd.toggle_file_explorer",
777        desc_key: "cmd.toggle_file_explorer_desc",
778        action: || Action::ToggleFileExplorer,
779        contexts: &[Normal, FileExplorer, Terminal],
780        custom_contexts: &[],
781    },
782    CommandDef {
783        name_key: "cmd.toggle_file_explorer_side",
784        desc_key: "cmd.toggle_file_explorer_side_desc",
785        action: || Action::ToggleFileExplorerSide,
786        contexts: &[Normal, FileExplorer, Terminal],
787        custom_contexts: &[],
788    },
789    CommandDef {
790        name_key: "cmd.toggle_menu_bar",
791        desc_key: "cmd.toggle_menu_bar_desc",
792        action: || Action::ToggleMenuBar,
793        contexts: &[Normal, FileExplorer, Terminal],
794        custom_contexts: &[],
795    },
796    CommandDef {
797        name_key: "cmd.toggle_tab_bar",
798        desc_key: "cmd.toggle_tab_bar_desc",
799        action: || Action::ToggleTabBar,
800        contexts: &[Normal, FileExplorer, Terminal],
801        custom_contexts: &[],
802    },
803    CommandDef {
804        name_key: "cmd.toggle_status_bar",
805        desc_key: "cmd.toggle_status_bar_desc",
806        action: || Action::ToggleStatusBar,
807        contexts: &[Normal, FileExplorer, Terminal],
808        custom_contexts: &[],
809    },
810    CommandDef {
811        name_key: "cmd.toggle_prompt_line",
812        desc_key: "cmd.toggle_prompt_line_desc",
813        action: || Action::TogglePromptLine,
814        contexts: &[Normal, FileExplorer, Terminal],
815        custom_contexts: &[],
816    },
817    CommandDef {
818        name_key: "cmd.toggle_vertical_scrollbar",
819        desc_key: "cmd.toggle_vertical_scrollbar_desc",
820        action: || Action::ToggleVerticalScrollbar,
821        contexts: &[Normal, FileExplorer, Terminal],
822        custom_contexts: &[],
823    },
824    CommandDef {
825        name_key: "cmd.toggle_horizontal_scrollbar",
826        desc_key: "cmd.toggle_horizontal_scrollbar_desc",
827        action: || Action::ToggleHorizontalScrollbar,
828        contexts: &[Normal, FileExplorer, Terminal],
829        custom_contexts: &[],
830    },
831    CommandDef {
832        name_key: "cmd.focus_file_explorer",
833        desc_key: "cmd.focus_file_explorer_desc",
834        action: || Action::FocusFileExplorer,
835        contexts: &[Normal, Terminal],
836        custom_contexts: &[],
837    },
838    CommandDef {
839        name_key: "cmd.focus_editor",
840        desc_key: "cmd.focus_editor_desc",
841        action: || Action::FocusEditor,
842        contexts: &[FileExplorer],
843        custom_contexts: &[],
844    },
845    CommandDef {
846        name_key: "cmd.toggle_dock_focus",
847        desc_key: "cmd.toggle_dock_focus_desc",
848        action: || Action::ToggleDockFocus,
849        contexts: &[Normal, FileExplorer, Terminal, CompositeBuffer, Dock],
850        custom_contexts: &[],
851    },
852    CommandDef {
853        name_key: "cmd.explorer_refresh",
854        desc_key: "cmd.explorer_refresh_desc",
855        action: || Action::FileExplorerRefresh,
856        contexts: &[FileExplorer],
857        custom_contexts: &[],
858    },
859    CommandDef {
860        name_key: "cmd.explorer_new_file",
861        desc_key: "cmd.explorer_new_file_desc",
862        action: || Action::FileExplorerNewFile,
863        contexts: &[FileExplorer],
864        custom_contexts: &[],
865    },
866    CommandDef {
867        name_key: "cmd.explorer_new_directory",
868        desc_key: "cmd.explorer_new_directory_desc",
869        action: || Action::FileExplorerNewDirectory,
870        contexts: &[FileExplorer],
871        custom_contexts: &[],
872    },
873    CommandDef {
874        name_key: "cmd.explorer_delete",
875        desc_key: "cmd.explorer_delete_desc",
876        action: || Action::FileExplorerDelete,
877        contexts: &[FileExplorer],
878        custom_contexts: &[],
879    },
880    CommandDef {
881        name_key: "cmd.explorer_rename",
882        desc_key: "cmd.explorer_rename_desc",
883        action: || Action::FileExplorerRename,
884        contexts: &[FileExplorer],
885        custom_contexts: &[],
886    },
887    CommandDef {
888        name_key: "cmd.toggle_hidden_files",
889        desc_key: "cmd.toggle_hidden_files_desc",
890        action: || Action::FileExplorerToggleHidden,
891        contexts: &[FileExplorer],
892        custom_contexts: &[],
893    },
894    CommandDef {
895        name_key: "cmd.toggle_gitignored_files",
896        desc_key: "cmd.toggle_gitignored_files_desc",
897        action: || Action::FileExplorerToggleGitignored,
898        contexts: &[FileExplorer],
899        custom_contexts: &[],
900    },
901    // View
902    CommandDef {
903        name_key: "cmd.toggle_line_wrap",
904        desc_key: "cmd.toggle_line_wrap_desc",
905        action: || Action::ToggleLineWrap,
906        contexts: &[Normal],
907        custom_contexts: &[],
908    },
909    CommandDef {
910        name_key: "cmd.toggle_current_line_highlight",
911        desc_key: "cmd.toggle_current_line_highlight_desc",
912        action: || Action::ToggleCurrentLineHighlight,
913        contexts: &[Normal],
914        custom_contexts: &[],
915    },
916    CommandDef {
917        name_key: "cmd.toggle_occurrence_highlight",
918        desc_key: "cmd.toggle_occurrence_highlight_desc",
919        action: || Action::ToggleOccurrenceHighlight,
920        contexts: &[Normal],
921        custom_contexts: &[],
922    },
923    CommandDef {
924        name_key: "cmd.toggle_page_view",
925        desc_key: "cmd.toggle_page_view_desc",
926        action: || Action::TogglePageView,
927        contexts: &[Normal],
928        custom_contexts: &[],
929    },
930    CommandDef {
931        name_key: "cmd.set_page_width",
932        desc_key: "cmd.set_page_width_desc",
933        action: || Action::SetPageWidth,
934        contexts: &[Normal],
935        custom_contexts: &[],
936    },
937    CommandDef {
938        name_key: "cmd.toggle_read_only",
939        desc_key: "cmd.toggle_read_only_desc",
940        action: || Action::ToggleReadOnly,
941        contexts: &[Normal],
942        custom_contexts: &[],
943    },
944    CommandDef {
945        name_key: "cmd.set_background",
946        desc_key: "cmd.set_background_desc",
947        action: || Action::SetBackground,
948        contexts: &[Normal],
949        custom_contexts: &[],
950    },
951    CommandDef {
952        name_key: "cmd.set_background_blend",
953        desc_key: "cmd.set_background_blend_desc",
954        action: || Action::SetBackgroundBlend,
955        contexts: &[Normal],
956        custom_contexts: &[],
957    },
958    // Search and replace
959    CommandDef {
960        name_key: "cmd.search",
961        desc_key: "cmd.search_desc",
962        action: || Action::Search,
963        contexts: &[Normal],
964        custom_contexts: &[],
965    },
966    CommandDef {
967        name_key: "cmd.find_in_selection",
968        desc_key: "cmd.find_in_selection_desc",
969        action: || Action::FindInSelection,
970        contexts: &[Normal],
971        custom_contexts: &[],
972    },
973    CommandDef {
974        name_key: "cmd.find_next",
975        desc_key: "cmd.find_next_desc",
976        action: || Action::FindNext,
977        contexts: &[Normal],
978        custom_contexts: &[],
979    },
980    CommandDef {
981        name_key: "cmd.find_previous",
982        desc_key: "cmd.find_previous_desc",
983        action: || Action::FindPrevious,
984        contexts: &[Normal],
985        custom_contexts: &[],
986    },
987    CommandDef {
988        name_key: "cmd.find_selection_next",
989        desc_key: "cmd.find_selection_next_desc",
990        action: || Action::FindSelectionNext,
991        contexts: &[Normal],
992        custom_contexts: &[],
993    },
994    CommandDef {
995        name_key: "cmd.find_selection_previous",
996        desc_key: "cmd.find_selection_previous_desc",
997        action: || Action::FindSelectionPrevious,
998        contexts: &[Normal],
999        custom_contexts: &[],
1000    },
1001    CommandDef {
1002        name_key: "cmd.clear_search",
1003        desc_key: "cmd.clear_search_desc",
1004        action: || Action::ClearSearch,
1005        contexts: &[Normal],
1006        custom_contexts: &[],
1007    },
1008    CommandDef {
1009        name_key: "cmd.replace",
1010        desc_key: "cmd.replace_desc",
1011        action: || Action::Replace,
1012        contexts: &[Normal],
1013        custom_contexts: &[],
1014    },
1015    CommandDef {
1016        name_key: "cmd.query_replace",
1017        desc_key: "cmd.query_replace_desc",
1018        action: || Action::QueryReplace,
1019        contexts: &[Normal],
1020        custom_contexts: &[],
1021    },
1022    // Navigation
1023    CommandDef {
1024        name_key: "cmd.goto_line",
1025        desc_key: "cmd.goto_line_desc",
1026        action: || Action::GotoLine,
1027        contexts: &[Normal],
1028        custom_contexts: &[],
1029    },
1030    CommandDef {
1031        name_key: "cmd.scan_line_index",
1032        desc_key: "cmd.scan_line_index_desc",
1033        action: || Action::ScanLineIndex,
1034        contexts: &[Normal],
1035        custom_contexts: &[],
1036    },
1037    CommandDef {
1038        name_key: "cmd.smart_home",
1039        desc_key: "cmd.smart_home_desc",
1040        action: || Action::SmartHome,
1041        contexts: &[Normal],
1042        custom_contexts: &[],
1043    },
1044    CommandDef {
1045        name_key: "cmd.show_completions",
1046        desc_key: "cmd.show_completions_desc",
1047        action: || Action::LspCompletion,
1048        contexts: &[Normal],
1049        custom_contexts: &[],
1050    },
1051    CommandDef {
1052        name_key: "cmd.goto_definition",
1053        desc_key: "cmd.goto_definition_desc",
1054        action: || Action::LspGotoDefinition,
1055        contexts: &[Normal],
1056        custom_contexts: &[],
1057    },
1058    CommandDef {
1059        name_key: "cmd.show_hover_info",
1060        desc_key: "cmd.show_hover_info_desc",
1061        action: || Action::LspHover,
1062        contexts: &[Normal],
1063        custom_contexts: &[],
1064    },
1065    CommandDef {
1066        name_key: "cmd.find_references",
1067        desc_key: "cmd.find_references_desc",
1068        action: || Action::LspReferences,
1069        contexts: &[Normal],
1070        custom_contexts: &[],
1071    },
1072    CommandDef {
1073        name_key: "cmd.goto_implementation",
1074        desc_key: "cmd.goto_implementation_desc",
1075        action: || Action::LspImplementation,
1076        contexts: &[Normal],
1077        custom_contexts: &[],
1078    },
1079    CommandDef {
1080        name_key: "cmd.show_signature_help",
1081        desc_key: "cmd.show_signature_help_desc",
1082        action: || Action::LspSignatureHelp,
1083        contexts: &[Normal],
1084        custom_contexts: &[],
1085    },
1086    CommandDef {
1087        name_key: "cmd.code_actions",
1088        desc_key: "cmd.code_actions_desc",
1089        action: || Action::LspCodeActions,
1090        contexts: &[Normal],
1091        custom_contexts: &[],
1092    },
1093    CommandDef {
1094        name_key: "cmd.start_restart_lsp",
1095        desc_key: "cmd.start_restart_lsp_desc",
1096        action: || Action::LspRestart,
1097        contexts: &[Normal],
1098        custom_contexts: &[],
1099    },
1100    CommandDef {
1101        name_key: "cmd.stop_lsp",
1102        desc_key: "cmd.stop_lsp_desc",
1103        action: || Action::LspStop,
1104        contexts: &[Normal],
1105        custom_contexts: &[],
1106    },
1107    CommandDef {
1108        name_key: "cmd.toggle_lsp_for_buffer",
1109        desc_key: "cmd.toggle_lsp_for_buffer_desc",
1110        action: || Action::LspToggleForBuffer,
1111        contexts: &[Normal],
1112        custom_contexts: &[],
1113    },
1114    CommandDef {
1115        name_key: "cmd.toggle_mouse_hover",
1116        desc_key: "cmd.toggle_mouse_hover_desc",
1117        action: || Action::ToggleMouseHover,
1118        contexts: &[],
1119        custom_contexts: &[],
1120    },
1121    CommandDef {
1122        name_key: "cmd.navigate_back",
1123        desc_key: "cmd.navigate_back_desc",
1124        action: || Action::NavigateBack,
1125        contexts: &[Normal],
1126        custom_contexts: &[],
1127    },
1128    CommandDef {
1129        name_key: "cmd.navigate_forward",
1130        desc_key: "cmd.navigate_forward_desc",
1131        action: || Action::NavigateForward,
1132        contexts: &[Normal],
1133        custom_contexts: &[],
1134    },
1135    // Smart editing
1136    CommandDef {
1137        name_key: "cmd.toggle_comment",
1138        desc_key: "cmd.toggle_comment_desc",
1139        action: || Action::ToggleComment,
1140        contexts: &[Normal],
1141        custom_contexts: &[],
1142    },
1143    CommandDef {
1144        name_key: "cmd.dedent_selection",
1145        desc_key: "cmd.dedent_selection_desc",
1146        action: || Action::DedentSelection,
1147        contexts: &[Normal],
1148        custom_contexts: &[],
1149    },
1150    CommandDef {
1151        name_key: "cmd.goto_matching_bracket",
1152        desc_key: "cmd.goto_matching_bracket_desc",
1153        action: || Action::GoToMatchingBracket,
1154        contexts: &[Normal],
1155        custom_contexts: &[],
1156    },
1157    // Error navigation
1158    CommandDef {
1159        name_key: "cmd.jump_to_next_error",
1160        desc_key: "cmd.jump_to_next_error_desc",
1161        action: || Action::JumpToNextError,
1162        contexts: &[Normal],
1163        custom_contexts: &[],
1164    },
1165    CommandDef {
1166        name_key: "cmd.jump_to_previous_error",
1167        desc_key: "cmd.jump_to_previous_error_desc",
1168        action: || Action::JumpToPreviousError,
1169        contexts: &[Normal],
1170        custom_contexts: &[],
1171    },
1172    // LSP
1173    CommandDef {
1174        name_key: "cmd.rename_symbol",
1175        desc_key: "cmd.rename_symbol_desc",
1176        action: || Action::LspRename,
1177        contexts: &[Normal],
1178        custom_contexts: &[],
1179    },
1180    // Bookmarks and Macros
1181    CommandDef {
1182        name_key: "cmd.list_bookmarks",
1183        desc_key: "cmd.list_bookmarks_desc",
1184        action: || Action::ListBookmarks,
1185        contexts: &[Normal],
1186        custom_contexts: &[],
1187    },
1188    CommandDef {
1189        name_key: "cmd.list_macros",
1190        desc_key: "cmd.list_macros_desc",
1191        action: || Action::ListMacros,
1192        contexts: &[Normal],
1193        custom_contexts: &[],
1194    },
1195    CommandDef {
1196        name_key: "cmd.record_macro",
1197        desc_key: "cmd.record_macro_desc",
1198        action: || Action::PromptRecordMacro,
1199        contexts: &[Normal],
1200        custom_contexts: &[],
1201    },
1202    CommandDef {
1203        name_key: "cmd.stop_recording_macro",
1204        desc_key: "cmd.stop_recording_macro_desc",
1205        action: || Action::StopMacroRecording,
1206        contexts: &[Normal],
1207        custom_contexts: &[],
1208    },
1209    CommandDef {
1210        name_key: "cmd.play_macro",
1211        desc_key: "cmd.play_macro_desc",
1212        action: || Action::PromptPlayMacro,
1213        contexts: &[Normal],
1214        custom_contexts: &[],
1215    },
1216    CommandDef {
1217        name_key: "cmd.play_last_macro",
1218        desc_key: "cmd.play_last_macro_desc",
1219        action: || Action::PlayLastMacro,
1220        contexts: &[Normal],
1221        custom_contexts: &[],
1222    },
1223    CommandDef {
1224        name_key: "cmd.save_macro_to_init",
1225        desc_key: "cmd.save_macro_to_init_desc",
1226        action: || Action::PromptSaveMacroToInit,
1227        contexts: &[Normal],
1228        custom_contexts: &[],
1229    },
1230    CommandDef {
1231        name_key: "cmd.promote_macro",
1232        desc_key: "cmd.promote_macro_desc",
1233        action: || Action::PromptPromoteMacro,
1234        contexts: &[Normal],
1235        custom_contexts: &[],
1236    },
1237    CommandDef {
1238        name_key: "cmd.set_bookmark",
1239        desc_key: "cmd.set_bookmark_desc",
1240        action: || Action::PromptSetBookmark,
1241        contexts: &[Normal],
1242        custom_contexts: &[],
1243    },
1244    CommandDef {
1245        name_key: "cmd.jump_to_bookmark",
1246        desc_key: "cmd.jump_to_bookmark_desc",
1247        action: || Action::PromptJumpToBookmark,
1248        contexts: &[Normal],
1249        custom_contexts: &[],
1250    },
1251    // Help
1252    CommandDef {
1253        name_key: "cmd.show_manual",
1254        desc_key: "cmd.show_manual_desc",
1255        action: || Action::ShowHelp,
1256        contexts: &[],
1257        custom_contexts: &[],
1258    },
1259    CommandDef {
1260        name_key: "cmd.show_keyboard_shortcuts",
1261        desc_key: "cmd.show_keyboard_shortcuts_desc",
1262        action: || Action::ShowKeyboardShortcuts,
1263        contexts: &[],
1264        custom_contexts: &[],
1265    },
1266    CommandDef {
1267        name_key: "cmd.show_warnings",
1268        desc_key: "cmd.show_warnings_desc",
1269        action: || Action::ShowWarnings,
1270        contexts: &[],
1271        custom_contexts: &[],
1272    },
1273    CommandDef {
1274        name_key: "cmd.show_lsp_status",
1275        desc_key: "cmd.show_lsp_status_desc",
1276        action: || Action::ShowLspStatus,
1277        contexts: &[],
1278        custom_contexts: &[],
1279    },
1280    CommandDef {
1281        name_key: "cmd.show_remote_indicator_menu",
1282        desc_key: "cmd.show_remote_indicator_menu_desc",
1283        action: || Action::ShowRemoteIndicatorMenu,
1284        contexts: &[],
1285        custom_contexts: &[],
1286    },
1287    CommandDef {
1288        name_key: "cmd.clear_warnings",
1289        desc_key: "cmd.clear_warnings_desc",
1290        action: || Action::ClearWarnings,
1291        contexts: &[],
1292        custom_contexts: &[],
1293    },
1294    // Config
1295    CommandDef {
1296        name_key: "cmd.dump_config",
1297        desc_key: "cmd.dump_config_desc",
1298        action: || Action::DumpConfig,
1299        contexts: &[],
1300        custom_contexts: &[],
1301    },
1302    CommandDef {
1303        name_key: "cmd.redraw_screen",
1304        desc_key: "cmd.redraw_screen_desc",
1305        action: || Action::RedrawScreen,
1306        contexts: &[],
1307        custom_contexts: &[],
1308    },
1309    CommandDef {
1310        name_key: "cmd.toggle_inlay_hints",
1311        desc_key: "cmd.toggle_inlay_hints_desc",
1312        action: || Action::ToggleInlayHints,
1313        contexts: &[Normal],
1314        custom_contexts: &[],
1315    },
1316    // Theme selection
1317    CommandDef {
1318        name_key: "cmd.select_theme",
1319        desc_key: "cmd.select_theme_desc",
1320        action: || Action::SelectTheme,
1321        contexts: &[],
1322        custom_contexts: &[],
1323    },
1324    // Theme inspection
1325    CommandDef {
1326        name_key: "cmd.inspect_theme_at_cursor",
1327        desc_key: "cmd.inspect_theme_at_cursor_desc",
1328        action: || Action::InspectThemeAtCursor,
1329        contexts: &[Normal],
1330        custom_contexts: &[],
1331    },
1332    // Keybinding map selection
1333    CommandDef {
1334        name_key: "cmd.select_keybinding_map",
1335        desc_key: "cmd.select_keybinding_map_desc",
1336        action: || Action::SelectKeybindingMap,
1337        contexts: &[],
1338        custom_contexts: &[],
1339    },
1340    // Cursor style selection
1341    CommandDef {
1342        name_key: "cmd.select_cursor_style",
1343        desc_key: "cmd.select_cursor_style_desc",
1344        action: || Action::SelectCursorStyle,
1345        contexts: &[],
1346        custom_contexts: &[],
1347    },
1348    // Locale selection
1349    CommandDef {
1350        name_key: "cmd.select_locale",
1351        desc_key: "cmd.select_locale_desc",
1352        action: || Action::SelectLocale,
1353        contexts: &[],
1354        custom_contexts: &[],
1355    },
1356    // Settings
1357    CommandDef {
1358        name_key: "cmd.open_settings",
1359        desc_key: "cmd.open_settings_desc",
1360        action: || Action::OpenSettings,
1361        contexts: &[],
1362        custom_contexts: &[],
1363    },
1364    // Keybinding editor
1365    CommandDef {
1366        name_key: "cmd.open_keybinding_editor",
1367        desc_key: "cmd.open_keybinding_editor_desc",
1368        action: || Action::OpenKeybindingEditor,
1369        contexts: &[],
1370        custom_contexts: &[],
1371    },
1372    // Input calibration
1373    CommandDef {
1374        name_key: "cmd.calibrate_input",
1375        desc_key: "cmd.calibrate_input_desc",
1376        action: || Action::CalibrateInput,
1377        contexts: &[],
1378        custom_contexts: &[],
1379    },
1380    // Terminal commands
1381    CommandDef {
1382        name_key: "cmd.open_terminal",
1383        desc_key: "cmd.open_terminal_desc",
1384        action: || Action::OpenTerminal,
1385        contexts: &[],
1386        custom_contexts: &[],
1387    },
1388    CommandDef {
1389        name_key: "cmd.open_terminal_right",
1390        desc_key: "cmd.open_terminal_right_desc",
1391        action: || Action::OpenTerminalRight,
1392        contexts: &[],
1393        custom_contexts: &[],
1394    },
1395    CommandDef {
1396        name_key: "cmd.open_terminal_below",
1397        desc_key: "cmd.open_terminal_below_desc",
1398        action: || Action::OpenTerminalBelow,
1399        contexts: &[],
1400        custom_contexts: &[],
1401    },
1402    CommandDef {
1403        name_key: "cmd.focus_terminal",
1404        desc_key: "cmd.focus_terminal_desc",
1405        action: || Action::FocusTerminal,
1406        contexts: &[Normal],
1407        custom_contexts: &[],
1408    },
1409    CommandDef {
1410        name_key: "cmd.send_selection_to_terminal",
1411        desc_key: "cmd.send_selection_to_terminal_desc",
1412        action: || Action::SendSelectionToTerminal,
1413        contexts: &[Normal],
1414        custom_contexts: &[],
1415    },
1416    CommandDef {
1417        name_key: "cmd.exit_terminal_mode",
1418        desc_key: "cmd.exit_terminal_mode_desc",
1419        action: || Action::TerminalEscape,
1420        contexts: &[Terminal],
1421        custom_contexts: &[],
1422    },
1423    CommandDef {
1424        name_key: "cmd.toggle_keyboard_capture",
1425        desc_key: "cmd.toggle_keyboard_capture_desc",
1426        action: || Action::ToggleKeyboardCapture,
1427        contexts: &[Terminal],
1428        custom_contexts: &[],
1429    },
1430    // Shell command operations
1431    CommandDef {
1432        name_key: "cmd.shell_command",
1433        desc_key: "cmd.shell_command_desc",
1434        action: || Action::ShellCommand,
1435        contexts: &[Normal],
1436        custom_contexts: &[],
1437    },
1438    CommandDef {
1439        name_key: "cmd.shell_command_replace",
1440        desc_key: "cmd.shell_command_replace_desc",
1441        action: || Action::ShellCommandReplace,
1442        contexts: &[Normal],
1443        custom_contexts: &[],
1444    },
1445    // Debugging
1446    CommandDef {
1447        name_key: "cmd.event_debug",
1448        desc_key: "cmd.event_debug_desc",
1449        action: || Action::EventDebug,
1450        contexts: &[],
1451        custom_contexts: &[],
1452    },
1453    // Process control (Unix job-control suspend)
1454    CommandDef {
1455        name_key: "cmd.suspend_process",
1456        desc_key: "cmd.suspend_process_desc",
1457        action: || Action::SuspendProcess,
1458        contexts: &[Normal, FileExplorer, Terminal],
1459        custom_contexts: &[],
1460    },
1461    // Plugin development
1462    CommandDef {
1463        name_key: "cmd.load_plugin_from_buffer",
1464        desc_key: "cmd.load_plugin_from_buffer_desc",
1465        action: || Action::LoadPluginFromBuffer,
1466        contexts: &[Normal],
1467        custom_contexts: &[],
1468    },
1469    // User init.ts
1470    CommandDef {
1471        name_key: "cmd.init_reload",
1472        desc_key: "cmd.init_reload_desc",
1473        action: || Action::InitReload,
1474        contexts: &[Normal],
1475        custom_contexts: &[],
1476    },
1477    CommandDef {
1478        name_key: "cmd.init_edit",
1479        desc_key: "cmd.init_edit_desc",
1480        action: || Action::InitEdit,
1481        contexts: &[Normal],
1482        custom_contexts: &[],
1483    },
1484    CommandDef {
1485        name_key: "cmd.init_check",
1486        desc_key: "cmd.init_check_desc",
1487        action: || Action::InitCheck,
1488        contexts: &[Normal],
1489        custom_contexts: &[],
1490    },
1491    // Live Grep (issue #1796) — `cmd.live_grep` itself is registered
1492    // by the live_grep plugin (palette title is plugin-controlled);
1493    // these are the editor-side actions that should also be palette-
1494    // discoverable so the user can find them by name.
1495    CommandDef {
1496        name_key: "cmd.resume_live_grep",
1497        desc_key: "cmd.resume_live_grep_desc",
1498        action: || Action::ResumeLiveGrep,
1499        contexts: &[Normal],
1500        custom_contexts: &[],
1501    },
1502    CommandDef {
1503        name_key: "cmd.toggle_utility_dock",
1504        desc_key: "cmd.toggle_utility_dock_desc",
1505        action: || Action::ToggleUtilityDock,
1506        contexts: &[Normal],
1507        custom_contexts: &[],
1508    },
1509    CommandDef {
1510        name_key: "cmd.open_terminal_in_dock",
1511        desc_key: "cmd.open_terminal_in_dock_desc",
1512        action: || Action::OpenTerminalInDock,
1513        contexts: &[Normal],
1514        custom_contexts: &[],
1515    },
1516    // Workspace trust — a single command that opens the trust dialog; the
1517    // dialog itself is where the level is chosen.
1518    CommandDef {
1519        name_key: "cmd.workspace_trust",
1520        desc_key: "cmd.workspace_trust_desc",
1521        action: || Action::WorkspaceTrustPrompt,
1522        contexts: &[],
1523        custom_contexts: &[],
1524    },
1525];
1526
1527/// Get all available commands for the command palette
1528pub fn get_all_commands() -> Vec<Command> {
1529    COMMAND_DEFS
1530        .iter()
1531        .map(|def| Command {
1532            name: t!(def.name_key).to_string(),
1533            description: t!(def.desc_key).to_string(),
1534            action: (def.action)(),
1535            contexts: def.contexts.to_vec(),
1536            custom_contexts: def.custom_contexts.iter().map(|s| s.to_string()).collect(),
1537            source: CommandSource::Builtin,
1538            // Built-in commands use the legacy `is_terminal_ui_action`
1539            // path; the plugin-driven `terminal_bypass` flag isn't
1540            // wired into them.
1541            terminal_bypass: false,
1542        })
1543        .collect()
1544}
1545
1546/// Filter commands by fuzzy matching the query, with context awareness
1547pub fn filter_commands(
1548    query: &str,
1549    current_context: KeyContext,
1550    keybinding_resolver: &crate::input::keybindings::KeybindingResolver,
1551) -> Vec<Suggestion> {
1552    let query_lower = query.to_lowercase();
1553    let commands = get_all_commands();
1554
1555    // Helper function to check if command is available in current context
1556    let is_available = |cmd: &Command| -> bool {
1557        // Empty contexts means available in all contexts
1558        cmd.contexts.is_empty() || cmd.contexts.contains(&current_context)
1559    };
1560
1561    // Helper function for fuzzy matching
1562    let matches_query = |cmd: &Command| -> bool {
1563        if query.is_empty() {
1564            return true;
1565        }
1566
1567        let name_lower = cmd.name.to_lowercase();
1568        let mut query_chars = query_lower.chars();
1569        let mut current_char = query_chars.next();
1570
1571        for name_char in name_lower.chars() {
1572            if let Some(qc) = current_char {
1573                if qc == name_char {
1574                    current_char = query_chars.next();
1575                }
1576            } else {
1577                break;
1578            }
1579        }
1580
1581        current_char.is_none() // All query characters matched
1582    };
1583
1584    // Filter and convert to suggestions
1585    let current_context_ref = &current_context;
1586    let mut suggestions: Vec<Suggestion> = commands
1587        .into_iter()
1588        .filter(|cmd| matches_query(cmd))
1589        .map(|cmd| {
1590            let available = is_available(&cmd);
1591            let keybinding = keybinding_resolver
1592                .get_keybinding_for_action(&cmd.action, current_context_ref.clone());
1593            Suggestion::new(cmd.name.clone())
1594                .with_description(cmd.description)
1595                .set_disabled(!available)
1596                .with_keybinding(keybinding)
1597        })
1598        .collect();
1599
1600    // Sort: available commands first, then disabled ones
1601    suggestions.sort_by_key(|s| s.disabled);
1602
1603    suggestions
1604}