car-ast 0.13.0

Tree-sitter AST parsing for code-aware inference
Documentation
use super::{extract_doc_comment, extract_signature, field_text, node_text};
use crate::types::*;

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) {
                    symbols.push(sym);
                }
            }
            "method_declaration" => {
                if let Some(sym) = extract_method(&child, source) {
                    symbols.push(sym);
                }
            }
            "type_declaration" => {
                extract_type_decl(&child, source, &mut symbols);
            }
            "import_declaration" => {
                // Go imports can be single or grouped
                let text = node_text(&child, source);
                imports.push(Import {
                    path: text.to_string(),
                    alias: None,
                    span: Span::from_node(&child),
                });
            }
            "const_declaration" | "var_declaration" => {
                extract_var_const(&child, source, &mut symbols);
            }
            _ => {}
        }
    }

    (symbols, imports)
}

fn extract_function(node: &tree_sitter::Node, source: &[u8]) -> Option<Symbol> {
    let name = field_text(node, "name", source)?;
    Some(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(),
    })
}

fn extract_method(node: &tree_sitter::Node, source: &[u8]) -> Option<Symbol> {
    let name = field_text(node, "name", source)?;
    // Get receiver type
    let receiver = node
        .child_by_field_name("receiver")
        .map(|r| node_text(&r, source).to_string());

    Some(Symbol {
        name: name.to_string(),
        kind: SymbolKind::Method,
        span: Span::from_node(node),
        signature: extract_signature(node, "body", source),
        doc_comment: extract_doc_comment(node, source),
        parent: receiver,
        children: Vec::new(),
    })
}

fn extract_type_decl(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() == "type_spec" {
            if let Some(name) = field_text(&child, "name", source) {
                if let Some(type_node) = child.child_by_field_name("type") {
                    let kind = match type_node.kind() {
                        "struct_type" => SymbolKind::Struct,
                        "interface_type" => SymbolKind::Interface,
                        _ => SymbolKind::TypeAlias,
                    };

                    let mut methods = Vec::new();
                    if kind == SymbolKind::Interface {
                        // Extract interface methods
                        let mut inner = type_node.walk();
                        for field in type_node.children(&mut inner) {
                            if field.kind() == "method_spec" {
                                if let Some(mname) = field_text(&field, "name", source) {
                                    methods.push(Symbol {
                                        name: mname.to_string(),
                                        kind: SymbolKind::Method,
                                        span: Span::from_node(&field),
                                        signature: node_text(&field, source).to_string(),
                                        doc_comment: extract_doc_comment(&field, source),
                                        parent: Some(name.to_string()),
                                        children: Vec::new(),
                                    });
                                }
                            }
                        }
                    }

                    symbols.push(Symbol {
                        name: name.to_string(),
                        kind,
                        span: Span::from_node(&child),
                        signature: node_text(&child, source).to_string(),
                        doc_comment: extract_doc_comment(node, source),
                        parent: None,
                        children: methods,
                    });
                }
            }
        }
    }
}

fn extract_var_const(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() == "const_spec" || child.kind() == "var_spec" {
            if let Some(name_node) = child.child_by_field_name("name") {
                let name = node_text(&name_node, source);
                if !name.is_empty() {
                    symbols.push(Symbol {
                        name: name.to_string(),
                        kind: SymbolKind::Const,
                        span: Span::from_node(&child),
                        signature: node_text(&child, source).to_string(),
                        doc_comment: extract_doc_comment(&child, source),
                        parent: None,
                        children: Vec::new(),
                    });
                }
            }
        }
    }
}