发现emacs stackechange 不够活跃,贴了个问题,关注有点少,想在这里再咨询一下:
这是一个简化的示例:通过以下代码,我创建了一个 buffer 或文件,并运行 M-x buffer-overlay
来插入文本并添加带有 modification-hooks
的 overlay,然后运行 M-x serialize-to-buffer
来删除并插入新文本。之后,我运行 undo
来还原旧文本。我原本期望 buffer-update-hook
会在还原之前和还原旧文本之后运行。然而,消息输出如下所示,表明 buffer-update-hook
仅在撤销过程中(在删除和插入新文本之间)运行了。为什么会这样?为什么这个 hook 没有在整个撤销操作完成后(即还原到旧文本之后)运行?我认为我已经使用了 combine-change-calls
将删除和插入操作组合成一个 undo 单元。另外,为什么 hook 没有在还原旧文本后运行?最后,我该如何重构代码以确保 hook 仅在整个 undo 操作完成后运行?
测试示例代码:
(defun buffer-update-hook (ov after beg end &optional length)
(message "--- after? %S beg: %d end: %d" after beg end)
(let ((inhibit-modification-hooks nil)) ; 只是确保它没有被设为 t
(when after ; 在文本更改之后
(message "--- buffer %s" (buffer-substring (point-min) (point-max))))))
(defun serialize-to-buffer ()
(interactive)
(let* ((ov (car (overlays-in (point-min) (point-max))))
(beg (point-min))
(end (point-max))
;; 临时禁用所有修改 hooks,包括 overlay 的 modification hooks。
(inhibit-modification-hooks t)
;; (inhibit-read-only t)
)
;; (setq buffer-read-only nil)
(save-excursion
;; 使用 undo-boundary 来确保它作为一个独立的单位出现在 undo 列表中
(undo-boundary)
;; 将删除和插入操作组合成一个 undo 单元
(combine-change-calls beg end
(delete-region beg end)
(insert "new-inserted"))
(undo-boundary))))
(defun buffer-overlay ()
(interactive)
(insert "old-text")
(remove-overlays)
(let ((ov (make-overlay (point-min) (point-max) nil nil t)))
(overlay-put ov 'modification-hooks '(buffer-update-hook))))
messages 的输出:
---after? nil beg: 1 end: 13
---after? t beg: 1 end: 1
--- buffer