在节前的一篇文章中,我们探讨了fontlock 的基本机制。它主要起到一个桥梁的作用,将语法块和face样式做一个绑定,未来在扫描时发现语法块符合匹配时就采用对应的样式显示文本。
背景知识
符号与 face 绑定
在前面我们已经使用过face,我们知道要设置文本的显示样式,修改修改它的属性,而文本属性中有 face、font-lock-face、mouse-face 和 cursor 等等各种face。那么face 到底是什么?以及如何设置、修改一个face 呢?
首先我在之前介绍Emacs基础语法的文章中提到,elisp中的符号这一概念。符号是一个容器,你可以将它想象成一个柜子,里面有若干个格子。每个格子可以存储不同的内容。对于elisp 来说,符号可以存储具体的值, 这种情况是它最像变量的一种情况了。
(set 'num 10)
num ;; ==> 10我们使用 setq 定义变量时执行的就是类似于 set 指令。setq 的全称是 set quote,它在一个符号存储值的格子中放入一个值。
另一种情况,格子可以放下函数定义,这个时候我们通过(sym) 调用函数,实际上就是在查找 sym 符号中函数格子里面的放入的内容。
它还可以放置一个 plist 属性列表,这个属性列表我们可以存储它的文档信息,也可以存储一个face表。
我们可以通过 defface 定义一个face
(defface v
'((t :foreground "red"))
"My face named v.")但是此时产生的效果与 setq 不同,setq 是创建一个符号,并在符号 value 槽中添加一个值。但是符号中并没有名为 face 的槽,defface 是在 外部的 face 映射表中添加了一条记录,由符号 v 作为键,而我们定义的这个face 样式表作为值。在后续查询的时候实际上是通过在表中查找对应符号名是否绑定了face条目。
我们可以使用 face-attribute 来查询。通过 set-face-attribute 来修改。
这里我们只需要记住face 本身并没有绑定到一个变量上或者符号上,我们将face绑定到具体符号上,并不会影响符号之前保存的内容。至于更详细的内容,读者可以自行查阅相关文档。
Emacs 内置的 font-lock-face
首先,我们在上一篇的例子中定义了名为 my-c-block-comment-face 的变量,作为注释的face。Emacs提供了一系列内置的准的 Font Lock Face。为了使我们开发的mode能轻松适配其他的theme,我们应该尽量使用Emacs自定的这些变量名。而如果我们要开发新的theme,也可以只修改这些变量的值。我们列举一些常用的:
font-lock-keyword-face关键字,例如this、class、return等等font-lock-type-face数据类型或者类名font-lock-variable-name-face变量名font-lock-function-name-face函数名或者方法名font-lock-constant-face常量或者字面量font-lock-builtin-face语言内置函数或者指令font-lock-comment-face注释
以 font-lock-keyword-face 为例,我们来看看该如何使用它们。我们查看它的定义发现,它被定义在Emacs的 font-lock.el 文件中:
(defvar font-lock-keyword-face 'font-lock-keyword-face
"Face name to use for keywords.")我们发现这个变量被定义为了同名的符号。对它求值我们得到这个符号本身。
对于c/c++ 我们可以这样定义关键字到具体face 的映射关系
("\\<\\(if\\|else\\|while\\|for\\|return\\)\\>" . font-lock-keyword-face)这里我们定义了 if、else、while、for、return 关键字对应的face。
因为使用Emacs定义了 font-lock-keyword-face 变量实际返回的是同名的符号,所以它实际上对应着我们在在上一篇文章中定义的规则:
("/\\*\\(?:.\\|\n\\)*?\\*/" 0 'my-c-block-comment-face t)Emacs 在实际渲染是,查询到这些关键字都被映射到一个名为 font-lock-keyword-face 的符号上,它会查找外部face映射表中是否有对应符号的face。如果有则采用face中的定义来渲染。Emacs已经为它提供了默认的face
(defface font-lock-keyword-face
'((((class grayscale) (background light)) :foreground "LightGray" :weight bold)
(((class grayscale) (background dark)) :foreground "DimGray" :weight bold)
(((class color) (min-colors 88) (background light)) :foreground "Purple")
(((class color) (min-colors 88) (background dark)) :foreground "Cyan1")
(((class color) (min-colors 16) (background light)) :foreground "Purple")
(((class color) (min-colors 16) (background dark)) :foreground "Cyan")
(((class color) (min-colors 8)) :foreground "cyan" :weight bold)
(t :weight bold))
"Font Lock mode face used to highlight keywords."
:group 'font-lock-faces)所以我们如果想要修改显示效果,只需要通过 defface 来进行绑定,或者通过 set-face-attribute 设置。
练习
本次的练习,我们尝试通过上面介绍的变量定义一个简单的c语言的语法高亮:
- 以关键字的形式显示
if、else、while、for、return、break、continue - 显示简单类型的语法高亮,
int、float、double、char、void、bool - 高亮显示常量,
true、false、null、NULL - 显示注释的高亮, 行注释和块注释,都是同一种样式
(defvar my-simplec-font-lock-keywords
`(
("\\<\\(if\\|else\\|while\\|for\\|return\\)\\>" . font-lock-keyword-face)
("\\<\\(int\\|float\\|double\\|char\\|void\\|bool\\)\\>" . font-lock-type-face)
("\\<\\(true\\|false\\|null\\|NULL\\)\\>" . font-lock-constant-face)
("/\\*\\(?:.\\|\n\\)*?\\*/" 0 font-lock-comment-face)
("//.*$" 0 font-lock-comment-face)
)
"MySimpleC 的 Font Lock 正则匹配与 Face 绑定规则。")
(define-derived-mode my-simplec-mode prog-mode "MySimpleC"
"配置简单的 Font Lock 关键字代码高亮"
(setq-local font-lock-defaults '(my-simplec-font-lock-keywords nil nil)))总结
今天我们讨论了symbol 和 face 的绑定关系,symbol 本身可以绑定一个值、一个函数和一个plist 结构,但是无法绑定face,通过 defface 实际上是在外部建立了一个symbol 和 face 映射。在需要使用face时会查找这张表。
另外Emacs中提供了一系列默认的 font lock face变量,它们的值是同名的 symbol,我们直接使用它来进行语法层面的关联。如果开发theme可以修改这些符号绑定的 face 即可
评论 (0)