如何防止 Emacs 设置撤消边界



我写了一个Emacs Lisp函数,它调用了一个shell命令来处理给定的字符串并返回生成的字符串。 这是一个简化的示例,仅调用tr将文本转换为大写:

(defun test-shell-command (str)
  "Apply tr to STR to convert lowercase letters to uppercase."
  (let ((buffer (generate-new-buffer "*temp*")))
    (with-current-buffer buffer
      (insert str)
      (call-process-region (point-min) (point-max) "tr" t t nil "'a-z'" "'A-Z'")
      (buffer-string))))

此函数创建一个临时缓冲区,插入文本,调用 tr ,将文本替换为结果,并返回结果。

上面的函数按预期工作,但是,当我编写包装器时围绕此函数将命令应用于区域,有两个步骤是被添加到撤消历史记录中。 这是另一个示例:

(defun test-shell-command-region (begin end)
  "Apply tr to region from BEGIN to END."
  (interactive "*r")
  (insert (test-shell-command (delete-and-extract-region begin end))))

当我调用M-x test-shell-command-on-region时,该区域被替换使用大写文本,但是当我按 C-_ ( undo ( 时,第一个撤消历史记录中的步骤是已删除文本的状态。 会后退两步,恢复原文。

我的问题是,如何防止中间步骤出现添加到撤消历史记录? 我已经阅读了关于撤消的Emacs文档,但据我所知,它似乎并没有解决这个问题。

这是一个函数,它通过调用内置的Emacs函数upcase,就像以前一样:在结果上 delete-and-extract-region结果被移交给 insert

(defun test-upcase-region (begin end)
  "Apply upcase to region from BEGIN to END."
  (interactive "*r")
  (insert (upcase (delete-and-extract-region begin end))))

调用M-x test-upcase-region时,只有一个步骤在按预期撤消历史记录。 所以,似乎情况是打电话 test-shell-command创建撤消边界。 这是可以避免的吗不知何故?

键是缓冲区名称。 请参阅维护撤消:

在新创建的缓冲区中记录撤消信息通常从 开始;但如果缓冲区名称以空格开头,则最初禁用撤消记录。您可以使用以下两个功能显式启用或禁用撤消记录,也可以通过自己设置缓冲区撤消列表来显式启用或禁用撤消记录。

with-temp-buffer创建一个名为 ␣*temp* 的缓冲区(请注意前导空格(,而您的函数使用 *temp*

若要删除代码中的撤消边界,请使用带有前导空格的缓冲区名称,或使用 buffer-disable-undo 显式禁用临时缓冲区中的撤消重新编码。

但一般来说,使用with-temp-buffer,真的。 这是 Emacs 中处理此类操作的标准方式,让任何阅读您的代码的人都清楚您的意图。 此外,with-temp-buffer会努力正确清理临时缓冲区。


至于为什么在临时缓冲区中撤消会在

当前缓冲区中创建撤消边界:如果之前的更改是可撤消的,并且在其他缓冲区(本例中为临时缓冲区(中进行,则会创建一个隐式边界。 从undo-boundary

每当在某个其他缓冲区中进行先前可撤消的更改时,所有缓冲区修改都会添加边界。这是为了确保每个命令在进行更改的每个缓冲区中建立边界。

因此,在

临时缓冲区中禁止撤消也会删除当前缓冲区中的撤消边界:以前的更改根本无法再撤消,因此不会创建隐式边界。

在许多情况下,使用临时缓冲区是不切实际的。例如,很难调试正在发生的事情。

在这些情况下,你可以让绑定undo-inhibit-record-point来防止 Emacs 决定在哪里放置边界:

(let ((undo-inhibit-record-point t))
  ;; Then record boundaries manually
  (undo-boundary)
  (do-lots-of-stuff)
  (undo-boundary))

在这种情况下,解决方案是创建临时输出缓冲区使用 with-temp-buffer ,而不是显式创建一个 generate-new-buffer . 以下替代版本的第一个函数不会创建撤消边界:

(defun test-shell-command (str)
  "Apply tr to STR to convert lowercase letters to uppercase."
  (with-temp-buffer
    (insert str)
    (call-process-region (point-min) (point-max) "tr" t t nil "'a-z'" "'A-Z'")
    (buffer-string)))

我无法确定generate-new-buffer是否确实是创建撤消边界,但这解决了问题。 generate-new-buffer调用 get-buffer-create ,定义见C 源代码,但我无法快速确定是什么在撤消历史记录方面发生。

我怀疑这个问题可能与以下段落有关Emacs Lisp 手册条目用于undo-boundary

所有缓冲区修改都会在前一个时添加边界 在其他一些缓冲区中进行了可撤消的更改。 这是为了确保 每个命令在它使 变化。

即使with-temp-buffer宏调用generate-new-buffer就像在原始函数中一样,文档 with-temp-buffer声明不会保存撤消信息(即使尽管在 Emacs Lisp 源代码中没有任何内容表明这一点会是这样(:

默认情况下,撤消(请参阅撤消(不会记录在由 创建的缓冲区中 此宏(但如果需要,正文可以启用它(。

最新更新