38.3 获取节点

以下是编写 tree-sitter 函数文档时使用的术语与约定。

语法树中的节点对应缓冲区中程序文本的某一段范围。 如果一个节点覆盖的缓冲区文本范围比另一个节点更小或更大, 我们就称该节点比另一节点 “更小(smaller)” 或 “更大(larger)”。由于在树中位置更深(“更低(lower)”)的节点 是树中 “更高层(higher)” 节点的子节点,因此在节点层级中, 更低层的节点一定比更高层的节点更小。 语法树中更高层的节点包含一个或多个更小的节点作为其子节点, 因此覆盖的缓冲区文本范围更大。

当函数无法找到节点时,返回 nil。 为方便使用,所有以节点为参数并返回节点的函数, 同样接受参数为 nil 的节点,并在这种情况下直接返回 nil

关联缓冲区被修改时,节点不会自动更新, 且节点一旦获取后就无法再更新。 使用已过期的节点会触发 treesit-node-outdated 错误。

从语法树获取节点

Function: treesit-node-at pos &optional parser-or-lang named

该函数返回缓冲区位置 pos 处的一个 叶子(leaf) 节点。 叶子节点是没有任何子节点的节点。

该函数会尝试返回覆盖 pos 的节点: 节点起始位置小于等于 pos, 节点结束位置大于等于 pos

如果没有叶子节点的范围覆盖 pos(例如 pos 位于 两个叶子节点之间的空白处),该函数返回 pos 之后的第一个叶子节点。

最后,如果 pos 之后没有叶子节点,则返回 pos 之前的第一个叶子节点。

如果 parser-or-lang 是解析器对象,该函数使用该解析器; 如果 parser-or-lang 是一种语言,该函数使用当前缓冲区中 该语言的第一个解析器,若不存在则创建一个; 如果 parser-or-langnil,该函数通过调用 treesit-language-at(see 多语言文本解析) 尝试推断 pos 处的语言。

若该函数无法找到合适的节点返回,则返回 nil

如果 named 为非 nil,该函数只查找命名节点 (see named node)。

示例:

;; 在 C 解析器的语法树中查找光标处的节点。
(treesit-node-at (point) 'c)
  ⇒ #<treesit-node (primitive_type) in 23-27>
Function: treesit-node-on beg end &optional parser-or-lang named

该函数返回覆盖缓冲区文本中 begend 区域的 最小 节点。换句话说,节点的起始位置在 beg 之前或等于 beg, 节点的结束位置在 end 之后或等于 end

注意: 在不属于任何顶层结构(函数定义等)的空行上调用此函数, 很可能会返回根节点,因为根节点是覆盖该空行的最小节点。 大多数情况下,你应当改用 treesit-node-at

如果 parser-or-lang 是解析器对象,该函数使用该解析器; 如果 parser-or-lang 是一种语言,该函数使用当前缓冲区中 该语言的第一个解析器,若不存在则创建一个; 如果 parser-or-langnil,该函数通过调用 treesit-language-at 尝试推断 beg 处的语言。

如果 named 为非 nil,该函数只查找命名节点 (see named node)。

Function: treesit-parser-root-node parser

该函数返回由 parser 生成的语法树的根节点。

Function: treesit-buffer-root-node &optional language

该函数查找当前缓冲区中 language 的第一个解析器, 若不存在则创建一个,并返回该解析器生成的根节点。 如果省略 language,则使用解析器列表中的第一个解析器。 若无法找到合适的解析器,返回 nil

给定一个节点,Lisp 程序可以从该节点出发获取其他节点, 或查询该节点的相关信息。

从已有节点获取其他节点

按亲属关系

Function: treesit-node-parent node

该函数返回 node 的直接父节点。

如果 node 在解析树中的深度超过 1000 层, 返回值未定义。当前会返回 nil,但未来可能改变。

Function: treesit-node-child node n &optional named

该函数返回 node 的第 n 个子节点。 如果 named 为非 nil,则只统计命名节点 (see named node)。

例如,在表示字符串 "text" 的节点中, 有三个子节点:左引号 "、字符串内容 text 和右引号 "。在这些节点中,第一个子节点是左引号 ", 第一个命名子节点是字符串内容。

如果不存在第 n 个子节点,该函数返回 niln 可以为负数,例如 −1 表示最后一个子节点。

Function: treesit-node-children node &optional named

该函数以列表形式返回 node 的所有子节点。 如果 named 为非 nil,则只获取命名节点。

Function: treesit-node-next-sibling node &optional named

该函数查找 node 的下一个兄弟节点。 如果 named 为非 nil,则查找下一个命名兄弟节点。

Function: treesit-node-prev-sibling node &optional named

该函数查找 node 的上一个兄弟节点。 如果 named 为非 nil,则查找上一个命名兄弟节点。

按字段名

为了让语法树更易于分析,许多语言语法会为子节点 分配 字段名(field names)(see field name)。 例如,一个 function_definition 节点可以有 declarator 子节点和 body 子节点。

Function: treesit-node-child-by-field-name node field-name

该函数查找 node 中字段名为 field-name(字符串)的子节点。

;; 获取字段名为 "body" 的子节点。
(treesit-node-child-by-field-name node "body")
  ⇒ #<treesit-node (compound_statement) in 45-89>

按位置

Function: treesit-node-first-child-for-pos node pos &optional named

该函数查找 node 中第一个覆盖到缓冲区位置 pos 的子节点。 “覆盖到(Extends beyond)” 表示子节点的结束位置大于等于 pos。 该函数只查找 node 的直接子节点,不查找孙节点。 如果 named 为非 nil,则查找第一个命名子节点 (see named node)。

Function: treesit-node-descendant-for-range node beg end &optional named

该函数查找 node 中覆盖 begend 文本区域的 最小 后代节点。用法与 treesit-node-at 类似。 如果 named 为非 nil,则查找最小的命名子节点。

搜索节点

Function: treesit-search-subtree node predicate &optional backward all depth

该函数遍历 node 的子树(包含 node 自身), 查找使 predicate 返回非 nil 的节点。 predicate 可以是一个正则表达式,用于匹配每个节点的类型; 也可以是一个谓词函数,接收节点并在匹配时返回非 nilpredicate 还可以是事物符号或事物定义(see 用户自定义 “对象(Things)” 与导航)。 使用未定义的事物不会报错,函数只会返回 nil

该函数返回第一个匹配的节点,若无匹配则返回 nil

默认情况下,该函数只遍历命名节点; 但若 all 为非 nil,则遍历所有节点。 如果 backward 为非 nil,则反向遍历(即向下遍历时优先访问最后一个子节点)。 如果 depth 为非 nil,则必须为数字,用于限制向下遍历的层数。 如果 depthnil,默认值为 1000。

Function: treesit-search-forward start predicate &optional backward all

treesit-search-subtree 类似,该函数同样遍历解析树, 并使用 predicate 匹配每个节点(start 除外)。 predicate 可以是正则表达式或谓词函数, 也可以是事物符号或事物定义(see 用户自定义 “对象(Things)” 与导航)。 使用未定义的事物不会报错,函数只会返回 nil

对于下方所示的树结构,其中 start 标记为 ‘S’, 该函数按数字 1 到 12 的顺序遍历:

              12
              |
     S--------3----------11
     |        |          |
o--o-+--o  1--+--2    6--+-----10
|  |                  |        |
o  o                +-+-+   +--+--+
                    |   |   |  |  |
                    4   5   7  8  9

注意,该函数不会遍历 start 的子树, 并且总是先遍历叶子节点,再向上回溯。

treesit-search-subtree 一样,该函数默认只搜索命名节点; 但若 all 为非 nil,则搜索所有节点。 如果 backward 为非 nil,则反向搜索。

treesit-search-subtree 遍历单个节点的子树, 而本函数从节点 start 出发,按缓冲区位置顺序遍历其后的所有节点, 即起始位置大于 start 结束位置的节点。

在上图中,treesit-search-subtree 遍历节点 ‘S’(start) 和标记为 o 的节点,而本函数遍历标记为数字的节点。 该函数常用于解决类似 “缓冲区中 start 之后第一个满足某条件的节点是什么?” 的问题。

Function: treesit-search-forward-goto node predicate &optional start backward all

该函数将光标移动到缓冲区中 node 之后 下一个匹配 predicate 的节点的起始或结束位置。 如果 start 为非 nil,则停在节点起始位置而非结束位置。

该函数保证返回的匹配节点在缓冲区位置上是向后推进的: 返回节点的起始/结束位置始终大于 node 的对应位置。

参数 predicatebackwardalltreesit-search-forward 中的用法一致。

Function: treesit-induce-sparse-tree root predicate &optional process-fn depth

该函数从 root 的子树中生成一棵稀疏树。

它处理 root 下的子树,只保留匹配 predicate 的节点。 与前面的函数一样,predicate 可以是匹配节点类型的正则字符串, 也可以是接收节点并在匹配时返回非 nil 的函数。 predicate 还可以是事物符号或事物定义(see 用户自定义 “对象(Things)” 与导航)。 使用未定义的事物不会报错,函数只会返回 nil

例如,左侧的子树同时包含数字和字母, 若 predicate 为 “只保留字母(letter only)”, 返回的树如右侧所示。

    a                 a              a
    |                 |              |
+---+---+         +---+---+      +---+---+
|   |   |         |   |   |      |   |   |
b   1   2         b   |   |      b   c   d
    |   |     =>      |   |  =>      |
    c   +--+          c   +          e
    |   |  |          |   |
 +--+   d  4       +--+   d
 |  |              |
 e  5              e

如果 process-fn 为非 nil,该函数不会直接返回匹配节点, 而是将每个节点传入 process-fn,并使用其返回值。 如果 depth 为非 nil,则限制从 root 向下遍历的层数。 如果 depthnil,默认值为 1000。

返回树中的每个节点格式为 (tree-sitter-node . (child …))。 如果 root 不匹配 predicate,该树根节点的 tree-sitter-nodenil。 若无节点匹配 predicate,函数返回 nil

更多便捷函数

Function: treesit-node-get node instructions

这是一个便捷函数,可将多个节点访问函数链式调用。 例如,获取 node 的父节点的下一个兄弟节点的第二个子节点的文本:

(treesit-node-get node
   '((parent 1)
    (sibling 1 nil)
    (child 1 nil)
    (text nil)))

instruction 是形如 (fn arg...) 的指令列表。 支持以下 fn

(child idx named)

获取第 idx 个子节点。

(parent n)

向上查找父节点 n 次。

(field-name)

获取当前节点的字段名。

(type)

获取当前节点的类型。

(text no-property)

获取当前节点的文本。

(children named)

获取子节点列表。

(sibling step named)

获取第 n 个上一个/下一个兄弟节点, step 为负表示上一个兄弟节点,为正表示下一个兄弟节点。

注意,与原函数不同,namedno-property 等参数不可省略。

Function: treesit-filter-child node predicate &optional named

该函数查找 node 中满足 predicate 的直接子节点。

predicate 函数接收节点作为参数, 返回非 nil 表示保留该节点。 如果 named 为非 nil,该函数只检查命名节点。

Function: treesit-parent-until node predicate &optional include-node

该函数不断查找 node 的父节点, 并返回满足 predicate 的父节点。 predicate 可以是接收节点并返回 tnil 的函数, 也可以是匹配节点类型名的正则表达式, 或是 treesit-thing-settings 中描述的其他有效谓词。 若无父节点满足条件,函数返回 nil

默认情况下,该函数只检查 node 的父节点,不包含 node 自身。 但若 include-node 为非 nil,且 node 满足 predicate, 则直接返回 node

Function: treesit-parent-while node predicate

该函数从 node 开始向上遍历树, 只要节点满足谓词函数 predicate 就继续向上。 也就是说,该函数返回 node 最高的、仍满足 predicate 的父节点。 注意,如果 node 满足条件但直接父节点不满足,则返回 node 自身。

Function: treesit-node-top-level node &optional predicate include-node

该函数返回 node 最高的、与 node 类型相同的父节点。 若不存在则返回 nil。 因此该函数也可用于判断 node 是否为顶层节点。

如果 predicatenil,该函数使用 node 的类型查找父节点。 如果 predicate 为非 nil,则搜索满足该谓词的父节点。 如果 include-node 为非 nil,且 node 满足 predicate, 则返回 node