reovim-kernel 0.14.4

Core kernel mechanisms for reovim (Linux kernel/ equivalent)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
//! Kernel-level events (mechanism layer).
//!
//! Linux equivalent: Events similar to `struct file_operations` callbacks
//!
//! These events represent fundamental state changes that occurred in the kernel.
//! They are pure **notifications** - they tell you what happened, not what to do about it.
//!
//! # Design Philosophy
//!
//! Following "mechanism, not policy":
//! - Kernel events = notifications (something happened)
//! - Request events = policy (stay in modules)
//!
//! # Priority Constants
//!
//! ```ignore
//! CRITICAL (0): Lifecycle events (Shutdown)
//! CORE (10): System state changes (Buffer/Window lifecycle)
//! NORMAL (50): Content changes (BufferModified, CursorMoved)
//! PLUGIN (100): Default for plugin handlers
//! LOW (200): Cleanup/finalization handlers
//! ```
//!
//! # Example
//!
//! ```
//! use reovim_kernel::api::v1::{Event, EventBus, EventResult};
//! use reovim_kernel::api::v1::events::kernel::{BufferCreated, BufferModified, Modification};
//!
//! let bus = EventBus::new();
//!
//! // Subscribe to buffer creation
//! let _sub = bus.subscribe::<BufferCreated, _>(100, |event| {
//!     println!("Buffer {} created", event.buffer_id);
//!     EventResult::Handled
//! });
//!
//! // Emit when a buffer is created
//! bus.emit(BufferCreated { buffer_id: 1 });
//! ```

use crate::{
    core::{ModeId, OptionScopeId},
    ipc::Event,
};

/// Priority constants for event handlers.
///
/// Lower numbers = higher priority (run first).
pub mod priority {
    /// Highest priority - for critical handlers (lifecycle events)
    pub const CRITICAL: u32 = 0;
    /// High priority - for core system handlers
    pub const CORE: u32 = 10;
    /// Normal priority - for standard handlers
    pub const NORMAL: u32 = 50;
    /// Default priority for plugins
    pub const PLUGIN: u32 = 100;
    /// Low priority - for cleanup/finalization handlers
    pub const LOW: u32 = 200;
}

// =============================================================================
// Buffer Events
// =============================================================================

/// A buffer was created in the kernel.
///
/// Emitted when `mm::Buffer` is allocated and registered.
/// This is a pure notification - no action is expected from handlers.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BufferCreated {
    /// ID of the created buffer
    pub buffer_id: u64,
}

impl Event for BufferCreated {}

/// A buffer was closed/destroyed.
///
/// Emitted when a buffer is deallocated from the kernel.
/// Handlers should clean up any state associated with this buffer.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BufferClosed {
    /// ID of the closed buffer
    pub buffer_id: u64,
}

impl Event for BufferClosed {}

/// A buffer's content was modified.
///
/// Emitted after any change to buffer content (insert, delete, replace).
/// Handlers can use this to trigger re-parsing, update highlights, etc.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BufferModified {
    /// ID of the modified buffer
    pub buffer_id: u64,
    /// Type of modification that occurred
    pub modification: Modification,
}

impl Event for BufferModified {}

/// Type of buffer modification.
///
/// Describes what kind of change was made to the buffer.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Modification {
    /// Text was inserted at a position.
    Insert {
        /// Start position (line, column) - 0-indexed
        start: (u32, u32),
        /// The inserted text
        text: String,
        /// Byte offset where the insertion begins (for incremental parsing).
        start_byte: usize,
    },
    /// Text was deleted from a range.
    Delete {
        /// Start position (line, column) - 0-indexed
        start: (u32, u32),
        /// End position (line, column) - 0-indexed
        end: (u32, u32),
        /// The deleted text
        text: String,
        /// Byte offset where the deletion begins (for incremental parsing).
        start_byte: usize,
    },
    /// Text was replaced (delete + insert combined).
    Replace {
        /// Start position (line, column) - 0-indexed
        start: (u32, u32),
        /// End position (line, column) - 0-indexed
        end: (u32, u32),
        /// Old text that was replaced
        old_text: String,
        /// New text
        new_text: String,
        /// Byte offset where the replacement begins (for incremental parsing).
        start_byte: usize,
    },
    /// Entire buffer content was replaced (e.g., file reload).
    FullReplace,
}

/// Active buffer changed.
///
/// Emitted when the kernel switches focus to a different buffer.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BufferSwitched {
    /// Previous buffer ID (if any)
    pub from: Option<u64>,
    /// New active buffer ID
    pub to: u64,
}

impl Event for BufferSwitched {}

/// A buffer is about to be saved to disk.
///
/// Emitted before buffer content is persisted. Subscribers can modify
/// the buffer content (e.g., format-on-save) and the write command will
/// pick up the formatted content. Dispatch is synchronous.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BufferWillSave {
    /// ID of the buffer about to be saved
    pub buffer_id: u64,
    /// Path the buffer will be saved to
    pub path: String,
}

impl Event for BufferWillSave {}

/// A buffer was saved to disk.
///
/// Emitted after buffer content is persisted to the filesystem.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BufferSaved {
    /// ID of the saved buffer
    pub buffer_id: u64,
    /// Path the buffer was saved to
    pub path: String,
}

impl Event for BufferSaved {}

// =============================================================================
// Cursor Events
// =============================================================================

/// Cursor position changed in a buffer.
///
/// Emitted after the cursor moves to a new position.
/// Note: This is a notification only - it doesn't request movement.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CursorMoved {
    /// Buffer ID where cursor moved
    pub buffer_id: u64,
    /// Previous position (line, column) - 0-indexed
    pub from: (u32, u32),
    /// New position (line, column) - 0-indexed
    pub to: (u32, u32),
}

impl Event for CursorMoved {}

// =============================================================================
// Mode Events
// =============================================================================

/// Editor mode changed.
///
/// Emitted after a mode transition occurs (e.g., Normal → Insert).
/// The mode strings are intentionally generic - policy (specific modes)
/// is defined by the runtime and modules.
///
/// # Type-Safe Mode Transitions
///
/// When `target_mode` is set, it provides the exact `ModeId` to transition to.
/// This enables event-driven mode transitions without hardcoding command names
/// in the runner layer.
///
/// # Example
///
/// ```ignore
/// // Commands emit with target_mode for type-safe transitions
/// ctx.event_bus.emit(ModeChanged::with_mode_id("normal", editor_visual_mode));
///
/// // Runner subscribes and updates mode_stack from the event
/// bus.subscribe::<ModeChanged, _>(priority::CORE, |event| {
///     if let Some(mode_id) = event.target_mode() {
///         app.mode_stack.set(mode_id.clone());
///     }
///     EventResult::Handled
/// });
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ModeChanged {
    /// Previous mode description (for display/logging).
    pub from: String,
    /// New mode description (for display/logging).
    pub to: String,
    /// Target mode ID for type-safe transitions.
    ///
    /// When set, the runner uses this to update the mode stack instead of
    /// predicting mode transitions based on command names.
    target_mode: Option<ModeId>,
}

impl ModeChanged {
    /// Create a new mode change event with string descriptions only.
    ///
    /// Use this for backward compatibility or when the target mode is
    /// implicit (e.g., commands that emit events for logging only).
    #[must_use]
    pub fn new(from: impl Into<String>, to: impl Into<String>) -> Self {
        Self {
            from: from.into(),
            to: to.into(),
            target_mode: None,
        }
    }

    /// Create a mode change event with a specific target mode ID.
    ///
    /// The runner subscribes to these events and updates the mode stack
    /// using the provided `ModeId`, eliminating the need for hardcoded
    /// command name mappings.
    #[must_use]
    pub fn with_mode_id(from: impl Into<String>, target: ModeId) -> Self {
        let to = target.name().to_string();
        Self {
            from: from.into(),
            to,
            target_mode: Some(target),
        }
    }

    /// Get the target mode ID, if set.
    ///
    /// Returns `Some(&ModeId)` when the event was created with `with_mode_id()`,
    /// `None` for legacy events created with just string descriptions.
    #[must_use]
    pub const fn target_mode(&self) -> Option<&ModeId> {
        self.target_mode.as_ref()
    }

    /// Check if this event has a type-safe target mode.
    #[must_use]
    pub const fn has_target_mode(&self) -> bool {
        self.target_mode.is_some()
    }
}

impl Event for ModeChanged {}

// =============================================================================
// Window Events
// =============================================================================

/// A window was created.
///
/// Emitted when a new window is added to the layout.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WindowCreated {
    /// ID of the created window
    pub window_id: u64,
}

impl Event for WindowCreated {}

/// A window was closed.
///
/// Emitted when a window is removed from the layout.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WindowClosed {
    /// ID of the closed window
    pub window_id: u64,
}

impl Event for WindowClosed {}

/// Active window changed.
///
/// Emitted when focus moves to a different window.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WindowFocused {
    /// Previous window ID (if any)
    pub from: Option<u64>,
    /// New active window ID
    pub to: u64,
}

impl Event for WindowFocused {}

/// Viewport scrolled within a window.
///
/// Emitted when the visible portion of a buffer changes due to scrolling.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ViewportScrolled {
    /// ID of the window that scrolled
    pub window_id: u64,
    /// ID of the buffer being viewed
    pub buffer_id: u64,
    /// First visible line (0-indexed)
    pub top_line: u32,
    /// Last visible line (0-indexed)
    pub bottom_line: u32,
}

impl Event for ViewportScrolled {}

// =============================================================================
// Layout Events
// =============================================================================

/// Direction of a window split.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SplitDirection {
    /// Horizontal split (windows stacked top/bottom).
    Horizontal,
    /// Vertical split (windows side by side).
    Vertical,
}

/// Type of layout change that occurred.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LayoutChangeKind {
    /// Window was split.
    Split {
        /// ID of the new window created by the split.
        new_window: u64,
        /// Direction of the split.
        direction: SplitDirection,
    },
    /// Window was closed.
    Close {
        /// ID of the closed window.
        closed_window: u64,
        /// ID of the window that received focus (if any).
        new_focus: Option<u64>,
    },
    /// Focus changed to a different window.
    Focus {
        /// Previous focused window (if any).
        from: Option<u64>,
        /// New focused window.
        to: u64,
    },
    /// Window was resized.
    Resize {
        /// ID of the resized window.
        window: u64,
    },
    /// All windows were equalized in size.
    Equalize,
}

/// Layout changed event.
///
/// Emitted after any layout operation (split, close, focus, resize, equalize).
/// This is the primary event for notifying clients of layout changes.
///
/// The runner subscribes to this event and converts it to an RPC notification
/// for connected clients.
///
/// # Example
///
/// ```ignore
/// use reovim_kernel::api::v1::{EventBus, EventResult, events::kernel::LayoutChanged};
///
/// let bus = EventBus::new();
/// let _sub = bus.subscribe::<LayoutChanged, _>(100, |event| {
///     println!("Layout changed: {:?}", event.kind);
///     EventResult::Handled
/// });
/// ```
#[derive(Debug, Clone)]
pub struct LayoutChanged {
    /// Type of layout change that occurred.
    pub kind: LayoutChangeKind,
    /// Total window count after the change.
    pub window_count: usize,
    /// Currently focused window (if any).
    pub focused_window: Option<u64>,
}

impl Event for LayoutChanged {}

// =============================================================================
// File Events
// =============================================================================

/// A file was opened into a buffer.
///
/// Emitted after file content is loaded into a buffer.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FileOpened {
    /// Buffer ID the file was loaded into
    pub buffer_id: u64,
    /// Path of the opened file
    pub path: String,
}

impl Event for FileOpened {}

/// File type was detected or changed.
///
/// Emitted when the file type (language) is determined or updated.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FileTypeChanged {
    /// Buffer ID
    pub buffer_id: u64,
    /// Detected file type (e.g., "rust", "python", "markdown")
    pub file_type: String,
}

impl Event for FileTypeChanged {}

// =============================================================================
// Option Events
// =============================================================================

/// Source of an option value change.
///
/// Indicates how an option was modified, useful for handlers
/// that need to distinguish user commands from programmatic changes.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ChangeSource {
    /// Changed via `:set` command by the user.
    #[default]
    UserCommand,
    /// Changed programmatically by a plugin/module.
    Plugin,
    /// Loaded from configuration file.
    Config,
    /// Set to default during initialization.
    Default,
    /// Changed via settings menu/UI.
    SettingsMenu,
    /// Changed via RPC (server mode).
    Rpc,
}

/// An option value changed.
///
/// Emitted after any option value is modified via `OptionRegistry::set()`.
/// Handlers can react to option changes (e.g., re-render, update state).
///
/// # Example
///
/// ```ignore
/// use reovim_kernel::api::v1::{EventBus, EventResult, events::kernel::OptionChanged};
///
/// let bus = EventBus::new();
/// bus.subscribe::<OptionChanged, _>(100, |event| {
///     println!("Option '{}' changed from {} to {}",
///         event.name, event.old_value, event.new_value);
///     EventResult::Handled
/// });
/// ```
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OptionChanged {
    /// Full option name that changed.
    pub name: String,
    /// Previous value.
    pub old_value: String,
    /// New value.
    pub new_value: String,
    /// Source of the change.
    pub source: ChangeSource,
    /// Runtime scope where the change was applied (which specific buffer or window).
    ///
    /// This is the runtime context, NOT the option's static scope declaration
    /// (see `OptionSpec::scope` for the capability declaration).
    pub scope: OptionScopeId,
}

impl Event for OptionChanged {}

/// An option was reset to its default value.
///
/// Emitted when an option is reset via `:set option&` or `OptionRegistry::reset()`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OptionReset {
    /// Option name that was reset.
    pub name: String,
    /// Previous value (before reset).
    pub old_value: String,
    /// Default value (after reset).
    pub default_value: String,
    /// Runtime scope where the reset was applied (which specific buffer or window).
    ///
    /// This is the runtime context, NOT the option's static scope declaration
    /// (see `OptionSpec::scope` for the capability declaration).
    pub scope: OptionScopeId,
}

impl Event for OptionReset {}

// =============================================================================
// Lifecycle Events
// =============================================================================

/// Kernel is shutting down.
///
/// Emitted before the kernel terminates. Handlers should perform cleanup.
/// This is the last event handlers will receive before termination.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Shutdown;

impl Event for Shutdown {}