如何防止Emacs设置撤消边界?

Jas*_*ins 10 emacs elisp undo

我编写了一个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))))
Run Code Online (Sandbox Code Playgroud)

此函数创建临时缓冲区,插入文本,调用 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))))
Run Code Online (Sandbox Code Playgroud)

当我调用时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))))
Run Code Online (Sandbox Code Playgroud)

在调用时M-x test-upcase-region,撤消历史记录中只有一个步骤,如预期的那样.因此,似乎调用 test-shell-command会创建一个撤消边界.这可以以某种方式避免吗?

lun*_*orn 7

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

通常在新创建的缓冲区中记录撤消信息以启动; 但如果缓冲区名称以空格开头,则最初禁用撤消录制.您可以使用以下两个功能显式启用或禁用撤消录制,也可以自行设置buffer-undo-list.

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

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

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


至于为什么临时缓冲区中的undo会在当前缓冲区中创建撤消边界:如果先前的更改是可撤消的并且在某个其他缓冲区(在本例中为临时缓冲区)中进行,则会创建隐式边界.来自undo-boundary:

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

因此,禁止临时缓冲区中的撤销也会删除当前缓冲区中的撤消边界:先前的更改不再是可撤消的,因此不会创建隐式边界.