reovim_module_window_ops/lib.rs
1#![cfg_attr(coverage_nightly, allow(unused_features))]
2#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
3//! Window Operations Module
4//!
5//! This module handles window lifecycle and viewport events from the kernel `EventBus`.
6//! It subscribes to `WindowCreated`, `WindowClosed`, `WindowFocused`, and `ViewportScrolled`
7//! events and provides coordinated window state management.
8//!
9//! # Architecture
10//!
11//! Following the kernel's "mechanism vs policy" principle:
12//! - Kernel provides the window events (mechanism)
13//! - This module decides how to react (policy)
14//!
15//! # Command IDs
16//!
17//! This module also provides command ID constants for window operations
18//! (focus, split, resize, etc.) used by keybinding modules like `vim`.
19//!
20//! # Commands
21//!
22//! This module implements command handlers for window operations:
23//! - Focus navigation: `<C-w>h/j/k/l` - move focus directionally
24//! - Focus cycling: `<C-w>w/W` - cycle through windows
25//! - Splitting: `<C-w>s/v` - horizontal/vertical splits
26//! - Closing: `<C-w>c/o` - close current/close others
27//! - Float zone: Toggle, raise, lower floating windows
28//!
29//! # Event Subscriptions
30//!
31//! - `WindowCreated`: Initialize window-specific state
32//! - `WindowClosed`: Cleanup window-specific resources
33//! - `WindowFocused`: Update active window tracking, trigger highlights
34//! - `ViewportScrolled`: Handle lazy loading, update visible ranges
35
36pub mod command;
37pub mod ids;
38
39use std::sync::Arc;
40
41use {
42 reovim_driver_command::{CommandHandler, CommandHandlerStore, CommandProvider},
43 reovim_kernel::api::v1::{
44 EventResult, Module, ModuleContext, ModuleError, ModuleId, ProbeResult, Subscription,
45 Version,
46 events::kernel::{ViewportScrolled, WindowClosed, WindowCreated, WindowFocused, priority},
47 pr_info,
48 },
49};
50
51/// Window operations module.
52///
53/// Manages window lifecycle events and viewport tracking across the editor.
54/// Subscriptions are stored to keep handlers active until module exit.
55pub struct WindowOps {
56 /// Active subscriptions - MUST be stored to prevent immediate drop (RAII pattern)
57 subscriptions: Vec<Subscription>,
58}
59
60impl WindowOps {
61 /// Create a new `WindowOps` module instance.
62 #[must_use]
63 pub const fn new() -> Self {
64 Self {
65 subscriptions: Vec::new(),
66 }
67 }
68}
69
70impl Default for WindowOps {
71 fn default() -> Self {
72 Self::new()
73 }
74}
75
76impl Module for WindowOps {
77 fn id(&self) -> ModuleId {
78 ModuleId::new("window-ops")
79 }
80
81 fn name(&self) -> &'static str {
82 "Window Operations"
83 }
84
85 fn version(&self) -> Version {
86 Version::new(0, 1, 0)
87 }
88
89 #[cfg_attr(coverage_nightly, coverage(off))]
90 fn init(&mut self, ctx: &ModuleContext) -> ProbeResult {
91 // Register command handlers (Epic #417 Part 3)
92 let command_store = ctx.services.get_or_create::<CommandHandlerStore>();
93 for handler in self.command_handlers() {
94 command_store.add(handler);
95 }
96
97 let bus = Arc::clone(&ctx.kernel.event_bus);
98
99 // Subscribe to window creation events
100 // Priority: CORE (10) - system state change, needs early handling
101 let sub_created =
102 bus.subscribe_with_context::<WindowCreated, _>(priority::CORE, |event, _ctx| {
103 pr_info!("Window {} created", event.window_id);
104 // Future: Initialize window-specific module state (status line, borders)
105 // _ctx.request_render() available when needed
106 EventResult::Handled
107 });
108 // Detach so handlers survive module drop (bootstrap drops modules after init).
109 sub_created.detach();
110 self.subscriptions.push(sub_created);
111
112 // Subscribe to window close events
113 // Priority: LOW (200) - cleanup, run after other handlers
114 let sub_closed =
115 bus.subscribe_with_context::<WindowClosed, _>(priority::LOW, |event, _ctx| {
116 pr_info!("Window {} closed", event.window_id);
117 // Future: Cleanup window-specific module state
118 EventResult::Handled
119 });
120 sub_closed.detach();
121 self.subscriptions.push(sub_closed);
122
123 // Subscribe to window focus events
124 // Priority: CORE (10) - affects active window state
125 let sub_focused =
126 bus.subscribe_with_context::<WindowFocused, _>(priority::CORE, |event, _ctx| {
127 pr_info!("Window focus: {:?} -> {}", event.from, event.to);
128 // Future: Update status line, trigger cursor highlight
129 // _ctx.request_render() available when needed
130 EventResult::Handled
131 });
132 sub_focused.detach();
133 self.subscriptions.push(sub_focused);
134
135 // Subscribe to viewport scroll events
136 // Priority: NORMAL (50) - standard priority for viewport updates
137 let sub_scrolled =
138 bus.subscribe_with_context::<ViewportScrolled, _>(priority::NORMAL, |event, _ctx| {
139 pr_info!(
140 "Window {} viewport scrolled: buffer={}, lines {}..{}",
141 event.window_id,
142 event.buffer_id,
143 event.top_line,
144 event.bottom_line
145 );
146 // Future: Trigger lazy syntax highlighting for visible range
147 // _ctx.request_render() available when needed
148 EventResult::Handled
149 });
150 sub_scrolled.detach();
151 self.subscriptions.push(sub_scrolled);
152
153 pr_info!(
154 "WindowOps module initialized with {} commands and {} subscriptions",
155 command::all_commands().len(),
156 self.subscriptions.len()
157 );
158 ProbeResult::Success
159 }
160
161 fn exit(&mut self) -> Result<(), ModuleError> {
162 // Subscriptions auto-unsubscribe when dropped (RAII)
163 let count = self.subscriptions.len();
164 self.subscriptions.clear();
165 pr_info!("WindowOps module exiting, cleared {} subscriptions", count);
166 Ok(())
167 }
168}
169
170impl CommandProvider for WindowOps {
171 fn command_handlers(&self) -> Vec<Box<dyn CommandHandler>> {
172 command::all_commands()
173 }
174}
175
176// Generate FFI entry points for dynamic loading (only when building standalone cdylib)
177#[cfg(feature = "dynamic")]
178reovim_module_macros::declare_module!(WindowOps);
179
180#[cfg(test)]
181#[path = "lib_tests.rs"]
182mod tests;