如何在 emacs 中撤消 call-last-kbd-macro

问题描述 投票:0回答:3

在 emacs 中,我有时会错误地调用 call-last-kbd-macro。当撤消时,我希望撤消能够自动撤消键盘宏的整个效果,但这并没有发生。相反,我发现自己必须一次撤消宏的每一步。如何让emacs回到宏执行前的缓冲区状态?

emacs undo
3个回答
3
投票

恐怕仅使用内置的

'undo
机制无法做到这一点。 Emacs 中的宏系统实际上会回放内容,就像用户实际键入击键(或鼠标事件)一样,因此 undo 历史记录 (
buffer-undo-list
) 会正常更新。

这里有一个关于如何扩展当前

undo
机制来实现您想要的功能的建议。

  1. 扩展

    'undo
    以了解撤消列表中的新条目、
    macro-begin
    标记和
    macro-end
    元素

  2. 建议/更改宏播放以在宏播放的开头/结尾插入标记

  3. undo
    代码将两个标记之间的所有撤消事件视为一个单元并撤消所有事件(并在撤消历史记录的末尾添加适当的标记,这样当您
    redo
    事物时,它们仍然被视为单个块)

注意事项:

  • 这仅适用于在单个缓冲区中运行的宏,如果您的宏切换缓冲区(或在其他缓冲区中产生副作用),这些更改将不会被视为块。
  • 如果您的宏在与开始时不同的缓冲区中结束,那么您必须干净地处理它 - 您不希望撤消列表中出现“不平衡”
    macro-begin
    macro-end
    标记。

不用说,这是一项复杂的工作。祝你好运。


1
投票

这可以很容易地通过覆盖

undo-boundary
来完成,例如
noflet
套餐。

给出宏定义(由

insert-kbd-macro
生成)如下:

(fset 'my-macro
   [... keys ...])

或:

(fset 'my-macro
   (lambda (&optional arg) "Keyboard macro." (interactive "p") (kmacro-exec-ring-item (quote ([...keys...] 0 "%d")) arg)))

按如下方式编辑:

(require 'noflet)

(fset 'my-macro
      (lambda (&optional arg) "Keyboard macro." (interactive "p")
        (undo-boundary)
        (noflet ((undo-boundary ()))
          (kmacro-exec-ring-item (quote ([...keys...] 0 "%d")) arg))
        (undo-boundary)))

如果您不想编辑所有宏定义,您也可以添加一个包装器,该包装器调用作为参数给出的宏,同时如上所述创建/抑制撤消边界。


0
投票

[13 年后...] Emacs 29.1 引入了一个命令,使这种方式变得更容易:

with-undo-amalgamate
。这是我的
.emacs

(defun undoable-call-last-kbd-macro (&optional PREFIX LOOPFUNC)
  "Thin wrapper around `call-last-kbd-macro' so that the <undo> command undoes the entire macro."
  (interactive "P")
  (with-undo-amalgamate (call-last-kbd-macro PREFIX LOOPFUNC)))

这正是您想要的。 (边缘情况:假设您使用前缀命令调用它,例如

M-5 call-last-kbd-macro
。然后
<undo>
将撤消宏的所有 5 个应用程序,而不仅仅是第五个。)同样的技巧也适用于使
kmacro-end-or-call-macro
原子地可撤消。


旁注:您可以使用命名宏执行相同的操作。例如,如果您有按键绑定

(global-set-key (kbd "C-c C-c") 'my-macro)

你应该将其替换为

(global-set-key (kbd "C-c C-c") (lambda () (interactive) (with-undo-amalgamate (my-macro))))
© www.soinside.com 2019 - 2024. All rights reserved.