请教个 font-lock-add-keywords 的问题,通过正则高亮了一个区域,但是编辑其中的内容后新添加的 face 就失效了

不知道标题这么描述准不准确

通过 mixed-pitch 给 org-mode 的正文使用非等宽字体,drawer 内全部使用等宽字体,实现的代码如下

(font-lock-add-keywords 'org-mode
                        '(("SCHEDULED:\\(\\(.\\|\n\\)+:\s*\n\\)" 1 'my/org-fixed-pitch)
                          ("^\s*:?.+?:\\(.+\\)$" 1 'my/org-fixed-pitch)))

呈现上没问题,但是正则匹配的区域一旦编辑后就失效了,需要再调用 font-lock-update 恢复,实在搞不定了,请教一下大家这个有办法解决吗?

感谢感谢

1 个赞

根本原因是在 Emacs 为了效率,在 buffer 內容修改后,只会从更改的地方开始重新 font-lock,所以一开始从上至下 font-lock 时是用完整的全文用多行的正则匹配是正常的,但一旦编辑后 font-lock-keywords 的正则只能看到一行內容,找不到 SCHEDULED: 就不会 font-lock drawer 的內容。

:SCHEDULED:
text <修改这里后 font-lock 只看到这一行內容
:end:

有个办法能让 Emacs 用多行內容来重新 font-lock,就是给 text 加 font-lock-multiline property。更复杂的可以参考 org-fontify-meta-lines-and-blocks-1 的实现。

(defun org-font-lock-drawer (limit)
  (when (re-search-forward
         ":SCHEDULED:\s*\n\\(\\(?:.\\|\n\\)+?\\):end:\s*\n" limit t)
    (let ((beg (match-beginning 1))
          (end (match-end 1)))
      (put-text-property beg end 'face 'bold)
      (put-text-property (match-beginning 0) (match-end 0) 'font-lock-multiline 't)
      (goto-char end))))
(font-lock-add-keywords 'org-mode
                        '(org-font-lock-drawer))
:SCHEDULED:
text <修改这里后 font-lock 通过 `font-lock-extend-region-functions` 
变量中的 `font-lock-extend-region-multiline` 找到开头的 `:SCHEDULED:` 和结束的 `:end:`
:end:

但是,这样还是有个问题,在 :SCHEDULED::end: 改动后,font-lock-multiline 属性会丢失,又回到 font-lock 只能看到当前行的状态,就又要用 font-lock-update 了。

Org 用设置 font-lock-extend-after-change-region-functionorg-fontify-extend-region 处理 #+begin#+end,但 drawer 不在这个的处理范围內。因为我不想继续在解決这个问题上花时间所以你对我给的样例效果还是不满意的话就要自己想辦法了或等別人来想解決了。

3 个赞

另外,你写的正则多少是有点问题的,我稍微改了下但可能也没处理边角情况,建议你用 re-builder 检查一下。

感谢大佬,解决了,非常完美,非常感谢

最终版本

(defun my/org-font-lock-drawer (limit)
  (when
      (or (re-search-forward
           "SCHEDULED:\\(\\(.\\|\n\\)+:\s*\n\\)" limit t)
          (re-search-forward
           ".+?:\s+\\(.+\\)$" limit t))
    (let ((beg (match-beginning 1))
          (end (match-end 1)))
      (put-text-property beg end 'face 'fixed-pitch)
      (put-text-property (match-beginning 0) (match-end 0) 'font-lock-multiline 't)
      (goto-char end))))

fixed-pitch 来自 mixed-pitch 这个包。

对 org 的理解还有限,目前已经满足个人的使用场景了。

正则部分感谢提醒,有点怪但可以用,因为要匹配结尾的换行符,同一行用了两种不同的字体会导致折叠 drawer 的时候有轻微的错位抖动,严重的强迫症,哈哈哈

再次感谢!