ast-grep 是一个强大的结构代码搜索工具,它利用语法树来查找源代码中的模式。这使得它比传统的基于文本的搜索工具更加准确和灵活。
ast-grep 可以很好地搜索一种单一语言的文件,但很难提取嵌入在文档中的子语言。
然而,在现代开发中,遇到多语言文档是很常见的。这些是包含用多种不同语言编写的代码的源文件。值得注意的例子包括:
这些多语言文档可以根据父语法树进行建模,其中一个或多个注入语法树驻留在内部父树的某些节点。
ast-grep 现在支持处理语言注入的功能,允许您在另一种语言的文档中搜索用一种语言编写的代码。
这个概念和术语来自tree-sitter的语言注入,这意味着你可以注入另一种语言到语言文档中。 (顺便说一句,neovim 也接受这个术语。)
让我们从一个简单的示例开始,使用 ast-grep 的命令行界面 (cli) 在 html 文件中搜索 javascript 和 css。
ast-grep 内置支持在 html 文件中搜索 javascript 和 css。
假设我们有一个如下所示的 html 文件:
<style> h1 { color: red; } </style><h1> hello world! </h1> <script> alert('hello world!') </script>
运行这个 ast-grep 命令将从 html 文件中提取匹配的 css 样式代码!
sg run -p 'color: $color'
ast-grep 输出这个漂亮的 cli 报告。
./test.html 2│ h1 { color: red; }
ast-grep 即使只提供模式而不指定模式语言也能很好地工作!
您还可以使用ast-grep的规则文件来搜索注入的语言。
例如,我们可以在 javascript 中警告使用警报,即使它位于 html 文件内部。
id: no-alert language: javascript severity: warning rule: pattern: alert($msg) message: prefer use appropriate custom ui instead of obtrusive alert call.
上面的规则将检测 javascript 中警报的使用。通过 sg scan 运行规则。
sg scan --rule no-alert.yml
该命令利用 ast-grep 中的内置行为来无缝处理语言注入。它将为上面的 html 文件生成以下警告消息。
warning[no-alert]: prefer use appropriate custom ui instead of obtrusive alert call. ┌─ ./test.html:8:3 │ 8 │ alert('hello world!') │ ^^^^^^^^^^^^^^^^^^^^^
ast-grep 采用多步骤过程来有效处理语言注入。以下是工作流程的详细分解:
文件发现:cli 首先通过古老的忽略板条箱发现磁盘上的文件,这是 ripgrep 引擎盖下的同一个库。
语言推断:ast-grep 根据文件扩展名推断每个发现文件的语言。
注入提取:对于包含用多种语言编写的代码的文档(例如,嵌入 js 的 html),ast-grep 提取注入的语言子区域。 目前,ast-grep 原生处理 html/js/css .
代码匹配:ast-grep 根据这些区域匹配指定的模式或规则。模式代码将根据注入的语言(例如 js/css)而不是父文档语言(例如 html)进行解释。
您可以通过 sgconfig.yml 配置文件自定义语言注入。这允许您根据您的具体需求指定ast-grep如何处理多语言文档,而无需修改ast-grep的内置行为。
让我们看一个在 javascript 中搜索 css 代码的示例。 styled-components 是一个使用 css-in-js 设计 react 应用程序样式的库。它允许您通过标记模板文字直接在 javascript 中编写 css,将样式元素创建为 react 组件。
该示例将配置 ast-grep 来检测 styled-components 的 css。
可以在项目配置文件 sgconfig.yml 中添加 languageinjections 部分。
languageinjections: - hostlanguage: js rule: pattern: styled.$tag`$content` injected: css
让我们分解一下配置。
hostlanguage:指定文档的主要语言。本例中设置为js(javascript)。
rule:定义ast-grep规则以识别宿主语言中的注入语言区域。
- `pattern`: the pattern matches styled components syntax where `styled` is followed by a tag (e.g., `button`, `div`) and a template literal containing css. - the rule should have a meta variable `$content` to specify the subregion of injected language. in this case, it is the content inside the template string.
考虑使用样式化组件的 jsx 文件:
import styled from 'styled-components'; const button = styled.button` background: red; color: white; padding: 10px 20px; border-radius: 3px; ` exporrt default function app() { return <button>click me</button> }
使用上述 languageinjections 配置,ast-grep 将:
您可以使用以下命令在项目配置文件夹中的 javascript 中搜索 css:
sg -p 'background: $color' -c 2
它将产生匹配结果:
./styled.js 2│ 3│const button = styled.button` 4│ background: red; 5│ color: white; 6│ padding: 10px 20px;
最后,让我们看一个在 javascript 文件中搜索 graphql 的示例。
这证明了 ast-grep 在处理自定义语言注入方面的灵活性。
首先,我们需要在ast-grep中将graphql注册为自定义语言。有关更多详细信息,请参阅自定义语言参考。
customlanguages: graphql: librarypath: graphql.so # the graphql tree-sitter parser dynamic library extensions: [graphql] # graphql file extension expandochar: $ # see reference above for explanation
接下来,我们需要自定义 javascript 中哪些区域应该被解析为 graphql 字符串。这类似于上面的样式组件示例。
languageinjections: - hostlanguage: js rule: pattern: graphql`$content` injected: graphql
假设我们有来自 relay(一个 graphql 客户端框架)的 javascript 文件。
import react from "react" import { graphql } from "react-relay" const artistsquery = graphql` query artistquery($artistid: string!) { artist(id: $artistid) { name ...artistdescription_artist } } `
我们可以通过这个 --inline-rules 扫描来搜索 graphql 片段。
sg scan --inline-rules="{id: test, language: graphql, rule: {kind: fragment_spread}}"
输出
help[test]: ┌─ ./relay.js:8:7 │ 8 │ ...ArtistDescription_artist │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
按照以下步骤,您可以有效地使用 ast-grep 在同一文档中跨多种语言搜索和分析代码,增强您管理和理解复杂代码库的能力。
此功能扩展到 vue 和 svelte 等各种框架,支持在 react 服务器操作中搜索 sql,并支持 vue-vine 等新模式。
希望您喜欢这个功能!快乐的 ast grep!