Skip to main content

reovim_driver_command/
traits.rs

1//! Command traits for the command execution framework.
2//!
3//! This module defines the core traits for the command system:
4//! - [`Command`] - Self-describing command metadata
5//! - [`CommandHandler`] - Command execution trait
6
7use {
8    crate::{ArgSpec, CommandContext, CommandResult},
9    reovim_driver_session::SessionRuntime,
10    reovim_kernel::api::v1::CommandId,
11};
12
13/// Priority level for command registration (#545).
14///
15/// When multiple handlers register for the same [`CommandId`],
16/// higher priority wins. Equal priority uses last-wins semantics
17/// (preserving existing `HashMap::insert` behavior).
18///
19/// # Usage
20///
21/// Base module commands use `Normal` (the default). Adapter modules
22/// that override a base command use `Override` to guarantee they win
23/// regardless of module initialization order.
24#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub enum CommandPriority {
26    /// Default priority for base module commands.
27    #[default]
28    Normal = 0,
29    /// Override priority for adapter commands that enhance base behavior.
30    Override = 10,
31}
32
33/// Self-describing command metadata.
34///
35/// Commands implement this trait to provide metadata about themselves:
36/// - Unique identifier ([`CommandId`])
37/// - Human-readable description
38/// - Argument specifications
39/// - Command aliases (for ex commands like `:w`, `:write`)
40///
41/// # Design Philosophy
42///
43/// This trait separates command metadata from execution. The [`CommandHandler`]
44/// trait handles actual execution. This allows querying command information
45/// without executing.
46///
47/// # Example
48///
49/// ```ignore
50/// use reovim_driver_command::{Command, ArgSpec, ArgKind};
51/// use reovim_kernel::api::v1::{CommandId, ModuleId};
52///
53/// struct DeleteLine;
54///
55/// impl Command for DeleteLine {
56///     fn id(&self) -> CommandId {
57///         CommandId::new(ModuleId::new("editor"), "delete-line")
58///     }
59///
60///     fn description(&self) -> &'static str {
61///         "Delete the current line"
62///     }
63///
64///     fn args(&self) -> Vec<ArgSpec> {
65///         vec![ArgSpec::optional("count", ArgKind::Count, "Number of lines")]
66///     }
67/// }
68/// ```
69pub trait Command: Send + Sync + 'static {
70    /// Get the unique identifier for this command.
71    fn id(&self) -> CommandId;
72
73    /// Get a human-readable description of what this command does.
74    fn description(&self) -> &'static str;
75
76    /// Get the argument specifications for this command.
77    ///
78    /// Returns an empty vector if the command takes no arguments.
79    fn args(&self) -> Vec<ArgSpec> {
80        vec![]
81    }
82
83    /// Get command name aliases.
84    ///
85    /// These are used for ex commands (command-line mode). For example,
86    /// `:w` and `:write` are aliases for the same command.
87    fn names(&self) -> &[&'static str] {
88        &[]
89    }
90
91    /// Get tab-completion candidates for this command's arguments.
92    ///
93    /// Called when the user presses Tab while editing arguments for
94    /// this command. Returns a list of completion candidates matching
95    /// the partial input.
96    ///
97    /// Default implementation returns an empty list (no completions).
98    fn complete(&self, _partial: &str) -> Vec<String> {
99        vec![]
100    }
101
102    /// Registration priority (#545).
103    ///
104    /// When multiple handlers register for the same [`CommandId`],
105    /// higher priority wins. Equal priority uses last-wins semantics.
106    ///
107    /// Override this to [`CommandPriority::Override`] in adapter commands
108    /// that replace a base module's handler.
109    fn priority(&self) -> CommandPriority {
110        CommandPriority::Normal
111    }
112}
113
114/// Command execution trait.
115///
116/// Commands that can be executed implement this trait in addition to [`Command`].
117///
118/// # Design Philosophy
119///
120/// Separating execution from metadata allows:
121/// - Querying command info without execution capability
122/// - Different execution strategies (sync, async, background)
123/// - Testing command metadata independently
124///
125/// # Session Runtime
126///
127/// Commands receive a [`SessionRuntime`] which provides access to:
128/// - **`ModeApi`** - Mode stack operations (push, pop, set)
129/// - **`BufferApi`** - Buffer content and cursor operations
130/// - **`WindowApi`** - Window management and focus
131/// - **`ExtensionApi`** - Per-session module state
132/// - **`ChangeTracker`** - State change accumulation
133///
134/// For operations not yet covered by these APIs, use the escape hatch:
135/// ```ignore
136/// let kernel = runtime.kernel();
137/// let buffer = kernel.buffers.get(buffer_id)?;
138/// ```
139///
140/// # Example
141///
142/// ```ignore
143/// use reovim_driver_command::{Command, CommandHandler, CommandContext, CommandResult};
144/// use reovim_driver_session::{SessionRuntime, ModeApi, TransitionContext};
145/// use reovim_kernel::api::v1::{CommandId, ModuleId};
146///
147/// struct EnterInsertMode;
148///
149/// impl Command for EnterInsertMode {
150///     fn id(&self) -> CommandId {
151///         CommandId::new(ModuleId::new("vim"), "enter-insert-mode")
152///     }
153///     fn description(&self) -> &'static str { "Enter insert mode" }
154/// }
155///
156/// impl CommandHandler for EnterInsertMode {
157///     fn execute(&self, runtime: &mut SessionRuntime<'_>, _args: &CommandContext) -> CommandResult {
158///         let insert_mode = ModeId::new(ModuleId::new("vim"), "insert");
159///         runtime.set_mode(insert_mode, TransitionContext::new());
160///         CommandResult::Success
161///     }
162/// }
163/// ```
164pub trait CommandHandler: Command {
165    /// Execute the command.
166    ///
167    /// # Arguments
168    ///
169    /// * `runtime` - Session runtime providing API trait access and kernel escape hatch
170    /// * `args` - The command arguments parsed from user input
171    ///
172    /// # Returns
173    ///
174    /// A [`CommandResult`] indicating success, error, or special results like quit.
175    fn execute(&self, runtime: &mut SessionRuntime<'_>, args: &CommandContext) -> CommandResult;
176}
177
178#[cfg(test)]
179#[path = "traits_tests.rs"]
180mod tests;