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);
}

功能说明

  1. 添加注释:使用add_comment方法可以添加行注释或块注释
  2. 获取注释get_leadingget_trailing方法可以获取特定位置的前导/后置注释
  3. 遍历注释with_leadingwith_trailing方法可以遍历所有注释
  4. 移除注释take_leadingtake_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();
}

功能说明

  1. 注释类型处理:支持行注释(//)和块注释(/**/)两种类型
  2. 位置精准控制:可以精确到行号和列号添加注释
  3. 批量操作:支持批量获取、遍历和移除注释
  4. 线程安全:设计为线程安全的注释处理器

高级用法

// 检查是否存在特定注释
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());
    }
}

这个完整示例演示了:

  1. 如何解析JavaScript代码并收集注释
  2. 如何遍历和打印所有注释
  3. 如何在特定位置添加新注释
  4. 如何修改现有注释
  5. 如何提取特定类型的注释(如JSDoc)
  6. 最终查看所有注释(包括修改后的)

运行此程序将输出类似以下内容:

=== 所有注释 ===
位置: BytePos(5), 类型: Line, 内容: 这是一个单行注释
位置: BytePos(50), 类型: Block, 内容: 这是一个多行注释
* 这是第二行

=== 添加新注释 ===
在位置150添加了新注释

=== 修改注释 ===
修改位置5的注释: '这是一个单行注释' -> '修改后的单行注释'

=== JSDoc注释 ===
找到JSDoc注释:
位置 BytePos(50): 这是一个多行注释
* 这是第二行

=== 最终所有注释 ===
位置: BytePos(5), 内容: 修改后的单行注释
位置: BytePos(50), 内容: 这是一个多行注释
* 这是第二行
位置: BytePos(150), 内容: 这是一个新添加的块注释
回到顶部