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);
}
}
这个示例展示了如何:
- 初始化tree-sitter parser并设置jsdoc语言
- 解析包含JSDoc注释的JavaScript代码
- 遍历语法树查找注释节点
- 专门解析JSDoc注释内容
- 打印出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);
}
}
这个增强版示例展示了:
运行此代码将输出每个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