插件窝 干货文章 在 ast-grep 中搜索多语言文档

在 ast-grep 中搜索多语言文档

语言 class 文件 注入 601    来源:    2024-10-23

介绍

ast-grep 是一个强大的结构代码搜索工具,它利用语法树来查找源代码中的模式。这使得它比传统的基于文本的搜索工具更加准确和灵活。

ast-grep 可以很好地搜索一种单一语言的文件,但很难提取嵌入在文档中的子语言。

然而,在现代开发中,遇到多语言文档是很常见的。这些是包含用多种不同语言编写的代码的源文件。值得注意的例子包括:

  • html 文件:这些文件可以在 <script> 标签内包含 javascript,在 <style> 标签内包含 <a style='color:#f60; text-decoration:underline;' href="https://www.php.cn/zt/15716.html" target="_blank">css。</script>
  • javascript 文件:这些文件通常包含正则表达式、css 样式和查询语言(例如 graphql)。
  • ruby 文件:这些文件可以包含h​​eredoc 文字内的代码片段,其中heredoc 分隔符通常指示语言。

这些多语言文档可以根据父语法树进行建模,其中一个或多个注入语法树驻留在内部父树的某些节点。

ast-grep 现在支持处理语言注入的功能,允许您在另一种语言的文档中搜索用一种语言编写的代码。

这个概念和术语来自tree-sitter的语言注入,这意味着你可以注入另一种语言到语言文档中。 (顺便说一句,neovim 也接受这个术语。)

示例:在 cli 中搜索 js/css

让我们从一个简单的示例开始,使用 ast-grep 的命令行界面 (cli) 在 html 文件中搜索 javascript 和 css。
ast-grep 内置支持在 html 文件中搜索 javascript 和 css。

使用 sg run:在 html 文件中查找 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 即使只提供模式而不指定模式语言也能很好地工作!

使用 sg scan:使用规则文件在 html 中查找 javascript

您还可以使用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 采用多步骤过程来有效处理语言注入。以下是工作流程的详细分解:

  1. 文件发现:cli 首先通过古老的忽略板条箱发现磁盘上的文件,这是 ripgrep 引擎盖下的同一个库。

  2. 语言推断:ast-grep 根据文件扩展名推断每个发现文件的语言。

  3. 注入提取:对于包含用多种语言编写的代码的文档(例如,嵌入 js 的 html),ast-grep 提取注入的语言子区域。 目前,ast-grep 原生处理 html/js/css .

  4. 代码匹配:ast-grep 根据这些区域匹配指定的模式或规则。模式代码将根据注入的语言(例如 js/css)而不是父文档语言(例如 html)进行解释。

自定义语言注入:javascript 中的样式组件

您可以通过 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

让我们分解一下配置。

  1. hostlanguage:指定文档的主要语言。本例中设置为js(javascript)。

  2. 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.
  1. 注入:指定已识别区域内的注入语言。在本例中,它是 css。

匹配示例

考虑使用样式化组件的 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 将:

  1. 将 styled.button 块识别为 css 区域。
  2. 提取模板文字中的 css 代码。
  3. 在此提取的区域内应用任何特定于 css 的模式搜索。

您可以使用以下命令在项目配置文件夹中的 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 在处理自定义语言注入方面的灵活性。

在 sgconfig.yml 中定义 graphql 自定义语言。

首先,我们需要在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

在 sgconfig.yml 中定义 graphql 注入。

接下来,我们需要自定义 javascript 中哪些区域应该被解析为 graphql 字符串。这类似于上面的样式组件示例。

languageinjections:
- hostlanguage: js
  rule:
    pattern: graphql`$content`
  injected: graphql

在 javascript 中搜索 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!