38.8 使用 tree-sitter 开发主模式

本节介绍为某一主模式开发 tree-sitter 集成功能的通用规范。

支持 tree-sitter 功能的主模式应大致遵循如下结构:

(define-derived-mode woomy-mode prog-mode "Woomy"
  "A mode for Woomy programming language."
  (when (treesit-ready-p 'woomy)
    (setq-local treesit-variables ...)
    ...
    (treesit-major-mode-setup)))

若启用 tree-sitter 的条件未满足,treesit-ready-p 会自动发出警告。

如果某个 tree-sitter 主模式与其 “原生(native)” 版本共用部分配置, 可以创建一个包含通用设置的 “基础模式(base mode)”,示例如下:

(define-derived-mode woomy--base-mode prog-mode "Woomy"
  "Woomy 编程语言的内部基础模式。"
  (common-setup)
  ...)

(define-derived-mode woomy-mode woomy--base-mode "Woomy"
  "Woomy 编程语言的编辑模式。"
  (native-setup)
  ...)

(define-derived-mode woomy-ts-mode woomy--base-mode "Woomy"
  "Woomy 编程语言的 tree-sitter 编辑模式。"
  (when (treesit-ready-p 'woomy)
    (setq-local treesit-variables ...)
    ...
    (treesit-major-mode-setup)))
Function: treesit-ready-p language &optional quiet

该函数检查启用 tree-sitter 所需的条件。 它会检查 Emacs 是否编译支持 tree-sitter、缓冲区大小是否在 tree-sitter 可处理范围内, 以及系统中是否存在 language 对应的语法文件 (see Tree-sitter 语言语法库)。

若无法启用 tree-sitter,该函数会发出警告。 若 quietmessage,警告会转为普通消息; 若 quiett,则不显示任何警告或消息。

所有必要条件均满足时,该函数返回非 nil; 否则返回 nil

Function: treesit-major-mode-setup

该函数为主模式启用若干 tree-sitter 功能。

目前,它会配置以下功能:

  • treesit-font-lock-settings (see 基于解析器的字体锁定) 非 nil,则设置语法高亮。
  • treesit-simple-indent-rulestreesit-indent-function (see 基于解析器的缩进) 非 nil,则设置缩进规则。
  • treesit-defun-type-regexpnil, 则为 beginning-of-defunend-of-defun 配置代码块导航函数。
  • treesit-defun-name-functionnil, 则设置 add-log-current-defun 使用的名称提取函数。
  • treesit-simple-imenu-settings (see Imenu) 非 nil,则设置 Imenu 索引。
  • treesit-outline-predicate (see 大纲次要模式) 非 nil,则设置大纲次要模式。
  • treesit-thing-settings (see 用户自定义 “对象(Things)” 与导航) 中定义了 sexp 和/或 sentence, 则通过设置 forward-sexp-functionforward-sentence-function 等变量启用对应的结构导航命令。

关于这些内置 tree-sitter 功能的更多说明, 参见 see 基于解析器的字体锁定、see 基于解析器的缩进 以及 see 移动遍历平衡表达式

如需在主模式中支持多语言混合,参见 see 多语言文本解析

beginning-of-defunend-of-defun 外, Emacs 还提供了额外的代码块操作函数: treesit-defun-at-point 返回光标处的代码块节点, treesit-defun-name 返回代码块节点的名称。

Function: treesit-defun-at-point

该函数返回光标所在位置的代码块节点,未找到则返回 nil。 它遵循 treesit-defun-tactic 设置: 若值为 top-level,返回顶层代码块; 若值为 nested,返回直接包含当前位置的内层代码块。

该函数依赖 treesit-defun-type-regexp, 若其为 nil,函数直接返回 nil

Function: treesit-defun-name node

该函数返回 node 对应的代码块名称。 若 node 无名称、不是代码块节点或为 nil,则返回 nil

根据语言与主模式不同,代码块名称可以是函数名、类名、结构体名等。

treesit-defun-name-functionnil, 该函数始终返回 nil

Variable: treesit-defun-name-function

若非 nil,该变量应为一个接收节点参数并返回其代码块名称的函数。 该函数语义与 treesit-defun-name 一致: 节点非代码块、是代码块但无名、或节点为 nil 时均返回 nil