use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::ops::Range;
use tree_sitter::{Language, Node, Parser, Point, Tree};
pub mod context;
pub mod walker;
pub use context::{AstContext, ContextNode};
pub use walker::AstWalker;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Position {
pub line: usize,
pub column: usize,
pub offset: usize,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct NodeMetadata {
pub is_scope: bool,
pub is_definition: bool,
pub is_declaration: bool,
pub visibility: Option<String>,
pub documentation: Option<String>,
}
#[derive(Debug, Clone)]
pub struct LanguageConfig {
pub scope_types: Vec<String>,
pub context_types: Vec<String>,
pub definition_types: Vec<String>,
pub declaration_types: Vec<String>,
pub name_fields: Vec<String>,
}
impl LanguageConfig {
pub fn for_language(lang: &str) -> Self {
match lang {
"rust" => Self {
scope_types: vec![
"function_item".to_string(),
"impl_item".to_string(),
"struct_item".to_string(),
"enum_item".to_string(),
"trait_item".to_string(),
"mod_item".to_string(),
],
context_types: vec![
"function_item".to_string(),
"impl_item".to_string(),
"struct_item".to_string(),
"enum_item".to_string(),
"trait_item".to_string(),
"mod_item".to_string(),
],
definition_types: vec![
"function_item".to_string(),
"struct_item".to_string(),
"enum_item".to_string(),
"trait_item".to_string(),
],
declaration_types: vec![
"function_signature".to_string(),
"trait_item".to_string(),
],
name_fields: vec!["name".to_string(), "identifier".to_string()],
},
_ => Self::default(),
}
}
}
impl Default for LanguageConfig {
fn default() -> Self {
Self {
scope_types: vec![
"function".to_string(),
"method".to_string(),
"class".to_string(),
"struct".to_string(),
"interface".to_string(),
],
context_types: vec![
"function".to_string(),
"method".to_string(),
"class".to_string(),
"struct".to_string(),
"interface".to_string(),
],
definition_types: vec![
"function".to_string(),
"class".to_string(),
"struct".to_string(),
],
declaration_types: vec![
"function_declaration".to_string(),
"class_declaration".to_string(),
],
name_fields: vec!["name".to_string(), "identifier".to_string()],
}
}
}
impl From<Point> for Position {
fn from(point: Point) -> Self {
Self {
line: point.row,
column: point.column,
offset: 0, }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AstNode {
pub kind: String,
pub name: Option<String>,
pub start: Position,
pub end: Position,
pub range: Range<usize>,
pub depth: usize,
pub parent: Option<usize>,
pub children: Vec<usize>,
pub metadata: NodeMetadata,
}
pub struct AstAnalyzer {
parser: Parser,
_language: Language,
}
#[derive(Debug)]
pub struct ParsedAst {
pub tree: Tree,
pub nodes: Vec<AstNode>,
pub source: String,
}
impl ParsedAst {
pub fn get_context_for_line(&self, line_idx: usize) -> Option<AstContext> {
let mut context = AstContext::new();
for node in &self.nodes {
if node.start.line <= line_idx && line_idx <= node.end.line {
context.add_node(node, &self.nodes);
}
}
if context.nodes.is_empty() {
None
} else {
context.build(&self.source);
Some(context)
}
}
}
impl AstAnalyzer {
pub fn new(language: Language) -> Result<Self> {
let mut parser = Parser::new();
parser.set_language(language)?;
Ok(Self {
parser,
_language: language,
})
}
pub fn analyze(&mut self, code: &str) -> Option<Tree> {
self.parser.parse(code, None)
}
pub fn parse(&mut self, source: &str) -> Result<ParsedAst> {
let tree = self.analyze(source)
.ok_or_else(|| anyhow::anyhow!("Failed to parse source code"))?;
let nodes = self.extract_nodes(&tree, source);
Ok(ParsedAst {
tree,
nodes,
source: source.to_string(),
})
}
pub fn extract_nodes(&self, tree: &Tree, code: &str) -> Vec<AstNode> {
let mut nodes = Vec::new();
let walker = tree.walk();
self.extract_nodes_recursive(walker.node(), code, &mut nodes, 0, None);
nodes
}
fn extract_nodes_recursive(
&self,
node: Node,
code: &str,
nodes: &mut Vec<AstNode>,
depth: usize,
parent_index: Option<usize>,
) -> usize {
let node_index = nodes.len();
let new_node = AstNode {
kind: node.kind().to_string(),
name: self.get_node_name(node, code),
start: node.start_position().into(),
end: node.end_position().into(),
range: node.byte_range(),
depth,
parent: parent_index,
children: Vec::new(),
metadata: NodeMetadata::default(),
};
nodes.push(new_node);
for child in node.children(&mut node.walk()) {
let child_index =
self.extract_nodes_recursive(child, code, nodes, depth + 1, Some(node_index));
if let Some(parent_node) = nodes.get_mut(node_index) {
parent_node.children.push(child_index);
}
}
node_index
}
fn get_node_name(&self, node: Node, code: &str) -> Option<String> {
let name_node = node
.children(&mut node.walk())
.find(|n| n.kind().ends_with("identifier"));
if let Some(name_node) = name_node {
return Some(code[name_node.byte_range()].to_string());
}
None
}
}