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

问题描述:

我写了一个Emacs Lisp函数,它调用一个shell命令 处理给定的字符串并返回结果字符串。这里是一个 简化的例子只是调用tr将文本转换为大写:如何防止Emacs设置撤消边界?

(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 documentation on undo, ,但似乎没有解决这个问题,据我所知。

下面是其通过调用完成同样的事情的函数 内置的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创建撤消边界。以某种方式可以避免 ?

+0

的常用方法,以防止弄乱命令'(撤消)'是找到一个不同的方式,一个不这样做。这里的例子是,你几乎不应该创建一个临时缓冲区,而是使用对象。 – PascalVKooten 2013-02-26 18:58:39

+0

除了手动读取和写入临时文件,我不确定是否有另一种捕获过程输出的方式。即使像'start-process'这样的异步进程命令似乎也想将输出发送到缓冲区。 – 2013-02-26 19:46:56

+1

@JasonBlevins'shell-command-on-region'的第五个参数是REPLACE,那么为什么你需要包装它呢? – 2013-02-27 00:26:51

关键是缓冲区名称。请参阅Maintaining Undo

在新创建的缓冲区中记录撤销信息通常启用以;但如果缓冲区名称以空格开头,则撤消记录最初将被禁用。您可以使用以下两个函数或通过自己设置buffer-undo-list来显式启用或禁用撤销录制。

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

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

但是一般情况下,使用with-temp-buffer,真的。这是Emacs中这种事情的标准方式,让读者清楚你的意图。此外,with-temp-buffer会尽力清理临时缓冲区。


至于为什么在临时缓冲区撤消会在当前撤销边界:如果以前的变化是可撤销的,并在其他一些缓冲液(临时一个在这种情况下),将创建一个隐含的边界。从undo-boundary

所有缓冲区修改添加每当以前可撤销的改变在一些其他缓冲制成的边界。这是为了确保每个命令在每个缓冲区中进行更改的边界。

因此,抑制复原在暂时缓冲器中删除当前缓冲区撤消边界,也:先前变化根本就不是可撤消了,并且因此没有创建隐边界。

+1

我验证了在原始函数中使用'(generate-new-buffer“* temp *”)'并且'with-temp-buffer'确实创建了一个名称具有前导空格的缓冲区。 我读过这部分文档,但我不认为这是问题,因为撤消信息是特定于每个缓冲区的。为什么我应该关心撤销信息是否记录在'* temp *'缓冲区中?但是我从文档中没有收集到的信息显然是,在其他缓冲区中记录撤消信息的行为必须在当前缓冲区中设置撤消边界。 – 2013-02-27 16:14:05

+0

@JasonBlevins如果以前的更改是可撤销的并在其他缓冲区中创建,则创建隐式边界,在您的情况下为临时边界。我编辑了我的答案来解释细节。希望现在一切都很清楚。 – lunaryorn 2013-02-27 17:13:26

在这种情况下的解决方案是使用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 Manual entry for undo-boundary涉及到下面这段话:

所有缓冲区的修改补充,只要前面的 可撤销的改变在其他一些缓冲作出了边界。这是为了确保 每个命令在每个缓冲区中产生一个边界,使 发生更改。

即使with-temp-buffer宏调用generate-new-buffer 之多,在原有的功能, with-temp-buffer文档指出,任何撤销信息保存(即使 虽然没有什么在的Emacs Lisp源启示该 会如此):

默认情况下,撤消(撤消见)没有记录在 这个宏创建缓冲区(但身体可以启用,如果需要的话)。

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

在这种情况下,你可以让绑定undo-inhibit-record-point防止Emacs的从决定往哪里放界限:

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