Tree-sitter 与 nvim-treesitter 概览
理解 Tree-sitter 增量解析引擎如何为 Neovim 带来极速、精准的语法高亮与代码智能
什么是 Tree-sitter?
Tree-sitter 是一个开源的增量式解析框架,专门为代码编辑器设计。它能在毫秒级时间内构建完整的 具体语法树(CST),并在文档编辑时高效地增量更新,而无需从头重新解析整个文件。
传统的正则表达式高亮(Vim 的 syntax files、TextMate grammars)基于模式匹配,无法真正理解代码结构。Tree-sitter 则通过构建完整的语法树来实现精确、一致的语法高亮。
正则表达式高亮有两个根本性问题:歧义性(同一个词在不同上下文可能是函数名、变量名或关键字)和性能瓶颈(复杂正则在长文件上会导致明显卡顿)。Tree-sitter 通过构建语法树解决了这两个问题——它理解代码的结构和上下文,并且只在编辑区域附近做增量更新,实现近乎零延迟的高亮刷新。
增量解析
编辑代码时只重新解析变化的部分,O(log n) 复杂度的增量更新。即使文件有数万行,语法树更新也在毫秒级完成。这是 Tree-sitter 的核心优势。
错误容忍
即使代码有语法错误,Tree-sitter 也能构建出尽可能完整的语法树。错误节点(ERROR node)被标记但不影响其他正确部分的解析,这对实时编辑至关重要。
代码智能
语法树不仅是高亮的基础,还支持代码折叠、增量选择、文本对象(textobjects)、结构导航等高级编辑功能。一个解析器驱动多种功能。
语言全覆盖
社区为 60+ 种编程语言提供了 Tree-sitter 解析器:JavaScript、Python、Rust、Go、C/C++、Java、TypeScript、Lua、HTML、CSS 等。新的语言解析器可以独立开发,无需修改核心引擎。
nvim-treesitter 架构
nvim-treesitter 是 Neovim 的 Tree-sitter 集成插件。它利用 Neovim 内置的 Tree-sitter 绑定(libtree-sitter C 库),为 Neovim 提供语法高亮、代码折叠、增量选择等功能。理解其架构有助于正确配置和调试。
Neovim 核心 — 内置 Tree-sitter 支持
nvim-treesitter 插件 — 功能扩展层
Tree-sitter 核心 — 解析引擎
快速安装与配置
-- lazy.nvim 安装配置
{
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
config = function()
local ts = require("nvim-treesitter.configs")
ts.setup({
-- 安装这些语言的解析器
ensure_installed = {
"lua", "python", "javascript",
"typescript", "rust", "go",
"html", "css", "json",
},
-- 自动安装缺失的解析器
auto_install = true,
-- 启用语法高亮
highlight = {
enable = true,
-- 对大文件禁用(性能保护)
disable = function(lang, buf)
local max_filesize = 100 * 1024
local ok, stats = pcall(
vim.loop.fs_stat,
vim.api.nvim_buf_get_name(buf)
)
if ok and stats and
stats.size > max_filesize then
return true
end
end,
},
-- 启用增量选择
incremental_selection = {
enable = true,
keymaps = {
init_selection = "gnn",
node_incremental = "grn",
scope_incremental = "grc",
node_decremental = "grm",
},
},
-- 启用代码折叠
indent = { enable = true },
})
end,
}
nvim-treesitter 配置的核心要素:
- ensure_installed:预装的语言解析器列表,确保常用语言开箱即用
- auto_install:打开未知语言文件时自动安装对应解析器
- highlight.enable:用 Tree-sitter 语法树替代 Vim 正则语法高亮
- 性能保护:对超过 100KB 的文件自动禁用,避免卡顿
- incremental_selection:基于语法树的选中范围扩展(函数 -> 类 -> 模块)
- indent:使用 Tree-sitter 提供更准确的缩进计算
课程路线图
Tree-sitter 相比正则表达式语法高亮的核心优势是什么?
核心概念
语法树节点、Tree-sitter 查询语言、高亮映射和语言注入——理解 nvim-treesitter 的技术基石
语法树节点与类型系统
Tree-sitter 解析代码后会生成一棵由 节点(Node) 组成的树结构。理解节点类型是编写 Tree-sitter 查询和自定义高亮的基础。
-- 在 Neovim 中探索语法树节点
local parsers = require("nvim-treesitter.parsers")
local ts_utils = require("nvim-treesitter.ts_utils")
-- 获取当前 buffer 的语法树根节点
local buf = vim.api.nvim_get_current_buf()
local parser = vim.treesitter.get_parser(buf, "lua")
local tree = parser:parse()[1]
local root = tree:root()
-- 遍历根节点的直接子节点
for node in root:iter_children() do
print(node:type()) -- "function_declaration"
print(node:named()) -- true(命名节点)
print(node:child_count()) -- 子节点数量
-- 获取节点的文本范围
local sr, sc = node:start() -- 起始行、列
local er, ec = node:end_() -- 结束行、列
print(f"Range: {sr}:{sc} - {er}:{ec}")
end
-- 常见的 Lua 节点类型:
-- function_declaration, function_name
-- parameters, identifier, string
-- if_statement, for_statement, while_loop
-- binary_expression, unary_expression
-- table_constructor, field
语法树节点操作的核心 API:
- get_parser(buf, lang):获取指定 buffer 的 Tree-sitter 解析器
- tree:root():获取语法树的根节点(通常是 "program" 或 "source_file")
- node:type():节点类型名称,对应语法定义中的规则名
- node:named():是否为命名节点(有意义的语法结构 vs 标点符号)
- node:iter_children():遍历直接子节点的迭代器
- node:start() / end_():获取节点在源码中的位置范围
Tree-sitter 查询语言(Query)
Tree-sitter 使用一种基于 S-expression 的模式匹配语言来查询语法树。这是 nvim-treesitter 实现高亮、折叠、textobjects 等功能的核心机制。
每种语言都有一组查询文件(.scm 后缀):highlights.scm 定义如何高亮、folds.scm 定义如何折叠、textobjects.scm 定义文本对象范围。这些文件使用统一的查询语言,但匹配不同语言的语法树结构。你可以通过覆盖这些查询文件来自定义行为。
; highlights.scm — Lua 语言高亮查询
;
; 查询语法:用 S-expression 模式匹配语法树节点
; @capture_name 将匹配的节点标记为特定高亮组
; 关键字
["if" "then" "else" "elseif"
"end" "for" "while" "repeat"
"until" "do" "return" "local"] @keyword
; 函数声明 — 捕获函数名
(function_declaration
name: (identifier) @function)
; 方法调用 — 捕获方法名
(method_index_expression
method: (identifier) @function.method)
; 字符串字面量
(string) @string
; 注释
(comment) @comment
; 数字字面量
(number) @number
; 布尔值和 nil
["true" "false" "nil"] @boolean
; 运算符
["and" "or" "not" "in"] @keyword.operator
; 表构造器
(table_constructor
(field
name: (identifier) @property))
Tree-sitter 查询语言的核心要素:
- 节点匹配:(function_declaration ...) 匹配该类型的节点
- 字段访问:name: (identifier) 匹配 name 字段的子节点
- 捕获标记:@keyword、@function 等映射到 Neovim 高亮组
- 列表匹配:[... ] 匹配多个备选字面量
- 嵌套匹配:可以深入多层子节点精确匹配
- 注释说明:用 ; 开头添加注释
高亮映射:从捕获到颜色
查询文件中的 @capture 名称(如 @function、@keyword)需要映射到 Neovim 的高亮组(highlight groups),才能最终显示颜色。nvim-treesitter 定义了一套标准的捕获名到高亮组的映射关系。
@keyword
映射到 Statement 或 Keyword 高亮组。用于 if、else、for、while、return 等控制流关键字。这是最基础的高亮类别之一。
@function
映射到 Function 高亮组。用于函数声明、函数调用中的函数名。区分 @function.call(调用)和 @function(声明)是常见做法。
@string
映射到 String 高亮组。字符串字面量、模板字符串、文档字符串等。还包括 @string.escape(转义序列)和 @string.special(特殊字符串)。
@comment
映射到 Comment 高亮组。单行注释、多行注释、文档注释。@comment.documentation 用于 API 文档注释(如 JSDoc、docstring)。
-- 在 Neovim 配置中自定义 Tree-sitter 高亮组
-- 这些映射覆盖默认的捕获名到高亮组关系
-- 方法 1:直接设置高亮组
vim.api.nvim_set_hl(0, "@keyword", {
fg = "#C678DD", -- 紫色关键字
bold = true,
})
vim.api.nvim_set_hl(0, "@function", {
fg = "#61AFEF", -- 蓝色函数名
italic = true,
})
vim.api.nvim_set_hl(0, "@string", {
fg = "#98C379", -- 绿色字符串
})
vim.api.nvim_set_hl(0, "@comment", {
fg = "#5C6370", -- 灰色注释
italic = true,
})
-- 方法 2:链接到现有高亮组
vim.api.nvim_set_hl(0, "@function.call", {
link = "@function", -- 继承 @function 的样式
})
-- 方法 3:语言特定的高亮覆盖
vim.api.nvim_set_hl(0, "@function.python", {
fg = "#E5C07B", -- Python 函数用黄色
})
高亮自定义的三种方式:
- 直接设置:用 fg/bg/bold/italic 定义全新样式
- 链接继承:link = "@function" 复用已有高亮组
- 语言限定:"@function.python" 只对 Python 生效
- 优先级:语言限定 > 直接设置 > 链接继承
- 主题集成:大多数 colorscheme 会自动设置 @ 捕获组
语言注入(Injection)
语言注入是 Tree-sitter 最强大的特性之一。它允许在一个语言的语法树中嵌入另一个语言的解析。比如在 Markdown 中的代码块、在 HTML 中嵌入 JavaScript/CSS、在 Python 的 f-string 中的表达式。
; injections.scm — 定义语言注入规则
; Markdown 中的代码块 → 注入对应语言
(fenced_code_block
(info_string
(language) @injection.language)
(code_fence_content) @injection.content)
; HTML 中的 <script> 标签 → 注入 JavaScript
(script_element
(raw_text) @injection.content
(#set! injection.language "javascript"))
; HTML 中的 <style> 标签 → 注入 CSS
(style_element
(raw_text) @injection.content
(#set! injection.language "css"))
; Lua 中的 vim.cmd() 调用 → 注入 Vim script
(function_call
name: (identifier) @_vimcmd
(arguments
(string
(string_content) @injection.content))
(#match? @_vimcmd "^vim%.(cmd|api%.nvim_cmd)")
(#set! injection.language "vim"))
; JavaScript 模板字面量 → 注入 SQL
(call_expression
function: (identifier) @_sql_func
arguments: (template_string) @injection.content
(#match? @_sql_func "^(sql|query)")
(#set! injection.language "sql"))
语言注入的核心概念:
- @injection.language:动态指定注入的语言(从代码中提取,如 Markdown 的 ```python)
- #set! injection.language:静态指定注入的语言(固定值,如 HTML 的 script 标签固定注入 JS)
- @injection.content:标记需要用注入语言解析的文本内容
- #match?:谓词函数,用 Lua 模式匹配过滤节点
- 嵌套解析:注入会创建独立的子语法树,支持任意深度的嵌套
在 Tree-sitter 查询中,@function 和 @function.call 有什么区别?
实践应用
Textobjects、代码折叠、增量选择和彩虹括号——将语法树转化为编辑能力
Textobjects:基于语法树的文本对象
Tree-sitter 的 textobjects 是传统 Vim 文本对象(如 iw、ip)的升级版。它们基于语法树精确识别函数、类、条件块等结构,而不是依赖简单的正则或缩进。这让编辑操作(删除、复制、选择)更加精准。
传统 Vim 文本对象(ip 段落、im 方法)依赖缩进和空行判断,在嵌套结构中经常出错。Tree-sitter textobjects 基于语法树,能精确区分外层函数和内层 if 块。你可以用 "选择整个函数" 或 "选择参数列表" 这种语义级别的操作。
-- textobjects 配置
textobjects = {
select = {
enable = true,
lookahead = true, -- 向前查找
keymaps = {
-- af = 包含外围的整个函数
["af"] = "@function.outer",
-- if = 只选函数体(不含声明行)
["if"] = "@function.inner",
-- ac = 整个类/结构体
["ac"] = "@class.outer",
["ic"] = "@class.inner",
-- 条件块
["ai"] = "@conditional.outer",
["ii"] = "@conditional.inner",
-- 循环块
["al"] = "@loop.outer",
["il"] = "@loop.inner",
-- 参数/参数列表
["aa"] = "@parameter.outer",
["ia"] = "@parameter.inner",
},
},
move = {
enable = true,
set_jumps = true, -- 添加到 jumplist
goto_next_start = {
["]m"] = "@function.outer",
["]]"] = "@class.outer",
},
goto_previous_start = {
["[m"] = "@function.outer",
["[@"] = "@class.outer",
},
},
},
Textobjects 的两种核心用法:
- select(选择):用 af/if 选函数,ac/ic 选类,配合 d/c/y 操作符使用
- move(跳转):]m 跳到下一个函数开头,[m 跳到上一个
- .outer vs .inner:outer 包含声明行和结束标记,inner 只包含核心内容
- lookahead:当光标不在目标上时,向前查找最近的匹配
- set_jumps:跳转记录到 Vim 的 jumplist,可用 Ctrl+O/I 回跳
基于语法树的代码折叠
Tree-sitter 驱动的代码折叠比基于缩进的折叠更精确。它理解代码的真实结构(函数、类、条件块),不会因为缩进不一致而折叠错误。
-- 启用 Tree-sitter 折叠
vim.o.foldmethod = "expr"
vim.o.foldexpr = "nvim_treesitter#foldexpr()"
-- 折叠行为配置
vim.o.foldlevel = 99 -- 打开文件时不自动折叠
vim.o.foldlevelstart = 99
vim.o.foldenable = true
-- 自定义折叠文本显示
vim.o.foldtext = "v:lua.vim.treesitter.foldtext()"
-- folds.scm 查询示例(Python)
;
; 这些节点类型可以折叠
; [
; (function_definition)
; (class_definition)
; (if_statement)
; (for_statement)
; (with_statement)
; (try_statement)
; (list)
; (dictionary)
; (string)
; ] @fold
-- 折叠快捷键
vim.keymap.set("n", "ff" , function()
vim.cmd("normal! zc") -- 关闭折叠
end)
vim.keymap.set("n", "fo" , function()
vim.cmd("normal! zo") -- 打开折叠
end)
vim.keymap.set("n", "fO" , function()
vim.cmd("normal! zR") -- 打开所有折叠
end)
Tree-sitter 折叠的关键配置:
- foldmethod = "expr":告诉 Vim 使用表达式计算折叠
- foldexpr:使用 nvim_treesitter 的折叠表达式
- foldlevel = 99:默认全部展开,避免打开文件时看到一堆折叠
- foldtext:使用 Treesitter 渲染折叠行文本(保留语法高亮)
- folds.scm:查询文件定义哪些语法节点可以折叠
增量选择与彩虹括号
增量选择(Incremental Selection)让你从光标位置逐步扩展选中范围——从当前单词到整个表达式、到语句、到函数、到类。这是基于语法树的"语义级选择"。
彩虹括号(Rainbow Parentheses)
nvim-ts-rainbow 插件利用 Tree-sitter 精确识别括号对,为不同层级的括号分配不同颜色。比传统正则方案更准确——不会把字符串中的括号也算进去。
上下文固定(Context)
nvim-treesitter-context 插件将当前函数/类的签名固定在窗口顶部。当编辑长函数时,始终能看到函数名和参数列表,不需要滚回顶部查看。
自动配对(Autopairs)
nvim-autopairs 集成 Tree-sitter,可以智能判断是否应该自动关闭括号。在字符串或注释中输入 ( 不会触发自动配对,在代码中则会自动补全 )。
增量选择相比传统的可视模式(Visual mode)有什么优势?
进阶主题
自定义查询、编写解析器、性能调优和调试技巧——成为 nvim-treesitter 高级用户
自定义查询覆盖
nvim-treesitter 的查询文件可以通过 after/queries/ 目录结构覆盖。这让你可以在不修改插件源码的情况下,自定义高亮规则、折叠行为和 textobjects。
nvim-treesitter 按以下优先级加载查询文件:1) 用户自定义(after/queries/) > 2) 插件内置(nvim-treesitter/queries/)。你可以完全覆盖,也可以用 ; extends 注释在原有查询基础上追加。使用 ; inherits 指令可以继承父语言的查询(如 TypeScript 继承 JavaScript)。
-- 目录结构:
-- ~/.config/nvim/after/queries/
-- lua/highlights.scm -- Lua 高亮覆盖
-- python/highlights.scm -- Python 高亮覆盖
-- rust/folds.scm -- Rust 折叠覆盖
-- typescript/textobjects.scm
--
-- 方法 1:完全覆盖(替换默认查询)
-- 文件内容即为完整的查询定义
-- 方法 2:追加查询(在默认基础上添加)
-- 文件第一行写 ; extends
-- after/queries/lua/highlights.scm
; extends
-- 额外高亮:vim 全局变量
("vim") @variable.builtin
-- 高亮 require 调用中的模块名
(function_call
name: (identifier) @_require
(arguments
(string
(string_content) @string.special))
(#eq? @_require "require"))
-- 高亮 local function 中的函数名
(variable_declaration
(declaration
(variable_declarator
name: (identifier) @function)
(function_expression)))
-- 自定义 Textobjects 查询
-- after/queries/lua/textobjects.scm
; extends
-- 将 table constructor 视为类对象
(table_constructor) @class.outer
-- 选择 do...end 块的内容
(do_statement) @loop.outer
自定义查询的关键知识点:
- after/queries/lang/:Neovim 的标准覆盖路径,按语言名组织
- ; extends:在默认查询基础上追加,而不是替换
- #eq?:精确字符串匹配谓词,用于过滤特定函数名
- #match?:Lua 模式匹配谓词,用于模糊匹配
- 查询文件类型:highlights.scm、folds.scm、textobjects.scm、injections.scm
性能调优与调试
Tree-sitter 本身性能优秀(毫秒级解析),但不当的配置可能导致 Neovim 卡顿。了解性能调优和调试方法,可以确保流畅的编辑体验。
大文件保护
对超过阈值的文件自动禁用 Tree-sitter。推荐阈值 100-200KB。大型 JSON/CSV/日志文件不需要语法高亮,禁用可以避免明显的打开延迟和滚动卡顿。
查询复杂度控制
避免在查询中使用过多嵌套层级和通配符。过深的嵌套匹配(5+ 层)会导致查询变慢。优先使用字段名(name:)精确定位,而不是用通配符遍历。
解析器编译优化
用 :TSInstallFromGrammar! 重新编译解析器时,确保系统有 C 编译器(gcc/clang)。编译后的 .so 文件性能远优于 WASM 版本。Linux 下可设置 CC=clang 获得更好的优化。
调试工具
:TSPlaygroundToggle 打开语法树可视化面板,实时显示光标位置的节点类型和层级。:Inspect 命令显示当前高亮使用的捕获组名称。这两个工具是调试高亮问题的利器。
-- 调试工具配置
-- 1. Playground 语法树可视化
{
"nvim-treesitter/playground",
config = function()
local pg = require("nvim-treesitter.configs")
pg.setup({
playground = {
enable = true,
updatetime = 25, -- 刷新延迟(ms)
persist_queries = true, -- 保存查询历史
},
})
end,
}
-- 2. 常用调试命令
-- :TSPlaygroundToggle 打开/关闭语法树面板
-- :TSHighlightCaptures 显示当前高亮捕获
-- :Inspect 显示光标位置的高亮信息
-- :TSInstallInfo 查看已安装的解析器
-- :TSUpdate all 更新所有解析器
-- 3. 查看解析耗时
vim.treesitter.query.set("lua", "highlights", "")
-- 清空查询 → 重新设置 → 对比性能差异
-- 4. 诊断解析问题
local parser = vim.treesitter.get_parser(0, "lua")
local tree = parser:parse()[1]
local root = tree:root()
-- 检查是否有错误节点
local has_errors = false
for node in root:iter_children() do
if node:type() == "ERROR" then
has_errors = true
local sr, sc = node:start()
print("Parse error at line " .. (sr+1))
end
end
调试 Tree-sitter 的核心工具和方法:
- Playground:实时语法树可视化,光标移动时同步高亮对应节点
- :Inspect:Neovim 内置命令,显示光标位置的捕获组和高亮链
- ERROR 节点检测:遍历语法树查找解析错误,定位问题行
- 查询清空对比:通过清空查询来测试是否是查询导致性能问题
- TSInstallInfo:检查解析器安装状态,确认版本一致性
编写自定义语言解析器
当 Tree-sitter 社区没有你需要的语言解析器时,可以自己编写。Tree-sitter 提供了 DSL(grammar.js)来定义语法规则,然后自动生成 C 语言解析器。
// grammar.js — 定义一个简单配置语言的语法
// 这是一个演示用的最小化示例
module.exports = grammar({
name: "myconfig",
rules: {
// 顶层规则:配置文件 = 多个语句
source_file: $ => repeat(
choice(
$.section_header,
$.key_value_pair,
$.comment,
)
),
// 段标题:[section_name]
section_header: $ => seq(
"[",
field("name", $.identifier),
"]",
$.newline,
),
// 键值对:key = value
key_value_pair: $ => seq(
field("key", $.identifier),
"=",
field("value", $.value),
$.newline,
),
// 值:字符串 | 数字 | 布尔
value: $ => choice(
$.string,
$.number,
$.boolean,
),
// 注释:# 开头
comment: $ => seq(
"#",
token(prec(-1, /.*/)),
$.newline,
),
// 基础 token
identifier: $ => /[a-zA-Z_]\w*/,
string: $ => /"[^"]*"/,
number: $ => /\d+(\.\d+)?/,
boolean: $ => choice("true", "false"),
newline: $ => /\n/,
},
});
Tree-sitter 语法定义的核心概念:
- grammar():创建语法定义,name 是语言标识
- seq():序列组合——按顺序匹配所有元素
- choice():选择组合——匹配任意一个备选
- repeat():零次或多次重复
- field():为子节点命名,查询中可以用 name: 引用
- token():标记为词法 token(不再向下解析)
- prec():设置优先级,解决歧义(-1 降低优先级)
在自定义查询文件中使用 "; extends" 的作用是什么?
总结与练习
回顾 Tree-sitter 与 nvim-treesitter 的核心知识,通过综合练习巩固技能
核心知识回顾
Tree-sitter 核心
增量解析引擎,O(n) 构建 CST,O(log n) 增量更新。错误容忍、语言无关、60+ 社区解析器。为代码编辑器提供比正则表达式更精确、更快速的语法分析。
查询语言
S-expression 模式匹配语法树。@capture 标记高亮组,字段名(name:)精确匹配子节点,谓词(#eq?、#match?)过滤节点。四种查询文件:highlights/folds/textobjects/injections。
编辑能力
Textobjects(语义级文本对象)、增量选择(节点->表达式->语句->函数逐级扩展)、语法树折叠、彩虹括号、上下文固定。一个语法树驱动多种编辑功能。
自定义与调试
after/queries/ 覆盖默认查询,; extends 追加自定义规则。Playground 可视化语法树,:Inspect 调试高亮。大文件保护、查询复杂度控制、解析器编译优化。
nvim-treesitter 最佳实践
综合练习:自定义 Lua 高亮配置
完成以下练习,巩固 Tree-sitter 查询语言和高亮映射的知识。你需要为一个 Lua 项目编写自定义的高亮查询和高亮组配置。
创建一个自定义查询文件,实现以下效果:1) 高亮 vim 全局变量(如 vim.cmd、vim.fn)为特殊的内置变量颜色;2) 区分函数声明和函数调用使用不同样式;3) 将 TODO/FIXME 注释标记为警告色;4) 将 require() 中的模块名标记为特殊字符串。
; after/queries/lua/highlights.scm
; 请补充完整实现
; TODO 1: 高亮 vim 全局变量
; 提示:匹配 (identifier) 节点,
; 值为 "vim" 时标记为 @variable.builtin
; TODO 2: 区分函数声明和调用
; 提示:function_declaration 的 name 用 @function
; function_call 的函数名用 @function.call
; TODO 3: 高亮 TODO/FIXME 注释
; 提示:在 (comment) 中使用 (#match? @comment.todo "TODO|FIXME")
; TODO 4: 高亮 require 模块名
; 提示:匹配 function_call name 为 "require" 的参数
完成这个练习的建议步骤:
- TODO 1:用 (#eq? @variable.builtin "vim") 精确匹配 vim 标识符
- TODO 2:function_declaration/name → @function,function_call/name → @function.call
- TODO 3:对 comment 节点使用 (#match? ...) 谓词匹配关键词
- TODO 4:匹配 require 调用的参数字符串内容
- 验证:用 :Inspect 命令检查光标位置是否使用了正确的高亮组
下一步学习方向
LSP 与 Tree-sitter 协作
Tree-sitter 提供语法级信息(结构、高亮),LSP 提供语义级信息(类型、定义、引用)。两者结合可以实现语法感知 + 类型感知的智能编辑。Neovim 的 LSP 客户端(nvim-lspconfig)与 Tree-sitter 可以无缝配合。
Neovim 生态集成
telescope-treesitter(语法结构搜索)、aerial.nvim(代码大纲)、navic(面包屑导航)、flash.nvim(语法感知跳转)。这些插件都基于 Tree-sitter 语法树工作,理解 Tree-sitter 是掌握 Neovim 生态的基础。
贡献解析器
为社区维护的语言编写或改进 Tree-sitter 解析器。tree-sitter 官方仓库接受高质量的语法定义贡献。这是深入理解解析理论和 Tree-sitter DSL 的最佳实践方式。
以下哪个不是 Tree-sitter 的特性?
nvim-treesitter 的查询文件中,; extends 指令的作用是什么?