如何拦截C-g
C-g的本质
关于 C-g
在 Emacs Manual中的Quitting and Aborting 有一段说明
C-g works by setting the variable quit-flag to t the instant C-g is typed; Emacs Lisp checks this variable frequently, and quits if it is non-nil. C-g is only actually executed as a command if you type it while Emacs is waiting for input. In that case, the command it runs is keyboard-quit.
也就是说, C-g
有两种工作模式:
当Emacs在等待用户输入时, C-g
实际上是调用了 keyboard-quit
命令,关于这个命令的说明很简单,它仅仅是抛出一个quit信号而已:
Signal a ‘quit’ condition. During execution of Lisp code, this character causes a quit directly. At top-level, as an editor command, this simply beeps.
在其他情况下, C-g
设置 quit-flag
为 t
, Emacs不断检测该变量,当检测到 quit-flag
不为 nil
时,则会退出正在执行的elisp代码。
关于 quit-flag
的说明如下:
Non-nil causes ‘eval’ to abort, unless ‘inhibit-quit’ is non-nil. If the value is t, that means do an ordinary quit. If the value equals ‘throw-on-input’, that means quit by throwing to the tag specified in ‘throw-on-input’; it’s for handling ‘while-no-input’. Typing C-g sets ‘quit-flag’ to t, regardless of ‘inhibit-quit’, but ‘inhibit-quit’ non-nil prevents anything from taking notice of that.
拦截C-g
知道 C-g
会做什么,我们就知道如何对 C-g
进行拦截了。
如果我们拦截等待输入时的 C-g
, 那么只需要忽略信号quit即可.我们可以使用 condition-case
捕获信息:
(condition-case sig (read-string "请输入字符串,按C-g退出") (quit (message "收到信号(%s),退出" sig)))
收到信号((quit)),退出
若是不允许通过 C-g
中断某段elisp代码,则可以通过设置 inhibit-quit
来进行:
(let ((inhibit-quit t)) (while t) (setq quit-flag nil))
执行上面语句后你再按 C-g
会发现无法退出死循环(要想退出这个死循环需要快速按下三次 C-g
)
with-local-quit
Emacs预定义了一个 with-local-quit
宏,可以限制 C-g
的退出范围。关于它的描述如下:
Execute BODY, allowing quits to terminate BODY but not escape further. When a quit terminates BODY, ‘with-local-quit’ returns nil but requests another quit. That quit will be processed as soon as quitting is allowed once again. (Immediately, if ‘inhibit-quit’ is nil.)
注意它的最后一句说明: That quit will be processed as soon as quitting is allowed once again. (Immediately, if ‘inhibit-quit’ is nil.)
因此我们一般这么用:
(let ((inhibit-quit t)) (with-local-quit ;; BODYS ) (setq quit-flag nil))