1use language::SyntaxLayer;
2
3pub fn count_tree_sitter_errors<'a>(layers: impl Iterator<Item = SyntaxLayer<'a>>) -> usize {
4 let mut total_count: usize = 0;
5 for layer in layers {
6 let node = layer.node();
7 let mut cursor = node.walk();
8 'layer: loop {
9 let current = cursor.node();
10 if current.is_error() || current.is_missing() {
11 total_count += 1;
12 }
13 if current.has_error() && cursor.goto_first_child() {
14 continue;
15 }
16 if cursor.goto_next_sibling() {
17 continue;
18 }
19 loop {
20 if !cursor.goto_parent() {
21 break 'layer;
22 }
23 if cursor.goto_next_sibling() {
24 continue;
25 }
26 }
27 }
28 }
29 total_count
30}
31
32#[cfg(test)]
33mod tests {
34 use std::ops::Range;
35
36 use super::count_tree_sitter_errors;
37 use gpui::{AppContext as _, TestAppContext};
38 use language::{Buffer, BufferSnapshot, rust_lang};
39
40 fn error_count_in_range(edited_buffer_snapshot: &BufferSnapshot, range: Range<usize>) -> usize {
41 let layers = edited_buffer_snapshot.syntax_layers_for_range(range, true);
42 count_tree_sitter_errors(layers)
43 }
44
45 fn rust_snapshot(text: &str, cx: &mut TestAppContext) -> BufferSnapshot {
46 let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(rust_lang(), cx));
47 while buffer.read_with(cx, |buffer, _| buffer.is_parsing()) {
48 cx.run_until_parked();
49 }
50 buffer.read_with(cx, |buffer, _| buffer.snapshot())
51 }
52
53 #[gpui::test]
54 async fn counts_no_errors_for_valid_rust(cx: &mut TestAppContext) {
55 let text = "fn helper(value: usize) -> usize {\n value + 1\n}\n";
56 let snapshot = rust_snapshot(text, cx);
57
58 assert_eq!(error_count_in_range(&snapshot, 0..snapshot.text.len()), 0);
59 }
60
61 #[gpui::test]
62 async fn counts_errors_for_invalid_rust(cx: &mut TestAppContext) {
63 let text = "fn helper(value: usize) -> usize {\n let total = ;\n total\n}\n";
64 let snapshot = rust_snapshot(text, cx);
65
66 assert_eq!(error_count_in_range(&snapshot, 0..snapshot.text.len()), 1);
67 }
68
69 #[gpui::test]
70 async fn counts_no_errors_for_subrange_of_valid_rust(cx: &mut TestAppContext) {
71 let text = "fn first() -> usize {\n let value = 1;\n value + 1\n}\n";
72 let snapshot = rust_snapshot(text, cx);
73 let body_start = text.find("let value").unwrap();
74 let body_end = body_start + "let value = 1;".len();
75
76 assert_eq!(error_count_in_range(&snapshot, body_start..body_end), 0);
77 }
78
79 #[gpui::test]
80 async fn counts_errors_for_subrange_of_invalid_rust(cx: &mut TestAppContext) {
81 let text = "fn second() -> usize {\n let broken = ;\n broken\n}\n";
82 let snapshot = rust_snapshot(text, cx);
83 let error_start = text.find("let broken = ;").unwrap();
84 let error_end = error_start + "let broken = ;".len();
85
86 assert_eq!(error_count_in_range(&snapshot, error_start..error_end), 1);
87 }
88}