tree_sitter_cli/fuzz/
scope_sequence.rs

1use tree_sitter::{Point, Range, Tree};
2
3#[derive(Debug)]
4pub struct ScopeSequence(Vec<ScopeStack>);
5
6type ScopeStack = Vec<&'static str>;
7
8impl ScopeSequence {
9    #[must_use]
10    pub fn new(tree: &Tree) -> Self {
11        let mut result = Self(Vec::new());
12        let mut scope_stack = Vec::new();
13
14        let mut cursor = tree.walk();
15        let mut visited_children = false;
16        loop {
17            let node = cursor.node();
18            for _ in result.0.len()..node.start_byte() {
19                result.0.push(scope_stack.clone());
20            }
21            if visited_children {
22                for _ in result.0.len()..node.end_byte() {
23                    result.0.push(scope_stack.clone());
24                }
25                scope_stack.pop();
26                if cursor.goto_next_sibling() {
27                    visited_children = false;
28                } else if !cursor.goto_parent() {
29                    break;
30                }
31            } else {
32                scope_stack.push(cursor.node().kind());
33                if !cursor.goto_first_child() {
34                    visited_children = true;
35                }
36            }
37        }
38
39        result
40    }
41
42    pub fn check_changes(
43        &self,
44        other: &Self,
45        text: &[u8],
46        known_changed_ranges: &[Range],
47    ) -> Result<(), String> {
48        let mut position = Point { row: 0, column: 0 };
49        for i in 0..(self.0.len().max(other.0.len())) {
50            let stack = &self.0.get(i);
51            let other_stack = &other.0.get(i);
52            if *stack != *other_stack && ![b'\r', b'\n'].contains(&text[i]) {
53                let containing_range = known_changed_ranges
54                    .iter()
55                    .find(|range| range.start_point <= position && position < range.end_point);
56                if containing_range.is_none() {
57                    let line = &text[(i - position.column)..]
58                        .split(|c| *c == b'\n')
59                        .next()
60                        .unwrap();
61                    return Err(format!(
62                        concat!(
63                            "Position: {}\n",
64                            "Byte offset: {}\n",
65                            "Line: {}\n",
66                            "{}^\n",
67                            "Old scopes: {:?}\n",
68                            "New scopes: {:?}\n",
69                            "Invalidated ranges: {:?}",
70                        ),
71                        position,
72                        i,
73                        String::from_utf8_lossy(line),
74                        String::from(" ").repeat(position.column + "Line: ".len()),
75                        stack,
76                        other_stack,
77                        known_changed_ranges,
78                    ));
79                }
80            }
81
82            if text[i] == b'\n' {
83                position.row += 1;
84                position.column = 0;
85            } else {
86                position.column += 1;
87            }
88        }
89        Ok(())
90    }
91}