Rust语法树解析库tree-sitter-jsdoc的使用,实现高效JavaScript文档注释提取与解析

Rust语法树解析库tree-sitter-jsdoc的使用,实现高效JavaScript文档注释提取与解析

tree-sitter-jsdoc是一个用于tree-sitter的JSDoc语法解析器。以下是使用tree-sitter-jsdoc提取和解析JavaScript文档注释的完整示例:

use tree_sitter::{Parser, Node};
use tree_sitter_jsdoc as jsdoc;

fn main() {
    // 1. 初始化parser并设置语言
    let mut parser = Parser::new();
    parser.set_language(jsdoc::language()).unwrap();

    // 2. 示例JSDoc注释
    let source_code = r#"
        /**
         * Calculates the sum of two numbers
         * [@param](/user/param) {number} a - first number
         * [@param](/user/param) {number} b - second number
         * [@returns](/user/returns) {number} sum of a and b
         */
        function add(a, b) {
            return a + b;
        }
    "#;

    // 3. 解析代码
    let tree = parser.parse(source_code, None).unwrap();
    let root_node = tree.root_node();

    // 4. 遍历语法树查找JSDoc注释
    let mut cursor = root_node.walk();
    for child in root_node.children(&mut cursor) {
        if child.kind() == "comment" {
            // 5. 解析JSDoc内容
            parse_jsdoc(child, source_code);
        }
    }
}

fn parse_jsdoc(node: Node, source_code: &str) {
    // 提取原始注释文本
    let text = node.utf8_text(source_code.as_bytes()).unwrap();
    
    // 初始化JSDoc parser
    let mut parser = Parser::new();
    parser.set_language(jsdoc::language()).unwrap();
    
    // 解析JSDoc注释
    let tree = parser.parse(text, None).unwrap();
    let root = tree.root_node();
    
    // 打印JSDoc结构
    print_jsdoc_structure(root, text, 0);
}

fn print_jsdoc_structure(node: Node, source: &str, depth: usize) {
    let indent = "  ".repeat(depth);
    let text = node.utf8_text(source.as_bytes()).unwrap_or("");
    
    println!("{}{}: {}", indent, node.kind(), text.trim());
    
    let mut cursor = node.walk();
    for child in node.children(&mut cursor) {
        print_jsdoc_structure(child, source, depth + 1);
    }
}

这个示例展示了如何:

  1. 初始化tree-sitter parser并设置jsdoc语言
  2. 解析包含JSDoc注释的JavaScript代码
  3. 遍历语法树查找注释节点
  4. 专门解析JSDoc注释内容
  5. 打印出JSDoc的结构化信息

运行结果会显示JSDoc注释的语法树结构,包括:

要使用这个库,需要在Cargo.toml中添加依赖:

[dependencies]
tree-sitter = "0.20"
tree-sitter-jsdoc = "0.23.2"

该库适用于需要分析JavaScript文档注释的工具开发,如文档生成器、IDE插件或代码质量检查工具。

完整示例demo

以下是基于上述内容的完整示例代码,增加了更多注释和功能展示:

use tree_sitter::{Parser, Node};
use tree_sitter_jsdoc as jsdoc;

fn main() {
    // 初始化parser并设置jsdoc语言
    let mut parser = Parser::new();
    parser.set_language(jsdoc::language()).expect("Error loading jsdoc grammar");

    // 示例JavaScript代码包含多个JSDoc注释
    let source_code = r#"
        /**
         * 计算两个数字的和
         * [@param](/user/param) {number} a - 第一个数字
         * [@param](/user/param) {number} b - 第二个数字
         * [@returns](/user/returns) {number} 两个数字的和
         * [@example](/user/example)
         * // 返回3
         * add(1, 2);
         */
        function add(a, b) {
            return a + b;
        }

        /**
         * 用户类
         * [@class](/user/class)
         * [@property](/user/property) {string} name - 用户名
         * [@property](/user/property) {number} age - 用户年龄
         */
        class User {
            constructor(name, age) {
                this.name = name;
                this.age = age;
            }
        }
    "#;

    // 解析代码
    let tree = parser.parse(source_code, None).unwrap();
    let root_node = tree.root_node();

    println!("发现JSDoc注释:");
    
    // 遍历语法树查找所有注释节点
    let mut cursor = root_node.walk();
    for child in root_node.children(&mut cursor) {
        if child.kind() == "comment" {
            println!("\n===== 发现JSDoc注释 =====\n");
            parse_jsdoc(child, source_code);
        }
    }
}

/// 解析单个JSDoc注释节点
fn parse_jsdoc(node: Node, source_code: &str) {
    // 提取原始注释文本
    let text = node.utf8_text(source_code.as_bytes()).unwrap();
    
    // 初始化专门用于JSDoc的parser
    let mut parser = Parser::new();
    parser.set_language(jsdoc::language()).expect("Error loading jsdoc grammar");
    
    // 解析JSDoc注释
    let tree = parser.parse(text, None).unwrap();
    let root = tree.root_node();
    
    // 打印JSDoc结构
    print_jsdoc_structure(root, text, 0);
    
    // 提取结构化信息
    extract_jsdoc_info(root, text);
}

/// 递归打印JSDoc语法树结构
fn print_jsdoc_structure(node: Node, source: &str, depth: usize) {
    let indent = "  ".repeat(depth);
    let text = node.utf8_text(source.as_bytes()).unwrap_or("");
    
    println!("{}{}: {}", indent, node.kind(), text.trim());
    
    let mut cursor = node.walk();
    for child in node.children(&mut cursor) {
        print_jsdoc_structure(child, source, depth + 1);
    }
}

/// 从JSDoc节点提取结构化信息
fn extract_jsdoc_info(node: Node, source: &str) {
    let mut cursor = node.walk();
    
    // 提取描述文本
    if node.kind() == "description" {
        let text = node.utf8_text(source.as_bytes()).unwrap_or("").trim();
        println!("\n描述: {}", text);
    }
    
    // 处理标签
    if node.kind() == "tag" {
        let tag_name = node.child_by_field_name("name").and_then(|n| n.utf8_text(source.as_bytes()).ok()).unwrap_or("");
        let tag_type = node.child_by_field_name("type").and_then(|n| n.utf8_text(source.as_bytes()).ok()).unwrap_or("");
        let tag_text = node.child_by_field_name("text").and_then(|n| n.utf8_text(source.as_bytes()).ok()).unwrap_or("").trim();
        
        match tag_name {
            "[@param](/user/param)" => println!("参数: 类型={}, 描述={}", tag_type, tag_text),
            "[@returns](/user/returns)" => println!("返回值: 类型={}, 描述={}", tag_type, tag_text),
            "[@example](/user/example)" => println!("示例: {}", tag_text),
            "[@class](/user/class)" => println!("这是一个类定义"),
            "[@property](/user/property)" => println!("属性: 类型={}, 描述={}", tag_type, tag_text),
            _ => println!("其他标签: {} (类型={})", tag_name, tag_type),
        }
    }
    
    // 递归处理子节点
    for child in node.children(&mut cursor) {
        extract_jsdoc_info(child, source);
    }
}

这个增强版示例展示了:

  1. 更完整的JSDoc注释示例,包含函数和类的文档
  2. 增加了中文注释和描述
  3. 添加了结构化信息提取功能
  4. 支持更多JSDoc标签如@class@property@example
  5. 改进了输出格式,更易读

运行此代码将输出每个JSDoc注释的完整语法树结构,并提取出关键信息以结构化方式显示。


1 回复

tree-sitter-jsdoc: Rust语法树解析库使用指南

介绍

tree-sitter-jsdoc 是一个基于 tree-sitter 的 Rust 库,专门用于解析 JavaScript 文档注释(JSDoc)。它能够高效地从 JavaScript 代码中提取和解析 JSDoc 注释,并将其转换为结构化的数据,便于进一步处理和分析。

主要特性

  • 高性能的 JSDoc 注释解析
  • 准确的语法树构建
  • 支持大多数常见的 JSDoc 标签
  • 易于集成的 Rust API

安装

在 Cargo.toml 中添加依赖:

[dependencies]
tree-sitter-jsdoc = "0.20"
tree-sitter = "0.20"

基本使用方法

1. 解析单个 JSDoc 注释

use tree_sitter_jsdoc::parse_jsdoc;

fn main() {
    let jsdoc_comment = r#"
    /**
     * Calculates the sum of two numbers
     * @param {number} a - first number
     * @param {number} b - second number
     * @returns {number} sum of a and b
     */
    "#;

    let parsed = parse_jsdoc(jsdoc_comment).unwrap();
    println!("{:#?}", parsed);
}

2. 从 JavaScript 文件中提取 JSDoc

use tree_sitter::{Parser, Tree};
use tree_sitter_jsdoc::extract_jsdoc_comments;

fn main() {
    let js_code = r#"
    /**
     * @module math
     */
    
    /**
     * Adds two numbers
     * @param {number} x 
     * @param {number} y
     * @returns {number}
     */
    function add(x, y) {
        return x + y;
    }
    "#;

    let mut parser = Parser::new();
    parser.set_language(tree_sitter_javascript::language()).unwrap();
    let tree = parser.parse(js_code, None).unwrap();
    
    let jsdocs = extract_jsdoc_comments(&tree, js_code);
    for jsdoc in jsdocs {
        println!("Found JSDoc:\n{}", jsdoc.text);
        let parsed = parse_jsdoc(&jsdoc.text).unwrap();
        println!("Parsed: {:#?}", parsed);
    }
}

完整示例代码

下面是一个完整的示例,展示如何使用 tree-sitter-jsdoc 从 JavaScript 文件中提取 JSDoc 注释并生成文档摘要:

use tree_sitter::{Parser, Tree};
use tree_sitter_jsdoc::{extract_jsdoc_comments, parse_jsdoc, JsDocTag};

fn main() {
    // 示例 JavaScript 代码包含多个 JSDoc 注释
    let js_code = r#"
    /**
     * @module math/operations
     * @description Math operations library
     */
    
    /**
     * Adds two numbers
     * @param {number} x - The first number
     * @param {number} y - The second number
     * @returns {number} The sum of x and y
     * @example
     * add(2, 3) // returns 5
     */
    function add(x, y) {
        return x + y;
    }
    
    /**
     * Multiplies two numbers
     * @param {number} a - First factor
     * @param {number} b - Second factor
     * @returns {number} The product of a and b
     * @custom This is a custom tag
     */
    function multiply(a, b) {
        return a * b;
    }
    "#;

    // 1. 初始化 tree-sitter 解析器
    let mut parser = Parser::new();
    parser.set_language(tree_sitter_javascript::language()).unwrap();
    
    // 2. 解析 JavaScript 代码
    let tree = parser.parse(js_code, None).unwrap();
    
    // 3. 提取所有 JSDoc 注释
    let jsdocs = extract_jsdoc_comments(&tree, js_code);
    
    // 4. 处理每个 JSDoc 注释
    for (i, jsdoc) in jsdocs.iter().enumerate() {
        println!("\n=== JSDoc {} ===", i + 1);
        
        // 4.1 打印原始 JSDoc 文本
        println!("Raw JSDoc:\n{}", jsdoc.text);
        
        // 4.2 解析 JSDoc
        match parse_jsdoc(&jsdoc.text) {
            Ok(parsed) => {
                // 4.3 生成并打印摘要
                let summary = generate_summary(&parsed);
                println!("\nGenerated Summary:\n{}", summary);
                
                // 4.4 处理自定义标签
                handle_custom_tags(&parsed);
            }
            Err(e) => {
                eprintln!("Failed to parse JSDoc: {}", e);
            }
        }
    }
}

/// 生成 JSDoc 注释的文本摘要
fn generate_summary(parsed: &tree_sitter_jsdoc::JsDoc) -> String {
    let mut summary = String::new();
    
    // 添加描述
    if let Some(desc) = &parsed.description {
        summary.push_str(&format!("Description: {}\n", desc));
    }
    
    // 处理所有标签
    for tag in &parsed.tags {
        match tag {
            JsDocTag::Param(param) => {
                summary.push_str(&format!(
                    "Parameter: {} ({}): {}\n", 
                    param.name, 
                    param.type_name.as_deref().unwrap_or("unknown"),
                    param.description.as_deref().unwrap_or("")
                ));
            }
            JsDocTag::Returns(ret) => {
                summary.push_str(&format!(
                    "Returns: {}: {}\n",
                    ret.type_name.as_deref().unwrap_or("unknown"),
                    ret.description.as_deref().unwrap_or("")
                ));
            }
            JsDocTag::Module(module) => {
                summary.push_str(&format!("Module: {}\n", module.name));
            }
            JsDocTag::Description(desc) => {
                summary.push_str(&format!("Description: {}\n", desc.text));
            }
            JsDocTag::Example(example) => {
                summary.push_str(&format!("Example: {}\n", example.text));
            }
            _ => {}
        }
    }
    
    summary
}

/// 处理自定义标签
fn handle_custom_tags(parsed: &tree_sitter_jsdoc::JsDoc) {
    for tag in &parsed.tags {
        if let JsDocTag::Unknown(tag_name, content) = tag {
            println!("Found custom tag @{}: {}", tag_name, content.as_deref().unwrap_or(""));
        }
    }
}

示例输出

运行上述代码将输出类似以下内容:

=== JSDoc 1 ===
Raw JSDoc:
/**
     * @module math/operations
     * @description Math operations library
     */

Generated Summary:
Module: math/operations
Description: Math operations library

=== JSDoc 2 ===
Raw JSDoc:
/**
     * Adds two numbers
     * @param {number} x - The first number
     * @param {number} y - The second number
     * @returns {number} The sum of x and y
     * @example
     * add(2, 3) // returns 5
     */

Generated Summary:
Description: Adds two numbers
Parameter: x (number): The first number
Parameter: y (number): The second number
Returns: number: The sum of x and y
Example: 
     * add(2, 3) // returns 5

=== JSDoc 3 ===
Raw JSDoc:
/**
     * Multiplies two numbers
     * @param {number} a - First factor
     * @param {number} b - Second factor
     * @returns {number} The product of a and b
     * @custom This is a custom tag
     */

Generated Summary:
Description: Multiplies two numbers
Parameter: a (number): First factor
Parameter: b (number): Second factor
Returns: number: The product of a and b

Found custom tag @custom: This is a custom tag
回到顶部