文本
文本的插入删除,查找替换操作已经在缓冲区一节中讲过了,这一节主要介绍文本属性 如果使用过其它图形界面的文本组件进行编程,它们对于文本的高亮一般都是采用给对应文本贴上相应标签的方法 Emacs 的处理方法也是类似的,但是相比之下,要强大的多
在 Emacs 里,在 不同位置上的每个字符 都可以有一个 属性列表 。 这个属性列表和符号的属性列表很相似,都是由一个 名字和值构成的对 组成:
- 名字和值都可以是一个 lisp 对象,但是通常名字都是一个 符号 ,这样可以用这个符号来查找相应的属性值
- 复制文本通常都会 复制 相应的 字符的文本属性 ,但是也可以用相应的函数只复制文本字符串,比如 substring-no-properties 、 insert-buffer-substring-no-properties 、 buffer-substring-no-properties
产生一个带属性的字符串可以用 propertize 函数:
(propertize "abc" 'face 'bold) ; => #("abc" 0 3 (face bold))
如果在一个 text-mode 的缓冲区内用 M-x eval-expression 用 insert 函数插入前面这个字符串,就会发现插入的文本已经是粗体字了
之所以不能在 *scratch* 产生这种效果,是因为通常我们是开启了 font-lock-mode 在 font-lock-mode 里,文本的 face 属性是实时计算出来的。 在插入文本之后,它的 face 属性已经很快地被改变了 可以在关闭 font-lock-mode 后再测试一次应该是可以看到 *scratch* 里也是可以用这种方法插入带 face 属性的文本的
虽然文本属性的名字可以是任意的,但是一些名字是有特殊含义的:
属性名 | 含义 |
category | 值必须是一个符号,这个符号的属性将作为这个字符的属性 |
face | 控制文本的字体和颜色 |
font-lock-face | 和 face 相似,可以作为 font-lock-mode 中静态文本的 face |
mouse-face | 当鼠标停在文本上时的文本 face |
fontified | 记录是否使用 font lock 标记了 face |
display | 改变文本的显示方式,比如高、低、长短、宽窄,或者用图片代替 |
help-echo | 鼠标停在文本上时显示的文字 |
keymap | 光标或者鼠标在文本上时使用的按键映射 |
local-map | 和 keymap 类似,通常只使用 keymap |
syntax-table | 字符的语法表 |
read-only | 不能修改文本,通过 stickness 来选择可插入的位置 |
invisible | 不显示在屏幕上 |
intangible | 把文本作为一个整体,光标不能进入 |
field | 一个特殊标记,有相应的函数可以操作带这个标记的文本 |
cursor | (不知道具体用途) |
pointer | 修改鼠标停在文本上时的图像 |
line-spacing | 新的一行的距离 |
line-height | 本行的高度 |
modification-hooks | 修改这个字符时调用的函数 |
insert-in-front-hooks | 与 modification-hooks 相似,在字符前插入调用的函数 |
insert-behind-hooks | 与 modification-hooks 相似,在字符后插入调用的函数 |
point-entered | 当光标进入时调用的函数 |
point-left | 当光标离开时调用的函数 |
composition | 将多个字符显示为一个字形 |
正是由于 emacs 的文本有如此丰富的属性,使得 emacs 里的文字才变得多彩, 变得人性化
查看文本属性
由于字符串和缓冲区都可以有文本属性,所以下面的函数通常不提供特定参数就是检查当前缓冲区的文本属性,如果提供文本对象,则是操作对应的文本属性
查看文本对象在某处的文本属性可以用 get-text-property 函数
(setq foo (concat "abc" (propertize "cde" 'face 'bold))) ; => #("abccde" 3 6 (face bold)) (get-text-property 3 'face foo) ; => bold (save-excursion (goto-char (point-min)) (insert foo)) (get-text-property 4 'face) ; => bold
- get-char-property 和 get-text-property 相似,但是它是先查找 overlay 的文本属性
- overlay 是 缓冲区文字 在屏幕上的显示方式,它属于某个缓冲区,具有起点和终点,也具有文本属性,可以修改缓冲区对应区域上文本的显示方式
- 用 text-properties-at 可以得到某个位置上文本的所有属性
修改文本属性
put-text-property 可以给文本对象添加一个属性:
(let ((str "abc")) (put-text-property 0 3 'face 'bold str) str) ; => #("abc" 0 3 (face bold))
- add-text-properties 可以给文本对象添加一系列的属性
- 可以用 set-text-properties 直接设置文本属性列表
(set-text-properties start end nil) 来除去某个区间上的文本属性
- 用 remove-text-properties 和 remove-list-of-text-properties 来除去某个区域的指定文本属性
这两个函数的属性列表参数只有名字起作用,值是被忽略的
(setq foo (propertize "abcdef" 'face 'bold 'pointer 'hand)) ;; => #("abcdef" 0 6 (pointer hand face bold)) (set-text-properties 0 2 nil foo) ; => t foo ; => #("abcdef" 2 6 (pointer hand face bold)) (remove-text-properties 2 4 '(face nil) foo) ; => t foo ; => #("abcdef" 2 4 (pointer hand) 4 6 (pointer hand face bold)) (remove-list-of-text-properties 4 6 '(face nil pointer nil) foo) ; => t foo ; => #("abcdef" 2 4 (pointer hand))
查找文本属性
文本属性通常都是连成一个区域的,所以查找文本属性的函数是查找属性变化的位置 这些函数一般都不作移动,只是返回查找到的位置。使用这些函数时最好使用 LIMIT 参数,这样可以提高效率 因为有时一个属性直到缓冲区末尾也没有变化,在这些文本中可能就是多余的
- next-property-change 查找从当前位置起任意一个文本属性发生改变的位置
- next-single-property-change 查找指定的一个文本属性改变的位置
- next-char-property-change 把 overlay 的文本属性考虑在内查找属性发生改变的位置
- next-single-property-change 类似的查找指定的一个考虑 overlay 后文本属性改变的位置
这四个函数都对应有 previous- 开头的函数,用于查找当前位置之前文本属性改变的位置
(setq foo (concat "ab" (propertize "cd" 'face 'bold) (propertize "ef" 'pointer 'hand))) ;; => #("abcdef" 2 4 (face bold) 4 6 (pointer hand)) (next-property-change 1 foo) ; => 2 (next-single-property-change 1 'pointer foo) ; => 4 (previous-property-change 6 foo) ; => 4 (previous-single-property-change 6 'face foo) ; => 4
- text-property-any 查找区域内第一个指定属性值为给定值的字符位置
- text-property-not-all 和它相反,查找区域内第一个指定属性值不是给定值的字符位置
(text-property-any 0 6 'face 'bold foo) ; => 2 (text-property-any 0 6 'face 'underline foo) ; => nil (text-property-not-all 2 6 'face 'bold foo) ; => 4 (text-property-not-all 2 6 'face 'underline foo) ; => 2