reovim-client-driver 0.14.4

Platform-agnostic trait contracts for Reovim client modules
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
use std::sync::Arc;

use crate::{
    AnnotationContext, BufferId, BufferUpdateEvent, ChromePosition, ClientModuleError, ColumnWidth,
    GutterCell, Insets, OptionValue, ProbeResult, Rect, RenderBehavior, RenderingModel, Style,
    SyntaxToken, TransformedLine, Version, ViewportContext, VirtualLine, WindowId, WindowLayout,
    types::{Color, ColorDepth, InlineDecoration},
};

// =============================================================================
// ClientModule — the single trait CORE interacts with
// =============================================================================

/// The central trait for all client-side modules.
///
/// Every UI feature (statusline, diagnostics, explorer, etc.) implements this
/// trait. CORE dispatches to modules via this interface and has zero knowledge
/// of specific UI features.
///
/// All methods have default no-op implementations so modules only override
/// what they need.
#[allow(unused_variables)]
pub trait ClientModule: Send + Sync + 'static {
    // ---- Identity ----

    /// Unique identifier for this module instance.
    fn id(&self) -> &'static str;

    /// Module kind (used for server-side extension matching).
    fn kind(&self) -> &'static str {
        self.id()
    }

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

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

    /// Required module dependencies (by kind).
    fn dependencies(&self) -> &[&str] {
        &[]
    }

    /// Optional module dependencies (by kind).
    fn optional_dependencies(&self) -> &[&str] {
        &[]
    }

    /// Server extension kinds this module handles notifications for.
    fn server_kinds(&self) -> Vec<&'static str> {
        vec![self.kind()]
    }

    // ---- Lifecycle ----

    /// Initialize the module. Called once after construction.
    fn init(&mut self, ctx: &ModuleContext) -> ProbeResult;

    /// Clean up resources. Called before the module is dropped.
    ///
    /// # Errors
    ///
    /// Returns `ClientModuleError` if cleanup fails.
    fn exit(&mut self) -> Result<(), ClientModuleError>;

    /// Called after all modules have been loaded.
    fn on_all_loaded(&mut self, ctx: &ModuleContext) {}

    // ---- Role declaration (checked every frame) ----

    /// Whether this module contributes chrome (statusline, sidebar, overlay).
    fn has_chrome(&self) -> bool {
        false
    }

    /// Whether this module contributes to buffer rendering.
    fn has_buffer_contrib(&self) -> bool {
        false
    }

    /// Whether this module contributes gutter annotations.
    fn has_annotations(&self) -> bool {
        false
    }

    // ---- Events ----

    /// Handle a server notification (JSON data).
    fn on_notification(&mut self, data: &str) {}

    /// Handle an option value change.
    fn on_option_changed(&mut self, name: &str, value: &OptionValue) {}

    /// Handle a buffer content update.
    fn on_buffer_update(&mut self, event: &BufferUpdateEvent) {}

    /// Handle a cursor position change.
    fn on_cursor_update(&mut self, buffer_id: BufferId, line: usize, col: usize) {}

    /// Handle focus change to a different buffer.
    fn on_buffer_focus(&mut self, buffer_id: BufferId) {}

    /// Handle a mode change (e.g., "normal", "insert", "visual").
    fn on_mode_change(&mut self, mode: &str) {}

    /// Handle platform capabilities change.
    fn on_capabilities_changed(&mut self, caps: &dyn PlatformCapabilities) {}

    /// Handle theme change.
    fn on_theme_changed(&mut self, theme: &dyn ThemeProvider) {}

    /// Periodic tick. Returns `true` if state changed and a redraw is needed.
    fn tick(&mut self) -> bool {
        false
    }

    // ---- Chrome ----

    /// Where this module's chrome is rendered.
    fn chrome_position(&self) -> ChromePosition {
        ChromePosition::Bottom
    }

    /// Requested size (height for Top/Bottom, width for Left/Right).
    fn chrome_requested_size(&self, caps: &dyn PlatformCapabilities) -> u16 {
        1
    }

    /// Priority for chrome allocation (higher = allocated first).
    fn chrome_priority(&self) -> u16 {
        0
    }

    /// Z-order for overlay chrome (higher = drawn on top).
    fn chrome_z_order(&self) -> u16 {
        0
    }

    /// Render chrome into the given surface region.
    #[cfg_attr(coverage_nightly, coverage(off))]
    fn chrome_render(
        &self,
        surface: &mut dyn RenderSurface,
        bounds: Rect,
        caps: &dyn PlatformCapabilities,
    ) {
    }

    // ---- Buffer contribution ----

    /// Priority for buffer contribution dispatch (higher = checked first).
    fn buffer_contrib_priority(&self) -> u16 {
        0
    }

    /// Classify a token category for rendering behavior.
    fn classify_token(&self, category: &str) -> Option<RenderBehavior> {
        None
    }

    /// Transform a line before rendering.
    fn transform_line(&self, buf: BufferId, line: usize, text: &str) -> Option<TransformedLine> {
        None
    }

    /// Map a cursor column to a display column.
    fn map_cursor_column(&self, buf: BufferId, line: usize, col: usize) -> Option<u16> {
        None
    }

    /// Return fold ranges (`start_line`, `line_count`) for hidden lines.
    fn fold_ranges(&self) -> &[(usize, usize)] {
        &[]
    }

    /// Return virtual lines to inject into the buffer display.
    fn virtual_lines(&self) -> &[VirtualLine] {
        &[]
    }

    /// Return inline decorations for a given line.
    fn inline_decorations(&self, line: usize) -> &[InlineDecoration] {
        &[]
    }

    /// Override cursor position (for modules that manage their own cursor).
    fn cursor_position(&self, w: u16, h: u16) -> Option<(u16, u16)> {
        None
    }

    // ---- Annotations (gutter) ----

    /// Width of this module's annotation column.
    fn annotation_column_width(
        &self,
        ctx: &AnnotationContext,
        caps: &dyn PlatformCapabilities,
    ) -> ColumnWidth {
        ColumnWidth::Fixed(0)
    }

    /// Render a gutter cell for the given line.
    fn annotate(&self, line: usize, ctx: &AnnotationContext) -> Option<GutterCell> {
        None
    }

    /// Priority for annotation ordering (higher = leftmost in gutter).
    fn annotation_priority(&self) -> u16 {
        0
    }
}

// =============================================================================
// ModuleContext
// =============================================================================

/// Context provided to modules during initialization and lifecycle events.
pub struct ModuleContext<'a> {
    pub capabilities: &'a dyn PlatformCapabilities,
    pub server: Arc<dyn ServerHandle>,
    pub theme: &'a dyn ThemeProvider,
    /// Client-side service registry for cross-module communication.
    ///
    /// `None` for FFI modules compiled against API version < 0.2.0.
    pub services: Option<&'a crate::services::ClientServiceRegistry>,
    /// Module state introspection (query which modules are loaded/running).
    ///
    /// `None` for FFI modules compiled against API version < 0.2.0.
    pub module_registry: Option<&'a dyn ClientModuleRegistry>,
}

// =============================================================================
// ClientModuleRegistry
// =============================================================================

/// Trait for querying client module state (read-only introspection).
///
/// Implemented by the loader (or a snapshot wrapper) and exposed via
/// `ModuleContext.module_registry`. Modules can query whether dependencies
/// are loaded and running.
pub trait ClientModuleRegistry: Send + Sync {
    /// Check if a module with the given kind is currently running.
    fn is_running(&self, kind: &str) -> bool;

    /// Get the state of a module by kind.
    fn module_state(&self, kind: &str) -> Option<crate::loader::ClientModuleState>;

    /// List all loaded module kinds.
    fn loaded_kinds(&self) -> Vec<String>;

    /// Count of currently running modules.
    fn running_count(&self) -> usize;
}

// =============================================================================
// PlatformCapabilities
// =============================================================================

/// Platform capabilities and constraints.
///
/// Queried by modules to adapt their behavior to the current platform
/// (terminal, web, mobile, etc.).
#[allow(unused_variables)]
pub trait PlatformCapabilities: Send + Sync {
    /// The rendering model this platform uses.
    fn rendering_model(&self) -> RenderingModel;

    /// Grid size in cells (columns, rows). `None` if not a grid-based platform.
    fn grid_size(&self) -> Option<(u16, u16)>;

    /// Color depth supported by the display.
    fn color_depth(&self) -> ColorDepth;

    /// Pixel dimensions of the display. `None` if not available.
    fn pixel_size(&self) -> Option<(u32, u32)>;

    /// Whether Unicode width calculations are reliable on this platform.
    fn reliable_unicode_width(&self) -> bool;

    /// Whether the display is in dark mode.
    fn dark_mode(&self) -> bool;

    /// Whether smooth scrolling is supported.
    fn smooth_scroll(&self) -> bool;

    /// Whether pointer (mouse) events are available.
    fn pointer_events(&self) -> bool;

    /// Whether touch input is available.
    fn touch_input(&self) -> bool;

    /// Whether haptic feedback is available.
    fn haptic(&self) -> bool;

    /// Safe area insets (for notch/rounded corners).
    fn safe_area(&self) -> Insets;

    /// Whether the window currently has focus.
    fn has_focus(&self) -> bool;

    /// Whether clipboard access is available.
    fn clipboard_available(&self) -> bool;

    /// Whether a screen reader is active.
    fn screen_reader_active(&self) -> bool;
}

// =============================================================================
// RenderSurface
// =============================================================================

/// Abstraction over the rendering target.
///
/// Provides primitive drawing operations. Platform adapters implement this
/// for their specific rendering backend (cell grid, canvas, native views).
pub trait RenderSurface {
    /// Write styled text at position. Returns the number of columns consumed.
    fn write_styled(&mut self, x: u16, y: u16, text: &str, style: Style) -> u16;

    /// Apply a style to an existing cell (without changing its content).
    fn apply_style(&mut self, x: u16, y: u16, style: Style);

    /// Override the background color of a cell.
    fn overlay_bg(&mut self, x: u16, y: u16, bg: Color);

    /// Fill a rectangle with a character and style.
    fn fill(&mut self, rect: Rect, ch: char, style: Style);

    /// Clear a rectangle (reset to default).
    fn clear(&mut self, rect: Rect);

    /// Size of the surface in cells (columns, rows).
    fn size(&self) -> (u16, u16);
}

// =============================================================================
// ServerHandle
// =============================================================================

/// Handle for communicating with the server from a client module.
#[allow(unused_variables)]
pub trait ServerHandle: Send + Sync {
    /// Query option values from the server.
    fn get_options(&self, names: &[&str]) -> Vec<(String, OptionValue)>;

    /// Execute a server command.
    fn execute_command(&self, command: &str);

    /// List available command names.
    ///
    /// Default: empty list (for backward compatibility with existing impls).
    #[cfg_attr(coverage_nightly, coverage(off))]
    fn list_commands(&self) -> Vec<String> {
        Vec::new()
    }

    /// Get metadata about a named option.
    ///
    /// Default: `None` (for backward compatibility with existing impls).
    #[cfg_attr(coverage_nightly, coverage(off))]
    fn get_option_metadata(&self, name: &str) -> Option<crate::OptionMetadata> {
        None
    }
}

// =============================================================================
// ThemeProvider
// =============================================================================

/// Provides theme/highlight information to modules.
pub trait ThemeProvider: Send + Sync {
    /// Get the style for a highlight group.
    fn highlight(&self, group: &str) -> Style;

    /// Get the style for the first matching highlight group in the list.
    fn highlight_with_fallback(&self, groups: &[&str]) -> Style;

    /// Default foreground style.
    fn foreground(&self) -> Style;

    /// Default background style.
    fn background(&self) -> Style;

    /// Whether the current theme is dark.
    fn is_dark(&self) -> bool;
}

// =============================================================================
// TokenProvider
// =============================================================================

/// Provides syntax tokens for viewport rendering.
///
/// Abstracts over the annotation/syntax cache. The TUI implements this
/// with `AnnotationCacheManager`, but other platforms can provide tokens
/// from different sources.
pub trait TokenProvider: Send + Sync {
    /// Get syntax tokens for a specific line in a buffer.
    fn tokens_for_line(&self, buffer_id: BufferId, line: u32) -> Vec<SyntaxToken>;
}

// =============================================================================
// ViewportRenderer
// =============================================================================

/// Renders buffer content into a viewport region.
///
/// The default implementation handles gutter, folds, virtual lines, token
/// classification, conceals, selections, and cursor rendering. Custom
/// implementations can override for specialized rendering (e.g., hex editor).
pub trait ViewportRenderer: Send + Sync {
    /// Calculate the total gutter width from annotation modules.
    fn gutter_width(
        &self,
        modules: &[Box<dyn ClientModule>],
        caps: &dyn PlatformCapabilities,
    ) -> u16;

    /// Render the buffer viewport into the surface.
    #[allow(clippy::too_many_arguments)]
    fn render_viewport(
        &self,
        surface: &mut dyn RenderSurface,
        viewport: Rect,
        ctx: &ViewportContext<'_>,
        modules: &[Box<dyn ClientModule>],
        tokens: &dyn TokenProvider,
        theme: &dyn ThemeProvider,
        caps: &dyn PlatformCapabilities,
    );
}

// =============================================================================
// LayoutPolicy
// =============================================================================

/// Determines how windows are arranged in the viewport.
pub trait LayoutPolicy: Send + Sync {
    /// Compute window positions given the available viewport and window list.
    fn layout(&self, viewport: Rect, windows: &[WindowId], focused: WindowId) -> Vec<WindowLayout>;
}

#[cfg(test)]
mod tests;