在缓冲区中识别并查找特定的 对象(things) 通常非常实用,例如函数与类定义、语句、代码块、字符串、注释等。Emacs 允许用户定义哪种 tree-sitter 节点对应某一 “对象(things)”。这一功能支持许多便捷操作,比如跳转到下一个函数、选中光标处的代码块,或是交换两个函数参数。
Emacs 中的 “对象(things)” 功能独立于 tree-sitter 的模式匹配功能,虽然功能相对简洁,但更适用于在解析树中导航与遍历。
你可以使用 treesit-thing-settings 定义对象,使用 treesit-thing-definition 获取已定义对象的判定规则,使用 treesit-thing-defined-p 检查某一对象是否已定义。
这是一个 alist,用于为每种语言存储对象定义。每个条目的键是语言符号,值是对象定义列表,格式为 (thing pred),其中 thing 是代表该对象的符号,如 defun、sexp 或 sentence;pred 用于指定哪种 tree-sitter 节点属于该对象。
pred 可以是匹配节点类型的正则表达式字符串;可以是一个以节点为参数、返回布尔值的函数,用于判断节点是否为该对象;也可以是 cons 对 (regexp . fn),同时结合正则表达式与函数—节点必须同时匹配正则表达式且满足函数条件,才会被视为该对象。
pred 还支持递归定义。可以写作 (or pred…),表示满足任意一个条件即为该对象;也可以写作 (not pred),表示不满足该条件即为该对象。
最后,pred 可以引用本列表中定义的其他对象。例如,(or sexp sentence) 定义的对象既是 alist 中其他规则定义的 sexp 对象,也是 sentence 对象。
以下是适用于 C 和 C++ 的 treesit-thing-settings 示例:
((c
(defun "function_definition")
(sexp (not "[](),[{}]"))
(comment "comment")
(string "raw_string_literal")
(text (or comment string)))
(cpp
(defun ("function_definition" . cpp-ts-mode-defun-valid-p))
(defclass "class_specifier")
(comment "comment")))
注意该示例经过简化用于教学,与实际 C 和 C++ 主模式中的对象定义不完全一致。
Emacs 内置函数已经在使用部分对象定义。命令 treesit-forward-sexp 在主模式定义了 sexp 时会使用该定义;treesit-forward-sentence 使用 sentence 定义。treesit-end-of-defun 等函数式移动函数使用 defun 定义(为兼容旧版,defun 定义会被 treesit-defun-type-regexp 覆盖)。主模式还可以定义 comment、string、text(通常包含注释与字符串)。
本节后续列出一些使用对象定义的函数。除下方函数外,其他部分函数也使用了对象功能,例如 treesit-search-forward、treesit-induce-sparse-tree 等遍历树的函数。See 获取节点。
该函数检查 node 是否为 thing 对象。
如果是则返回非 nil,否则返回 nil。为方便使用,若 node 为 nil,函数直接返回 nil。
thing 可以是对象符号(如 defun),也可以是直接定义对象的判定规则,如 "function_definition" 或 (or comment string)。
默认情况下,如果 thing 未定义或格式错误,函数会抛出 treesit-invalid-predicate 错误。如果 ignore-missing 为 t,则对象未定义时不报错,直接返回 nil;但对象格式错误时仍会报错。
该函数返回 position 之前第一个属于指定 thing 的节点。若无此节点则返回 nil。函数保证,若返回节点,其结束位置一定小于等于 position。也就是说,该函数绝不会返回包含 position 的节点。
同样,thing 可以是符号或判定规则。
该函数与 treesit-thing-prev 类似,仅返回 position 之后第一个属于该对象的节点。同样保证,若返回节点,其起始位置一定大于等于 position。
该函数基于 treesit-thing-prev 与 treesit-thing-next 实现,提供导航命令所需的常用功能。它从 position 出发,移动 arg 个 thing 对象后返回目标位置。若可导航的对象数量不足,则返回 nil。函数不会移动光标。
arg 为正数表示向前移动对应个数的对象 thing;负数表示向后移动。若 side 为 beg,则停在对象起始位置;若为 end,则停在对象结束位置。
与 treesit-thing-prev 相同,thing 可以是 treesit-thing-settings 中定义的符号,也可以是判定规则。
tactic 决定对象间的移动策略,可选值为 nested、top-level、restricted 或 nil。
nested 或 nil 表示普通嵌套导航:优先在同级兄弟节点间移动;若同级无更多节点,则向上移动到父节点,再遍历兄弟节点,依此类推。
top-level 表示仅在顶层对象间导航,忽略嵌套对象。
restricted 表示移动范围限制在包含 position 的对象内部(如果存在)。该策略适用于需要停留在当前嵌套层级、不向上跳转的命令。
该函数返回包含 position、且属于 thing 的最小节点;若无此节点则返回 nil。
返回节点必须包含 position,即其起始位置 ≤ position,结束位置 ≥ position。
若 strict 为非 nil,则使用严格比较,即起始位置必须严格小于 position,结束位置必须严格大于 position。
thing 可以是 treesit-thing-settings 中定义的符号,也可以是判定规则。
此外还有一些便捷包装函数。
treesit-beginning-of-thing 将光标移至对象开头,treesit-end-of-thing 移至对象结尾,treesit-thing-at-point 返回光标处的对象。
还有专门使用 defun 定义的函数式命令(作为 treesit-defun-type-regexp 的备选),如 treesit-beginning-of-defun、treesit-end-of-defun 和 treesit-defun-at-point。此外,这些函数使用 treesit-defun-tactic 作为导航策略。相关详细说明见其他章节(see 使用 tree-sitter 开发主模式)。