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}