Emacs 利用 Tree-sitter 实现自定义高亮显示,第一部分

几年前,我写过一篇关于在 Emacs 中使用基于 Tree-sitter 的自定义语法高亮显示的博客。一开始我只是用红色高亮某些关键词。我想要突出那些打断控制流的关键词:

屏幕截图展示了用黑色或红色高亮显示的关键词的语法

我用粗体高亮常规关键词(whileif)。我用红色高亮控制流中断关键词(returncontinue)。这就是语法高亮。

但是我也想根据名称的含义来高亮显示名称。在一些项目中,我处理水平和垂直测量,我认为以不同的方式高亮显示这些变量名会很好。在一些项目中,我使用 Delaunay+Voronoi 网格,我想以不同的方式高亮显示三角形、边和多边形。在一些项目中,我使用六边形网格,我想以不同的方式高亮显示三个轴,就像我在我的六边形网格指南中所做的那样。在一些项目中,我正在处理 html+js 混合代码,我想以不同的方式高亮显示 html 与 js,就像我在关于我如何编写交互式教程的教程中所做的那样。

在几年前的那篇博客文章中,我使用了 Emacs Tree-sitter mode 来实现这种类型的高亮显示。从那时起,Emacs 已经集成了自己的 Tree-sitter mode,它与第三方包的工作方式不同。我决定更新我的代码以使用新的 Tree-sitter 函数。

让我们从高亮显示关键词开始。这实际上不需要 Tree-sitter mode,但我正在使用它,因为我想在这篇文章的第二部分中高亮显示更多内容。treesit-font-lock-rules 函数将 Tree-sitter 模式编译成某种内部格式。由于每种语言的关键词集合都不同,因此我将为每种语言创建一个变量。这是 Python 的:

(defface amitp-control-keyword-face)
(defvar amitp/python-treesit-settings
 (treesit-font-lock-rules
  :language 'python
  :feature 'keyword
  :override t
  '(
   (["assert" "await" "break" "continue" "finally" "raise" "return" "yield"]
   @amitp-control-keyword-face)
   )
  ))

这将以我的 amitp-control-keyword-face 而不是默认的 font-lock-keyword-face 高亮显示这些关键词。

一个小提示 —— 我曾尝试重用我之前的 Tree-sitter face 名称,这些名称使用句点 (variable.horizontal) 和冒号 (amitp:keyword),但我遇到了一些错误,所以我切换为仅对新的 Tree-sitter modes 使用短划线 (variable-horizontal)。

然后我必须将这些高亮显示规则安装treesit-font-lock-settings 中,这是一个 buffer-local 变量。所以我从一个 hook 安装它:

(add-hook 'python-ts-mode-hook
     (lambda ()
      (setq-local treesit-font-lock-settings
            (append treesit-font-lock-settings
                amitp/python-treesit-settings))))

这就是我想要的方式高亮显示关键词所需的全部。但是等等,还有更多!

我注意到类型别名没有按照我想要的方式高亮显示。这是屏幕截图:

屏幕截图显示单词 “type” 未被高亮显示为关键词

关键词 type 在这里没有被高亮显示。我可以像其他关键词一样添加它:

(defvar amitp/python-treesit-settings
 (treesit-font-lock-rules
  :language 'python
  :feature 'keyword
  :override t
  '(
   (["assert" "await" "break" "continue" "finally" "raise" "return" "yield"]
   @amitp-control-keyword-face)
   (["type"] @font-lock-keyword-face) ;; <-- added
   )
  ))

或者,我可以修改 python.el 的 python--treesit-keywords 以包含 "type"

然而,与其他关键词不同,type 是 Python 中的一个软关键词,而不是一个完整的关键词。这意味着它可以在其他上下文中使用,例如函数调用:

屏幕截图显示单词 “type” 可以同时作为关键词和标识符出现

我不想高亮显示这些用法。我使用 M-x treesit-inspect-mode 来了解 Tree-sitter 解析树上下文。然后我制定了规则,当单词 type 出现在定义类型别名的上下文中时才高亮显示它:

(defvar amitp/python-treesit-settings
 (treesit-font-lock-rules
  :language 'python
  :feature 'keyword
  :override t
  '(
   (["assert" "await" "break" "continue" "finally" "raise" "return" "yield"]
   @amitp-control-keyword-face)
   ((type_alias_statement "type" @font-lock-keyword-face)) ;; <-- added
   )
  ))

这是结果:

屏幕截图显示当单词 “type” 用作关键词时,其高亮显示方式与用作函数时不同 "type" 关键词 vs "type" 函数

这是我通过使用 Tree-sitter 高亮而不是正则表达式高亮所获得的好处之一。

我还通过以不同的 face 标记 import NAMEfrom name import NAME,更改了 import 语句的高亮显示方式:

 (treesit-font-lock-rules
  :language 'python
  :feature 'definition
  :override t
  '(
   ((import_statement name: (dotted_name (identifier) @font-lock-variable-name-face)))
   ((import_from_statement module_name: (dotted_name (identifier))
               name: (dotted_name (identifier) @font-lock-variable-name-face)))
   )

我仍在试验以弄清楚我想要拥有的规则。我尝试一些事情,然后删除其中的一些。我发现 M-x treesit-inspect-mode 非常有用。

在第 2 部分中,我将探索基于我常用的变量名进行高亮显示的启发式方法。

标签:emacsAmit – 2025 年 2 月 27 日 星期四

0 条评论:

发表评论 较早的文章 主页 订阅:帖子评论 (Atom) 版权所有 © 2024 Amit Patel