#ast #diff #语法树 #shell-completion #内容 #difftool

bin+lib diffsitter

基于抽象语法树的差异工具,用于生成有意义的差异

31 个版本

0.8.4 2024 年 8 月 14 日
0.8.3 2024 年 4 月 28 日
0.8.2 2024 年 1 月 14 日
0.8.1 2023 年 7 月 24 日
0.0.1 2020 年 7 月 7 日

#82命令行工具

Download history 74/week @ 2024-04-29 16/week @ 2024-05-06 6/week @ 2024-05-20 6/week @ 2024-06-03 167/week @ 2024-07-29 139/week @ 2024-08-12

每月 306 次下载

MIT 许可证

204MB
6.5M SLoC

C 6.5M SLoC // 0.0% comments Rust 3K SLoC // 0.1% comments C++ 1.5K SLoC // 0.0% comments

diffsitter

CI CD codecov crates version GitHub release (latest by date) downloads license

asciicast

免责声明

diffsitter 目前还在开发中,尚未达到生产就绪状态。欢迎贡献!

概述

diffsitter 创建了具有语义意义的差异,忽略格式差异(如空格)。它通过在文件的抽象语法树(AST)上计算差异而不是在文件的文本内容上计算差异来实现这一点。

diffsitter 使用来自 tree-sitter 项目的解析器来解析源代码。因此,该工具支持的语言仅限于 tree-sitter 支持的语言。

diffsitter 支持以下语言

  • Bash
  • C#
  • C++
  • CSS
  • Go
  • Java
  • OCaml
  • PHP
  • Python
  • Ruby
  • Rust
  • Typescript/TSX
  • HCL

示例

以下是一些文件

a.rs

fn main() {
    let x = 1;
}

fn add_one {
}

b.rs

fn



main

()

{
}

fn addition() {
}

fn add_two() {
}

使用 diff 的标准输出将得到

1,2c1,12
< fn main() {
<     let x = 1;
---
> fn
>
>
>
> main
>
> ()
>
> {
> }
>
> fn addition() {
5c15
< fn add_one {
---
> fn add_two() {

您可以看到它选取了 main 函数的格式差异,尽管它们在语义上没有区别。

查看 diffsitter 的输出

test_data/short/rust/a.rs -> test_data/short/rust/b.rs
======================================================

9:
--
+ }

11:
---
+ fn addition() {

1:
--
-     let x = 1;

14:
---
+ fn add_two() {

4:
--
- fn add_one {

注意:数字对应于原始文件的行号。

您还可以通过配置文件过滤要考虑在差异中考虑的 tree-sitter 节点。

由于它使用 AST 来计算差异,因此知道两个文件在 main 中的格式差异不是有意义的差异,因此它不会显示在差异中。

diffsitter 也有一些漂亮的(终端感知)格式化功能

screenshot of rust diff

如果您想调试或查看计时信息,它还具有详细的日志记录

screenshot of rust diff with logs

节点过滤

您可以通过在配置文件中设置 include_nodesexclude_nodes 来过滤在 diff 中考虑的节点。注意,exclude_nodes 总是比 include_nodes 优先,而节点的类型是树-sitter 节点的 kindkind 直接对应于 tree-sitter API 报告的内容,因此这个例子可能会偶尔过时。

此功能目前仅适用于叶节点,但如果有需求,我们也可以递归地排除节点。

"input-processing": {
    // You can exclude different tree sitter node types - this rule takes precedence over `include_kinds`.
    "exclude_kinds": ["string_content"],
    // You can specifically allow only certain tree sitter node types
    "include_kinds": ["method_definition"],
}

安装

Packaging status

已发布二进制文件

该项目使用 Github actions 构建和发布每个标记版本的二进制文件。如果您的平台在上面列出,您可以从那里下载二进制文件。我们发布夜间版本以及标记的稳定版本

Cargo

您可以使用以下命令使用 cargo 从源代码构建

cargo install diffsitter --bin diffsitter

如果您想生成完成文件和其他资源,可以使用以下命令安装 diffsitter_completions 二进制文件

cargo install diffsitter --bin diffsitter_completions

Homebrew

您可以使用我的 tap 安装 diffsitter

brew tap afnanenayet/tap
brew install diffsitter
# brew install afnanenayet/tap/diffsitter

Arch Linux (AUR)

@samhh 已经将 diffsitter 打包在 AUR 上。使用您喜欢的 AUR 辅助工具安装 diffsitter-bin

Alpine Linux

从 Alpine Linux 仓库(v3.16+ 或 Edge)安装软件包 diffsitter

apk add diffsitter

Tree-sitter 语法分别打包(搜索 tree-sitter-*)。您可以安装所需的单个软件包或虚拟软件包 tree-sitter-grammars 来安装所有软件包。

使用 Docker 构建

我们还提供了一个 Docker 镜像,使用标准的 Rust 基础镜像构建 diffsitter。它将编译阶段与运行阶段分开,因此您可以使用以下命令构建和运行(假设您已在系统上安装了 Docker)

docker build -t diffsitter .
docker run -it --rm --name diffsitter-interactive diffsitter

使用方法

要获取详细信息,您可以运行 diffsitter --helpdiffsitter -h 提供简短的帮助信息)。

您可以使用配置文件来配置 diffsitter 的文件关联和格式化选项。如果没有提供配置,应用程序将使用默认配置,您可以使用 diffsitter dump-default-config 查看它。它将在 macOS 和 Linux 上的 ${XDG_HOME:-$HOME}/.config/diffsitter/config.json5 以及 Windows 的标准目录中查找配置。您还可以参考 示例配置

您可以使用 --config 标志覆盖默认配置路径,或设置环境变量 DIFFSITTER_CONFIG

注意:此 crate 的测试确保提供的示例配置是有效的配置。

Git 集成

要查看 diffsitter 中当前 git 仓库的更改,请将以下内容添加到您的仓库的 .git/config 并运行 git difftool

[diff]
        tool = diffsitter

[difftool]
        prompt = false

[difftool "diffsitter"]
        cmd = diffsitter "$LOCAL" "$REMOTE"

Shell 完成功能

您可以使用二进制文件通过 gen-completion 子命令生成 shell 完成脚本。这将在 STDOUT 中打印出指定 shell 的完成脚本。

您应使用帮助文本以获取最新用法信息,但一般用法如下

diffsitter gen-completion bash > completion.bash

我们目前支持以下 shell(通过 clap_complete

  • Bash
  • Zsh
  • Fish
  • Elvish
  • Powershell

依赖项

diffsitter 通常编译为静态二进制文件,因此 tree-sitter 语法/库被编译到二进制文件中作为静态库。有一个选项支持使用动态库进行构建,这将在用户的默认库路径中查找共享库文件。这将搜索以下形式的库文件:libtree-sitter-{lang}.{ext},其中 lang 是用户尝试diff的语言,ext 是共享库文件的特定平台扩展(如 .so.dylib 等)。用户可以在配置中覆盖每种语言的动态库文件,如下所示

{
    "grammar": {
        // You can specify the dynamic library names for each language
        "dylib-overrides": {
            // with a filename
            "rust": "libtree-sitter-rust.so",
            // with an absolute path
            "c": "/usr/lib/libtree-sitter-c.so",
            // with a relative path
            "cpp": "../libtree-sitter-c.so",
        },
    }
}

以上摘录来自 示例配置

问题、错误和支持

如果您发现任何错误、有问题、想看到新功能或只是有疑问,请随意打开 问题 或创建 讨论帖子

如果您提交问题,最好包含一个最小示例以及/或 diffsitter 的日志输出(您可以通过添加 -d/--debug 标志来完成此操作)。

贡献

查看 CONTRIBUTING.md

类似项目

依赖项

~14–30MB
~472K SLoC