Emacs 的 defface 與 propertizing text

(注意:Emacs 24 以上限定)
在 Emacs 裡 propertizing text (例如:給文字上色) 的大小事,不論是自己自訂 syntax highlight,或者要自己寫 mode 時都需要用到。分成兩個部份:

  1. 定義新的 face。
  2. 把 face 套用到文字上。

defface:定義 face

要給文字上色或加粗,你可以直接拿內建已經有的那些 font-faces 來用(可使用 M-x list-faces-display 查看整個目前環境可用的 font-faces 列表),也可以自己使用 defface 定義自己的 font-faces。

  • (defface FACE SPEC DOC &rest ARGS)
1
2
3
(defface test-face
'((((class color)) (:foreground "#ffffff" :background "#aaaaaa" :bold t)))
"This comment is necessary")

來看看複雜的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(defface highlight                      ;FACE
'( ;SPEC
(((class color) (min-colors 88) (background light))
:background "darkseagreen2")
(((class color) (min-colors 88) (background dark))
:background "darkolivegreen")
(((class color) (min-colors 16) (background light))
:background "darkseagreen2")
(((class color) (min-colors 16) (background dark))
:background "darkolivegreen")
(((class color) (min-colors 8))
:background "green" :foreground "black")
(t :inverse-video t)
)
"Basic face for highlighting." ;DOC
:group 'basic-faces)

一般是不用到那麼複雜。

麻煩的 SPEC

SPEC 的格式是:

1
((DISPLAY . ATTS)...)

DISPLAY

用來定義終端機內有限顏色時如何顯示。有三種可能:default, t 或一個 list(上面的例子就是 list),我覺得這太麻煩了就沒理他,有興趣的可以看 C-h f deffaceDefining Faces - GNU Emacs Lisp Reference Manual

ATTS

真正用來定義 text properties 的部份,截至 Emacs 24.3 為止,可用的 ATTS 有 :width, :height, :weight, :slant, :underline, :overline, :strike-through, :box, :foreground, :background, :stipple, :inverse-video, :inherit。關於各項 ATTS 詳細說明請看 Face Attributes - GNU Emacs Lisp Reference Manual

:inherit

可以使用 :inherit 直接抓現有的 face 來套用。例如 markdown-italic-face 就直接抓了 font-lock-variable-name-face 套用上去,然後再自己加上 italic:

1
2
3
4
(defface markdown-italic-face
'((t (:inherit font-lock-variable-name-face :slant italic)))
"Face for italic text."
:group 'markdown-faces)

給 text 加上 property

定義完 face 後,給 text 加上 property 有幾種作法:

  • put-text-property
  • add-text-properties
  • set-text-properties
  • propertize (有回傳值,最簡單)

以及移除 property 用的

  • remove-text-properties

以下所有的測試,請注意要在fundamental-mode(純文字)下,不然輸出的顏色可能會被目前的 mode 蓋過去而無效。

名子很像的三個 function

  • (put-text-property START END PROPERTY VALUE &optional OBJECT)
    給 text 賦予單一屬性(PROPERTY)。
    PROPERTYVALUE,前者為屬性,後者為值。 如 'bold t'face 'font-lock-preprocessor-face
    START 和 END 為 position,如果輸入的 OBJECT 是 variable,position 會從 0 開始算,
    如果缺乏 OBJECT 或者是 nil,會抓目前 buffer 作為 OBJECT。

  • (add-text-properties START END PROPERTIES &optional OBJECT)
    給 text 同時賦予多種屬性(PROPERTIES)。
    用 list 來表示多項目,如'(bold t italic t)
    其餘事項同 put-text-property

1
2
;; GNU 文件的範例
(add-text-properties start end '(comment t face highlight))
  • (set-text-properties START END PROPERTIES &optional OBJECT)
    把 text 的屬性完整的覆蓋掉。PROPERTIES 方法同上,只是當它的值為 nil 時,意義即為「移除文字所有屬性」。

Examples:

注意兩者的 prop 用法差別:

1
2
3
4
5
6
7
8
9
;; add-text-properties
(let ((s "Shirohime Kanata"))
(add-text-properties 0 (length s) '(face font-lock-preprocessor-face) s)
(insert s))

;; put-text-property
(let ((s "Shirohime Kanata"))
(put-text-property 0 (length s) 'face 'font-lock-preprocessor-face s)
(insert s))

  • 以上兩者的回傳值皆只有 tnil :「成功加上了」和「沒加」兩種意義。
  • 是直接改 variable 的值(所以輸入必須是 variable 不能是 string,不然會爆掉),是有副作用的 function。

還有一種方法是 propertize

  • (propertize STRING &rest PROPERTIES)
1
2
(propertize "String"
'face 'twittering-uri-face)
  • 這種方法最簡單,可以用直接用 string 作為輸入,不需要 variable。(因此他也會改 variable 的值)。
  • 直接吐給你上好了顏色的字。
  • 可以同時放一堆屬性。

筆記


[2014-01-16 木 17:48] 懶得寫更細了,共 3 小時 49 分。
[2014-01-15 水 14:49] 簡單先貼 code