#[cfg(feature = "tree-sitter")]
use tree_sitter::Node;
#[cfg(feature = "tree-sitter")]
pub(crate) fn for_each_descendant<'tree>(root: Node<'tree>, mut visit: impl FnMut(Node<'tree>)) {
let mut stack = vec![root];
while let Some(node) = stack.pop() {
visit(node);
push_children_in_order(&mut stack, node);
}
}
#[cfg(feature = "tree-sitter")]
pub(crate) fn for_each_descendant_pruned<'tree>(
root: Node<'tree>,
mut descend: impl FnMut(Node<'tree>) -> bool,
) {
let mut stack = vec![root];
while let Some(node) = stack.pop() {
if descend(node) {
push_children_in_order(&mut stack, node);
}
}
}
#[cfg(feature = "tree-sitter")]
pub(crate) fn find_descendant_by_kind<'tree>(root: Node<'tree>, kind: &str) -> Option<Node<'tree>> {
let mut stack = vec![root];
while let Some(node) = stack.pop() {
if node.kind() == kind {
return Some(node);
}
push_children_in_order(&mut stack, node);
}
None
}
#[cfg(feature = "tree-sitter")]
fn push_children_in_order<'tree>(stack: &mut Vec<Node<'tree>>, node: Node<'tree>) {
let mark = stack.len();
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
stack.push(child);
}
stack[mark..].reverse();
}
#[cfg(all(test, feature = "tree-sitter"))]
mod tests {
use super::*;
use tree_sitter::Parser;
fn parse_rust(src: &str) -> tree_sitter::Tree {
let mut parser = Parser::new();
parser
.set_language(&tree_sitter_rust::LANGUAGE.into())
.unwrap();
parser.parse(src, None).unwrap()
}
#[test]
fn visits_every_node_in_preorder() {
let tree = parse_rust("fn a() { let x = 1; }");
let mut kinds = Vec::new();
for_each_descendant(tree.root_node(), |n| kinds.push(n.kind()));
assert_eq!(kinds.first(), Some(&"source_file"));
assert!(kinds.contains(&"function_item"));
assert!(kinds.contains(&"let_declaration"));
}
#[test]
fn pruned_skips_subtree() {
let tree = parse_rust("fn a() { let x = 1; }");
let mut visited_let = false;
for_each_descendant_pruned(tree.root_node(), |n| {
if n.kind() == "let_declaration" {
visited_let = true;
}
n.kind() != "block"
});
assert!(!visited_let);
}
#[test]
fn find_descendant_returns_first_match() {
let tree = parse_rust("fn a() { let x = 1; }");
let found = find_descendant_by_kind(tree.root_node(), "let_declaration");
assert!(found.is_some());
assert!(find_descendant_by_kind(tree.root_node(), "no_such_kind").is_none());
}
#[test]
fn deeply_nested_tree_does_not_overflow() {
let depth = 20_000;
let src = format!("fn f() {{ {}0{} }}", "(".repeat(depth), ")".repeat(depth));
let tree = parse_rust(&src);
let mut count = 0usize;
for_each_descendant(tree.root_node(), |_| count += 1);
assert!(count > depth, "should visit the deep parenthesis chain");
}
}