use crate::core::traits::{ParseTree, TreeNode};
use crate::core::{Language, SourceSpan};
use streaming_iterator::StreamingIterator;
pub struct ZeroCopyParseTree {
tree: tree_sitter::Tree,
source: String,
language: Language,
}
impl ZeroCopyParseTree {
pub fn new(tree: tree_sitter::Tree, source: String, language: Language) -> Self {
Self {
tree,
source,
language,
}
}
pub fn ts_tree(&self) -> &tree_sitter::Tree {
&self.tree
}
pub fn source_code(&self) -> &str {
&self.source
}
pub fn language(&self) -> Language {
self.language
}
pub fn query_matches(&self, query_source: &str) -> Result<Vec<QueryMatch>, crate::core::Error> {
let ts_lang = self.tree.language();
let query = tree_sitter::Query::new(&ts_lang, query_source)
.map_err(|e| crate::core::Error::parse(format!("Invalid query: {e}")))?;
let mut cursor = tree_sitter::QueryCursor::new();
let mut matches = cursor.matches(&query, self.tree.root_node(), self.source.as_bytes());
let mut results = Vec::new();
while let Some(m) = matches.next() {
let mut captures = Vec::new();
for cap in m.captures {
let node = cap.node;
let text = &self.source[node.byte_range()];
let name = query.capture_names()[cap.index as usize].to_string();
captures.push(QueryCapture {
name,
text: text.to_string(),
start_byte: node.start_byte(),
end_byte: node.end_byte(),
start_line: node.start_position().row,
end_line: node.end_position().row,
});
}
results.push(QueryMatch {
pattern_index: m.pattern_index,
captures,
});
}
Ok(results)
}
pub fn text_for_range(&self, start: usize, end: usize) -> &str {
&self.source[start..end]
}
pub fn node_span(&self, node: &tree_sitter::Node) -> SourceSpan {
SourceSpan::new(node.start_byte(), node.end_byte())
}
}
impl std::fmt::Debug for ZeroCopyParseTree {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ZeroCopyParseTree")
.field("language", &self.language)
.field("source_len", &self.source.len())
.finish()
}
}
impl ParseTree for ZeroCopyParseTree {
fn language(&self) -> Language {
self.language
}
fn root(&self) -> Box<dyn TreeNode + '_> {
Box::new(ZeroCopyTreeNode {
node: self.tree.root_node(),
source: &self.source,
})
}
fn debug_string(&self) -> String {
self.tree.root_node().to_sexp()
}
fn source(&self) -> &str {
&self.source
}
}
pub struct ZeroCopyTreeNode<'a> {
node: tree_sitter::Node<'a>,
source: &'a str,
}
impl<'a> TreeNode for ZeroCopyTreeNode<'a> {
fn node_type(&self) -> &str {
self.node.kind()
}
fn children(&self) -> Vec<Box<dyn TreeNode + '_>> {
let mut cursor = self.node.walk();
self.node
.children(&mut cursor)
.map(|child| {
Box::new(ZeroCopyTreeNode {
node: child,
source: self.source,
}) as Box<dyn TreeNode + '_>
})
.collect()
}
fn text(&self) -> &str {
&self.source[self.node.byte_range()]
}
fn start_byte(&self) -> usize {
self.node.start_byte()
}
fn end_byte(&self) -> usize {
self.node.end_byte()
}
fn start_line(&self) -> usize {
self.node.start_position().row
}
fn start_column(&self) -> usize {
self.node.start_position().column
}
fn end_line(&self) -> usize {
self.node.end_position().row
}
fn end_column(&self) -> usize {
self.node.end_position().column
}
}
#[derive(Debug, Clone)]
pub struct QueryMatch {
pub pattern_index: usize,
pub captures: Vec<QueryCapture>,
}
#[derive(Debug, Clone)]
pub struct QueryCapture {
pub name: String,
pub text: String,
pub start_byte: usize,
pub end_byte: usize,
pub start_line: usize,
pub end_line: usize,
}