在前面的例子中,我们已经见识过文本属性。我们通过将buffer、file-name、line-number 等信息作为属性附加到字符串上,在用户选择了这个字符串执行跳转时又读取它的属性,找到之前保存的信息,从而完成了跳转。
背景知识
从上面的例子中我们知道,属性实际上是一个标签,也可以是一个信息存储的媒介。既可以存储官方支持的属性,也可以存储自定义的属性。
我们可以通过 add-text-properties 来添加一个属性。它的定义如下:
(add-text-properties START END PROPERTIES &optional OBJECT)各个参数的含义如下:
START和END:表示一个范围,给这个范围内的字符串添加属性PROPERTIES: 是一个表示各种属性的列表OBJECT: 是一个buffer,表示往哪个buffer中的字符串添加属性,默认不填的话表示当前buffer
PROPERTIES 是一个属性列表,它的形式为 (prop1 value1 prop2 value2 ...) 。
在Emacs中,可以通过设置buffer中字符串的相关属性实现例如文本高亮、交互效果等操作。这些都可以通过设置官方提供的属性来支持。官方提供的常用效果有:
- 文本显示效果的有:
| 属性名 | 作用 | 常用值示例 |
|---|---|---|
| face | 控制文本外观(颜色、粗体、斜体等) | 'bold、'font-lock-keyword-face、'(:foreground "red" :weight bold) |
| font-lock-face | 专门给 Font Lock 使用的 face(推荐用于语法高亮) | 同 face |
| mouse-face | 鼠标悬停时显示的 face | 'highlight |
| cursor-face | 光标位于该文本时显示的 face(需开启 cursor-face-highlight-mode) | 'highlight |
- 交互与提示的属性有:
| 属性名 | 作用 | 常用值示例 |
|---|---|---|
| help-echo | 鼠标悬停时在 echo area 或 tooltip 显示提示 | """点击查看详情"" 或函数" |
| keymap | 为这段文本添加局部按键映射(优先级高于 buffer 本地 map) | 一个 keymap 对象 |
| local-map | 替换 buffer 本地 map(较少用,一般优先用 keymap) | 一个 keymap 对象 |
- 显示控制的属性
| 属性名 | 作用 | 常用值示例 |
|---|---|---|
| display | 改变文本显示方式(替换为图片、字符串、调整高度/宽度等) | "'(image :type png :file ""icon.png"") 或 ""替换文本""" |
| invisible | 使文本不可见 | t 或某个符号(配合 buffer-invisibility-spec) |
| composition | 字符组合显示(用于复杂脚本或特殊效果) | 较少直接使用 |
- 编译与移动控制的属性
| 属性名 | 作用 | 常用值示例 | |
|---|---|---|---|
| read-only | 禁止修改该字符 | t 或错误提示字符串 | |
| inhibit-read-only | 即使在只读 buffer 中也允许编辑 | t | |
| cursor-intangible | 禁止光标停留(配合 cursor-intangible-mode) | t | |
| field | 定义“字段” | 某些移动命令(如 forward-word)会停在字段边界 | 任意符号或值 |
- 其他常用属性
| 属性名 | 作用 |
|---|---|
| category | 属性分类符号,该符号上的属性会作为默认值继承 |
| syntax-table | 覆盖该字符的语法表属性 |
| fontified | 标记文本是否已完成字体化(Font Lock 内部使用) |
| inhibit-isearch | 让 isearch 跳过这段文本 |
| cursor | 控制光标在 overlay / display 字符串中的显示位置 |
add-text-properties 可以一次性设置多个属性,而且前面设置的属性,后面可以通过该函数修改。如果调用该函数发生了属性修改,则函数返回 t 否则返回 nil
除了该函数,关于属性设置的函数还有:
put-text-property只设置一个属性remove-text-properties删除指定属性set-text-properties完全替换整段文本的所有属性,如果为nil则表示清空所有属性add-face-text-property专门用来添加face属性
另外我们可以通过 get-text-property 来获取属性值。
练习
不知道各位读者是否还记得,关于buffer的综合练习中的 App-Log 的内容。今天的练习我尝试对log函数进行一些改进,根据log等级来用不同的颜色显示,我这里规定:红色表示error、黄色表示warning、绿色表示debug、灰色表示info。
(defun my/log(level message)
(let* ((level-sym (if (stringp level) (intern (upcase level)) level))
(src-buf (current-buffer))
(file-or-buf (or (buffer-file-name) (buffer-name)))
(line-num (line-number-at-pos))
(log-buf (get-buffer-create "*App-Log*"))
;; 定义不同等级日志的字体颜色
(level-face (pcase level-sym
('ERROR '(:foreground "red" :weight bold))
('WARNING '(:foreground "orange" :weight bold))
('DEBUG '(:foreground "green"))
('INFO '(:foreground "gray50")))))
(with-current-buffer log-buf
(let ((inhibit-read-only t))
(goto-char (point-max))
(let ((start-pos (point)))
;; 格式 [LEVEL][buffer-name][line-number]:message
(insert (format "[%s][%s][%d]: %s\n"
level-sym file-or-buf line-num message))
;;为了便于后续跳转回来,这里我们保存一些隐藏信息
(add-text-properties start-pos (point)
`(log-file ,file-or-buf
face ,level-face
log-line ,line-num
log-buf ,src-buf)))))))这里使用了一个 pcase 宏用来作为条件判断,与 cond 相比它更为灵活,写出来的代码更加简洁。我们通过 pcase 判断当前等级,根据等级进行匹配,最终找到合适的颜色。
在设置属性的时候我们将属性添加进来,同样的需要使用 , 对属性值的变量进行求值并替换。
总结
本篇我们了解了属性的相关概念和应用,下面是今天提到的一些api函数
add-text-propertiesput-text-propertyremove-text-propertiesset-text-propertiesadd-face-text-propertyget-text-property
评论 (0)