如何一次性获得一个字符串的所有 Text Properties?

我打算用 Emacs 导出代码块到 HTML,并添加语法高亮。

比如 Emacs Lisp Mode 下的这样一句代码:

(defun foo () "A Function." 42)

Emacs 高亮之后会给出这样的 String,但是有没有现成的办法获得后面的全部的 Text Properties?

#("(defun foo () \"A Function.\" 42)" 0 1
  (fontified t)
  1 6
  (fontified t face font-lock-keyword-face)
  6 7
  (fontified t)
  7 10
  (fontified t face font-lock-function-name-face)
  10 11
  (fontified t)
  11 12
  (fontified t)
  12 13
  (fontified t)
  13 14
  (fontified t)
  14 26
  (fontified t face font-lock-doc-face)
  26 27
  (fontified t face font-lock-doc-face)
  27 28
  (fontified t)
  28 30
  (fontified t)
  30 31
  (fontified t))

我暂时用 next-property-change,但当初实现起来有些痛苦,一开始就先上面的思路应该会轻松不少。

我也有这样的疑惑,明明 buffer-string/buffer-substring 返回值携带了所有的信息,为什么不能好好利用呢?

EmacsWiki: mon-text-property-utils.elmon-get-text-properties-region 函数看到一种不走寻常路的操作:

    (setq mgtpr-bfr-str (substring (format "%S" (buffer-substring start end)) 1))
    (setq mgtpr-bfr-str (car (read-from-string mgtpr-bfr-str)))

buffer-string/buffer-substring 返回的内容再次 format。然后去掉前面的 # 号,再读回来变成一般的 list。

2 个赞

刚刚想到这个帖子。

顺便测试了一下,打印再读取的效率比 format 更高:

(defun propertized-text-to-list (text)
  "Convert propertized TEXT to list."
  (with-temp-buffer
    (prin1 text (current-buffer))
    (goto-char (point-min))
    (when (looking-at "#(")
      (forward-char 1)
      (read (current-buffer)))))

(with-temp-buffer
  (emacs-lisp-mode)
  (insert "(defun foo () \"A Function.\" 42)")
  (list (benchmark-run 1000
          (propertized-text-to-list (buffer-string)))
        (benchmark-run 1000
          (mon-get-text-properties-region (point-min) (point-max)))))
;; => ((0.076705 0 0.0)
;;     (0.305013 1 0.2189820000000111))
4 个赞