reovim_plugin_context/
lib.rs1pub mod events;
14pub mod manager;
15
16use std::sync::Arc;
17
18pub use {
19 events::{CursorContextUpdated, ViewportContextUpdated},
20 manager::{CachedContext, ContextManager, SharedContextManager},
21};
22
23use reovim_core::{
24 event_bus::{BufferModified, CursorMoved, EventBus, EventResult, ViewportScrolled},
25 plugin::{Plugin, PluginContext, PluginId, PluginStateRegistry},
26};
27
28pub struct ContextPlugin {
30 manager: SharedContextManager,
31}
32
33impl Default for ContextPlugin {
34 fn default() -> Self {
35 Self::new()
36 }
37}
38
39impl ContextPlugin {
40 #[must_use]
42 pub fn new() -> Self {
43 Self {
44 manager: Arc::new(ContextManager::new()),
45 }
46 }
47}
48
49impl Plugin for ContextPlugin {
50 fn id(&self) -> PluginId {
51 PluginId::new("reovim:context")
52 }
53
54 fn name(&self) -> &'static str {
55 "Context"
56 }
57
58 fn description(&self) -> &'static str {
59 "Event-driven context updates for UI components"
60 }
61
62 fn build(&self, _ctx: &mut PluginContext) {
63 tracing::debug!("ContextPlugin: built");
64 }
65
66 fn init_state(&self, registry: &PluginStateRegistry) {
67 registry.register(Arc::clone(&self.manager));
69 tracing::debug!("ContextPlugin: initialized SharedContextManager");
70 }
71
72 fn subscribe(&self, bus: &EventBus, state: Arc<PluginStateRegistry>) {
73 let manager = Arc::clone(&self.manager);
75 let state_for_cursor = Arc::clone(&state);
76 bus.subscribe::<CursorMoved, _>(100, move |event, ctx| {
77 let buffer_id = event.buffer_id;
78 #[allow(clippy::cast_possible_truncation)]
79 let line = event.to.0 as u32;
80 #[allow(clippy::cast_possible_truncation)]
81 let col = event.to.1 as u32;
82
83 if !manager.cursor_changed(buffer_id, line, col) {
85 return EventResult::Handled;
86 }
87 manager.update_cursor(buffer_id, line, col);
88
89 let context = state_for_cursor.get_context(buffer_id, line, col, "");
93
94 manager.set_cursor_context(CachedContext {
96 buffer_id,
97 line,
98 col,
99 context: context.clone(),
100 });
101
102 ctx.emit(CursorContextUpdated {
104 buffer_id,
105 line,
106 col,
107 context,
108 });
109
110 tracing::trace!(buffer_id, line, col, "ContextPlugin: emitted CursorContextUpdated");
111
112 EventResult::Handled
113 });
114
115 let manager = Arc::clone(&self.manager);
117 let state_for_viewport = Arc::clone(&state);
118 bus.subscribe::<ViewportScrolled, _>(100, move |event, ctx| {
119 let buffer_id = event.buffer_id;
120 let top_line = event.top_line;
121
122 tracing::debug!(
123 buffer_id,
124 top_line,
125 window_id = event.window_id,
126 "ContextPlugin: received ViewportScrolled"
127 );
128
129 if !manager.viewport_changed(buffer_id, top_line) {
131 tracing::debug!(buffer_id, top_line, "ContextPlugin: viewport unchanged, skipping");
132 return EventResult::Handled;
133 }
134 manager.update_viewport(buffer_id, top_line);
135
136 let context = state_for_viewport.get_context(buffer_id, top_line, 0, "");
139 let has_context = context.is_some();
140
141 manager.set_viewport_context(CachedContext {
143 buffer_id,
144 line: top_line,
145 col: 0,
146 context: context.clone(),
147 });
148
149 ctx.emit(ViewportContextUpdated {
151 window_id: event.window_id,
152 buffer_id,
153 top_line,
154 context,
155 });
156
157 tracing::debug!(
158 buffer_id,
159 top_line,
160 window_id = event.window_id,
161 has_context,
162 "ContextPlugin: emitted ViewportContextUpdated"
163 );
164
165 EventResult::Handled
166 });
167
168 let manager = Arc::clone(&self.manager);
170 bus.subscribe::<BufferModified, _>(100, move |event, _ctx| {
171 manager.invalidate_buffer(event.buffer_id);
172 tracing::trace!(
173 buffer_id = event.buffer_id,
174 "ContextPlugin: invalidated context cache"
175 );
176 EventResult::Handled
177 });
178
179 tracing::debug!("ContextPlugin: subscribed to cursor, viewport, and buffer events");
180 }
181}