大家一定都用过 (elisp) Recursive Editing 这个功能了,比如 M-x toggle-debug-on-error
后进入 Debugger 就会用到它:
但有可能像我一样一直都并不了解它,Emacs 一次执行一个命令,Recursive Editing 可以让用户在执行一个命令期间暂时跳出这个命令,用户回到正常的 Emacs,最后再用 C-M-c
(exit-recursive-edit
) 返回这个命令。比如 M-x ediff-regions-wordwise
就是这样工作的。
利用这个机制,很容易解决标题中的需求:
(defun chunyang-swap-regions ()
(interactive)
(let ((hint
(substitute-command-keys
"When done, type \\[exit-recursive-edit]. Use \\[abort-recursive-edit] to abort"))
buf-A reg-A-beg reg-A-end reg-A-str
buf-B reg-B-beg reg-B-end reg-B-str)
;; Select the first region
(message "Select the first region (%s)" hint)
(recursive-edit)
(setq buf-A (current-buffer)
reg-A-beg (region-beginning)
reg-A-end (region-end)
reg-A-str (buffer-substring reg-A-beg reg-A-end))
(deactivate-mark)
;; Select the second region
(message "Select the second region (%s)" hint)
(recursive-edit)
(setq buf-B (current-buffer)
reg-B-beg (region-beginning)
reg-B-end (region-end)
reg-B-str (buffer-substring reg-B-beg reg-B-end))
(deactivate-mark)
;; Swap these two regions
(when (< reg-B-beg reg-A-beg)
(cl-psetq buf-A buf-B
reg-A-beg reg-B-beg
reg-A-end reg-B-end
reg-A-str reg-B-str
buf-B buf-A
reg-B-beg reg-A-beg
reg-B-end reg-A-end
reg-B-str reg-A-str))
(with-current-buffer buf-B
(delete-region reg-B-beg reg-B-end)
(goto-char reg-B-beg)
(insert reg-A-str))
(with-current-buffer buf-A
(delete-region reg-A-beg reg-A-end)
(goto-char reg-A-beg)
(insert reg-B-str))))
很久以前我用 Minor Mode 解决过,大概是实现了类似 Recursive Editing 的流程,结果一个小小的需求写了一堆乱糟糟的代码,最后随性不用了。