2个版本
0.1.0 | 2023年7月14日 |
---|---|
0.1.0-dev.0 | 2023年7月12日 |
#1320 在 过程宏 中
13KB
332 行
tree-sitter-grep
tree-sitter-grep 是一个类似grep的搜索工具,它递归地搜索当前目录中的 tree-sitter查询 模式。
双许可下MIT或UNLICENSE。
安装
安装了 Rust工具链 后,运行
$ cargo install tree-sitter-grep
使用方法
$ tree-sitter-grep -q '(trait_bounds) @t'
src/core.rs:14:pub struct Core<'s, M: 's, S> {
src/core.rs:30:impl<'s, M: Matcher, S: Sink> Core<'s, M, S> {
src/mod.rs:622: P: AsRef<Path>,
src/mod.rs:623: M: Matcher,
src/mod.rs:624: S: Sink,
src/mod.rs:644: M: Matcher,
[...]
指定查询
tree-sitter-grep 使用 tree-sitter查询 来指定“模式”进行匹配
你可以通过 -q
/--query
参数“内联”指定查询
$ tree-sitter-grep -q '(trait_bounds) @t'
或者通过指定到tree-sitter查询文件的路径(通常为 *.scm
)使用 -Q
/--query-file
参数
$ cat queries/trait_bounds.scm
(trait_bounds) @t
$ tree-sitter-grep -Q queries/trait_bounds.scm
tree-sitter-grep 使用tree-sitter查询“捕获”(@ whatever
)来指定“匹配”的tree-sitter AST节点
所以你的查询必须始终包含至少一个捕获
如果你的查询包含多个捕获(例如,如果你正在使用“预组合”查询或使用 谓词),tree-sitter-grep默认将查询中的第一个捕获(按字典顺序,我认为?)用作其“目标捕获”
要覆盖该行为,你可以传递 -c
/--capture
参数
$ tree-sitter-grep -q '((field_declaration name: (field_identifier) @field_name (#eq? @field_name "pos")) @f)' --capture f
我如何确定我想要的查询是什么?
作为起点,阅读 tree-sitter查询文档 是值得的
为了确定你想要编写的查询的相关树-sitter AST结构,一个树-sitter "游乐场"非常有价值,例如交互式在线版,或者我使用neovim的:InspectTree
在我的经验中,尽管树-sitter查询是一个稳固的起点,但它们并不总是足够“表达性”,以至于能够精确指定你想要匹配的AST节点集合
因此,我们还支持指定过滤器插件,你可以在其中对匹配或不匹配的条件有“完全的程序控制”
支持的查询“谓词”
树-sitter查询谓词允许执行一些匹配树-sitter AST节点的“过滤”操作
我们使用Rust tree-sitter绑定,因此“我们支持它们所做的任何谓词”
具体包括
#eq?
$ tree-sitter-grep -q '((field_declaration name: (field_identifier) @field_name (#eq? @field_name "pos")) @f)' --capture f
src/core.rs:20: pos: usize,
#match?
$ tree-sitter-grep -q '((field_declaration name: (field_identifier) @field_name (#match? @field_name "^p")) @f)' --capture f
src/core.rs:20: pos: usize,
src/mod.rs:157: passthru: bool,
过滤器插件
当你需要“编程语言的力量”来完全指定匹配“标准”时,你可以编写一个“过滤器插件”
使用过滤器插件
如果你有一个现有的过滤器插件,你可以通过-f
/--filter
参数(带编译好的过滤器动态库文件的路径.so
/.dll
/.dylib
文件)来指定你想使用它
$ tree-sitter-grep -q '(trait_bounds) @t' -f path/to/libmy-filter.so
如果过滤器插件期望传递一个“过滤器参数”(例如,用于以某种方式参数化/配置其行为),则可以使用-a
/--filter-arg
参数来指定
$ tree-sitter-grep -q '(trait_bounds) @t' -f path/to/libmy-filter-that-expects-argument.so -a '{ the_filter_plugin_can_parse_this: "however_it_wants" }'
值得注意的是,从技术上讲,如果你提供过滤器插件参数,你甚至不需要传递树-sitter查询参数(在这种情况下,过滤器插件将对“每个”树-sitter AST节点进行调用)
编写过滤器插件
TODO:添加一个“指南”
简而言之
虽然在理论上你可以在其他语言中编写过滤器插件,但“最佳路径”是在Rust中编写,并使用examples/
中的示例过滤器插件作为起点/参考
基本思路是,对于每个根据提供的查询参数可能匹配的树-sitter AST节点,过滤器插件随后还会被调用,并指示它是否认为该节点是匹配的(基本上作为一个(&tree_sitter::Node) -> bool
"谓词")
支持的目标语言
目前,tree-sitter-grep“内嵌”了对以下语言的搜索支持
- C
- C++
- C#
- CSS
- Dockerfile
- Elisp
- Elm
- Go
- HTML
- Java
- JavaScript
- JSON
- Kotlin
- Lua
- Objective-C
- Python
- Ruby
- Rust
- Swift
- Toml
- tree-sitter查询(多么元啊!)
- TypeScript
理论上,任何发布/可用的树-sitter语法crate的语言都应该是“可行的”。将来,我们可能支持动态指定/加载额外的语言
或者,请提交一个问题,要求添加对其他语言的“内嵌”支持
将查询限制为特定文件/语言
默认情况下,tree-sitter-grep将递归搜索所有“非忽略/隐藏”的文件,这些文件属于支持的语言/类型,如果它可以解析提供的查询与该语言的语法,则将搜索该文件的匹配内容
要显式指定/限制为单一语言,请使用-l
/--language
参数
$ tree-sitter-grep -q '(trait_bounds) @t' -l rust
您还可以通过提供路径参数来限制搜索到某些文件/目录
$ tree-sitter-grep -q '(trait_bounds) @t' src/main.rs src/compiler
其他标志/参数
要查看有关自定义匹配输出的附加参数的文档,请运行
$ tree-sitter-grep --help
总的来说,我们正在努力实现与ripgrep
兼容
性能
我还没有做过任何“真正的”基准测试,但普遍的看法似乎是tree-sitter-grep运行得相当快,出人意料地快(尤其是考虑到tree-sitter并未针对“从头开始解析”用例进行优化)
对于“不是很大”的代码库,我倾向于看到它运行在< 100ms
对于“巨大”的代码库,例如扫描> 300k行代码和输出> 7000个匹配项,我看到它运行在约360ms,这仍然感觉“相当快”
编辑器集成
TODO,我相信@peterstuart已经编写了一个初始版本的Emacs插件,我开始尝试编写neovim插件
基本思路可能倾向于您能够以与eg grep/ripgrep
匹配项交互的方式与tree-sitter-grep匹配项在您的编辑器中进行交互
欢迎贡献/如果您为您的首选编辑器编写了插件,请让我们知道
非目标
-
试图支持“一切和厨房用具”功能(是的,这有点像
ast-grep
的阴影)我们认为tree-sitter-grep确实有潜力成为有用的grep-like工具,除此之外,我们将其视为“构建块”,理论上可以由其他工具利用,例如用于搜索和替换、代码修改等
我已经在使用tree-sitter-grep作为“一次性大规模自动化重构”的一部分取得了成功
-
正在考虑我们自己定制的例如查询语法(哎呀,这里的阴影真多)
我实际上认为eg
ast-grep
采取的方法,提供一种“看起来像代码”的查询语法,相当直观,在许多情况下可能是“最容易触及的”但我个人并不倾向于将其作为工具的方法。我不喜欢它隐藏了“tree-sitter的一切”。它感觉像tree-sitter在一般意义上非常适合在底层技术之上构建各种不同类型的工具,因此我更倾向于“构建块”,这可以让你利用现有的知识/专业知识,并且从本质上来说,引导你获得更多的这种知识/专业知识。也许然后在此基础上(或受其启发)构建自己的东西
贡献/问题
代码库是一个相当典型的基于cargo
的Rust项目
因此,例如,运行cargo test
将运行测试套件
依赖关系
~0.5–1MB
~21K SLoC