Vim / Neovim 完全指南 / 14 - Tree-sitter
“Tree-sitter doesn’t just color your code — it understands it.”
14.1 什么是 Tree-sitter
14.1.1 增量解析器
Tree-sitter 是一个通用的增量解析框架,它将源代码解析为具体的语法树(AST)。与正则表达式匹配不同,Tree-sitter 真正理解代码结构。
正则匹配(传统高亮):
if (x > 0) → 关键字 + 括号 + 变量 + 运算符 + 数字
不理解嵌套、范围、作用域
Tree-sitter:
if (x > 0) → if_statement
├── condition: comparison_expression
│ ├── left: identifier "x"
│ ├── operator: ">"
│ └── right: number "0"
└── consequence: ...
完整的语法树结构
14.1.2 优势
| 特性 | 传统正则 | Tree-sitter |
|---|---|---|
| 速度 | 快 | 极快(增量) |
| 准确性 | 有误报 | 语法精确 |
| 嵌套 | 不支持 | 完整支持 |
| 跨行 | 困难 | 原生支持 |
| 自定义对象 | 不可能 | 可定义 |
| 增量更新 | 全量 | 只更新变更部分 |
14.2 安装与配置
14.2.1 nvim-treesitter
-- ~/.config/nvim/lua/plugins/treesitter.lua
return {
{ "nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
event = { "BufReadPost", "BufNewFile" },
opts = {
ensure_installed = {
"lua", "vim", "vimdoc", "query",
"python", "javascript", "typescript", "tsx",
"go", "rust", "c", "cpp", "java",
"html", "css", "scss",
"json", "yaml", "toml", "xml",
"markdown", "markdown_inline",
"bash", "fish", "dockerfile",
"regex", "sql", "graphql",
},
highlight = {
enable = true,
additional_vim_regex_highlighting = false,
},
indent = {
enable = true,
},
incremental_selection = {
enable = true,
keymaps = {
init_selection = "<C-space>",
node_incremental = "<C-space>",
scope_incremental = false,
node_decremental = "<BS>",
},
},
},
config = function(_, opts)
require("nvim-treesitter.configs").setup(opts)
end,
},
}
14.2.2 Tree-sitter 命令
:TSInstall python " 安装 Python 解析器
:TSUpdate " 更新所有解析器
:TSUpdate python " 更新 Python 解析器
:TSInstallInfo " 查看解析器信息
:TSModuleInfo " 查看模块启用状态
:TSHighlightCapturesUnderCursor " 查看光标下高亮组
14.3 语法高亮
14.3.1 高亮组
Tree-sitter 定义了一套语义化的高亮组:
| 高亮组 | 含义 | 示例 |
|---|---|---|
@variable | 变量 | x, count |
@variable.builtin | 内置变量 | self, this |
@function | 函数定义 | def foo() |
@function.call | 函数调用 | foo() |
@function.builtin | 内置函数 | print() |
@method | 方法 | obj.method() |
@keyword | 关键字 | if, for, return |
@string | 字符串 | "hello" |
@number | 数字 | 42, 3.14 |
@comment | 注释 | # comment |
@type | 类型 | int, str |
@operator | 运算符 | +, == |
@punctuation | 标点 | (, {, [ |
@tag | HTML 标签 | <div> |
@attribute | 属性 | @property |
@namespace | 命名空间 | module.name |
14.3.2 自定义高亮
vim.api.nvim_set_hl(0, "@variable", { fg = "#f8f8f2" })
vim.api.nvim_set_hl(0, "@function", { fg = "#50fa7b", bold = true })
vim.api.nvim_set_hl(0, "@keyword", { fg = "#ff79c6", italic = true })
14.4 文本对象
14.4.1 nvim-treesitter-textobjects
{ "nvim-treesitter/nvim-treesitter-textobjects",
dependencies = { "nvim-treesitter/nvim-treesitter" },
event = "VeryLazy",
config = function()
require("nvim-treesitter.configs").setup({
textobjects = {
select = {
enable = true,
lookahead = true,
keymaps = {
["af"] = "@function.outer",
["if"] = "@function.inner",
["ac"] = "@class.outer",
["ic"] = "@class.inner",
["aa"] = "@parameter.outer",
["ia"] = "@parameter.inner",
["al"] = "@loop.outer",
["il"] = "@loop.inner",
["ai"] = "@conditional.outer",
["ii"] = "@conditional.inner",
["ab"] = "@block.outer",
["ib"] = "@block.inner",
},
},
move = {
enable = true,
set_jumps = true,
goto_next_start = {
["]f"] = "@function.outer",
["]c"] = "@class.outer",
["]a"] = "@parameter.inner",
},
goto_next_end = {
["]F"] = "@function.outer",
["]C"] = "@class.outer",
},
goto_previous_start = {
["[f"] = "@function.outer",
["[c"] = "@class.outer",
["[a"] = "@parameter.inner",
},
goto_previous_end = {
["[F"] = "@function.outer",
["[C"] = "@class.outer",
},
},
swap = {
enable = true,
swap_next = {
["<leader>sa"] = "@parameter.inner",
},
swap_previous = {
["<leader>sA"] = "@parameter.inner",
},
},
},
})
end,
}
14.4.2 文本对象用法
def hello(name, age): # 光标在函数内
print(f"Hello {name}, age {age}")
return True
# vaf → 选择整个函数(含 def 行)
# vif → 选择函数体(不含 def 行)
# vac → 选择整个类
# via → 选择当前参数
# ]f → 跳到下一个函数
# [f → 跳到上一个函数
14.5 增量选择
incremental_selection = {
enable = true,
keymaps = {
init_selection = "<C-space>", -- 开始选择
node_incremental = "<C-space>", -- 扩展选择
scope_incremental = false, -- 扩展到作用域
node_decremental = "<BS>", -- 缩小选择
},
},
使用流程:
# 光标在 variable 上
result = calculate(a + b, c)
# <C-space> → 选中 variable (result)
# <C-space> → 选中 assignment (整个语句)
# <C-space> → 选中 expression (calculate(...))
# <BS> → 缩回到 assignment
14.6 缩进
indent = {
enable = true,
disable = { "python", "yaml" }, -- 某些语言禁用(可能不准确)
},
14.7 Playground
{ "nvim-treesitter/playground",
cmd = "TSPlaygroundToggle",
dependencies = { "nvim-treesitter/nvim-treesitter" },
}
:TSPlaygroundToggle " 打开语法树可视化
:TSHighlightCapturesUnderCursor " 查看高亮组
14.8 业务场景
| 场景 | Tree-sitter 功能 |
|---|---|
| 精确语法高亮 | 替代正则高亮 |
| 结构化文本对象 | vif, vac, via |
| 函数导航 | ]f, [f |
| 参数交换 | <leader>sa |
| 增量选择 | <C-space> |
| 语法树调试 | Playground |
14.9 总结
| 组件 | 用途 |
|---|---|
| nvim-treesitter | 语法高亮 + 增量解析 |
| nvim-treesitter-textobjects | 文本对象 + 移动 |
| playground | 语法树可视化 |
下一步:第 15 章 - Telescope 模糊搜索 → 用 Telescope 构建强大的搜索工作流。
扩展阅读
- Tree-sitter 官方文档
- nvim-treesitter 文档
- nvim-treesitter-textobjects
:h treesitter— Neovim 内置参考