Skip to main content

reovim_kernel/api/
context.rs

1//! Kernel context for drivers and modules.
2//!
3//! Provides a unified context struct bundling kernel services for easy access.
4
5use std::{fmt, path::PathBuf, sync::Arc};
6
7use reovim_arch::sync::RwLock;
8
9use crate::{
10    core::{MarkBank, MotionEngine, OptionRegistry, TextObjectEngine},
11    ipc::EventBus,
12};
13
14use super::{buffer_manager::BufferManager, module::ModuleId, service::ServiceRegistry};
15
16// ============================================================================
17// KernelContext
18// ============================================================================
19
20/// Kernel context bundling all kernel services.
21///
22/// This struct provides drivers and modules with access to kernel services.
23/// All fields are `Arc`-wrapped for cheap cloning and safe concurrent access.
24///
25/// # Example
26///
27/// ```ignore
28/// use reovim_kernel::api::v1::KernelContext;
29///
30/// fn setup_driver(ctx: KernelContext) {
31///     // Access event bus
32///     let _bus = ctx.event_bus.clone();
33///
34///     // Access buffer manager
35///     let count = ctx.buffers.count();
36///
37///     // Context is cheap to clone
38///     let ctx2 = ctx.clone();
39/// }
40/// ```
41#[derive(Clone)]
42pub struct KernelContext {
43    /// Event bus for publish/subscribe communication.
44    pub event_bus: Arc<EventBus>,
45    /// Buffer manager for buffer storage and retrieval.
46    pub buffers: Arc<dyn BufferManager>,
47    /// Motion calculation engine.
48    pub motion: Arc<MotionEngine>,
49    /// Text object calculation engine.
50    pub text_objects: Arc<TextObjectEngine>,
51    /// Global mark storage (A-Z, shared special marks) (#515).
52    ///
53    /// Per-client local marks (a-z) are stored in `EditingState.local_marks`.
54    pub global_marks: Arc<RwLock<MarkBank>>,
55    /// Option registry for editor settings.
56    pub options: Arc<OptionRegistry>,
57    /// Service registry for cross-module service discovery.
58    ///
59    /// Provides access to driver and module services like `ClipboardProvider`.
60    /// This is the same registry used in `ModuleContext.services`.
61    pub services: Arc<ServiceRegistry>,
62}
63
64impl KernelContext {
65    /// Create a new kernel context.
66    #[allow(clippy::too_many_arguments)]
67    pub fn new(
68        event_bus: Arc<EventBus>,
69        buffers: Arc<dyn BufferManager>,
70        motion: Arc<MotionEngine>,
71        text_objects: Arc<TextObjectEngine>,
72        global_marks: Arc<RwLock<MarkBank>>,
73        options: Arc<OptionRegistry>,
74        services: Arc<ServiceRegistry>,
75    ) -> Self {
76        Self {
77            event_bus,
78            buffers,
79            motion,
80            text_objects,
81            global_marks,
82            options,
83            services,
84        }
85    }
86}
87
88impl fmt::Debug for KernelContext {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        f.debug_struct("KernelContext")
91            .field("event_bus", &"Arc<EventBus>")
92            .field("buffers", &"Arc<dyn BufferManager>")
93            .field("motion", &"Arc<MotionEngine>")
94            .field("text_objects", &"Arc<TextObjectEngine>")
95            .field("global_marks", &"Arc<RwLock<MarkBank>>")
96            .field("options", &"Arc<OptionRegistry>")
97            .field("services", &"Arc<ServiceRegistry>")
98            .finish()
99    }
100}
101
102// ============================================================================
103// ModuleContext
104// ============================================================================
105
106/// Context provided to modules during initialization.
107///
108/// Extends `KernelContext` with module-specific paths for data and cache storage,
109/// plus access to the cross-module service registry.
110/// This is passed to `Module::init()` and provides everything a module needs
111/// to initialize itself.
112///
113/// # Example
114///
115/// ```ignore
116/// use reovim_kernel::api::v1::{Module, ModuleContext, ProbeResult};
117///
118/// impl Module for MyModule {
119///     fn init(&mut self, ctx: &ModuleContext) -> ProbeResult {
120///         // Access kernel services
121///         let bus = &ctx.kernel.event_bus;
122///
123///         // Access module-specific directories
124///         let config_path = ctx.data_dir.join("config.toml");
125///         let cache_path = ctx.cache_dir.join("cache.bin");
126///
127///         // Register services for other modules to discover
128///         ctx.services.register(Arc::new(MyService::new()));
129///
130///         ProbeResult::Success
131///     }
132/// }
133/// ```
134#[derive(Clone)]
135pub struct ModuleContext {
136    /// Kernel context for core services.
137    pub kernel: KernelContext,
138    /// Service registry for cross-module service discovery.
139    ///
140    /// Modules can register their services here during `init()` for other
141    /// modules to discover. See [`ServiceRegistry`] for usage patterns.
142    pub services: Arc<ServiceRegistry>,
143    /// Module's data directory (persistent storage).
144    ///
145    /// e.g., `~/.local/share/reovim/modules/<module-id>/`
146    pub data_dir: PathBuf,
147    /// Module's cache directory (ephemeral storage).
148    ///
149    /// e.g., `~/.cache/reovim/modules/<module-id>/`
150    pub cache_dir: PathBuf,
151    /// Optional dependencies that were successfully loaded (Phase 4.6 addition).
152    ///
153    /// Populated by the module registry before calling `init()`.
154    /// Use `has_optional_dep()` or `optional_deps()` to query.
155    loaded_optional_deps: Vec<ModuleId>,
156}
157
158impl ModuleContext {
159    /// Create a new module context.
160    #[must_use]
161    #[allow(clippy::missing_const_for_fn)] // Arc::new is not const
162    pub fn new(
163        kernel: KernelContext,
164        services: Arc<ServiceRegistry>,
165        data_dir: PathBuf,
166        cache_dir: PathBuf,
167    ) -> Self {
168        Self {
169            kernel,
170            services,
171            data_dir,
172            cache_dir,
173            loaded_optional_deps: Vec::new(),
174        }
175    }
176
177    /// Create a module context with optional dependencies info.
178    #[must_use]
179    #[allow(clippy::missing_const_for_fn)] // Arc::new is not const
180    pub fn with_optional_deps(
181        kernel: KernelContext,
182        services: Arc<ServiceRegistry>,
183        data_dir: PathBuf,
184        cache_dir: PathBuf,
185        loaded_optional_deps: Vec<ModuleId>,
186    ) -> Self {
187        Self {
188            kernel,
189            services,
190            data_dir,
191            cache_dir,
192            loaded_optional_deps,
193        }
194    }
195
196    /// Check if an optional dependency was loaded.
197    ///
198    /// # Example
199    ///
200    /// ```ignore
201    /// use reovim_kernel::api::v1::{ModuleContext, ModuleId};
202    ///
203    /// fn init(ctx: &ModuleContext) {
204    ///     if ctx.has_optional_dep(&ModuleId::new("lsp")) {
205    ///         // Enable LSP integration
206    ///     }
207    /// }
208    /// ```
209    #[must_use]
210    pub fn has_optional_dep(&self, id: &ModuleId) -> bool {
211        self.loaded_optional_deps.iter().any(|dep| dep == id)
212    }
213
214    /// Get all loaded optional dependencies.
215    ///
216    /// Returns a slice of `ModuleId`s for optional dependencies that were
217    /// successfully loaded before this module's initialization.
218    ///
219    /// # Example
220    ///
221    /// ```ignore
222    /// use reovim_kernel::api::v1::ModuleContext;
223    ///
224    /// fn init(ctx: &ModuleContext) {
225    ///     for dep in ctx.optional_deps() {
226    ///         println!("Optional dep available: {}", dep.as_str());
227    ///     }
228    /// }
229    /// ```
230    #[must_use]
231    pub fn optional_deps(&self) -> &[ModuleId] {
232        &self.loaded_optional_deps
233    }
234}
235
236impl fmt::Debug for ModuleContext {
237    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238        f.debug_struct("ModuleContext")
239            .field("kernel", &self.kernel)
240            .field("services", &self.services)
241            .field("data_dir", &self.data_dir)
242            .field("cache_dir", &self.cache_dir)
243            .field("loaded_optional_deps", &self.loaded_optional_deps)
244            .finish()
245    }
246}
247
248impl Default for ModuleContext {
249    /// Create a default `ModuleContext` for testing purposes.
250    ///
251    /// Uses stub implementations and temporary directories.
252    /// Should only be used in tests where context fields are not accessed.
253    fn default() -> Self {
254        Self {
255            kernel: KernelContext::default(),
256            services: Arc::new(ServiceRegistry::new()),
257            data_dir: PathBuf::from("/tmp/reovim-test/data"),
258            cache_dir: PathBuf::from("/tmp/reovim-test/cache"),
259            loaded_optional_deps: Vec::new(),
260        }
261    }
262}
263
264impl KernelContext {
265    /// Create a `KernelContext` with a shared event bus and services.
266    ///
267    /// Uses stub implementations for other fields. Useful for module
268    /// initialization where modules need to subscribe to events but
269    /// don't need buffer management yet (#440).
270    ///
271    /// # Arguments
272    ///
273    /// * `event_bus` - Shared event bus for subscriptions
274    /// * `services` - Shared service registry
275    #[must_use]
276    pub fn with_event_bus_and_services(
277        event_bus: Arc<EventBus>,
278        services: Arc<ServiceRegistry>,
279    ) -> Self {
280        Self::with_event_bus_services_and_options(
281            event_bus,
282            services,
283            Arc::new(OptionRegistry::new()),
284        )
285    }
286
287    /// Create a `KernelContext` with shared event bus, services, and options.
288    ///
289    /// This variant allows sharing the option registry between module init
290    /// and the final session context (#458). Modules can register options
291    /// during `init()` and they will be available in the session.
292    ///
293    /// # Arguments
294    ///
295    /// * `event_bus` - Shared event bus for subscriptions
296    /// * `services` - Shared service registry
297    /// * `options` - Shared option registry
298    #[must_use]
299    pub fn with_event_bus_services_and_options(
300        event_bus: Arc<EventBus>,
301        services: Arc<ServiceRegistry>,
302        options: Arc<OptionRegistry>,
303    ) -> Self {
304        use crate::core::{MarkBank, MotionEngine, TextObjectEngine};
305
306        Self {
307            event_bus,
308            buffers: Arc::new(StubBufferManager),
309            motion: Arc::new(MotionEngine),
310            text_objects: Arc::new(TextObjectEngine),
311            global_marks: Arc::new(RwLock::new(MarkBank::new())),
312            options,
313            services,
314        }
315    }
316}
317
318impl Default for KernelContext {
319    /// Create a default `KernelContext` for testing purposes.
320    ///
321    /// Uses stub implementations. Should only be used in tests.
322    fn default() -> Self {
323        use crate::core::{MarkBank, MotionEngine, TextObjectEngine};
324
325        Self {
326            event_bus: Arc::new(crate::ipc::EventBus::new()),
327            buffers: Arc::new(StubBufferManager),
328            motion: Arc::new(MotionEngine),
329            text_objects: Arc::new(TextObjectEngine),
330            global_marks: Arc::new(RwLock::new(MarkBank::new())),
331            options: Arc::new(OptionRegistry::new()),
332            services: Arc::new(ServiceRegistry::new()),
333        }
334    }
335}
336
337/// Stub buffer manager for testing.
338///
339/// This implementation does nothing and returns empty/None for all operations.
340/// Used only for tests where the buffer manager is not actually accessed.
341pub struct StubBufferManager;
342
343impl BufferManager for StubBufferManager {
344    fn get(
345        &self,
346        _id: crate::mm::BufferId,
347    ) -> Option<Arc<reovim_arch::sync::RwLock<crate::mm::Buffer>>> {
348        None
349    }
350
351    fn create(&self) -> crate::mm::BufferId {
352        crate::mm::BufferId::new()
353    }
354
355    fn register(&self, _buffer: crate::mm::Buffer) -> crate::mm::BufferId {
356        crate::mm::BufferId::new()
357    }
358
359    fn unregister(
360        &self,
361        id: crate::mm::BufferId,
362    ) -> Result<crate::mm::Buffer, super::buffer_manager::BufferError> {
363        Err(super::buffer_manager::BufferError::NotFound(id))
364    }
365
366    fn list(&self) -> Vec<crate::mm::BufferId> {
367        Vec::new()
368    }
369
370    fn count(&self) -> usize {
371        0
372    }
373}