Emacs 启动时会自动进入命令循环。 这一顶层命令循环调用永不退出;只要 Emacs 运行, 它就会持续运行。Lisp 程序也可调用命令循环。 由于这会导致命令循环被多次激活,我们称之为 递归编辑(recursive editing)。递归编辑层级的作用是挂起调用它的 任意命令,并允许用户在恢复该命令前执行任意编辑操作。
递归编辑期间可用的命令与顶层编辑循环中 可用的命令相同,且均在按键映射中定义。 仅有少数特殊命令能退出递归编辑层级; 其他命令执行完毕后会返回递归编辑层级。 (退出用的特殊命令始终可用,但无递归编辑 进行时它们不执行任何操作。)
所有命令循环(包括递归循环)均会设置通用错误 处理程序,因此命令循环中运行的命令若出错, 不会导致循环退出。
迷你缓冲区输入是一种特殊的递归编辑。 它有一些特殊处理,例如启用迷你缓冲区和 迷你缓冲区窗口的显示,但数量比你预想的少。 部分按键在迷你缓冲区中的行为不同,但这仅因 迷你缓冲区的本地映射;若切换窗口,仍会获得 常规的 Emacs 命令。
调用 recursive-edit 函数可进入递归编辑层级。
该函数包含命令循环;同时会调用带 exit 标签的
catch,因此可通过向 exit 抛出值
退出递归编辑层级(see 显式非局部退出:catch 和 throw)。
抛出 t 值会使 recursive-edit 退出,
控制权返回上一层命令循环。这一操作称为
中止(aborting),可通过 C-](abort-recursive-edit)
完成。类似地,抛出字符串值会使 recursive-edit
触发错误,并打印该字符串作为提示信息。
若抛出函数,recursive-edit 会在返回前无参调用该函数。
抛出其他任意值会使 recursive-edit 正常返回至
调用它的函数。命令 C-M-c(exit-recursive-edit)
即实现此功能。
除使用迷你缓冲区的场景外,大多数应用不应使用 递归编辑。通常,临时将当前缓冲区的主模式切换为 专用主模式(并定义返回原模式的命令)会更便于用户操作。 (Rmail 中的 e 命令即采用此技术。) 或者,若需让用户递归编辑不同文本,可创建并选中 一个专用模式的新缓冲区。在该模式中定义完成处理 并返回原缓冲区的命令。(Rmail 中的 m 命令 即采用此方式。)
递归编辑在调试中十分有用。你可在函数定义中插入
debug 调用作为断点,以便函数执行到此处时
查看运行环境。debug 会调用递归编辑,
同时提供调试器的其他功能。
递归编辑层级也用于 query-replace 中键入
C-r 或使用 C-x q(kbd-macro-query)时。
该函数调用编辑器命令循环。Emacs 初始化时会自动 调用它,让用户开始编辑。从 Lisp 程序中调用时, 它会进入递归编辑层级。
若当前缓冲区与选中窗口的缓冲区不同,
recursive-edit 会保存并恢复当前缓冲区。
否则,若切换缓冲区,recursive-edit 返回后,
切换后的缓冲区将成为当前缓冲区。
以下示例中,函数 simple-rec 先将光标前移一个单词,
再进入递归编辑,并在回显区打印提示信息。
用户可执行任意编辑操作,然后键入 C-M-c
退出并继续执行 simple-rec。
(defun simple-rec ()
(forward-word 1)
(message "Recursive edit in progress")
(recursive-edit)
(forward-word 1))
⇒ simple-rec
(simple-rec)
⇒ nil
该函数退出最内层的递归编辑(包括迷你缓冲区输入)。
其实现等效于 (throw 'exit nil)。
该函数在退出递归编辑后触发 quit,从而中止
请求最内层递归编辑的命令(包括迷你缓冲区输入)。
其实现等效于 (throw 'exit t)。
See 退出。
该函数退出所有递归编辑层级;它无返回值, 会直接跳出所有计算过程,返回至主命令循环。
该函数返回当前递归编辑的深度。无递归编辑激活时, 返回 0。