Rust语法树注释处理库swc_node_comments的使用,高效解析和操作JavaScript/TypeScript代码注释
Rust语法树注释处理库swc_node_comments的使用,高效解析和操作JavaScript/TypeScript代码注释
安装
在项目目录中运行以下Cargo命令:
cargo add swc_node_comments
或者在Cargo.toml中添加以下行:
swc_node_comments = "14.0.0"
使用示例
以下是使用swc_node_comments库解析和操作JavaScript/TypeScript代码注释的完整示例:
use swc_common::comments::{Comment, CommentKind, Comments};
use swc_node_comments::SwcComments;
fn main() {
// 创建注释处理器实例
let comments = SwcComments::default();
// 添加行注释
comments.add_comment(
1, // 行号
0, // 列号
Comment {
kind: CommentKind::Line,
span: Default::default(),
text: "这是一个行注释".into(),
},
);
// 添加块注释
comments.add_comment(
3, // 行号
0, // 列号
Comment {
kind: CommentKind::Block,
span: Default::default(),
text: "这是一个块注释".into(),
},
);
// 获取特定行上的注释
let line_1_comments = comments.get_leading(1);
if let Some(comments) = line_1_comments {
for comment in comments {
println!("找到注释: {}", comment.text);
}
}
// 遍历所有注释
comments.with_leading(|line, comments| {
println!("行 {} 有 {} 个前导注释", line, comments.len());
});
// 移除注释
comments.take_leading(1);
}
功能说明
- 添加注释:使用
add_comment
方法可以添加行注释或块注释 - 获取注释:
get_leading
和get_trailing
方法可以获取特定位置的前导/后置注释 - 遍历注释:
with_leading
和with_trailing
方法可以遍历所有注释 - 移除注释:
take_leading
和take_trailing
方法可以移除注释
完整示例代码
use swc_common::comments::{Comment, CommentKind, Comments};
use swc_node_comments::SwcComments;
fn process_comments() {
// 初始化注释处理器
let comments = SwcComments::default();
// 示例1: 添加不同类型的注释
comments.add_comment(
1,
0,
Comment {
kind: CommentKind::Line,
span: Default::default(),
text: "// 这是一个示例行注释".into(),
},
);
comments.add_comment(
2,
4,
Comment {
kind: CommentKind::Block,
span: Default::default(),
text: "/* 这是一个示例块注释 */".into(),
},
);
// 示例2: 获取并打印注释
if let Some(leading) = comments.get_leading(1) {
for comment in leading {
match comment.kind {
CommentKind::Line => println!("行注释: {}", comment.text),
CommentKind::Block => println!("块注释: {}", comment.text),
}
}
}
// 示例3: 遍历所有前导注释
comments.with_leading(|line, cmts| {
println!("在行 {} 发现 {} 个前导注释", line, cmts.len());
});
// 示例4: 移除注释
let removed = comments.take_leading(1);
println!("移除了 {} 个注释", removed.unwrap().len());
}
fn main() {
process_comments();
}
功能说明
- 注释类型处理:支持行注释(//)和块注释(/**/)两种类型
- 位置精准控制:可以精确到行号和列号添加注释
- 批量操作:支持批量获取、遍历和移除注释
- 线程安全:设计为线程安全的注释处理器
高级用法
// 检查是否存在特定注释
fn has_specific_comment(comments: &SwcComments, line: u32, text: &str) -> bool {
comments.get_leading(line).map_or(false, |cmts| {
cmts.iter().any(|c| c.text.contains(text))
})
}
// 复制注释到新位置
fn copy_comment(comments: &SwcComments, from_line: u32, to_line: u32) {
if let Some(src_cmts) = comments.get_leading(from_line) {
for comment in src_cmts {
comments.add_comment(
to_line,
comment.span.lo.0,
Comment {
kind: comment.kind,
span: Default::default(),
text: comment.text.clone(),
},
);
}
}
}
1 回复
Rust语法树注释处理库swc_node_comments的使用指南
以下是内容中提供的示例代码:
基本使用方法
1. 解析代码并获取注释
use swc_common::{SourceMap, comments::Comments};
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax};
fn parse_code_with_comments(code: &str) {
let cm = SourceMap::default();
let comments = swc_node_comments::SwcComments::default();
let lexer = Lexer::new(
Syntax::Es(Default::default()),
Default::default(),
StringInput::from(&*cm.new_source_file("test.js".into(), code.into())),
Some(&comments),
);
let mut parser = Parser::new_from(lexer);
let _module = parser.parse_module().unwrap();
// 现在可以访问comments对象来获取注释信息
}
2. 遍历注释
use swc_common::BytePos;
fn print_comments(comments: &SwcComments) {
// 获取所有注释
let all_comments = comments.get_all();
for (pos, comment) in all_comments {
println!("Comment at {:?}: {}", pos, comment.text);
}
// 获取特定位置的注释
if let Some(comment) = comments.get_leading(BytePos(10)) {
println!("Leading comment at position 10: {}", comment.text);
}
}
3. 添加注释
use swc_common::{BytePos, comments::Comment};
fn add_comment(comments: &mut SwcComments) {
let comment = Comment {
kind: swc_common::comments::CommentKind::Line,
text: " This is a new comment".to_string(),
span: Default::default(),
};
// 在特定位置添加注释
comments.add_leading(BytePos(50), comment);
}
4. 修改注释
fn modify_comment(comments: &mut SwcComments) {
if let Some(comment) = comments.get_leading_mut(BytePos(10)) {
comment.text = " Modified comment text".to_string();
}
}
高级用法
1. 与SWC转换器一起使用
use swc_ecma_visit::{VisitMut, VisitMutWith};
struct CommentProcessor {
comments: SwcComments,
}
impl VisitMut for CommentProcessor {
fn visit_mut_module(&mut self, module: &mut Module) {
// 处理AST节点
module.visit_mut_children_with(self);
// 处理相关注释
if let Some(comments) = self.comments.get_leading(module.span.lo) {
println!("Module has leading comments: {:?}", comments);
}
}
}
2. 提取JSDoc注释
fn extract_jsdocs(comments: &SwcComments) -> Vec<String> {
comments
.get_all()
.into_iter()
.filter(|(_, comment)| comment.text.trim_start().starts_with('*'))
.map(|(_, comment)| comment.text.clone())
.collect()
}
完整示例demo
下面是一个完整的示例,展示了如何使用swc_node_comments处理JavaScript代码中的注释:
use swc_common::{comments::Comment, BytePos, comments::Comments, SourceMap};
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax};
use swc_node_comments::SwcComments;
fn main() {
// 示例JavaScript代码
let code = r#"
// 这是一个单行注释
function greet(name) {
/*
* 这是一个多行注释
* 这是第二行
*/
console.log('Hello, ' + name);
}
// 另一个单行注释
greet('World');
"#;
// 初始化SourceMap和注释收集器
let cm = SourceMap::default();
let mut comments = SwcComments::default();
// 创建词法分析器
let lexer = Lexer::new(
Syntax::Es(Default::default()), // 使用默认的ES语法配置
Default::default(), // ECMAScript版本
StringInput::from(&*cm.new_source_file("example.js".into(), code.into())),
Some(&comments), // 传递注释收集器
);
// 创建解析器并解析代码
let mut parser = Parser::new_from(lexer);
let _module = parser.parse_module().expect("Failed to parse module");
// 1. 打印所有注释
println!("=== 所有注释 ===");
for (pos, comment) in comments.get_all() {
println!("位置: {:?}, 类型: {:?}, 内容: {}",
pos,
comment.kind,
comment.text.trim()
);
}
// 2. 添加新注释
println!("\n=== 添加新注释 ===");
let new_comment = Comment {
kind: swc_common::comments::CommentKind::Block,
text: " 这是一个新添加的块注释 ".to_string(),
span: Default::default(),
};
comments.add_leading(BytePos(150), new_comment);
println!("在位置150添加了新注释");
// 3. 修改现有注释
println!("\n=== 修改注释 ===");
if let Some(comment) = comments.get_leading_mut(BytePos(5)) {
let original_text = comment.text.clone();
comment.text = " 修改后的单行注释 ".to_string();
println!("修改位置5的注释: '{}' -> '{}'",
original_text.trim(),
comment.text.trim()
);
}
// 4. 提取JSDoc风格注释
println!("\n=== JSDoc注释 ===");
let jsdocs = comments.get_all()
.into_iter()
.filter(|(_, c)| c.text.trim_start().starts_with('*'))
.collect::<Vec<_>>();
if !jsdocs.is_empty() {
println!("找到JSDoc注释:");
for (pos, comment) in jsdocs {
println!("位置 {:?}: {}", pos, comment.text);
}
} else {
println!("没有找到JSDoc注释");
}
// 5. 打印修改后的所有注释
println!("\n=== 最终所有注释 ===");
for (pos, comment) in comments.get_all() {
println!("位置: {:?}, 内容: {}", pos, comment.text.trim());
}
}
这个完整示例演示了:
- 如何解析JavaScript代码并收集注释
- 如何遍历和打印所有注释
- 如何在特定位置添加新注释
- 如何修改现有注释
- 如何提取特定类型的注释(如JSDoc)
- 最终查看所有注释(包括修改后的)
运行此程序将输出类似以下内容:
=== 所有注释 ===
位置: BytePos(5), 类型: Line, 内容: 这是一个单行注释
位置: BytePos(50), 类型: Block, 内容: 这是一个多行注释
* 这是第二行
=== 添加新注释 ===
在位置150添加了新注释
=== 修改注释 ===
修改位置5的注释: '这是一个单行注释' -> '修改后的单行注释'
=== JSDoc注释 ===
找到JSDoc注释:
位置 BytePos(50): 这是一个多行注释
* 这是第二行
=== 最终所有注释 ===
位置: BytePos(5), 内容: 修改后的单行注释
位置: BytePos(50), 内容: 这是一个多行注释
* 这是第二行
位置: BytePos(150), 内容: 这是一个新添加的块注释