除了简单的语法字体锁定和基于正则表达式的字体锁定外, Emacs 还借助解析器提供完整的语法字体锁定能力。 目前,Emacs 使用 tree-sitter 库实现这一功能(see Parsing Program Source)。
基于解析器的字体锁定与其他字体锁定机制并不互斥。 默认情况下,若启用,基于解析器的字体会先运行,替代语法字体锁定, 之后再运行基于正则表达式的字体锁定。
尽管基于解析器的字体锁定与基于正则表达式的字体锁定不共用同一套自定义变量,
但采用了相似的自定义方案。font-lock-keywords 对应的 tree-sitter 变量是
treesit-font-lock-settings。
通常,tree-sitter 高亮的工作流程如下:
font-lock-keyword 的节点会使用
font-lock-keyword-face 进行高亮。
关于查询、模式和捕获名称的更多信息,参见 Pattern Matching Tree-sitter Nodes。
要设置 tree-sitter 高亮,主模式应先使用
treesit-font-lock-rules 的输出设置
treesit-font-lock-settings,然后调用
treesit-major-mode-setup。
该函数用于设置 treesit-font-lock-settings。
它负责编译查询和其他后处理,并输出一个
treesit-font-lock-settings 可接受的值。示例如下:
(treesit-font-lock-rules :language 'javascript :feature 'constant :override t '((true) @font-lock-constant-face (false) @font-lock-constant-face) :language 'html :feature 'script "(script_element) @font-lock-builtin-face")
该函数接收一系列 query-spec,每个 query-spec 是在一个 query 前面加上一个或多个 keyword/value 对。 每个 query 是一个字符串形式、S 表达式形式或已编译形式的 tree-sitter 查询。
对每个 query,其前面的 keyword/value 对为其附加元信息。
:language 关键字声明 query 的语言。
:feature 关键字设置 query 的功能名称。
用户可以通过 treesit-font-lock-level 和
treesit-font-lock-feature-list(见下文)控制启用哪些功能。
这两个关键字是必需的(个别情况除外)。
其他关键字为可选:
| 关键字 | 取值 | 说明 |
|---|---|---|
:override | nil | 若区域已有外观,则丢弃新外观 |
t | 始终应用新外观 | |
append | 将新外观追加到现有外观之后 | |
prepend | 将新外观插入到现有外观之前 | |
keep | 仅为无外观的区域填充新外观 | |
:default-language | language | 此关键字之后的所有 query 默认使用该语言 |
Lisp 程序在 query 中用捕获名称(以 @ 开头的名称)标记模式,
tree-sitter 会返回带有相同捕获名称标记的匹配节点。
出于高亮目的,query 中的捕获名称应当是外观名,
如 font-lock-keyword-face。被捕获的节点将使用该外观高亮。
捕获名称也可以是函数名。此时该函数会接收 4 个参数:
node、override、start 和 end,
其中 node 是节点本身,override 是捕获该节点的规则的
:override 属性,start 和 end 限定了该函数应当高亮的区域。
(如果该函数希望遵守 override 参数,可以使用 treesit-fontify-with-override。)
除了给出的 4 个参数外,该函数还应接受更多可选参数,以便未来扩展。
如果一个捕获名称既是外观又是函数,外观优先。 如果一个捕获名称既不是外观也不是函数,则会被忽略。
这是一个由功能符号列表组成的列表。列表中的每个元素代表一个装饰级别。
treesit-font-lock-level 控制哪些级别被激活。
列表中的每个元素形如 (feature …),
其中每个 feature 对应 treesit-font-lock-rules 中定义的查询的
:feature 值。从该列表中移除某个功能符号会在字体锁定时禁用对应的查询。
许多编程语言常用的功能名称包括:
definition、type、assignment、builtin、
constant、keyword、string-interpolation、
comment、doc、string、operator、
preprocessor、escape-sequence 和 key。
主模式可自由细分或扩展这些通用功能。
其中部分功能需要说明:
definition 高亮被定义的对象,例如函数定义中的函数名、结构体定义中的结构体名、变量定义中的变量名;
assignment 高亮被赋值的对象,例如赋值语句中的变量或字段;
key 高亮键值对中的键,例如 JSON 对象或 Python 字典中的键;
doc 高亮文档字符串或文档注释。
例如,该变量的值可以是:
((comment string doc) ; level 1 (function-name keyword type builtin constant) ; level 2 (variable-name string-interpolation key)) ; level 3
主模式应在调用 treesit-major-mode-setup 之前设置此变量。
要让该变量生效,Lisp 程序应调用
treesit-font-lock-recompute-features(会相应重置
treesit-font-lock-settings),
或调用 treesit-major-mode-setup(它内部会调用
treesit-font-lock-recompute-features)。
基于 tree-sitter 的字体锁定的设置列表。
每个设置的具体格式被视为内部实现。
应当始终使用 treesit-font-lock-rules 来设置此变量。
多语言主模式应在 treesit-range-functions 中提供范围函数,
Emacs 会在高亮某一区域前相应设置范围(see Parsing Text in Multiple Languages)。