Skip to main content

agentic_codebase/parse/
treesitter.rs

1//! Tree-sitter wrapper utilities shared across language parsers.
2
3use std::path::PathBuf;
4
5use crate::types::{AcbError, AcbResult, Span};
6
7/// Get the text content of a tree-sitter node.
8pub fn get_node_text<'a>(node: tree_sitter::Node, source: &'a str) -> &'a str {
9    &source[node.byte_range()]
10}
11
12/// Convert a tree-sitter node to a Span.
13pub fn node_to_span(node: tree_sitter::Node) -> Span {
14    let start = node.start_position();
15    let end = node.end_position();
16    Span::new(
17        start.row as u32 + 1,
18        start.column as u32,
19        end.row as u32 + 1,
20        end.column as u32,
21    )
22}
23
24/// Find the first child of a node with the given kind.
25pub fn find_child_by_kind<'a>(
26    node: tree_sitter::Node<'a>,
27    kind: &str,
28) -> Option<tree_sitter::Node<'a>> {
29    let mut cursor = node.walk();
30    let result = node
31        .children(&mut cursor)
32        .find(|child| child.kind() == kind);
33    result
34}
35
36/// Collect all direct children of a node with the given kind.
37pub fn collect_children_by_kind<'a>(
38    node: tree_sitter::Node<'a>,
39    kind: &str,
40) -> Vec<tree_sitter::Node<'a>> {
41    let mut cursor = node.walk();
42    node.children(&mut cursor)
43        .filter(|c| c.kind() == kind)
44        .collect()
45}
46
47/// Parse source code with error recovery using tree-sitter.
48pub fn parse_with_language(
49    source: &str,
50    language: tree_sitter::Language,
51) -> AcbResult<tree_sitter::Tree> {
52    let mut parser = tree_sitter::Parser::new();
53    parser
54        .set_language(&language)
55        .map_err(|e| AcbError::ParseError {
56            path: PathBuf::new(),
57            message: format!("Failed to set tree-sitter language: {}", e),
58        })?;
59    parser
60        .parse(source, None)
61        .ok_or_else(|| AcbError::ParseError {
62            path: PathBuf::new(),
63            message: "Failed to parse source".into(),
64        })
65}
66
67/// Count decision points in a subtree for cyclomatic complexity.
68pub fn count_complexity(node: tree_sitter::Node, decision_kinds: &[&str]) -> u32 {
69    let mut complexity = 1u32;
70    count_complexity_inner(node, decision_kinds, &mut complexity);
71    complexity
72}
73
74fn count_complexity_inner(node: tree_sitter::Node, decision_kinds: &[&str], count: &mut u32) {
75    if decision_kinds.contains(&node.kind()) {
76        *count += 1;
77    }
78    let mut cursor = node.walk();
79    for child in node.children(&mut cursor) {
80        count_complexity_inner(child, decision_kinds, count);
81    }
82}