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}