//! Command palette system for executing editor actions by name
//!
//! # Naming convention for settings toggles
//!
//! A toggle's *scope* must be readable from its name alone — the palette is a
//! flat list, and "Toggle Line Wrap" sitting next to "Toggle Line Wrap (Current
//! Buffer)" is only unambiguous if the suffix is used consistently. Two forms,
//! no third:
//!
//! * **`Toggle X`** — changes the editor-wide default and **saves it to the
//!   config layer that defines it** (project config if the project overrides
//!   the key, the user config otherwise) via `Editor::persist_config_change`,
//!   so it survives a restart. Every unsuffixed toggle must persist; one that only mutates the
//!   in-memory `Arc<Config>` (or a `Window` flag) silently forgets the user's
//!   choice on the next launch. `Config::config_mut` does *not* persist on its
//!   own — it is only `Arc::make_mut`. `persist_config_change` accepts only a
//!   [`SettingKey`](crate::config_keys::SettingKey) constant — never a raw
//!   JSON-pointer string — and every constant carries a generated CI test
//!   pinning it to serde's actual spelling and value type.
//! * **`Toggle X (Current Buffer)`** — changes only the active buffer and
//!   leaves the global default and every other buffer alone. The override is
//!   recorded on `BufferSettings` (or `BufferViewState`) as an explicit
//!   `*_override: Option<_>` field, and persisted in the per-file workspace
//!   state (`SerializedFileState`) so it too survives a restart.
//!
//! An `Option<_>` override rather than a bare bool matters: `None` keeps the
//! buffer following the global default, so a later config edit still reaches
//! buffers the user never pinned, and `BufferSettings::apply_config` knows
//! which fields it must not re-stamp.
//!
//! Two per-buffer toggles are deliberately session-scoped because they drive
//! process lifecycle rather than display — `Toggle Auto-Revert (Current
//! Buffer)` (file watching) and `Toggle LSP for Current Buffer` (server
//! lifecycle). They still name their scope, which is what matters;
//! resurrecting "LSP off for this file" across restarts is a separate decision.
//!
//! `Toggle LSP for Current Buffer` is also the one command that states its
//! scope in prose rather than with the parenthetical suffix, and must stay that
//! way. It is *disabled* for any language with no server configured, and the
//! palette sorts disabled commands last — so in the suffixed form every enabled
//! `(Current Buffer)` sibling that fuzzy-matches "toggle…lsp" outranks it
//! ("Toggle **V**irtua**l** **S**pace" matches, among others) and the user
//! cannot type their way to it. The prose form keeps it reachable.

use crate::input::keybindings::{Action, KeyContext};
use crate::types::context_keys;
use rust_i18n::t;

/// Source of a command (builtin or from a plugin)
#[derive(Debug, Clone, PartialEq)]
pub enum CommandSource {
    /// Built-in editor command
    Builtin,
    /// Command registered by a plugin (contains plugin filename without extension)
    Plugin(String),
}

/// A command that can be executed from the command palette
#[derive(Debug, Clone)]
pub struct Command {
    /// Command name (e.g., "Open File")
    pub name: String,
    /// Command description
    pub description: String,
    /// The action to trigger
    pub action: Action,
    /// Contexts where this command is available (empty = available in all contexts)
    pub contexts: Vec<KeyContext>,
    /// Custom contexts required for this command (plugin-defined contexts like "config-editor")
    /// If non-empty, all custom contexts must be active for the command to be available
    pub custom_contexts: Vec<String>,
    /// Source of the command (builtin or plugin)
    pub source: CommandSource,
    /// When `true`, a key bound to this command bypasses terminal
    /// keyboard capture: the action fires even while a terminal pane
    /// owns the keyboard. Plugins set this via
    /// `editor.registerCommand(..., { terminalBypass: true })` so
    /// commands the user must always reach (orchestrator picker /
    /// new-session form / panic-exit) aren't trapped by a focused
    /// PTY. Built-in editor commands like `CommandPalette` rely on
    /// `KeybindingResolver::is_terminal_ui_action` instead, so the
    /// flag stays `false` for them and they still bypass the
    /// existing way.
    pub terminal_bypass: bool,
}

impl Command {
    /// Get the localized name of the command
    pub fn get_localized_name(&self) -> String {
        if self.name.starts_with('%') {
            if let CommandSource::Plugin(ref plugin_name) = self.source {
                return crate::i18n::translate_plugin_string(
                    plugin_name,
                    &self.name[1..],
                    &std::collections::HashMap::new(),
                );
            }
        }
        self.name.clone()
    }

    /// Get the localized description of the command
    pub fn get_localized_description(&self) -> String {
        if self.description.starts_with('%') {
            if let CommandSource::Plugin(ref plugin_name) = self.source {
                return crate::i18n::translate_plugin_string(
                    plugin_name,
                    &self.description[1..],
                    &std::collections::HashMap::new(),
                );
            }
        }
        self.description.clone()
    }
}

/// A single suggestion item for autocomplete
#[derive(Debug, Clone, PartialEq)]
pub struct Suggestion {
    /// The text to display
    pub text: String,
    /// Optional description
    pub description: Option<String>,
    /// Optional styled rendering of `description`. When present, these
    /// spans are rendered (in order) instead of the plain `description`
    /// string, letting a suggestion highlight part of its row (e.g. the
    /// symbol word inside a code-line snippet).
    pub description_spans: Option<Vec<fresh_core::api::StyledText>>,
    /// The value to use when selected (defaults to text if None)
    pub value: Option<String>,
    /// Whether this suggestion is disabled (greyed out)
    pub disabled: bool,
    /// Optional keyboard shortcut
    pub keybinding: Option<String>,
    /// Source of the command (for command palette)
    pub source: Option<CommandSource>,
}

impl Suggestion {
    /// Create an active (selectable) suggestion
    pub fn new(text: String) -> Self {
        Self {
            text,
            description: None,
            description_spans: None,
            value: None,
            disabled: false,
            keybinding: None,
            source: None,
        }
    }

    /// Create a disabled (greyed-out) suggestion used for hints or errors
    pub fn disabled(text: String) -> Self {
        Self {
            text,
            description: None,
            description_spans: None,
            value: None,
            disabled: true,
            keybinding: None,
            source: None,
        }
    }

    pub fn with_description(mut self, description: String) -> Self {
        self.description = Some(description);
        self
    }

    pub fn with_description_spans(
        mut self,
        spans: Option<Vec<fresh_core::api::StyledText>>,
    ) -> Self {
        self.description_spans = spans;
        self
    }

    pub fn with_value(mut self, value: String) -> Self {
        self.value = Some(value);
        self
    }

    pub fn with_keybinding(mut self, keybinding: Option<String>) -> Self {
        self.keybinding = keybinding;
        self
    }

    pub fn with_source(mut self, source: Option<CommandSource>) -> Self {
        self.source = source;
        self
    }

    pub fn set_disabled(mut self, disabled: bool) -> Self {
        self.disabled = disabled;
        self
    }

    pub fn get_value(&self) -> &str {
        self.value.as_ref().unwrap_or(&self.text)
    }
}

/// Static definition of a builtin command (all data except translated strings)
struct CommandDef {
    name_key: &'static str,
    desc_key: &'static str,
    action: fn() -> Action,
    contexts: &'static [KeyContext],
    custom_contexts: &'static [&'static str],
}

use KeyContext::{CompositeBuffer, Dock, FileExplorer, Normal, Terminal};

/// All builtin command definitions as static data.
/// Translation happens at runtime via the loop in get_all_commands().
static COMMAND_DEFS: &[CommandDef] = &[
    // File operations
    CommandDef {
        name_key: "cmd.open_file",
        desc_key: "cmd.open_file_desc",
        action: || Action::Open,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.switch_project",
        desc_key: "cmd.switch_project_desc",
        action: || Action::SwitchProject,
        contexts: &[],
        custom_contexts: &[],
    },
    // Saving is application-wide, which is why `is_application_wide_action`
    // lets Ctrl+S through from the file explorer. Focusing the tree doesn't
    // change which buffer is active, so the save lands on the last focused
    // buffer — the palette offers it there for the same reason the keybinding
    // works there. Not in a terminal: Ctrl+S goes to the PTY, and the active
    // buffer in that context is the terminal itself.
    CommandDef {
        name_key: "cmd.save_file",
        desc_key: "cmd.save_file_desc",
        action: || Action::Save,
        contexts: &[Normal, FileExplorer],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.save_file_as",
        desc_key: "cmd.save_file_as_desc",
        action: || Action::SaveAs,
        contexts: &[Normal, FileExplorer],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.save_all",
        desc_key: "cmd.save_all_desc",
        action: || Action::SaveAll,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.new_file",
        desc_key: "cmd.new_file_desc",
        action: || Action::New,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.close_buffer",
        desc_key: "cmd.close_buffer_desc",
        action: || Action::Close,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.close_tab",
        desc_key: "cmd.close_tab_desc",
        action: || Action::CloseTab,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.revert_file",
        desc_key: "cmd.revert_file_desc",
        action: || Action::Revert,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_auto_revert",
        desc_key: "cmd.toggle_auto_revert_desc",
        action: || Action::ToggleAutoRevert,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.format_buffer",
        desc_key: "cmd.format_buffer_desc",
        action: || Action::FormatBuffer,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.trim_trailing_whitespace",
        desc_key: "cmd.trim_trailing_whitespace_desc",
        action: || Action::TrimTrailingWhitespace,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.ensure_final_newline",
        desc_key: "cmd.ensure_final_newline_desc",
        action: || Action::EnsureFinalNewline,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.quit",
        desc_key: "cmd.quit_desc",
        action: || Action::Quit,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.detach",
        desc_key: "cmd.detach_desc",
        action: || Action::Detach,
        contexts: &[],
        custom_contexts: &[context_keys::SESSION_MODE],
    },
    // Quick Open variants
    CommandDef {
        name_key: "cmd.quick_open_buffers",
        desc_key: "cmd.quick_open_buffers_desc",
        action: || Action::QuickOpenBuffers,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.quick_open_files",
        desc_key: "cmd.quick_open_files_desc",
        action: || Action::QuickOpenFiles,
        contexts: &[],
        custom_contexts: &[],
    },
    // Edit operations
    CommandDef {
        name_key: "cmd.undo",
        desc_key: "cmd.undo_desc",
        action: || Action::Undo,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.redo",
        desc_key: "cmd.redo_desc",
        action: || Action::Redo,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.copy",
        desc_key: "cmd.copy_desc",
        action: || Action::Copy,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.copy_with_formatting",
        desc_key: "cmd.copy_with_formatting_desc",
        action: || Action::CopyWithTheme(String::new()),
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.copy_file_path",
        desc_key: "cmd.copy_file_path_desc",
        action: || Action::CopyFilePath,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.copy_relative_file_path",
        desc_key: "cmd.copy_relative_file_path_desc",
        action: || Action::CopyRelativeFilePath,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.cut",
        desc_key: "cmd.cut_desc",
        action: || Action::Cut,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.paste",
        desc_key: "cmd.paste_desc",
        action: || Action::Paste,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.delete_line",
        desc_key: "cmd.delete_line_desc",
        action: || Action::DeleteLine,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.delete_word_backward",
        desc_key: "cmd.delete_word_backward_desc",
        action: || Action::DeleteWordBackward,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.delete_word_forward",
        desc_key: "cmd.delete_word_forward_desc",
        action: || Action::DeleteWordForward,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.delete_to_end_of_line",
        desc_key: "cmd.delete_to_end_of_line_desc",
        action: || Action::DeleteToLineEnd,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.transpose_characters",
        desc_key: "cmd.transpose_characters_desc",
        action: || Action::TransposeChars,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.transform_uppercase",
        desc_key: "cmd.transform_uppercase_desc",
        action: || Action::ToUpperCase,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.transform_lowercase",
        desc_key: "cmd.transform_lowercase_desc",
        action: || Action::ToLowerCase,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.sort_lines",
        desc_key: "cmd.sort_lines_desc",
        action: || Action::SortLines,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.open_line",
        desc_key: "cmd.open_line_desc",
        action: || Action::OpenLine,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.duplicate_line",
        desc_key: "cmd.duplicate_line_desc",
        action: || Action::DuplicateLine,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.recenter",
        desc_key: "cmd.recenter_desc",
        action: || Action::Recenter,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.set_mark",
        desc_key: "cmd.set_mark_desc",
        action: || Action::SetMark,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.cancel_mark",
        desc_key: "cmd.cancel_mark_desc",
        action: || Action::CancelMark,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.clear_mark",
        desc_key: "cmd.clear_mark_desc",
        action: || Action::ClearMark,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    // Selection
    CommandDef {
        name_key: "cmd.select_all",
        desc_key: "cmd.select_all_desc",
        action: || Action::SelectAll,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.select_word",
        desc_key: "cmd.select_word_desc",
        action: || Action::SelectWord,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.select_line",
        desc_key: "cmd.select_line_desc",
        action: || Action::SelectLine,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.expand_selection",
        desc_key: "cmd.expand_selection_desc",
        action: || Action::ExpandSelection,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    // Multi-cursor
    CommandDef {
        name_key: "cmd.add_cursor_above",
        desc_key: "cmd.add_cursor_above_desc",
        action: || Action::AddCursorAbove,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.add_cursor_below",
        desc_key: "cmd.add_cursor_below_desc",
        action: || Action::AddCursorBelow,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.add_cursor_next_match",
        desc_key: "cmd.add_cursor_next_match_desc",
        action: || Action::AddCursorNextMatch,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.add_cursors_to_line_ends",
        desc_key: "cmd.add_cursors_to_line_ends_desc",
        action: || Action::AddCursorsToLineEnds,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.remove_secondary_cursors",
        desc_key: "cmd.remove_secondary_cursors_desc",
        action: || Action::RemoveSecondaryCursors,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    // Buffer navigation
    CommandDef {
        name_key: "cmd.next_buffer",
        desc_key: "cmd.next_buffer_desc",
        action: || Action::NextBuffer,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.previous_buffer",
        desc_key: "cmd.previous_buffer_desc",
        action: || Action::PrevBuffer,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.switch_to_previous_tab",
        desc_key: "cmd.switch_to_previous_tab_desc",
        action: || Action::SwitchToPreviousTab,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.switch_to_tab_by_name",
        desc_key: "cmd.switch_to_tab_by_name_desc",
        action: || Action::SwitchToTabByName,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    // Split operations
    CommandDef {
        name_key: "cmd.split_horizontal",
        desc_key: "cmd.split_horizontal_desc",
        action: || Action::SplitHorizontal,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.split_vertical",
        desc_key: "cmd.split_vertical_desc",
        action: || Action::SplitVertical,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.close_split",
        desc_key: "cmd.close_split_desc",
        action: || Action::CloseSplit,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.next_split",
        desc_key: "cmd.next_split_desc",
        action: || Action::NextSplit,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.previous_split",
        desc_key: "cmd.previous_split_desc",
        action: || Action::PrevSplit,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.next_window",
        desc_key: "cmd.next_window_desc",
        action: || Action::NextWindow,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.previous_window",
        desc_key: "cmd.previous_window_desc",
        action: || Action::PrevWindow,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.extract_tab_to_new_workspace",
        desc_key: "cmd.extract_tab_to_new_workspace_desc",
        action: || Action::ExtractTabToNewWorkspace,
        // Terminal too: extracting a terminal tab (rooted at the shell's
        // cwd) is reached from terminal mode via the palette.
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.next_pane",
        desc_key: "cmd.next_pane_desc",
        action: || Action::NextPane,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.previous_pane",
        desc_key: "cmd.previous_pane_desc",
        action: || Action::PrevPane,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.increase_split_size",
        desc_key: "cmd.increase_split_size_desc",
        action: || Action::IncreaseSplitSize,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.decrease_split_size",
        desc_key: "cmd.decrease_split_size_desc",
        action: || Action::DecreaseSplitSize,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_maximize_split",
        desc_key: "cmd.toggle_maximize_split_desc",
        action: || Action::ToggleMaximizeSplit,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    // View toggles
    CommandDef {
        name_key: "cmd.toggle_line_numbers",
        desc_key: "cmd.toggle_line_numbers_desc",
        action: || Action::ToggleLineNumbers,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_line_numbers_current_buffer",
        desc_key: "cmd.toggle_line_numbers_current_buffer_desc",
        action: || Action::ToggleLineNumbersCurrentBuffer,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_line_wrap_current_buffer",
        desc_key: "cmd.toggle_line_wrap_current_buffer_desc",
        action: || Action::ToggleLineWrapCurrentBuffer,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_virtual_space_current_buffer",
        desc_key: "cmd.toggle_virtual_space_current_buffer_desc",
        action: || Action::ToggleVirtualSpaceCurrentBuffer,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_indentation_guide_current_buffer",
        desc_key: "cmd.toggle_indentation_guide_current_buffer_desc",
        action: || Action::ToggleIndentationGuideCurrentBuffer,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_fold_indicators_current_buffer",
        desc_key: "cmd.toggle_fold_indicators_current_buffer_desc",
        action: || Action::ToggleFoldIndicatorsCurrentBuffer,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_current_line_highlight_current_buffer",
        desc_key: "cmd.toggle_current_line_highlight_current_buffer_desc",
        action: || Action::ToggleCurrentLineHighlightCurrentBuffer,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_occurrence_highlight_current_buffer",
        desc_key: "cmd.toggle_occurrence_highlight_current_buffer_desc",
        action: || Action::ToggleOccurrenceHighlightCurrentBuffer,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.wave_animation",
        desc_key: "cmd.wave_animation_desc",
        action: || Action::TriggerWaveAnimation,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_scroll_sync",
        desc_key: "cmd.toggle_scroll_sync_desc",
        action: || Action::ToggleScrollSync,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_fold",
        desc_key: "cmd.toggle_fold_desc",
        action: || Action::ToggleFold,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.debug_toggle_highlight",
        desc_key: "cmd.debug_toggle_highlight_desc",
        action: || Action::ToggleDebugHighlights,
        // Per-buffer, not editor-wide: `Window::toggle_debug_highlights` flips
        // `debug_highlight_mode` on the *active buffer*, so it belongs with the
        // other buffer-scoped settings and stays out of the explorer/terminal.
        contexts: &[Normal],
        custom_contexts: &[],
    },
    // Rulers
    CommandDef {
        name_key: "cmd.add_ruler",
        desc_key: "cmd.add_ruler_desc",
        action: || Action::AddRuler,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.remove_ruler",
        desc_key: "cmd.remove_ruler_desc",
        action: || Action::RemoveRuler,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    // Buffer settings
    CommandDef {
        name_key: "cmd.set_tab_size",
        desc_key: "cmd.set_tab_size_desc",
        action: || Action::SetTabSize,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.set_line_ending",
        desc_key: "cmd.set_line_ending_desc",
        action: || Action::SetLineEnding,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.set_encoding",
        desc_key: "cmd.set_encoding_desc",
        action: || Action::SetEncoding,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.reload_with_encoding",
        desc_key: "cmd.reload_with_encoding_desc",
        action: || Action::ReloadWithEncoding,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.set_language",
        desc_key: "cmd.set_language_desc",
        action: || Action::SetLanguage,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_indentation",
        desc_key: "cmd.toggle_indentation_desc",
        action: || Action::ToggleIndentationStyle,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_tab_indicators",
        desc_key: "cmd.toggle_tab_indicators_desc",
        action: || Action::ToggleTabIndicators,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_whitespace_indicators",
        desc_key: "cmd.toggle_whitespace_indicators_desc",
        action: || Action::ToggleWhitespaceIndicators,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.reset_buffer_settings",
        desc_key: "cmd.reset_buffer_settings_desc",
        action: || Action::ResetBufferSettings,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.scroll_up",
        desc_key: "cmd.scroll_up_desc",
        action: || Action::ScrollUp,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.scroll_down",
        desc_key: "cmd.scroll_down_desc",
        action: || Action::ScrollDown,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.scroll_tabs_left",
        desc_key: "cmd.scroll_tabs_left_desc",
        action: || Action::ScrollTabsLeft,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.scroll_tabs_right",
        desc_key: "cmd.scroll_tabs_right_desc",
        action: || Action::ScrollTabsRight,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_mouse_support",
        desc_key: "cmd.toggle_mouse_support_desc",
        action: || Action::ToggleMouseCapture,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    // File explorer
    CommandDef {
        name_key: "cmd.toggle_file_explorer",
        desc_key: "cmd.toggle_file_explorer_desc",
        action: || Action::ToggleFileExplorer,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_file_explorer_side",
        desc_key: "cmd.toggle_file_explorer_side_desc",
        action: || Action::ToggleFileExplorerSide,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_menu_bar",
        desc_key: "cmd.toggle_menu_bar_desc",
        action: || Action::ToggleMenuBar,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_tab_bar",
        desc_key: "cmd.toggle_tab_bar_desc",
        action: || Action::ToggleTabBar,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_status_bar",
        desc_key: "cmd.toggle_status_bar_desc",
        action: || Action::ToggleStatusBar,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_prompt_line",
        desc_key: "cmd.toggle_prompt_line_desc",
        action: || Action::TogglePromptLine,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_vertical_scrollbar",
        desc_key: "cmd.toggle_vertical_scrollbar_desc",
        action: || Action::ToggleVerticalScrollbar,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_horizontal_scrollbar",
        desc_key: "cmd.toggle_horizontal_scrollbar_desc",
        action: || Action::ToggleHorizontalScrollbar,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.focus_file_explorer",
        desc_key: "cmd.focus_file_explorer_desc",
        action: || Action::FocusFileExplorer,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.focus_editor",
        desc_key: "cmd.focus_editor_desc",
        action: || Action::FocusEditor,
        contexts: &[FileExplorer],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_dock_focus",
        desc_key: "cmd.toggle_dock_focus_desc",
        action: || Action::ToggleDockFocus,
        contexts: &[Normal, FileExplorer, Terminal, CompositeBuffer, Dock],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.explorer_refresh",
        desc_key: "cmd.explorer_refresh_desc",
        action: || Action::FileExplorerRefresh,
        contexts: &[FileExplorer],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.explorer_new_file",
        desc_key: "cmd.explorer_new_file_desc",
        action: || Action::FileExplorerNewFile,
        contexts: &[FileExplorer],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.explorer_new_directory",
        desc_key: "cmd.explorer_new_directory_desc",
        action: || Action::FileExplorerNewDirectory,
        contexts: &[FileExplorer],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.explorer_delete",
        desc_key: "cmd.explorer_delete_desc",
        action: || Action::FileExplorerDelete,
        contexts: &[FileExplorer],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.explorer_rename",
        desc_key: "cmd.explorer_rename_desc",
        action: || Action::FileExplorerRename,
        contexts: &[FileExplorer],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_hidden_files",
        desc_key: "cmd.toggle_hidden_files_desc",
        action: || Action::FileExplorerToggleHidden,
        contexts: &[FileExplorer],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_gitignored_files",
        desc_key: "cmd.toggle_gitignored_files_desc",
        action: || Action::FileExplorerToggleGitignored,
        contexts: &[FileExplorer],
        custom_contexts: &[],
    },
    // View
    CommandDef {
        name_key: "cmd.toggle_line_wrap",
        desc_key: "cmd.toggle_line_wrap_desc",
        action: || Action::ToggleLineWrap,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_current_line_highlight",
        desc_key: "cmd.toggle_current_line_highlight_desc",
        action: || Action::ToggleCurrentLineHighlight,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_occurrence_highlight",
        desc_key: "cmd.toggle_occurrence_highlight_desc",
        action: || Action::ToggleOccurrenceHighlight,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_page_view",
        desc_key: "cmd.toggle_page_view_desc",
        action: || Action::TogglePageView,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.set_page_width",
        desc_key: "cmd.set_page_width_desc",
        action: || Action::SetPageWidth,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_read_only",
        desc_key: "cmd.toggle_read_only_desc",
        action: || Action::ToggleReadOnly,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.set_background",
        desc_key: "cmd.set_background_desc",
        action: || Action::SetBackground,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.set_background_blend",
        desc_key: "cmd.set_background_blend_desc",
        action: || Action::SetBackgroundBlend,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    // Search and replace
    CommandDef {
        name_key: "cmd.search",
        desc_key: "cmd.search_desc",
        action: || Action::Search,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.find_in_selection",
        desc_key: "cmd.find_in_selection_desc",
        action: || Action::FindInSelection,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.find_next",
        desc_key: "cmd.find_next_desc",
        action: || Action::FindNext,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.find_previous",
        desc_key: "cmd.find_previous_desc",
        action: || Action::FindPrevious,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.find_selection_next",
        desc_key: "cmd.find_selection_next_desc",
        action: || Action::FindSelectionNext,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.find_selection_previous",
        desc_key: "cmd.find_selection_previous_desc",
        action: || Action::FindSelectionPrevious,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.clear_search",
        desc_key: "cmd.clear_search_desc",
        action: || Action::ClearSearch,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.replace",
        desc_key: "cmd.replace_desc",
        action: || Action::Replace,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.query_replace",
        desc_key: "cmd.query_replace_desc",
        action: || Action::QueryReplace,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    // Navigation
    CommandDef {
        name_key: "cmd.goto_line",
        desc_key: "cmd.goto_line_desc",
        action: || Action::GotoLine,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.scan_line_index",
        desc_key: "cmd.scan_line_index_desc",
        action: || Action::ScanLineIndex,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.smart_home",
        desc_key: "cmd.smart_home_desc",
        action: || Action::SmartHome,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.show_completions",
        desc_key: "cmd.show_completions_desc",
        action: || Action::LspCompletion,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.goto_definition",
        desc_key: "cmd.goto_definition_desc",
        action: || Action::LspGotoDefinition,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.show_hover_info",
        desc_key: "cmd.show_hover_info_desc",
        action: || Action::LspHover,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.find_references",
        desc_key: "cmd.find_references_desc",
        action: || Action::LspReferences,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.goto_implementation",
        desc_key: "cmd.goto_implementation_desc",
        action: || Action::LspImplementation,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.show_signature_help",
        desc_key: "cmd.show_signature_help_desc",
        action: || Action::LspSignatureHelp,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.code_actions",
        desc_key: "cmd.code_actions_desc",
        action: || Action::LspCodeActions,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.start_restart_lsp",
        desc_key: "cmd.start_restart_lsp_desc",
        action: || Action::LspRestart,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.stop_lsp",
        desc_key: "cmd.stop_lsp_desc",
        action: || Action::LspStop,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_lsp_for_buffer",
        desc_key: "cmd.toggle_lsp_for_buffer_desc",
        action: || Action::LspToggleForBuffer,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_mouse_hover",
        desc_key: "cmd.toggle_mouse_hover_desc",
        action: || Action::ToggleMouseHover,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.navigate_back",
        desc_key: "cmd.navigate_back_desc",
        action: || Action::NavigateBack,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.navigate_forward",
        desc_key: "cmd.navigate_forward_desc",
        action: || Action::NavigateForward,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    // Smart editing
    CommandDef {
        name_key: "cmd.toggle_comment",
        desc_key: "cmd.toggle_comment_desc",
        action: || Action::ToggleComment,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.dedent_selection",
        desc_key: "cmd.dedent_selection_desc",
        action: || Action::DedentSelection,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.goto_matching_bracket",
        desc_key: "cmd.goto_matching_bracket_desc",
        action: || Action::GoToMatchingBracket,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    // Error navigation
    CommandDef {
        name_key: "cmd.jump_to_next_error",
        desc_key: "cmd.jump_to_next_error_desc",
        action: || Action::JumpToNextError,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.jump_to_previous_error",
        desc_key: "cmd.jump_to_previous_error_desc",
        action: || Action::JumpToPreviousError,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    // LSP
    CommandDef {
        name_key: "cmd.rename_symbol",
        desc_key: "cmd.rename_symbol_desc",
        action: || Action::LspRename,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    // Bookmarks and Macros
    CommandDef {
        name_key: "cmd.list_bookmarks",
        desc_key: "cmd.list_bookmarks_desc",
        action: || Action::ListBookmarks,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.list_macros",
        desc_key: "cmd.list_macros_desc",
        action: || Action::ListMacros,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.record_macro",
        desc_key: "cmd.record_macro_desc",
        action: || Action::PromptRecordMacro,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.stop_recording_macro",
        desc_key: "cmd.stop_recording_macro_desc",
        action: || Action::StopMacroRecording,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.play_macro",
        desc_key: "cmd.play_macro_desc",
        action: || Action::PromptPlayMacro,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.play_last_macro",
        desc_key: "cmd.play_last_macro_desc",
        action: || Action::PlayLastMacro,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.save_macro_to_init",
        desc_key: "cmd.save_macro_to_init_desc",
        action: || Action::PromptSaveMacroToInit,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.promote_macro",
        desc_key: "cmd.promote_macro_desc",
        action: || Action::PromptPromoteMacro,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.set_bookmark",
        desc_key: "cmd.set_bookmark_desc",
        action: || Action::PromptSetBookmark,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.jump_to_bookmark",
        desc_key: "cmd.jump_to_bookmark_desc",
        action: || Action::PromptJumpToBookmark,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    // Help
    CommandDef {
        name_key: "cmd.show_manual",
        desc_key: "cmd.show_manual_desc",
        action: || Action::ShowHelp,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.show_keyboard_shortcuts",
        desc_key: "cmd.show_keyboard_shortcuts_desc",
        action: || Action::ShowKeyboardShortcuts,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.show_warnings",
        desc_key: "cmd.show_warnings_desc",
        action: || Action::ShowWarnings,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.show_lsp_status",
        desc_key: "cmd.show_lsp_status_desc",
        action: || Action::ShowLspStatus,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.update_fresh",
        desc_key: "cmd.update_fresh_desc",
        action: || Action::UpdateFresh,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.open_update_log",
        desc_key: "cmd.open_update_log_desc",
        action: || Action::OpenUpdateLog,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.show_remote_indicator_menu",
        desc_key: "cmd.show_remote_indicator_menu_desc",
        action: || Action::ShowRemoteIndicatorMenu,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.clear_warnings",
        desc_key: "cmd.clear_warnings_desc",
        action: || Action::ClearWarnings,
        contexts: &[],
        custom_contexts: &[],
    },
    // Config
    CommandDef {
        name_key: "cmd.dump_config",
        desc_key: "cmd.dump_config_desc",
        action: || Action::DumpConfig,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.redraw_screen",
        desc_key: "cmd.redraw_screen_desc",
        action: || Action::RedrawScreen,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_inlay_hints",
        desc_key: "cmd.toggle_inlay_hints_desc",
        action: || Action::ToggleInlayHints,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    // Theme selection
    CommandDef {
        name_key: "cmd.select_theme",
        desc_key: "cmd.select_theme_desc",
        action: || Action::SelectTheme,
        contexts: &[],
        custom_contexts: &[],
    },
    // Theme inspection
    CommandDef {
        name_key: "cmd.inspect_theme_at_cursor",
        desc_key: "cmd.inspect_theme_at_cursor_desc",
        action: || Action::InspectThemeAtCursor,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    // Keybinding map selection
    CommandDef {
        name_key: "cmd.select_keybinding_map",
        desc_key: "cmd.select_keybinding_map_desc",
        action: || Action::SelectKeybindingMap,
        contexts: &[],
        custom_contexts: &[],
    },
    // Cursor style selection
    CommandDef {
        name_key: "cmd.select_cursor_style",
        desc_key: "cmd.select_cursor_style_desc",
        action: || Action::SelectCursorStyle,
        contexts: &[],
        custom_contexts: &[],
    },
    // Locale selection
    CommandDef {
        name_key: "cmd.select_locale",
        desc_key: "cmd.select_locale_desc",
        action: || Action::SelectLocale,
        contexts: &[],
        custom_contexts: &[],
    },
    // Settings
    CommandDef {
        name_key: "cmd.open_settings",
        desc_key: "cmd.open_settings_desc",
        action: || Action::OpenSettings,
        contexts: &[],
        custom_contexts: &[],
    },
    // Keybinding editor
    CommandDef {
        name_key: "cmd.open_keybinding_editor",
        desc_key: "cmd.open_keybinding_editor_desc",
        action: || Action::OpenKeybindingEditor,
        contexts: &[],
        custom_contexts: &[],
    },
    // Input calibration
    CommandDef {
        name_key: "cmd.calibrate_input",
        desc_key: "cmd.calibrate_input_desc",
        action: || Action::CalibrateInput,
        contexts: &[],
        custom_contexts: &[],
    },
    // Terminal commands
    CommandDef {
        name_key: "cmd.open_terminal",
        desc_key: "cmd.open_terminal_desc",
        action: || Action::OpenTerminal,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.open_terminal_right",
        desc_key: "cmd.open_terminal_right_desc",
        action: || Action::OpenTerminalRight,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.open_terminal_below",
        desc_key: "cmd.open_terminal_below_desc",
        action: || Action::OpenTerminalBelow,
        contexts: &[],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.focus_terminal",
        desc_key: "cmd.focus_terminal_desc",
        action: || Action::FocusTerminal,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    // A terminal whose process quit has already dropped out of Terminal
    // context into read-only scrollback — the `Normal` entry is what covers
    // that state, which is exactly the one this command acts on. The other two
    // are for reaching it from a sibling live terminal or the explorer.
    CommandDef {
        name_key: "cmd.restart_terminal",
        desc_key: "cmd.restart_terminal_desc",
        action: || Action::RestartTerminal,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.send_selection_to_terminal",
        desc_key: "cmd.send_selection_to_terminal_desc",
        action: || Action::SendSelectionToTerminal,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.exit_terminal_mode",
        desc_key: "cmd.exit_terminal_mode_desc",
        action: || Action::TerminalEscape,
        contexts: &[Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_keyboard_capture",
        desc_key: "cmd.toggle_keyboard_capture_desc",
        action: || Action::ToggleKeyboardCapture,
        contexts: &[Terminal],
        custom_contexts: &[],
    },
    // Shell command operations
    CommandDef {
        name_key: "cmd.shell_command",
        desc_key: "cmd.shell_command_desc",
        action: || Action::ShellCommand,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.shell_command_replace",
        desc_key: "cmd.shell_command_replace_desc",
        action: || Action::ShellCommandReplace,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    // Debugging
    CommandDef {
        name_key: "cmd.event_debug",
        desc_key: "cmd.event_debug_desc",
        action: || Action::EventDebug,
        contexts: &[],
        custom_contexts: &[],
    },
    // Process control (Unix job-control suspend)
    CommandDef {
        name_key: "cmd.suspend_process",
        desc_key: "cmd.suspend_process_desc",
        action: || Action::SuspendProcess,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    // Plugin development
    CommandDef {
        name_key: "cmd.load_plugin_from_buffer",
        desc_key: "cmd.load_plugin_from_buffer_desc",
        action: || Action::LoadPluginFromBuffer,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    // User init.ts
    CommandDef {
        name_key: "cmd.init_reload",
        desc_key: "cmd.init_reload_desc",
        action: || Action::InitReload,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.init_edit",
        desc_key: "cmd.init_edit_desc",
        action: || Action::InitEdit,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.init_check",
        desc_key: "cmd.init_check_desc",
        action: || Action::InitCheck,
        contexts: &[Normal, FileExplorer, Terminal],
        custom_contexts: &[],
    },
    // Live Grep (issue #1796) — `cmd.live_grep` itself is registered
    // by the live_grep plugin (palette title is plugin-controlled);
    // these are the editor-side actions that should also be palette-
    // discoverable so the user can find them by name.
    CommandDef {
        name_key: "cmd.resume_live_grep",
        desc_key: "cmd.resume_live_grep_desc",
        action: || Action::ResumeLiveGrep,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.toggle_utility_dock",
        desc_key: "cmd.toggle_utility_dock_desc",
        action: || Action::ToggleUtilityDock,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    CommandDef {
        name_key: "cmd.open_terminal_in_dock",
        desc_key: "cmd.open_terminal_in_dock_desc",
        action: || Action::OpenTerminalInDock,
        contexts: &[Normal],
        custom_contexts: &[],
    },
    // Workspace trust — a single command that opens the trust dialog; the
    // dialog itself is where the level is chosen.
    CommandDef {
        name_key: "cmd.workspace_trust",
        desc_key: "cmd.workspace_trust_desc",
        action: || Action::WorkspaceTrustPrompt,
        contexts: &[],
        custom_contexts: &[],
    },
];

/// Get all available commands for the command palette
pub fn get_all_commands() -> Vec<Command> {
    COMMAND_DEFS
        .iter()
        .map(|def| Command {
            name: t!(def.name_key).to_string(),
            description: t!(def.desc_key).to_string(),
            action: (def.action)(),
            contexts: def.contexts.to_vec(),
            custom_contexts: def.custom_contexts.iter().map(|s| s.to_string()).collect(),
            source: CommandSource::Builtin,
            // Built-in commands use the legacy `is_terminal_ui_action`
            // path; the plugin-driven `terminal_bypass` flag isn't
            // wired into them.
            terminal_bypass: false,
        })
        .collect()
}

/// Filter commands by fuzzy matching the query, with context awareness
pub fn filter_commands(
    query: &str,
    current_context: KeyContext,
    keybinding_resolver: &crate::input::keybindings::KeybindingResolver,
) -> Vec<Suggestion> {
    let query_lower = query.to_lowercase();
    let commands = get_all_commands();

    // Helper function to check if command is available in current context
    let is_available = |cmd: &Command| -> bool {
        // Empty contexts means available in all contexts
        cmd.contexts.is_empty() || cmd.contexts.contains(&current_context)
    };

    // Helper function for fuzzy matching
    let matches_query = |cmd: &Command| -> bool {
        if query.is_empty() {
            return true;
        }

        let name_lower = cmd.name.to_lowercase();
        let mut query_chars = query_lower.chars();
        let mut current_char = query_chars.next();

        for name_char in name_lower.chars() {
            if let Some(qc) = current_char {
                if qc == name_char {
                    current_char = query_chars.next();
                }
            } else {
                break;
            }
        }

        current_char.is_none() // All query characters matched
    };

    // Filter and convert to suggestions
    let current_context_ref = &current_context;
    let mut suggestions: Vec<Suggestion> = commands
        .into_iter()
        .filter(|cmd| matches_query(cmd))
        .map(|cmd| {
            let available = is_available(&cmd);
            let keybinding = keybinding_resolver
                .get_keybinding_for_action(&cmd.action, current_context_ref.clone());
            Suggestion::new(cmd.name.clone())
                .with_description(cmd.description)
                .set_disabled(!available)
                .with_keybinding(keybinding)
        })
        .collect();

    // Sort: available commands first, then disabled ones
    suggestions.sort_by_key(|s| s.disabled);

    suggestions
}
