代码之家  ›  专栏  ›  技术社区  ›  Jason Blevins

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

  •  9
  • Jason Blevins  · 技术社区  · 12 年前

    我已经编写了一个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 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 创建撤消边界。这能避免吗 以某种方式

    3 回复  |  直到 12 年前
        1
  •  7
  •   user355252 user355252    12 年前

    密钥是缓冲区名称。看见 Maintaining Undo :

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

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

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

    但一般来说,使用 带临时缓冲器 真正地这是Emacs中进行此类操作的标准方式,让任何阅读您代码的人都能清楚地了解您的意图。而且 带临时缓冲器 努力正确地清理临时缓冲区。


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

    所有缓冲区修改 添加边界 无论何时 无法撤消的更改 是在某些地方制造的 其他缓冲器 这是为了确保每个命令在其进行更改的每个缓冲区中形成边界。

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

        2
  •  4
  •   Lionel Henry    9 年前

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

    在这些情况下,您可以让bind undo-inhibit-record-point 为了防止Emacs决定将边界放在哪里:

    (let ((undo-inhibit-record-point t))
      ;; Then record boundaries manually
      (undo-boundary)
      (do-lots-of-stuff)
      (undo-boundary))
    
        3
  •  2
  •   Jason Blevins    12 年前

    这种情况下的解决方案是创建临时输出缓冲区 使用 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)))
    

    我无法确定 生成新的缓冲区 确实是 创建撤消边界,但这解决了问题。 生成新的缓冲区 电话 get-buffer-create ,定义于 C源代码,但我无法快速确定是什么 根据撤销历史发生。

    我怀疑这个问题可能与 这个 Emacs Lisp Manual entry for undo-boundary :

    所有缓冲区修改都会添加一个边界,只要之前 在其他缓冲区中进行了无法撤消的更改。这是为了确保 每个命令在每个缓冲区中创建一个边界 变化。

    尽管 带临时缓冲器 宏调用 生成新的缓冲区 与原始函数中一样 带临时缓冲器 表示不保存撤消信息(甚至 尽管Emacs Lisp源代码中没有任何内容表明这一点 将是这种情况):

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