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
//! Module identity, traits, and registration types.
//!
//! Linux equivalent: `include/linux/module.h`
//!
//! This module provides the core module mechanism for the kernel. Modules are
//! loadable components that can extend the editor's functionality. The kernel
//! defines the `Module` trait (mechanism); the runner implements loading (policy).
//!
//! # Linux Kernel Patterns
//!
//! The design follows Linux kernel patterns:
//! - `Module` trait ≈ `struct module` + `module_init`/`module_exit`
//! - `ProbeResult` ≈ `probe()` return with `-EPROBE_DEFER` support
//! - `RegistrationFlags` ≈ module flags and capabilities
//! - Registration types ≈ `struct platform_driver`, `struct notifier_block`

mod error;
mod flags;
mod id;
mod probe;
mod registration;
mod state;

pub use {
    error::{ModuleError, ProbeResult},
    flags::RegistrationFlags,
    id::ModuleId,
    probe::ModuleProbe,
    registration::{CommandRegistration, EventHandlerRegistration, KeybindingRegistration},
    state::{ModuleInfo, ModuleState},
};

use super::{
    context::ModuleContext,
    version::{API_VERSION, Version},
};

// Import BufferId for on_buffer_focus hook
use crate::mm::BufferId;

/// Trait for all loadable modules.
///
/// Linux equivalent: `struct module` with `module_init`/`module_exit`.
///
/// Modules encapsulate functionality that can be loaded dynamically at runtime.
/// The kernel defines this trait (mechanism); the runner implements loading (policy).
///
/// # Lifecycle
///
/// 1. Module is loaded (by runner's module loader)
/// 2. `init()` is called with `ModuleContext`
///    - Returns `ProbeResult::Success` → module is ready
///    - Returns `ProbeResult::Defer` → retry later
///    - Returns `ProbeResult::Failed` → permanent failure
/// 3. Module is now `Running`
/// 4. `exit()` is called before unload
///
/// # Thread Safety
///
/// Modules must be `Send + Sync` as they may be accessed from multiple threads.
/// The threading model is:
///
/// - **Exclusive access methods** (`&mut self`): `init()` and `exit()` are guaranteed
///   to be called with exclusive access. The runner/kernel ensures no concurrent calls.
/// - **Shared access methods** (`&self`): `id()`, `name()`, `version()`, `commands()`,
///   `keybindings()`, `event_handlers()`, etc. may be called concurrently from multiple
///   threads. Implementations should return const data or use internal synchronization.
/// - **Hot reload methods**: `save_state()` (`&self`) and `restore_state()` (`&mut self`)
///   follow the same exclusive/shared access patterns.
///
/// # Example
///
/// ```ignore
/// use reovim_kernel::api::v1::*;
///
/// pub struct MyModule;
///
/// impl Module for MyModule {
///     fn id(&self) -> ModuleId { ModuleId::new("my-module") }
///     fn name(&self) -> &'static str { "My Module" }
///     fn version(&self) -> Version { Version::new(1, 0, 0) }
///
///     fn init(&mut self, _ctx: &ModuleContext) -> ProbeResult {
///         pr_info!("MyModule initialized");
///         ProbeResult::Success
///     }
///
///     fn exit(&mut self) -> Result<(), ModuleError> {
///         pr_info!("MyModule exiting");
///         Ok(())
///     }
/// }
/// ```
pub trait Module: Send + Sync + 'static {
    // ========================================================================
    // Identity
    // ========================================================================

    /// Unique module identifier.
    ///
    /// Convention: kebab-case, e.g., "lang-rust", "feat-completion"
    fn id(&self) -> ModuleId;

    /// Human-readable name.
    fn name(&self) -> &'static str;

    /// Module version.
    fn version(&self) -> Version;

    /// Required kernel API version.
    ///
    /// Defaults to current API version. Override to require specific version.
    fn api_version(&self) -> Version {
        API_VERSION
    }

    // ========================================================================
    // Dependencies
    // ========================================================================

    /// Required dependencies (must be loaded before this module).
    ///
    /// The runner will ensure all required dependencies are loaded and initialized
    /// before calling `init()` on this module. If any dependency fails to load,
    /// this module will not be initialized.
    fn dependencies(&self) -> Vec<ModuleId> {
        Vec::new()
    }

    /// Optional dependencies (load before if available, but not required).
    ///
    /// # Semantics
    ///
    /// - Optional dependencies are loaded before this module **if they exist**
    /// - If an optional dependency fails to load, this module can still initialize
    /// - Unlike `dependencies()`, missing optional dependencies don't cause init failure
    /// - Useful for feature detection and graceful degradation
    ///
    /// # Use Cases
    ///
    /// - LSP enhancement: Load LSP plugin if available for better completions
    /// - Syntax integration: Use treesitter if available, fall back to regex
    /// - Theme support: Load icon theme if available
    ///
    /// # Example
    ///
    /// ```ignore
    /// fn optional_dependencies(&self) -> Vec<ModuleId> {
    ///     vec![
    ///         ModuleId::new("feat-lsp"),       // Enhance with LSP if available
    ///         ModuleId::new("feat-treesitter"), // Better syntax if available
    ///     ]
    /// }
    /// ```
    fn optional_dependencies(&self) -> Vec<ModuleId> {
        Vec::new()
    }

    /// Extension kinds pushed by this module via `ExtensionStateBridge` (#584).
    ///
    /// Returns the extension kind identifiers that this module registers
    /// bridges for. Used by server startup validation to detect orphaned
    /// bridges (bridge registered but no module claims to push that kind).
    ///
    /// Modules that register bridges in `init()` should override this to
    /// declare the kinds they push. Define kind constants locally in each
    /// module crate.
    ///
    /// # Kernel Purity
    ///
    /// The kernel defines this with `&[&'static str]` return type. Modules
    /// define their own kind constants locally — no shared crate needed.
    fn extension_kinds(&self) -> &[&'static str] {
        &[]
    }

    /// Capabilities provided by this module (#618).
    ///
    /// Returns capability identifiers that this module provides (e.g.,
    /// `"syntax-highlighting"`, `"undo-provider"`). Used for abstract
    /// dependency matching: a module that `requires` a capability is
    /// satisfied by any module that `provides` it.
    ///
    /// Use constants from `reovim-capabilities`.
    ///
    /// # Kernel Purity
    ///
    /// Same as `extension_kinds()`: the kernel defines the method with
    /// `&[&'static str]` return type. Capability constants live in
    /// `shared/capabilities/`, not in the kernel.
    fn provides(&self) -> &[&'static str] {
        &[]
    }

    /// Capabilities required by this module (#618).
    ///
    /// Returns capability identifiers that this module requires (e.g.,
    /// `"lsp-provider"`). During dependency resolution, the depgraph
    /// resolver matches these against `provides()` from other modules
    /// and adds implicit ordering edges.
    ///
    /// If no module provides a required capability, the module may fail
    /// to initialize or operate in degraded mode.
    ///
    /// Use constants from `reovim-capabilities`.
    fn requires(&self) -> &[&'static str] {
        &[]
    }

    /// Version constraints on dependencies (#619).
    ///
    /// Returns `(module_id, version_range_string)` pairs declaring minimum
    /// version requirements on specific dependencies. The version range
    /// follows Cargo semver syntax:
    ///
    /// - `"^1.2.3"` — compatible (same major, `>=1.2.3, <2.0.0`)
    /// - `"=1.2.3"` — exact match only
    /// - `">=1.2.3"` — at least this version
    /// - `"1.2.3"` — shorthand for `"^1.2.3"`
    ///
    /// Constraints are checked after dependency resolution but before init.
    /// Violations are logged and the constrained module may be skipped.
    ///
    /// # Kernel Purity
    ///
    /// The kernel carries opaque `(ModuleId, &'static str)` pairs.
    /// The depgraph driver parses and evaluates the version range strings.
    fn version_constraints(&self) -> Vec<(ModuleId, &'static str)> {
        Vec::new()
    }

    // ========================================================================
    // Lifecycle (Linux: module_init / module_exit)
    // ========================================================================

    /// Initialize module (Linux equivalent: `probe()` function).
    ///
    /// Returns `ProbeResult` to support deferred probing:
    /// - `Success` - Module is ready
    /// - `Defer(reason)` - Retry later (like Linux `-EPROBE_DEFER`)
    /// - `Failed(err)` - Permanent failure
    fn init(&mut self, ctx: &ModuleContext) -> ProbeResult;

    /// Cleanup module (Linux equivalent: `remove()` function).
    ///
    /// # Errors
    ///
    /// Returns `ModuleError` if cleanup fails.
    fn exit(&mut self) -> Result<(), ModuleError>;

    // ========================================================================
    // Lifecycle Hooks (Epic #417 Part 2)
    // ========================================================================

    /// Called after ALL modules are loaded (post-init phase).
    ///
    /// Use for cross-module discovery via `ServiceRegistry`. At this point,
    /// all modules have completed their `init()` and registered their services.
    ///
    /// # Use Cases
    ///
    /// - Query services registered by other modules
    /// - Set up inter-module communication channels
    /// - Perform late initialization that depends on other modules
    ///
    /// # Example
    ///
    /// ```ignore
    /// fn on_all_loaded(&mut self, ctx: &ModuleContext) {
    ///     // Now safe to query services from other modules
    ///     if let Some(lsp) = ctx.services.get::<dyn LspProvider>() {
    ///         self.lsp_client = Some(lsp);
    ///     }
    /// }
    /// ```
    fn on_all_loaded(&mut self, _ctx: &ModuleContext) {
        // Default: no-op
    }

    /// Called when session focuses on a buffer.
    ///
    /// Use for lazy initialization of buffer-specific state. This hook is
    /// called whenever the active buffer changes, allowing modules to:
    ///
    /// - Load buffer-specific configuration
    /// - Initialize syntax highlighting
    /// - Set up LSP connections for the buffer's language
    ///
    /// # Arguments
    ///
    /// * `buffer_id` - The newly focused buffer
    /// * `ctx` - Module context for accessing kernel services
    ///
    /// # Example
    ///
    /// ```ignore
    /// fn on_buffer_focus(&mut self, buffer_id: BufferId, ctx: &ModuleContext) {
    ///     // Load undo history for this buffer
    ///     if let Some(undo_provider) = ctx.services.get::<dyn UndoProvider>() {
    ///         undo_provider.load_graceful(buffer_id, &self.buffer_path);
    ///     }
    /// }
    /// ```
    fn on_buffer_focus(&mut self, _buffer_id: BufferId, _ctx: &ModuleContext) {
        // Default: no-op
    }

    /// Called before module unload for cleanup.
    ///
    /// Unlike `exit()`, this hook is specifically for releasing resources
    /// registered with the kernel (services, event handlers, etc.).
    ///
    /// # Difference from `exit()`
    ///
    /// - `exit()`: General cleanup, may fail
    /// - `on_unload()`: Release kernel resources, should not fail
    ///
    /// # Example
    ///
    /// ```ignore
    /// fn on_unload(&mut self) -> Result<(), ModuleError> {
    ///     // Unregister services
    ///     if let Some(registry) = self.service_registry.take() {
    ///         registry.unregister_all();
    ///     }
    ///     Ok(())
    /// }
    /// ```
    ///
    /// # Errors
    ///
    /// Returns `ModuleError` if resource release fails.
    fn on_unload(&mut self) -> Result<(), ModuleError> {
        // Default: no-op
        Ok(())
    }

    // ========================================================================
    // Registration (method-based per Clean Arch Proposal)
    // ========================================================================

    /// Get command registrations.
    fn commands(&self) -> Vec<CommandRegistration> {
        Vec::new()
    }

    /// Get keybinding registrations.
    fn keybindings(&self) -> Vec<KeybindingRegistration> {
        Vec::new()
    }

    /// Get event handler registrations.
    fn event_handlers(&self) -> Vec<EventHandlerRegistration> {
        Vec::new()
    }

    // ========================================================================
    // Hot Reload Support
    // ========================================================================

    /// Whether this module supports hot reload.
    ///
    /// If true, `save_state()` and `restore_state()` should be implemented.
    /// Hot reload allows updating module code without restarting the editor.
    fn supports_hot_reload(&self) -> bool {
        false
    }

    /// Save module state for hot reload.
    ///
    /// Returns opaque bytes that `restore_state()` can use to restore state.
    /// Return `None` if no state to save.
    ///
    /// # Serialization Format
    ///
    /// The binary format is module-specific. Recommended practices:
    ///
    /// - **Include a version header** for forward compatibility (e.g., first 4 bytes)
    /// - **Use a stable serialization format** like `bincode`, `postcard`, or `rmp-serde`
    /// - **Keep state minimal** - only save what's necessary to restore user experience
    /// - **Handle missing fields gracefully** in `restore_state()`
    ///
    /// # Example
    ///
    /// ```ignore
    /// fn save_state(&self) -> Option<Box<[u8]>> {
    ///     let state = MyModuleState {
    ///         version: 1,
    ///         cursor_history: self.cursor_history.clone(),
    ///         settings: self.settings.clone(),
    ///     };
    ///     bincode::serialize(&state).ok().map(Vec::into_boxed_slice)
    /// }
    /// ```
    fn save_state(&self) -> Option<Box<[u8]>> {
        None
    }

    /// Restore module state after hot reload.
    ///
    /// Called with bytes from previous `save_state()` call.
    ///
    /// # State Version Compatibility
    ///
    /// Modules should handle version mismatches gracefully:
    /// - If the saved state version is **newer** than current, return an error
    /// - If the saved state version is **older**, migrate or use defaults
    /// - If deserialization fails, return an error (module will re-initialize)
    ///
    /// # Errors
    ///
    /// Returns `ModuleError::InitFailed` if:
    /// - Hot reload is not supported
    /// - State version is incompatible
    /// - Deserialization fails
    ///
    /// # Example
    ///
    /// ```ignore
    /// fn restore_state(&mut self, state: &[u8]) -> Result<(), ModuleError> {
    ///     let saved: MyModuleState = bincode::deserialize(state)
    ///         .map_err(|e| ModuleError::InitFailed(format!("deserialize: {e}")))?;
    ///
    ///     if saved.version > CURRENT_VERSION {
    ///         return Err(ModuleError::InitFailed("state version too new".into()));
    ///     }
    ///
    ///     self.cursor_history = saved.cursor_history;
    ///     self.settings = saved.settings;
    ///     Ok(())
    /// }
    /// ```
    fn restore_state(&mut self, _state: &[u8]) -> Result<(), ModuleError> {
        Err(ModuleError::InitFailed("hot reload not supported".into()))
    }
}

#[cfg(test)]
#[path = "mod_tests.rs"]
mod tests;