Rust语法树解析库tree-sitter-diff的使用,高效实现代码差异分析和语法树比对

Rust语法树解析库tree-sitter-diff的使用,高效实现代码差异分析和语法树比对

简介

tree-sitter-diff 是一个用于解析 diff 文件的 tree-sitter 语法库。它可以用于代码差异分析和语法树比对。

示例

以下是 .diff 文件的高亮显示示例:

diff文件高亮示例

将语法注入到 tree-sitter-git-commit 中,用于 verbose 提交 (git commit --verbose):

git commit verbose示例

完整示例代码

use tree_sitter::{Parser, Language};
use tree_sitter_diff::language;

fn main() {
    // 初始化tree-sitter-diff语言
    let diff_language = unsafe { Language::from_raw(language()) };
    
    // 创建parser
    let mut parser = Parser::new();
    parser.set_language(diff_language).unwrap();
    
    // 示例diff内容
    let diff_content = r"diff --git a/src/main.rs b/src/main.rs
index abc1234..def5678 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,5 @@
 fn main() {
-    println!(\"Hello, world!\");
+    println!(\"Hello, Rust!\");
    
     let x = 10;
 }";
    
    // 解析diff
    let tree = parser.parse(diff_content, None).unwrap();
    
    // 获取根节点
    let root_node = tree.root_node();
    
    // 打印语法树
    print_tree(root_node, 0);
}

// 递归打印语法树
fn print_tree(node: tree_sitter::Node, depth: usize) {
    let indent = "  ".repeat(depth);
    println!("{}{:?}", indent, node);
    
    // 遍历子节点
    let mut cursor = node.walk();
    for child in node.children(&mut cursor) {
        print_tree(child, depth + 1);
    }
}

安装

在项目目录中运行以下Cargo命令:

cargo add tree-sitter-diff

或者在Cargo.toml中添加:

tree-sitter-diff = "0.1.0"

特性

  • 支持解析标准的diff格式
  • 可以与git commit信息结合使用
  • 提供完整的语法树分析能力
  • 适用于代码差异分析和语法树比对场景

许可证

MIT License


1 回复

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

简介

tree-sitter-diff是一个基于tree-sitter的Rust库,专门用于代码差异分析和语法树比对。它能够高效地解析不同版本的代码,并生成语法层面的差异报告,比传统的文本差异工具更智能地理解代码结构变化。

主要特性

  • 基于tree-sitter的精确语法解析
  • 支持多种编程语言的差异分析
  • 语法树级别的变更检测
  • 高性能的差异计算算法
  • 可定制的差异报告输出

安装方法

在Cargo.toml中添加依赖:

[dependencies]
tree-sitter-diff = "0.3"
tree-sitter = "0.20"

基本使用方法

1. 初始化解析器

use tree_sitter_diff::{DiffParser, LanguageConfig};

// 配置要解析的语言(这里以JavaScript为例)
let config = LanguageConfig::javascript();
let mut parser = DiffParser::new(config).unwrap();

2. 比较两个代码版本

let old_code = r#"
function add(a, b) {
    return a + b;
}
"#;

let new_code = r#"
function add(a, b, c) {
    return a + b + c;
}
"#;

let diff = parser.diff(old_code, new_code).unwrap();

3. 分析差异结果

for change in diff.changes() {
    println!("Change type: {:?}", change.change_type());
    println!("Old node: {:?}", change.old_node());
    println!("New node: {:?}", change.new_node());
    println!("Location: {:?}", change.location());
}

高级用法

自定义语言配置

use tree_sitter_diff::{LanguageConfig, ParserConfig};

let config = LanguageConfig::new(
    tree_sitter_rust::language(),  // Rust语言解析器
    tree_sitter_rust::HIGHLIGHTS_QUERY,  // 高亮查询
    Some(tree_sitter_rust::INJECTIONS_QUERY),  // 注入查询
    Some(tree_sitter_rust::LOCALS_QUERY),  // 局部变量查询
);

let parser_config = ParserConfig::new()
    .with_timeout(100)  // 设置解析超时(ms)
    .with_debug(true);  // 启用调试模式

let parser = DiffParser::with_config(config, parser_config).unwrap();

忽略特定类型的变更

let diff = parser.diff(old_code, new_code)
    .unwrap()
    .ignore_whitespace()  // 忽略空白变化
    .ignore_comments();   // 忽略注释变化

生成HTML差异报告

use tree_sitter_diff::html::HtmlDiffRenderer;

let renderer = HtmlDiffRenderer::new()
    .with_line_numbers(true)
    .with_highlighting(true);

let html_report = renderer.render(&diff).unwrap();
std::fs::write("diff.html", html_report).unwrap();

实际应用示例:检测API变更

fn detect_api_changes(old_api: &str, new_api: &str) -> Vec<String> {
    let config = LanguageConfig::rust();
    let parser = DiffParser::new(config).unwrap();
    
    let diff = parser.diff(old_api, new_api)
        .unwrap()
        .only_function_signatures();  // 只关注函数签名变化
    
    diff.changes()
        .iter()
        .filter(|c| c.is_breaking_change())
        .map(|c| c.description())
        .collect()
}

完整示例DEMO

下面是一个完整的tree-sitter-diff使用示例,展示如何比较两个Rust代码版本并生成差异报告:

use tree_sitter_diff::{DiffParser, LanguageConfig, ParserConfig};
use tree_sitter_rust;

fn main() {
    // 1. 配置Rust语言解析器
    let config = LanguageConfig::new(
        tree_sitter_rust::language(),
        tree_sitter_rust::HIGHLIGHTS_QUERY,
        Some(tree_sitter_rust::INJECTIONS_QUERY),
        Some(tree_sitter_rust::LOCALS_QUERY),
    );

    // 2. 创建带有调试模式的解析器
    let parser_config = ParserConfig::new()
        .with_timeout(100)
        .with_debug(true);

    let mut parser = DiffParser::with_config(config, parser_config).unwrap();

    // 3. 定义新旧代码版本
    let old_code = r#"
    pub fn calculate(a: i32, b: i32) -> i32 {
        a + b
    }
    "#;

    let new_code = r#"
    pub fn calculate(a: i32, b: i32, c: Option<i32>) -> i32 {
        if let Some(c) = c {
            a + b + c
        } else {
            a + b
        }
    }
    "#;

    // 4. 比较代码差异
    let diff = parser.diff(old_code, new_code)
        .unwrap()
        .ignore_whitespace();

    // 5. 打印差异信息
    println!("Found {} changes:", diff.changes().len());
    for change in diff.changes() {
        println!("\nChange type: {:?}", change.change_type());
        println!("Old node: {}", change.old_node().unwrap().kind());
        println!("New node: {}", change.new_node().unwrap().kind());
        println!("Location: {:?}", change.location());
    }

    // 6. 生成HTML报告
    #[cfg(feature = "html")]
    {
        use tree_sitter_diff::html::HtmlDiffRenderer;
        let renderer = HtmlDiffRenderer::new()
            .with_line_numbers(true)
            .with_highlighting(true);
        
        let html = renderer.render(&diff).unwrap();
        std::fs::write("rust_diff.html", html).unwrap();
        println!("HTML report generated to rust_diff.html");
    }
}

性能优化建议

  1. 对于大型代码库,考虑增量分析
  2. 缓存已经解析过的语法树
  3. 并行处理多个文件的差异
  4. 合理设置解析超时时间

注意事项

  • tree-sitter-diff需要对应语言的tree-sitter语法解析器
  • 某些边缘情况的语法变更可能检测不准确
  • 对于非常规格式的代码可能需要调整配置

通过tree-sitter-diff,开发者可以更精确地理解代码变更,特别适合代码审查、版本迁移分析和自动化重构工具的开发。

回到顶部