reovim_core/command/
traits.rs

1//! Core command trait definitions for extensible command system
2
3use {
4    crate::{
5        buffer::Buffer,
6        event_bus::DynEvent,
7        modd::ModeState,
8        screen::{NavigateDirection, SplitDirection},
9    },
10    std::{any::Any, fmt::Debug},
11};
12
13/// Execution context passed to commands
14pub struct ExecutionContext<'a> {
15    /// The buffer to operate on
16    pub buffer: &'a mut Buffer,
17    /// Repeat count (e.g., 5j moves down 5 lines)
18    pub count: Option<usize>,
19    /// ID of the buffer being operated on
20    pub buffer_id: usize,
21    /// ID of the window containing the buffer
22    pub window_id: usize,
23}
24
25/// Result of command execution
26#[derive(Debug)]
27pub enum CommandResult {
28    /// Command succeeded, no further action needed
29    Success,
30    /// Command succeeded, screen needs re-render
31    NeedsRender,
32    /// Command triggers a mode change
33    ModeChange(ModeState),
34    /// Editor should quit
35    Quit,
36    /// Command produced text for clipboard (e.g., yank, delete)
37    /// `register` is the target register: None for unnamed, Some('a'-'z') for named, '+' for system
38    /// `mode_change` optionally specifies a mode to transition to after writing to clipboard
39    ClipboardWrite {
40        text: String,
41        register: Option<char>,
42        mode_change: Option<ModeState>,
43    },
44    /// Command needs Runtime access (deferred execution)
45    ///
46    /// DEPRECATED: Use `Deferred(Box<dyn DeferredActionHandler>)` for new code.
47    /// This variant is kept for backward compatibility during plugin migration.
48    DeferToRuntime(DeferredAction),
49    /// Command needs Runtime access via trait-based handler
50    ///
51    /// This is the preferred way for plugins to defer actions to the runtime.
52    /// The handler receives a `RuntimeContext` and can perform any runtime operation.
53    Deferred(Box<dyn DeferredActionHandler>),
54    /// Command emits an event to the event bus
55    EmitEvent(DynEvent),
56    /// Command failed with error message
57    Error(String),
58}
59
60/// Trait for deferred action handlers
61///
62/// This trait allows plugins to define custom actions that require
63/// runtime access. Instead of adding variants to `DeferredAction`,
64/// plugins implement this trait.
65///
66/// # Example
67///
68/// ```ignore
69/// struct MyPluginAction { /* ... */ }
70///
71/// impl DeferredActionHandler for MyPluginAction {
72///     fn handle(&self, ctx: &mut dyn RuntimeContext) {
73///         // Access runtime via context
74///         ctx.with_state_mut::<MyState, _, _>(|state| {
75///             // Modify state
76///         });
77///     }
78///
79///     fn name(&self) -> &'static str {
80///         "my_plugin_action"
81///     }
82/// }
83/// ```
84pub trait DeferredActionHandler: Send + Sync {
85    /// Execute the deferred action with runtime context
86    fn handle(&self, ctx: &mut dyn crate::runtime::RuntimeContext);
87
88    /// Name of this action for debugging
89    fn name(&self) -> &'static str;
90}
91
92impl std::fmt::Debug for dyn DeferredActionHandler {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        write!(f, "DeferredActionHandler({})", self.name())
95    }
96}
97
98/// Actions that require Runtime-level access
99///
100/// For new features, prefer using:
101/// - `CommandResult::Deferred(Box<dyn DeferredActionHandler>)` for complex handlers
102/// - `CommandResult::EmitEvent(DynEvent)` for event-driven features
103#[derive(Debug)]
104pub enum DeferredAction {
105    Paste {
106        before: bool,
107        register: Option<char>,
108    },
109    CommandLine(CommandLineAction),
110    JumpOlder,
111    JumpNewer,
112    OperatorMotion(OperatorMotionAction),
113    Window(WindowAction),
114    Tab(TabAction),
115    Buffer(BufferAction),
116    File(FileAction),
117}
118
119#[derive(Debug)]
120pub enum WindowAction {
121    SplitHorizontal {
122        filename: Option<String>,
123    },
124    SplitVertical {
125        filename: Option<String>,
126    },
127    Close {
128        force: bool,
129    },
130    CloseOthers,
131    FocusDirection {
132        direction: NavigateDirection,
133    },
134    MoveDirection {
135        direction: NavigateDirection,
136    },
137    Resize {
138        direction: SplitDirection,
139        delta: i16,
140    },
141    Equalize,
142    /// Swap current window with window in the given direction
143    SwapDirection {
144        direction: NavigateDirection,
145    },
146}
147
148#[derive(Debug)]
149pub enum TabAction {
150    New { filename: Option<String> },
151    Close,
152    Next,
153    Prev,
154    Goto { index: usize },
155}
156
157/// Buffer navigation actions
158#[derive(Debug)]
159pub enum BufferAction {
160    /// Go to previous buffer
161    Prev,
162    /// Go to next buffer
163    Next,
164    /// Delete current buffer
165    Delete { force: bool },
166}
167
168/// File operations that require Runtime access
169///
170/// Used by plugins (e.g., Explorer) to request file system operations
171/// that need Runtime access for buffer management.
172#[derive(Debug)]
173pub enum FileAction {
174    /// Open a file into a buffer
175    Open { path: String },
176    /// Create a new file or directory
177    Create { path: String, is_dir: bool },
178    /// Delete a file or directory
179    Delete { path: String },
180    /// Rename/move a file or directory
181    Rename { from: String, to: String },
182}
183
184/// Operator + motion action (e.g., dw, yj, c$)
185#[derive(Debug)]
186pub enum OperatorMotionAction {
187    /// Delete with motion (d + motion)
188    Delete {
189        motion: crate::motion::Motion,
190        count: usize,
191    },
192    /// Yank with motion (y + motion)
193    Yank {
194        motion: crate::motion::Motion,
195        count: usize,
196    },
197    /// Change with motion (c + motion)
198    Change {
199        motion: crate::motion::Motion,
200        count: usize,
201    },
202    /// Delete text object (di(, da{, etc.)
203    DeleteTextObject {
204        text_object: crate::textobject::TextObject,
205    },
206    /// Yank text object (yi(, ya{, etc.)
207    YankTextObject {
208        text_object: crate::textobject::TextObject,
209    },
210    /// Change text object (ci(, ca{, etc.)
211    ChangeTextObject {
212        text_object: crate::textobject::TextObject,
213    },
214    /// Delete semantic text object (dif, dac, etc.) - uses treesitter
215    DeleteSemanticTextObject {
216        text_object: crate::textobject::SemanticTextObjectSpec,
217    },
218    /// Yank semantic text object (yif, yac, etc.) - uses treesitter
219    YankSemanticTextObject {
220        text_object: crate::textobject::SemanticTextObjectSpec,
221    },
222    /// Change semantic text object (cif, cac, etc.) - uses treesitter
223    ChangeSemanticTextObject {
224        text_object: crate::textobject::SemanticTextObjectSpec,
225    },
226    /// Delete word text object (diw, daw, diW, daW)
227    DeleteWordTextObject {
228        text_object: crate::textobject::WordTextObject,
229    },
230    /// Yank word text object (yiw, yaw, yiW, yaW)
231    YankWordTextObject {
232        text_object: crate::textobject::WordTextObject,
233    },
234    /// Change word text object (ciw, caw, ciW, caW)
235    ChangeWordTextObject {
236        text_object: crate::textobject::WordTextObject,
237    },
238    /// Change entire line (cc) - clears line content and enters insert mode
239    ChangeLine,
240}
241
242/// Command line mode actions
243#[derive(Debug)]
244pub enum CommandLineAction {
245    /// Insert a character into command line
246    InsertChar(char),
247    /// Delete character (backspace)
248    Backspace,
249    /// Execute the command line
250    Execute,
251    /// Cancel command line mode
252    Cancel,
253}
254
255/// The core command trait - all commands must implement this
256///
257/// Commands are the fundamental unit of editor actions. They receive
258/// an execution context containing the buffer and metadata, and return
259/// a result indicating what action the runtime should take.
260pub trait CommandTrait: Debug + Send + Sync {
261    /// Unique identifier for this command (e.g., `cursor_up`, `enter_insert_mode`)
262    fn name(&self) -> &'static str;
263
264    /// Human-readable description for help/documentation
265    fn description(&self) -> &'static str;
266
267    /// Execute the command on the given buffer
268    ///
269    /// # Arguments
270    /// * `ctx` - Execution context containing buffer and metadata
271    ///
272    /// # Returns
273    /// Result indicating what action the runtime should take
274    fn execute(&self, ctx: &mut ExecutionContext) -> CommandResult;
275
276    /// Clone into a boxed trait object
277    ///
278    /// This is required because `Clone` is not object-safe
279    fn clone_box(&self) -> Box<dyn CommandTrait>;
280
281    /// Downcast support for type inspection
282    fn as_any(&self) -> &dyn Any;
283
284    /// Optional: which modes this command is valid in (None = all modes)
285    fn valid_modes(&self) -> Option<Vec<ModeState>> {
286        None
287    }
288
289    /// Optional: does this command support repeat count?
290    fn supports_count(&self) -> bool {
291        true
292    }
293
294    /// Whether this command is a "jump" that should be recorded in the jump list
295    ///
296    /// Commands like gg, G, search, etc. return true here so the cursor
297    /// position before execution is recorded in the jump list.
298    fn is_jump(&self) -> bool {
299        false
300    }
301
302    /// Whether this command modifies buffer text content
303    ///
304    /// Commands that insert, delete, or modify text return true here.
305    /// This is used to trigger treesitter reparsing after buffer modifications.
306    fn is_text_modifying(&self) -> bool {
307        false
308    }
309}
310
311impl Clone for Box<dyn CommandTrait> {
312    fn clone(&self) -> Self {
313        self.clone_box()
314    }
315}