Skip to main content

reovim_kernel/ipc/events/
kernel.rs

1//! Kernel-level events (mechanism layer).
2//!
3//! Linux equivalent: Events similar to `struct file_operations` callbacks
4//!
5//! These events represent fundamental state changes that occurred in the kernel.
6//! They are pure **notifications** - they tell you what happened, not what to do about it.
7//!
8//! # Design Philosophy
9//!
10//! Following "mechanism, not policy":
11//! - Kernel events = notifications (something happened)
12//! - Request events = policy (stay in modules)
13//!
14//! # Priority Constants
15//!
16//! ```ignore
17//! CRITICAL (0): Lifecycle events (Shutdown)
18//! CORE (10): System state changes (Buffer/Window lifecycle)
19//! NORMAL (50): Content changes (BufferModified, CursorMoved)
20//! PLUGIN (100): Default for plugin handlers
21//! LOW (200): Cleanup/finalization handlers
22//! ```
23//!
24//! # Example
25//!
26//! ```
27//! use reovim_kernel::api::v1::{Event, EventBus, EventResult};
28//! use reovim_kernel::api::v1::events::kernel::{BufferCreated, BufferModified, Modification};
29//!
30//! let bus = EventBus::new();
31//!
32//! // Subscribe to buffer creation
33//! let _sub = bus.subscribe::<BufferCreated, _>(100, |event| {
34//!     println!("Buffer {} created", event.buffer_id);
35//!     EventResult::Handled
36//! });
37//!
38//! // Emit when a buffer is created
39//! bus.emit(BufferCreated { buffer_id: 1 });
40//! ```
41
42use crate::{
43    core::{ModeId, OptionScopeId},
44    ipc::Event,
45};
46
47/// Priority constants for event handlers.
48///
49/// Lower numbers = higher priority (run first).
50pub mod priority {
51    /// Highest priority - for critical handlers (lifecycle events)
52    pub const CRITICAL: u32 = 0;
53    /// High priority - for core system handlers
54    pub const CORE: u32 = 10;
55    /// Normal priority - for standard handlers
56    pub const NORMAL: u32 = 50;
57    /// Default priority for plugins
58    pub const PLUGIN: u32 = 100;
59    /// Low priority - for cleanup/finalization handlers
60    pub const LOW: u32 = 200;
61}
62
63// =============================================================================
64// Buffer Events
65// =============================================================================
66
67/// A buffer was created in the kernel.
68///
69/// Emitted when `mm::Buffer` is allocated and registered.
70/// This is a pure notification - no action is expected from handlers.
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub struct BufferCreated {
73    /// ID of the created buffer
74    pub buffer_id: u64,
75}
76
77impl Event for BufferCreated {}
78
79/// A buffer was closed/destroyed.
80///
81/// Emitted when a buffer is deallocated from the kernel.
82/// Handlers should clean up any state associated with this buffer.
83#[derive(Debug, Clone, Copy, PartialEq, Eq)]
84pub struct BufferClosed {
85    /// ID of the closed buffer
86    pub buffer_id: u64,
87}
88
89impl Event for BufferClosed {}
90
91/// A buffer's content was modified.
92///
93/// Emitted after any change to buffer content (insert, delete, replace).
94/// Handlers can use this to trigger re-parsing, update highlights, etc.
95#[derive(Debug, Clone, PartialEq, Eq)]
96pub struct BufferModified {
97    /// ID of the modified buffer
98    pub buffer_id: u64,
99    /// Type of modification that occurred
100    pub modification: Modification,
101}
102
103impl Event for BufferModified {}
104
105/// Type of buffer modification.
106///
107/// Describes what kind of change was made to the buffer.
108#[derive(Debug, Clone, PartialEq, Eq)]
109pub enum Modification {
110    /// Text was inserted at a position.
111    Insert {
112        /// Start position (line, column) - 0-indexed
113        start: (u32, u32),
114        /// The inserted text
115        text: String,
116        /// Byte offset where the insertion begins (for incremental parsing).
117        start_byte: usize,
118    },
119    /// Text was deleted from a range.
120    Delete {
121        /// Start position (line, column) - 0-indexed
122        start: (u32, u32),
123        /// End position (line, column) - 0-indexed
124        end: (u32, u32),
125        /// The deleted text
126        text: String,
127        /// Byte offset where the deletion begins (for incremental parsing).
128        start_byte: usize,
129    },
130    /// Text was replaced (delete + insert combined).
131    Replace {
132        /// Start position (line, column) - 0-indexed
133        start: (u32, u32),
134        /// End position (line, column) - 0-indexed
135        end: (u32, u32),
136        /// Old text that was replaced
137        old_text: String,
138        /// New text
139        new_text: String,
140        /// Byte offset where the replacement begins (for incremental parsing).
141        start_byte: usize,
142    },
143    /// Entire buffer content was replaced (e.g., file reload).
144    FullReplace,
145}
146
147/// Active buffer changed.
148///
149/// Emitted when the kernel switches focus to a different buffer.
150#[derive(Debug, Clone, Copy, PartialEq, Eq)]
151pub struct BufferSwitched {
152    /// Previous buffer ID (if any)
153    pub from: Option<u64>,
154    /// New active buffer ID
155    pub to: u64,
156}
157
158impl Event for BufferSwitched {}
159
160/// A buffer is about to be saved to disk.
161///
162/// Emitted before buffer content is persisted. Subscribers can modify
163/// the buffer content (e.g., format-on-save) and the write command will
164/// pick up the formatted content. Dispatch is synchronous.
165#[derive(Debug, Clone, PartialEq, Eq)]
166pub struct BufferWillSave {
167    /// ID of the buffer about to be saved
168    pub buffer_id: u64,
169    /// Path the buffer will be saved to
170    pub path: String,
171}
172
173impl Event for BufferWillSave {}
174
175/// A buffer was saved to disk.
176///
177/// Emitted after buffer content is persisted to the filesystem.
178#[derive(Debug, Clone, PartialEq, Eq)]
179pub struct BufferSaved {
180    /// ID of the saved buffer
181    pub buffer_id: u64,
182    /// Path the buffer was saved to
183    pub path: String,
184}
185
186impl Event for BufferSaved {}
187
188// =============================================================================
189// Cursor Events
190// =============================================================================
191
192/// Cursor position changed in a buffer.
193///
194/// Emitted after the cursor moves to a new position.
195/// Note: This is a notification only - it doesn't request movement.
196#[derive(Debug, Clone, Copy, PartialEq, Eq)]
197pub struct CursorMoved {
198    /// Buffer ID where cursor moved
199    pub buffer_id: u64,
200    /// Previous position (line, column) - 0-indexed
201    pub from: (u32, u32),
202    /// New position (line, column) - 0-indexed
203    pub to: (u32, u32),
204}
205
206impl Event for CursorMoved {}
207
208// =============================================================================
209// Mode Events
210// =============================================================================
211
212/// Editor mode changed.
213///
214/// Emitted after a mode transition occurs (e.g., Normal → Insert).
215/// The mode strings are intentionally generic - policy (specific modes)
216/// is defined by the runtime and modules.
217///
218/// # Type-Safe Mode Transitions
219///
220/// When `target_mode` is set, it provides the exact `ModeId` to transition to.
221/// This enables event-driven mode transitions without hardcoding command names
222/// in the runner layer.
223///
224/// # Example
225///
226/// ```ignore
227/// // Commands emit with target_mode for type-safe transitions
228/// ctx.event_bus.emit(ModeChanged::with_mode_id("normal", editor_visual_mode));
229///
230/// // Runner subscribes and updates mode_stack from the event
231/// bus.subscribe::<ModeChanged, _>(priority::CORE, |event| {
232///     if let Some(mode_id) = event.target_mode() {
233///         app.mode_stack.set(mode_id.clone());
234///     }
235///     EventResult::Handled
236/// });
237/// ```
238#[derive(Debug, Clone, PartialEq, Eq)]
239pub struct ModeChanged {
240    /// Previous mode description (for display/logging).
241    pub from: String,
242    /// New mode description (for display/logging).
243    pub to: String,
244    /// Target mode ID for type-safe transitions.
245    ///
246    /// When set, the runner uses this to update the mode stack instead of
247    /// predicting mode transitions based on command names.
248    target_mode: Option<ModeId>,
249}
250
251impl ModeChanged {
252    /// Create a new mode change event with string descriptions only.
253    ///
254    /// Use this for backward compatibility or when the target mode is
255    /// implicit (e.g., commands that emit events for logging only).
256    #[must_use]
257    pub fn new(from: impl Into<String>, to: impl Into<String>) -> Self {
258        Self {
259            from: from.into(),
260            to: to.into(),
261            target_mode: None,
262        }
263    }
264
265    /// Create a mode change event with a specific target mode ID.
266    ///
267    /// The runner subscribes to these events and updates the mode stack
268    /// using the provided `ModeId`, eliminating the need for hardcoded
269    /// command name mappings.
270    #[must_use]
271    pub fn with_mode_id(from: impl Into<String>, target: ModeId) -> Self {
272        let to = target.name().to_string();
273        Self {
274            from: from.into(),
275            to,
276            target_mode: Some(target),
277        }
278    }
279
280    /// Get the target mode ID, if set.
281    ///
282    /// Returns `Some(&ModeId)` when the event was created with `with_mode_id()`,
283    /// `None` for legacy events created with just string descriptions.
284    #[must_use]
285    pub const fn target_mode(&self) -> Option<&ModeId> {
286        self.target_mode.as_ref()
287    }
288
289    /// Check if this event has a type-safe target mode.
290    #[must_use]
291    pub const fn has_target_mode(&self) -> bool {
292        self.target_mode.is_some()
293    }
294}
295
296impl Event for ModeChanged {}
297
298// =============================================================================
299// Window Events
300// =============================================================================
301
302/// A window was created.
303///
304/// Emitted when a new window is added to the layout.
305#[derive(Debug, Clone, Copy, PartialEq, Eq)]
306pub struct WindowCreated {
307    /// ID of the created window
308    pub window_id: u64,
309}
310
311impl Event for WindowCreated {}
312
313/// A window was closed.
314///
315/// Emitted when a window is removed from the layout.
316#[derive(Debug, Clone, Copy, PartialEq, Eq)]
317pub struct WindowClosed {
318    /// ID of the closed window
319    pub window_id: u64,
320}
321
322impl Event for WindowClosed {}
323
324/// Active window changed.
325///
326/// Emitted when focus moves to a different window.
327#[derive(Debug, Clone, Copy, PartialEq, Eq)]
328pub struct WindowFocused {
329    /// Previous window ID (if any)
330    pub from: Option<u64>,
331    /// New active window ID
332    pub to: u64,
333}
334
335impl Event for WindowFocused {}
336
337/// Viewport scrolled within a window.
338///
339/// Emitted when the visible portion of a buffer changes due to scrolling.
340#[derive(Debug, Clone, Copy, PartialEq, Eq)]
341pub struct ViewportScrolled {
342    /// ID of the window that scrolled
343    pub window_id: u64,
344    /// ID of the buffer being viewed
345    pub buffer_id: u64,
346    /// First visible line (0-indexed)
347    pub top_line: u32,
348    /// Last visible line (0-indexed)
349    pub bottom_line: u32,
350}
351
352impl Event for ViewportScrolled {}
353
354// =============================================================================
355// Layout Events
356// =============================================================================
357
358/// Direction of a window split.
359#[derive(Debug, Clone, Copy, PartialEq, Eq)]
360pub enum SplitDirection {
361    /// Horizontal split (windows stacked top/bottom).
362    Horizontal,
363    /// Vertical split (windows side by side).
364    Vertical,
365}
366
367/// Type of layout change that occurred.
368#[derive(Debug, Clone, PartialEq, Eq)]
369pub enum LayoutChangeKind {
370    /// Window was split.
371    Split {
372        /// ID of the new window created by the split.
373        new_window: u64,
374        /// Direction of the split.
375        direction: SplitDirection,
376    },
377    /// Window was closed.
378    Close {
379        /// ID of the closed window.
380        closed_window: u64,
381        /// ID of the window that received focus (if any).
382        new_focus: Option<u64>,
383    },
384    /// Focus changed to a different window.
385    Focus {
386        /// Previous focused window (if any).
387        from: Option<u64>,
388        /// New focused window.
389        to: u64,
390    },
391    /// Window was resized.
392    Resize {
393        /// ID of the resized window.
394        window: u64,
395    },
396    /// All windows were equalized in size.
397    Equalize,
398}
399
400/// Layout changed event.
401///
402/// Emitted after any layout operation (split, close, focus, resize, equalize).
403/// This is the primary event for notifying clients of layout changes.
404///
405/// The runner subscribes to this event and converts it to an RPC notification
406/// for connected clients.
407///
408/// # Example
409///
410/// ```ignore
411/// use reovim_kernel::api::v1::{EventBus, EventResult, events::kernel::LayoutChanged};
412///
413/// let bus = EventBus::new();
414/// let _sub = bus.subscribe::<LayoutChanged, _>(100, |event| {
415///     println!("Layout changed: {:?}", event.kind);
416///     EventResult::Handled
417/// });
418/// ```
419#[derive(Debug, Clone)]
420pub struct LayoutChanged {
421    /// Type of layout change that occurred.
422    pub kind: LayoutChangeKind,
423    /// Total window count after the change.
424    pub window_count: usize,
425    /// Currently focused window (if any).
426    pub focused_window: Option<u64>,
427}
428
429impl Event for LayoutChanged {}
430
431// =============================================================================
432// File Events
433// =============================================================================
434
435/// A file was opened into a buffer.
436///
437/// Emitted after file content is loaded into a buffer.
438#[derive(Debug, Clone, PartialEq, Eq)]
439pub struct FileOpened {
440    /// Buffer ID the file was loaded into
441    pub buffer_id: u64,
442    /// Path of the opened file
443    pub path: String,
444}
445
446impl Event for FileOpened {}
447
448/// File type was detected or changed.
449///
450/// Emitted when the file type (language) is determined or updated.
451#[derive(Debug, Clone, PartialEq, Eq)]
452pub struct FileTypeChanged {
453    /// Buffer ID
454    pub buffer_id: u64,
455    /// Detected file type (e.g., "rust", "python", "markdown")
456    pub file_type: String,
457}
458
459impl Event for FileTypeChanged {}
460
461// =============================================================================
462// Option Events
463// =============================================================================
464
465/// Source of an option value change.
466///
467/// Indicates how an option was modified, useful for handlers
468/// that need to distinguish user commands from programmatic changes.
469#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
470pub enum ChangeSource {
471    /// Changed via `:set` command by the user.
472    #[default]
473    UserCommand,
474    /// Changed programmatically by a plugin/module.
475    Plugin,
476    /// Loaded from configuration file.
477    Config,
478    /// Set to default during initialization.
479    Default,
480    /// Changed via settings menu/UI.
481    SettingsMenu,
482    /// Changed via RPC (server mode).
483    Rpc,
484}
485
486/// An option value changed.
487///
488/// Emitted after any option value is modified via `OptionRegistry::set()`.
489/// Handlers can react to option changes (e.g., re-render, update state).
490///
491/// # Example
492///
493/// ```ignore
494/// use reovim_kernel::api::v1::{EventBus, EventResult, events::kernel::OptionChanged};
495///
496/// let bus = EventBus::new();
497/// bus.subscribe::<OptionChanged, _>(100, |event| {
498///     println!("Option '{}' changed from {} to {}",
499///         event.name, event.old_value, event.new_value);
500///     EventResult::Handled
501/// });
502/// ```
503#[derive(Debug, Clone, PartialEq, Eq)]
504pub struct OptionChanged {
505    /// Full option name that changed.
506    pub name: String,
507    /// Previous value.
508    pub old_value: String,
509    /// New value.
510    pub new_value: String,
511    /// Source of the change.
512    pub source: ChangeSource,
513    /// Runtime scope where the change was applied (which specific buffer or window).
514    ///
515    /// This is the runtime context, NOT the option's static scope declaration
516    /// (see `OptionSpec::scope` for the capability declaration).
517    pub scope: OptionScopeId,
518}
519
520impl Event for OptionChanged {}
521
522/// An option was reset to its default value.
523///
524/// Emitted when an option is reset via `:set option&` or `OptionRegistry::reset()`.
525#[derive(Debug, Clone, PartialEq, Eq)]
526pub struct OptionReset {
527    /// Option name that was reset.
528    pub name: String,
529    /// Previous value (before reset).
530    pub old_value: String,
531    /// Default value (after reset).
532    pub default_value: String,
533    /// Runtime scope where the reset was applied (which specific buffer or window).
534    ///
535    /// This is the runtime context, NOT the option's static scope declaration
536    /// (see `OptionSpec::scope` for the capability declaration).
537    pub scope: OptionScopeId,
538}
539
540impl Event for OptionReset {}
541
542// =============================================================================
543// Lifecycle Events
544// =============================================================================
545
546/// Kernel is shutting down.
547///
548/// Emitted before the kernel terminates. Handlers should perform cleanup.
549/// This is the last event handlers will receive before termination.
550#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
551pub struct Shutdown;
552
553impl Event for Shutdown {}