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;