reovim_plugin_treesitter/
lib.rs1use std::sync::Arc;
8
9use {
10 reovim_core::{
11 event::RuntimeEvent,
12 event_bus::{EventBus, EventResult, FileOpened},
13 plugin::{Plugin, PluginContext, PluginId, PluginStateRegistry},
14 },
15 tokio::sync::mpsc,
16};
17
18pub mod command;
19pub mod context;
20pub mod edit;
21pub mod events;
22pub mod factory;
23pub mod highlighter;
24pub mod injection;
25pub mod manager;
26pub mod parser;
27pub mod queries;
28pub mod registry;
29pub mod state;
30pub mod syntax;
31pub mod text_objects;
32pub mod theme;
33
34pub use {
35 command::{JumpToNextScope, JumpToParentScope, JumpToPrevScope},
36 context::TreesitterContextProvider,
37 edit::BufferEdit,
38 events::{
39 HighlightsReady, ParseCompleted, ParseRequest, RegisterLanguage, TreesitterFoldRanges,
40 },
41 highlighter::Highlighter,
42 injection::{InjectionDetector, InjectionLayer, InjectionManager, InjectionRegion},
43 manager::TreesitterManager,
44 parser::BufferParser,
45 queries::{QueryCache, QueryType},
46 registry::{LanguageRegistry, LanguageSupport, RegisteredLanguage},
47 state::SharedTreesitterManager,
48 theme::TreesitterTheme,
49};
50
51pub use tree_sitter::Language;
53
54pub struct TreesitterPlugin {
60 manager: Arc<SharedTreesitterManager>,
61}
62
63impl Default for TreesitterPlugin {
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69impl TreesitterPlugin {
70 pub fn new() -> Self {
72 Self {
73 manager: Arc::new(SharedTreesitterManager::new()),
74 }
75 }
76}
77
78impl Plugin for TreesitterPlugin {
79 fn id(&self) -> PluginId {
80 PluginId::new("reovim:treesitter")
81 }
82
83 fn name(&self) -> &'static str {
84 "Treesitter"
85 }
86
87 fn description(&self) -> &'static str {
88 "Syntax highlighting and semantic analysis via tree-sitter"
89 }
90
91 fn build(&self, ctx: &mut PluginContext) {
92 use reovim_core::{
93 bind::{CommandRef, KeymapScope},
94 command::id::CommandId,
95 keys,
96 };
97
98 let _ = ctx.register_command(command::JumpToParentScope);
100 let _ = ctx.register_command(command::JumpToPrevScope);
101 let _ = ctx.register_command(command::JumpToNextScope);
102
103 ctx.bind_key_scoped(
105 KeymapScope::editor_normal(),
106 keys!['g' 'u'],
107 CommandRef::Registered(CommandId::new("jump_to_parent_scope")),
108 );
109
110 ctx.bind_key_scoped(
111 KeymapScope::editor_normal(),
112 keys!['[' 's'],
113 CommandRef::Registered(CommandId::new("jump_to_prev_scope")),
114 );
115
116 ctx.bind_key_scoped(
117 KeymapScope::editor_normal(),
118 keys![']' 's'],
119 CommandRef::Registered(CommandId::new("jump_to_next_scope")),
120 );
121
122 tracing::debug!("TreesitterPlugin: registered render stage and navigation commands");
123 }
124
125 fn init_state(&self, registry: &PluginStateRegistry) {
126 registry.set_text_object_source(Arc::clone(&self.manager) as _);
128
129 registry.register(Arc::clone(&self.manager));
131
132 let factory =
134 Arc::new(crate::factory::TreesitterSyntaxFactory::new(Arc::clone(&self.manager)));
135 registry.set_syntax_factory(factory);
136
137 let context_provider =
139 Arc::new(crate::context::TreesitterContextProvider::new(Arc::clone(&self.manager)));
140 registry.register_context_provider(context_provider);
141
142 tracing::debug!(
143 "TreesitterPlugin: initialized state with syntax factory and context provider"
144 );
145 }
146
147 fn subscribe(&self, bus: &EventBus, state: Arc<PluginStateRegistry>) {
148 {
150 let state = Arc::clone(&state);
151 bus.subscribe::<RegisterLanguage, _>(100, move |event, ctx| {
152 state.with_mut::<Arc<SharedTreesitterManager>, _, _>(|manager| {
153 manager.with_mut(|m| {
154 m.register_language(Arc::clone(&event.language));
155 });
156 tracing::info!(
157 language_id = %event.language.language_id(),
158 "Registered language with treesitter"
159 );
160 });
161 ctx.request_render();
163 EventResult::Handled
164 });
165 }
166
167 {
169 let state = Arc::clone(&state);
170 bus.subscribe::<FileOpened, _>(100, move |event, _ctx| {
171 state.with_mut::<Arc<SharedTreesitterManager>, _, _>(|manager| {
172 let language_id =
173 manager.with_mut(|m| m.init_buffer(event.buffer_id, Some(&event.path)));
174
175 if let Some(lang_id) = language_id {
176 tracing::debug!(
177 buffer_id = event.buffer_id,
178 language_id = %lang_id,
179 "Initialized treesitter for buffer"
180 );
181 }
182 });
183 EventResult::Handled
184 });
185 }
186
187 {
189 use reovim_core::event_bus::BufferClosed;
190 let state = Arc::clone(&state);
191 bus.subscribe::<BufferClosed, _>(100, move |event, _ctx| {
192 state.with_mut::<Arc<SharedTreesitterManager>, _, _>(|manager| {
193 manager.with_mut(|m| {
194 m.remove_buffer(event.buffer_id);
195 });
196 });
197 EventResult::Handled
198 });
199 }
200
201 {
203 use reovim_core::event_bus::BufferModified;
204 let state = Arc::clone(&state);
205 bus.subscribe::<BufferModified, _>(100, move |event, _ctx| {
206 state.with_mut::<Arc<SharedTreesitterManager>, _, _>(|manager| {
207 manager.with_mut(|m| {
208 if m.has_parser(event.buffer_id) {
209 m.schedule_reparse(event.buffer_id);
210 }
211 });
212 });
213 EventResult::Handled
214 });
215 }
216
217 {
219 let state = Arc::clone(&state);
220 bus.subscribe::<ParseRequest, _>(100, move |event, ctx| {
221 let result = state.with_mut::<Arc<SharedTreesitterManager>, _, _>(|manager| {
222 let (language_id, has_parser) = manager.with(|m| {
223 (m.buffer_language(event.buffer_id), m.has_parser(event.buffer_id))
224 });
225
226 if has_parser { language_id } else { None }
227 });
228
229 if let Some(Some(lang_id)) = result {
230 ctx.emit(ParseCompleted {
231 buffer_id: event.buffer_id,
232 language_id: lang_id,
233 });
234 }
235 EventResult::Handled
236 });
237 }
238
239 {
243 let _state = Arc::clone(&state);
244 bus.subscribe::<command::JumpToParentScope, _>(100, move |_event, _ctx| {
245 EventResult::NotHandled
253 });
254 }
255
256 {
258 let _state = Arc::clone(&state);
259 bus.subscribe::<command::JumpToPrevScope, _>(100, move |_event, _ctx| {
260 EventResult::NotHandled
262 });
263 }
264
265 {
267 let _state = Arc::clone(&state);
268 bus.subscribe::<command::JumpToNextScope, _>(100, move |_event, _ctx| {
269 EventResult::NotHandled
271 });
272 }
273
274 tracing::debug!("TreesitterPlugin: subscribed to events");
275 }
276
277 fn boot(
278 &self,
279 _bus: &EventBus,
280 _state: Arc<PluginStateRegistry>,
281 _event_tx: Option<mpsc::Sender<RuntimeEvent>>,
282 ) {
283 let manager = Arc::clone(&self.manager);
286 std::thread::spawn(move || {
287 let start = std::time::Instant::now();
288 let count = manager.with(|m| m.precompile_all_queries());
289 let elapsed = start.elapsed();
290 tracing::info!(
291 queries = count,
292 elapsed_ms = elapsed.as_millis(),
293 "Background query precompilation finished"
294 );
295 });
296 tracing::debug!("TreesitterPlugin: spawned background query precompilation");
297 }
298}