use crate::types::*;
use super::{node_text, field_text, extract_doc_comment, extract_signature};
pub fn extract(tree: &tree_sitter::Tree, source: &[u8]) -> (Vec<Symbol>, Vec<Import>) {
let root = tree.root_node();
let mut symbols = Vec::new();
let mut imports = Vec::new();
let mut cursor = root.walk();
for child in root.children(&mut cursor) {
match child.kind() {
"function_declaration" => {
if let Some(sym) = extract_function(&child, source, None) {
symbols.push(sym);
}
}
"class_declaration" => {
if let Some(sym) = extract_class(&child, source) {
symbols.push(sym);
}
}
"import_statement" => {
imports.push(Import {
path: node_text(&child, source).to_string(),
alias: None,
span: Span::from_node(&child),
});
}
"export_statement" => {
let mut inner = child.walk();
for grandchild in child.children(&mut inner) {
match grandchild.kind() {
"function_declaration" => {
if let Some(sym) = extract_function(&grandchild, source, None) {
symbols.push(sym);
}
}
"class_declaration" => {
if let Some(sym) = extract_class(&grandchild, source) {
symbols.push(sym);
}
}
_ => {}
}
}
}
"lexical_declaration" => {
extract_lexical_declarations(&child, source, &mut symbols);
}
_ => {}
}
}
(symbols, imports)
}
fn extract_function(node: &tree_sitter::Node, source: &[u8], parent: Option<&str>) -> Option<Symbol> {
let name = field_text(node, "name", source)?;
Some(Symbol {
name: name.to_string(),
kind: if parent.is_some() { SymbolKind::Method } else { SymbolKind::Function },
span: Span::from_node(node),
signature: extract_signature(node, "body", source),
doc_comment: extract_doc_comment(node, source),
parent: parent.map(|s| s.to_string()),
children: Vec::new(),
})
}
fn extract_class(node: &tree_sitter::Node, source: &[u8]) -> Option<Symbol> {
let name = field_text(node, "name", source)?;
let mut methods = Vec::new();
if let Some(body) = node.child_by_field_name("body") {
let mut cursor = body.walk();
for child in body.children(&mut cursor) {
if child.kind() == "method_definition" {
if let Some(mname) = field_text(&child, "name", source) {
methods.push(Symbol {
name: mname.to_string(),
kind: SymbolKind::Method,
span: Span::from_node(&child),
signature: extract_signature(&child, "body", source),
doc_comment: extract_doc_comment(&child, source),
parent: Some(name.to_string()),
children: Vec::new(),
});
}
}
}
}
Some(Symbol {
name: name.to_string(),
kind: SymbolKind::Class,
span: Span::from_node(node),
signature: extract_signature(node, "body", source),
doc_comment: extract_doc_comment(node, source),
parent: None,
children: methods,
})
}
fn extract_lexical_declarations(node: &tree_sitter::Node, source: &[u8], symbols: &mut Vec<Symbol>) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
if child.kind() == "variable_declarator" {
if let Some(name) = field_text(&child, "name", source) {
if let Some(value) = child.child_by_field_name("value") {
let kind = value.kind();
if kind == "arrow_function" || kind == "function" {
symbols.push(Symbol {
name: name.to_string(),
kind: SymbolKind::Function,
span: Span::from_node(node),
signature: extract_signature(node, "body", source),
doc_comment: extract_doc_comment(node, source),
parent: None,
children: Vec::new(),
});
} else if name.chars().all(|c| c.is_uppercase() || c == '_') {
symbols.push(Symbol {
name: name.to_string(),
kind: SymbolKind::Const,
span: Span::from_node(node),
signature: node_text(node, source).to_string(),
doc_comment: extract_doc_comment(node, source),
parent: None,
children: Vec::new(),
});
}
}
}
}
}
}