overlay modification-hooks的问题

发现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

‘modification-hooks’

  This property’s value is a list of functions to be called if any
  character within the overlay is changed or if text is inserted
  *strictly* within the overlay.

你可能还需要 insert-in-front-hooksinsert-behind-hooks.

好像确实是。在例子中全删除后在修改的情况下,insert-in-front-hooksinsert-behind-hooks这2个hooks都会被激活