tree_sitter.rs

 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}