Emacs:如何智能处理设置文本属性时的缓冲区修改?

7

文本属性的说明文档中说:

由于文本属性被认为是缓冲区(或字符串)内容的一部分,并且可能影响缓冲区在屏幕上的显示,因此对缓冲区文本属性的任何更改都会将缓冲区标记为已修改。

首先,我不理解这个策略。有人能解释一下吗?文本属性实际上并没有保存在文件中,当缓冲区被保存时也不会保存。那么为什么要将缓冲区标记为已修改呢?对我来说,缓冲区已修改表示“某些更改尚未保存”。但了解这个策略只是为了自己的娱乐。

更重要的是,是否已经有一种既定的方法可以在代码中更改缓冲区中文本的语法文本属性,同时保持缓冲区已修改标志设置为之前的状态?我想到了类似于save-excursion的东西。虽然编写这样的函数非常容易,但这似乎是一个常见的情况,如果可能的话,我想使用标准函数。

关于这种情况的更多信息 - 我有一个模式,它执行完整的文本扫描并在文本上设置语法表属性。打开缓冲区后,扫描运行,但它会导致将缓冲区修改标志设置为t。

像往常一样,谢谢。

3个回答

6

Emacs的新版本包含了一个名为“with-silent-modifications”的宏,可用于此目的:

C-h f with-silent-modifications
------------------------------------------------------
with-silent-modifications is a Lisp macro in `subr.el'.

(with-silent-modifications &rest BODY)

Execute BODY, pretending it does not modify the buffer.
If BODY performs real modifications to the buffer's text, other
than cosmetic ones, undo data may become corrupted.
Typically used around modifications of text-properties which do not really
affect the buffer's content.

Emacs 23.3 版本的更改日志提到了引入 with-silent-modifications,但 Windows 构建版本 23.3.1 仍然缺乏这样的定义。多么奇怪啊。http://www.gnu.org/software/emacs/NEWS.23.3 - seh

3

等一下!我在cc-defs.el中找到了这个。

;; The following is essentially `save-buffer-state' from lazy-lock.el.
;; It ought to be a standard macro.
(defmacro c-save-buffer-state (varlist &rest body)
  "Bind variables according to VARLIST (in `let*' style) and eval BODY,
then restore the buffer state under the assumption that no significant
modification has been made in BODY.  A change is considered
significant if it affects the buffer text in any way that isn't
completely restored again.  Changes in text properties like `face' or
`syntax-table' are considered insignificant.  This macro allows text
properties to be changed, even in a read-only buffer.

This macro should be placed around all calculations which set
\"insignificant\" text properties in a buffer, even when the buffer is
known to be writeable.  That way, these text properties remain set
even if the user undoes the command which set them.

This macro should ALWAYS be placed around \"temporary\" internal buffer
changes \(like adding a newline to calculate a text-property then
deleting it again\), so that the user never sees them on his
`buffer-undo-list'.  See also `c-tentative-buffer-changes'.

However, any user-visible changes to the buffer \(like auto-newlines\)
must not be within a `c-save-buffer-state', since the user then
wouldn't be able to undo them.

The return value is the value of the last form in BODY."
  `(let* ((modified (buffer-modified-p)) (buffer-undo-list t)
          (inhibit-read-only t) (inhibit-point-motion-hooks t)
          before-change-functions after-change-functions
          deactivate-mark
          buffer-file-name buffer-file-truename ; Prevent primitives checking
                                                ; for file modification
          ,@varlist)
     (unwind-protect
         (progn ,@body)
       (and (not modified)
            (buffer-modified-p)
            (set-buffer-modified-p nil)))))

不错,我没有看到那个。比pabbrev中的更完整一些。 - Trey Jackson

1

也许这只是因为它们被认为是字符串的一部分(就像文档所说的那样)。请记住,Emacs 是以缓冲区为中心而不是以文件为中心的,因此内容保存到磁盘上的事实在考虑缓冲区为中心时有些无关紧要。

此外,属性是可撤销的,这绝对符合将缓冲区标记为已修改的情况。

我不知道是否有一种标准的方法来保存缓冲区修改状态,但我在 pabbrev.el 库中看到了一种方法:

(defmacro pabbrev-save-buffer-modified-p (&rest body)
  "Eval BODY without affected buffer modification status"
  `(let ((buffer-modified (buffer-modified-p))
         (buffer-undo-list t))
     ,@body
     (set-buffer-modified-p buffer-modified)))

它不能防止非本地退出,因此您可能想要添加一个对unwind-protect的调用,如下所示:

(defmacro save-buffer-modified-p (&rest body)
  "Eval BODY without affected buffer modification status"
  `(let ((buffer-modified (buffer-modified-p))
         (buffer-undo-list t))
     (unwind-protect
         ,@body
       (set-buffer-modified-p buffer-modified))))

非常好,谢谢。我已经理解了 set-buffer-modified-p 部分,但是 buffer-undo-list 这个东西还没有搞明白。 - Cheeso
但是等等!这仍然有帮助,但看看我的答案。 - Cheeso

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接