1#[cfg(feature = "term-colors")]
11use colored::Colorize;
12use std::ops::Range;
13use std::path::Path;
14use tree_sitter::Node;
15use tree_sitter::Tree;
16
17#[derive(Debug)]
19pub enum ParseError<'tree> {
20 Missing(Node<'tree>),
22 Unexpected(Node<'tree>),
24}
25
26impl<'tree> ParseError<'tree> {
27 pub fn first(tree: &Tree) -> Option<ParseError> {
29 let mut errors = Vec::new();
30 find_errors(tree, &mut errors, true);
31 errors.into_iter().next()
32 }
33
34 pub fn into_first(tree: Tree) -> TreeWithParseErrorOption {
38 TreeWithParseErrorOption::into_first(tree)
39 }
40
41 pub fn all(tree: &'tree Tree) -> Vec<ParseError<'tree>> {
43 let mut errors = Vec::new();
44 find_errors(tree, &mut errors, false);
45 errors
46 }
47
48 pub fn into_all(tree: Tree) -> TreeWithParseErrorVec {
52 TreeWithParseErrorVec::into_all(tree)
53 }
54}
55
56impl<'tree> ParseError<'tree> {
57 pub fn node(&self) -> &Node<'tree> {
58 match self {
59 Self::Missing(node) => node,
60 Self::Unexpected(node) => node,
61 }
62 }
63
64 pub fn display(
65 &'tree self,
66 path: &'tree Path,
67 source: &'tree str,
68 ) -> impl std::fmt::Display + 'tree {
69 ParseErrorDisplay {
70 error: self,
71 path,
72 source,
73 }
74 }
75
76 pub fn display_pretty(
77 &'tree self,
78 path: &'tree Path,
79 source: &'tree str,
80 ) -> impl std::fmt::Display + 'tree {
81 ParseErrorDisplayPretty {
82 error: self,
83 path,
84 source,
85 }
86 }
87}
88
89struct ParseErrorDisplay<'tree> {
90 error: &'tree ParseError<'tree>,
91 path: &'tree Path,
92 source: &'tree str,
93}
94
95impl std::fmt::Display for ParseErrorDisplay<'_> {
96 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
97 let node = self.error.node();
98 write!(
99 f,
100 "{}:{}:{}: ",
101 self.path.display(),
102 node.start_position().row + 1,
103 node.start_position().column + 1
104 )?;
105 let node = match self.error {
106 ParseError::Missing(node) => {
107 write!(f, "missing syntax")?;
108 node
109 }
110 ParseError::Unexpected(node) => {
111 write!(f, "unexpected syntax")?;
112 node
113 }
114 };
115 if node.byte_range().is_empty() {
116 writeln!(f, "")?;
117 } else {
118 let end_byte = self.source[node.byte_range()]
119 .chars()
120 .take_while(|c| *c != '\n')
121 .map(|c| c.len_utf8())
122 .sum();
123 let text = &self.source[node.start_byte()..end_byte];
124 write!(f, ": {}", text)?;
125 }
126 Ok(())
127 }
128}
129
130struct ParseErrorDisplayPretty<'tree> {
131 error: &'tree ParseError<'tree>,
132 path: &'tree Path,
133 source: &'tree str,
134}
135
136impl std::fmt::Display for ParseErrorDisplayPretty<'_> {
137 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
138 let node = match self.error {
139 ParseError::Missing(node) => {
140 writeln!(f, "missing syntax")?;
141 node
142 }
143 ParseError::Unexpected(node) => {
144 writeln!(f, "unexpected syntax")?;
145 node
146 }
147 };
148 if node.byte_range().is_empty() {
149 writeln!(f, "")?;
150 } else {
151 let start_column = node.start_position().column;
152 let end_column = node.start_position().column
153 + self.source[node.byte_range()]
154 .chars()
155 .take_while(|c| *c != '\n')
156 .count();
157 write!(
158 f,
159 "{}",
160 Excerpt::from_source(
161 self.path,
162 self.source,
163 node.start_position().row,
164 start_column..end_column,
165 0,
166 ),
167 )?;
168 }
169 Ok(())
170 }
171}
172
173fn find_errors<'tree>(tree: &'tree Tree, errors: &mut Vec<ParseError<'tree>>, first_only: bool) {
175 if !tree.root_node().has_error() {
177 return;
178 }
179
180 let mut cursor = tree.walk();
181 let mut did_visit_children = false;
182 loop {
183 let node = cursor.node();
184 if node.is_error() {
185 errors.push(ParseError::Unexpected(node));
186 if first_only {
187 break;
188 }
189 did_visit_children = true;
190 } else if node.is_missing() {
191 errors.push(ParseError::Missing(node));
192 if first_only {
193 break;
194 }
195 did_visit_children = true;
196 }
197 if did_visit_children {
198 if cursor.goto_next_sibling() {
199 did_visit_children = false;
200 } else if cursor.goto_parent() {
201 did_visit_children = true;
202 } else {
203 break;
204 }
205 } else {
206 if cursor.goto_first_child() {
207 did_visit_children = false;
208 } else {
209 did_visit_children = true;
210 }
211 }
212 }
213 cursor.reset(tree.root_node());
214}
215
216pub struct TreeWithParseError {
231 tree: Tree,
232 error: ParseError<'static>,
234}
235
236impl TreeWithParseError {
237 pub fn tree(&self) -> &Tree {
238 &self.tree
239 }
240
241 pub fn into_tree(self) -> Tree {
242 self.tree
243 }
244
245 pub fn error(&self) -> &ParseError {
246 &self.error
247 }
248}
249
250impl std::fmt::Debug for TreeWithParseError {
251 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
252 write!(f, "{:?}", self.error)
253 }
254}
255
256unsafe impl Send for TreeWithParseError {}
259unsafe impl Sync for TreeWithParseError {}
260
261pub struct TreeWithParseErrorOption {
263 tree: Tree,
264 error: Option<ParseError<'static>>,
266}
267
268impl TreeWithParseErrorOption {
269 fn into_first(tree: Tree) -> TreeWithParseErrorOption {
270 let mut errors = Vec::new();
271 find_errors(&tree, &mut errors, true);
272 Self {
273 error: unsafe { std::mem::transmute(errors.into_iter().next()) },
274 tree: tree,
275 }
276 }
277}
278
279impl TreeWithParseErrorOption {
280 pub fn tree(&self) -> &Tree {
281 &self.tree
282 }
283
284 pub fn into_tree(self) -> Tree {
285 self.tree
286 }
287
288 pub fn error(&self) -> &Option<ParseError> {
289 &self.error
290 }
291
292 pub fn into_option(self) -> Option<TreeWithParseError> {
293 match self.error {
294 None => None,
295 Some(error) => Some(TreeWithParseError {
296 tree: self.tree,
297 error,
298 }),
299 }
300 }
301}
302
303impl std::fmt::Debug for TreeWithParseErrorOption {
304 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
305 write!(f, "{:?}", self.error)
306 }
307}
308
309unsafe impl Send for TreeWithParseErrorOption {}
312unsafe impl Sync for TreeWithParseErrorOption {}
313
314pub struct TreeWithParseErrorVec {
316 tree: Tree,
317 errors: Vec<ParseError<'static>>,
319}
320
321impl TreeWithParseErrorVec {
322 fn into_all(tree: Tree) -> TreeWithParseErrorVec {
323 let mut errors = Vec::new();
324 find_errors(&tree, &mut errors, false);
325 TreeWithParseErrorVec {
326 errors: unsafe { std::mem::transmute(errors) },
327 tree: tree,
328 }
329 }
330}
331
332impl TreeWithParseErrorVec {
333 pub fn tree(&self) -> &Tree {
334 &self.tree
335 }
336
337 pub fn into_tree(self) -> Tree {
338 self.tree
339 }
340
341 pub fn errors(&self) -> &Vec<ParseError> {
342 &self.errors
343 }
344}
345
346impl std::fmt::Debug for TreeWithParseErrorVec {
347 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
348 write!(f, "{:?}", self.errors)
349 }
350}
351
352unsafe impl Send for TreeWithParseErrorVec {}
355unsafe impl Sync for TreeWithParseErrorVec {}
356
357pub struct Excerpt<'a> {
361 path: &'a Path,
362 source: Option<&'a str>,
363 row: usize,
364 columns: Range<usize>,
365 indent: usize,
366}
367
368impl<'a> Excerpt<'a> {
369 pub fn from_source(
370 path: &'a Path,
371 source: &'a str,
372 row: usize,
373 mut columns: Range<usize>,
374 indent: usize,
375 ) -> Excerpt<'a> {
376 let source = source.lines().nth(row);
377 columns.end = std::cmp::min(columns.end, source.map(|s| s.len()).unwrap_or_default());
378 Excerpt {
379 path,
380 source,
381 row,
382 columns,
383 indent,
384 }
385 }
386
387 fn gutter_width(&self) -> usize {
388 ((self.row + 1) as f64).log10() as usize + 1
389 }
390}
391
392impl<'a> std::fmt::Display for Excerpt<'a> {
393 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
394 writeln!(
396 f,
397 "{}{}:{}:{}:",
398 " ".repeat(self.indent),
399 header_style(&self.path.to_string_lossy()),
400 header_style(&format!("{}", self.row + 1)),
401 header_style(&format!("{}", self.columns.start + 1)),
402 )?;
403 if let Some(source) = self.source {
404 writeln!(
406 f,
407 "{}{}{}{}",
408 " ".repeat(self.indent),
409 prefix_style(&format!("{}", self.row + 1)),
410 prefix_style(" | "),
411 source,
412 )?;
413 writeln!(
415 f,
416 "{}{}{}{}{}",
417 " ".repeat(self.indent),
418 " ".repeat(self.gutter_width()),
419 prefix_style(" | "),
420 " ".repeat(self.columns.start),
421 underline_style(&"^".repeat(self.columns.len())),
422 )?;
423 } else {
424 writeln!(f, "{}{}", " ".repeat(self.indent), "<missing source>",)?;
425 }
426 Ok(())
427 }
428}
429
430#[cfg(feature = "term-colors")]
433fn header_style(str: &str) -> impl std::fmt::Display {
434 str.bold()
435}
436#[cfg(not(feature = "term-colors"))]
437fn header_style<'a>(str: &'a str) -> impl std::fmt::Display + 'a {
438 str
439}
440
441#[cfg(feature = "term-colors")]
442fn prefix_style(str: &str) -> impl std::fmt::Display {
443 str.dimmed()
444}
445#[cfg(not(feature = "term-colors"))]
446fn prefix_style<'a>(str: &'a str) -> impl std::fmt::Display + 'a {
447 str
448}
449
450#[cfg(feature = "term-colors")]
451fn underline_style(str: &str) -> impl std::fmt::Display {
452 str.green()
453}
454#[cfg(not(feature = "term-colors"))]
455fn underline_style<'a>(str: &'a str) -> impl std::fmt::Display + 'a {
456 str
457}