在 emacs 中,我有时会错误地调用 call-last-kbd-macro。当撤消时,我希望撤消能够自动撤消键盘宏的整个效果,但这并没有发生。相反,我发现自己必须一次撤消宏的每一步。如何让emacs回到宏执行前的缓冲区状态?
恐怕仅使用内置的
'undo
机制无法做到这一点。 Emacs 中的宏系统实际上会回放内容,就像用户实际键入击键(或鼠标事件)一样,因此 undo 历史记录 (buffer-undo-list
) 会正常更新。
这里有一个关于如何扩展当前
undo
机制来实现您想要的功能的建议。
扩展
'undo
以了解撤消列表中的新条目、macro-begin
标记和macro-end
元素建议/更改宏播放以在宏播放的开头/结尾插入标记
让
undo
代码将两个标记之间的所有撤消事件视为一个单元并撤消所有事件(并在撤消历史记录的末尾添加适当的标记,这样当您redo
事物时,它们仍然被视为单个块)注意事项:
macro-begin
和macro-end
标记。不用说,这是一项复杂的工作。祝你好运。
这可以很容易地通过覆盖
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)))
如果您不想编辑所有宏定义,您也可以添加一个包装器,该包装器调用作为参数给出的宏,同时如上所述创建/抑制撤消边界。
[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))))