reovim_driver_syntax/driver.rs
1//! Syntax driver trait definition.
2//!
3//! This module defines the [`SyntaxDriver`] trait, the main interface for
4//! syntax highlighting implementations. Implementations handle parsing and
5//! highlighting for specific languages.
6
7use std::{ops::Range, sync::Arc};
8
9use crate::{
10 edit::SyntaxEdit,
11 factory::SyntaxDriverFactory,
12 fold::FoldRange,
13 highlight::{Annotation, SyntaxContext},
14 injection::Injection,
15 scope::ContextHierarchy,
16 textobject::{TextObjectKind, TextObjectRange, TextObjectScope},
17};
18
19/// Main parsing interface for syntax highlighting.
20///
21/// Implementors provide language-specific parsing and highlighting.
22/// This trait is designed to be parser-agnostic - tree-sitter is just
23/// one possible implementation.
24///
25/// # Lifecycle
26///
27/// 1. Create driver via [`SyntaxDriverFactory`](crate::SyntaxDriverFactory)
28/// 2. Call [`parse()`](Self::parse) with initial content
29/// 3. Call [`update()`](Self::update) for incremental edits
30/// 4. Call [`highlights()`](Self::highlights) to get highlights for rendering
31///
32/// # Thread Safety
33///
34/// Implementations must be `Send + Sync` to allow use across threads.
35/// The driver may be shared between multiple views of the same buffer.
36///
37/// # Example
38///
39/// ```ignore
40/// // Get driver from factory
41/// let mut driver = factory.create("rust")?;
42///
43/// // Initial parse
44/// driver.parse("fn main() {}");
45/// assert!(driver.is_parsed());
46///
47/// // Get highlights for visible range
48/// let highlights = driver.highlights(0..100);
49///
50/// // After edit, update incrementally
51/// driver.update("fn main() { println!(); }", &edit);
52/// ```
53pub trait SyntaxDriver: Send + Sync {
54 /// Get the language identifier.
55 ///
56 /// Returns a unique identifier like "rust", "python", "javascript".
57 /// This should match the language ID used to create the driver.
58 fn language(&self) -> &str;
59
60 /// Perform a full parse of the content.
61 ///
62 /// Called on initial file open or when incremental update isn't possible.
63 /// After calling this, [`is_parsed()`](Self::is_parsed) should return true.
64 fn parse(&mut self, content: &str);
65
66 /// Apply an incremental edit.
67 ///
68 /// For efficient re-parsing after buffer modifications.
69 /// The edit describes what changed; the driver updates its internal state.
70 ///
71 /// # Arguments
72 ///
73 /// * `content` - The new full content after the edit
74 /// * `edit` - Description of what changed
75 fn update(&mut self, content: &str, edit: &SyntaxEdit);
76
77 /// Get annotations for a byte range.
78 ///
79 /// Returns all annotations that overlap with the given range.
80 /// Results should be sorted by start position.
81 ///
82 /// # Arguments
83 ///
84 /// * `byte_range` - The byte range to get annotations for
85 ///
86 /// # Returns
87 ///
88 /// A vector of annotations overlapping the requested range.
89 fn highlights(&self, byte_range: Range<usize>) -> Vec<Annotation>;
90
91 /// Get language injection points.
92 ///
93 /// Returns regions where a different language should be highlighted.
94 /// Used for embedded languages (markdown code blocks, HTML scripts, etc.).
95 ///
96 /// # Default
97 ///
98 /// Returns an empty vector. Override for languages that support injections.
99 fn injections(&self) -> Vec<Injection> {
100 Vec::new()
101 }
102
103 /// Get decoration annotations for a byte range.
104 ///
105 /// Returns annotations from decoration queries (e.g., markdown heading
106 /// markers, list bullets, code blocks). These carry semantic categories
107 /// like `markup.heading.1` that clients map to render behaviors.
108 /// Use-sites call this separately from [`highlights()`](Self::highlights)
109 /// to opt in to decoration rendering.
110 ///
111 /// # Default
112 ///
113 /// Returns an empty vector. Override for languages with decoration support.
114 fn decorations(&self, _byte_range: Range<usize>) -> Vec<Annotation> {
115 Vec::new()
116 }
117
118 /// Get foldable ranges.
119 ///
120 /// Returns all foldable regions in the document.
121 ///
122 /// # Default
123 ///
124 /// Returns an empty vector. Override to provide folding support.
125 fn folds(&self) -> Vec<FoldRange> {
126 Vec::new()
127 }
128
129 /// Get suggested indentation for a line.
130 ///
131 /// Returns the suggested indent level (in spaces) for a given line,
132 /// or None if indentation cannot be determined.
133 ///
134 /// # Arguments
135 ///
136 /// * `line` - The 0-indexed line number
137 ///
138 /// # Default
139 ///
140 /// Returns None. Override to provide indentation hints.
141 fn indent_for(&self, _line: usize) -> Option<usize> {
142 None
143 }
144
145 /// Configure injection support with the given factory.
146 ///
147 /// Called by the runtime after driver creation to enable recursive
148 /// injection highlighting. Implementations that support injections
149 /// use this factory to create child drivers for embedded languages.
150 ///
151 /// # Default
152 ///
153 /// No-op. Override for drivers that support injection highlighting.
154 fn set_injection_factory(&mut self, _factory: Arc<dyn SyntaxDriverFactory>) {}
155
156 /// Set the injection nesting depth for this driver.
157 ///
158 /// Used to propagate depth through the injection hierarchy so that
159 /// `MAX_INJECTION_DEPTH` is correctly enforced. Depth 0 = root driver,
160 /// depth 1 = direct child, etc.
161 ///
162 /// # Default
163 ///
164 /// No-op. Override for drivers that support injection highlighting.
165 fn set_injection_depth(&mut self, _depth: u8) {}
166
167 /// Set the default injection language for bare code blocks.
168 ///
169 /// When a child driver encounters an injection region with no explicit
170 /// language tag (e.g., bare ` ``` ` in Markdown), it falls back to this
171 /// value. The injection pipeline sets this to the parent driver's
172 /// language ID, so bare fences in Rust doc comments default to Rust,
173 /// bare fences in Python docstrings default to Python, etc.
174 ///
175 /// # Default
176 ///
177 /// No-op. Override for drivers that support injection highlighting.
178 fn set_default_injection_language(&mut self, _language: &str) {}
179
180 /// Get the enclosing scope hierarchy at a position.
181 ///
182 /// Returns the scope boundaries (function, class, module, etc.)
183 /// that contain the given line and column. Items are ordered
184 /// outermost-first.
185 ///
186 /// # Arguments
187 ///
188 /// * `line` - 0-indexed line number
189 /// * `col` - 0-indexed column number
190 ///
191 /// # Default
192 ///
193 /// Returns an empty hierarchy. Override for scope-aware behavior.
194 fn scopes(&self, _line: u32, _col: u32) -> ContextHierarchy {
195 ContextHierarchy::empty()
196 }
197
198 /// Get the syntax context at a byte position.
199 ///
200 /// Returns the syntactic context (code, string, comment) at the given
201 /// byte offset. Used by features like auto-pair to skip insertion
202 /// inside strings or comments.
203 ///
204 /// # Default
205 ///
206 /// Returns `SyntaxContext::Code`. Override for context-aware behavior.
207 #[cfg_attr(coverage_nightly, coverage(off))]
208 fn context_at_byte(&self, _byte_offset: usize) -> SyntaxContext {
209 SyntaxContext::Code
210 }
211
212 /// Get the range of a semantic text object at a position.
213 ///
214 /// Resolves language-aware text objects like functions, classes, arguments,
215 /// etc. at the given cursor position. Returns the byte and position range
216 /// of the matching construct, or `None` if no match exists.
217 ///
218 /// # Arguments
219 ///
220 /// * `kind` - The type of text object (function, class, etc.)
221 /// * `scope` - Inner (body only) or outer (full construct)
222 /// * `line` - 0-indexed cursor line
223 /// * `col` - 0-indexed cursor column
224 ///
225 /// # Default
226 ///
227 /// Returns `None`. Override for drivers with treesitter query support.
228 #[cfg_attr(coverage_nightly, coverage(off))]
229 fn textobject_range(
230 &self,
231 _kind: TextObjectKind,
232 _scope: TextObjectScope,
233 _line: u32,
234 _col: u32,
235 ) -> Option<TextObjectRange> {
236 None
237 }
238
239 /// Check if the driver has valid parse state.
240 ///
241 /// Returns true if the driver has successfully parsed content.
242 /// This should return false before [`parse()`](Self::parse) is called,
243 /// and true after a successful parse.
244 fn is_parsed(&self) -> bool;
245}
246
247#[cfg(test)]
248#[path = "driver_tests.rs"]
249mod tests;