# 使用 Tree-sitter Query 为 aerial.nvim 添加自定义 symbol
# aerial.nvim
Neovim 的 aerial.nvim 插件是一个提供 Outline 的插件。
但是写 React (Typescript) 的时候有一个问题是无法显示像 useRequest
等等 Hook 函数的位置。
aerial.nvim 的配置
默认的 aerial.nvim 配置不显示 Symbol 为 Contant
和 Varible
内容。因此可以在配置文件中
手动进行如下配置
filter_kind = {
"Class",
"Constructor",
"Enum",
"Function",
"Interface",
"Module",
"Method",
"Struct",
"Constant",
"Variable",
},
# Symbol 和 Symbol Kind
复习一下编译器的工作流程(编译原理):
- 首先第一步要进行的是词法分析,也就是将一整个文本拆分成若干个 "Token"。
- 语法分析:将 token stream 解析为某个数据结构(例如 AST, Abstarct Syntax Tree)
- 中间代码生成:将上述的某个数据结构生成为中间代码
- 目标代码生成:生成对应平台的,对应架构的代码
对于 Tree-sitter, LSP 等等工具来说,只关心前两个步骤,因为是开发工具,而非编译器。
Tree-sitter 解析出的每个节点,都可以对应到 LSP 中的某个 Symbol.
参考: https://github.com/neovim/neovim/blob/master/runtime/lua/vim/lsp/protocol.lua 可以发现在 neovim 中提供了如下 26 种不同的 Symbol
SymbolKind = {
File = 1,
Module = 2,
Namespace = 3,
Package = 4,
Class = 5,
Method = 6,
Property = 7,
Field = 8,
Constructor = 9,
Enum = 10,
Interface = 11,
Function = 12,
Variable = 13,
Constant = 14,
String = 15,
Number = 16,
Boolean = 17,
Array = 18,
Object = 19,
Key = 20,
Null = 21,
EnumMember = 22,
Struct = 23,
Event = 24,
Operator = 25,
TypeParameter = 26,
},
# Tree-sitter 的三个概念
Tree-sitter 有三个很重要的概念:
- parser: 自不必说,将源代码 parse 为 AST
- query: 在 AST 中查询检索,使用
scm
进行 - module: 模块化,可以拓展支持的源代码
本文关心的是 Query,也就是说如何在已经 parse 出来的 AST 中找到自己想要的东西。
Query
使用 scm
进行 Query. 可以在 Tree-sitter 提供的官方 playgound: https://tree-sitter.github.io/tree-sitter/playground
中进行测试。
例如有如下语句:
const { data, refetch, status } = useFetch(() => {});
如何能得到 useFetch
呢?
勾选 Query 选项以进行 Query 测试
(lexical_declaration
(variable_declarator
value: (call_expression
function: (identifier) @symbol
)
)
)
可以看到成功 query 到了 useFetch (和 @symbol 同色)
Query 语法:
- 括号配对
- node_type(也就是 lexical_declaration 等) 外面要有括号
- 可以带 field(也就是上面的 value 等)
在 Neovim 里面有
# 给 neovim 添加 query
参考 https://github.com/nvim-treesitter/nvim-treesitter?tab=readme-ov-file#advanced-setup
在配置文件的根目录的 after/queries/tsx/aerial.scm
中添加
下面是我上面提到的,解析出所有函数调用的一个示例:
;; extends
(lexical_declaration
(
variable_declarator
value: (
call_expression
function: (identifier) @name
)
(#set! "kind" "Constant")
)
)@symbol
参考 Neovim 官方文档:https://neovim.io/doc/user/treesitter.html#_lua-module:-vim.treesitter.query
和 aerial 的 Github 仓库 README: https://github.com/stevearc/aerial.nvim?tab=readme-ov-file#treesitter-queries
需要提供 @name 和 @symbol 两个 metadata
(通过 #set!
语句进行测试)
set! treesitter-directive-set!
Sets key/value metadata for a specific match or capture. Value is
accessible as either metadata[key] (match specific) or
metadata[capture_id][key] (capture specific).