Emacs的process sentinel
今天早上去水木Emacs版看到一个网友发了一个帖子,实现编译成功后自动关闭*compilation* buffer的功能,主要是利用compilation-finish-functions这个变量,这个变量的作用就是当编译完成后执行这个变量对应的函数,它的实现如下:
1 2 3 4 5 6 | (setq compilation-finish-functions (lambda (buf str) (when (and (string= (buffer-name buf) "*compilation*") (not (string-match "exited abnormally" str))) (run-at-time 0.5 nil 'delete-windows-on buf) ))) |
他的帖子中还提到了compilation-mode中两个比较有意思的变量,compilation-auto-jump-to-first-error当编译失败的时候,自动跳到第一个错误的地方,比较不错。还有一个是compilation-scroll-output,就是编译的时候自动向下滚动编译输出的结果,这个选项比较有用,因为默认的只能看到最上面的编译结果,下面的结果必须先切换到*compilation* buffer,然后把鼠标移到buffer尾,我以前一直希望有这个功能,自己用end-of-buffer-other-window函数实现了这个功能。
在term-mode中退出term的时候,不会自动关闭term的buffer,multi-term增加了自动关闭buffer的功能,我看了一下multi-term中这个功能的实现,是利用set-process-sentinel这个方法,这个方法为进程设置一个process sentinel,当进程的状态发生改变的时候sentinel会调用设定好的函数。set-process-sentinel的定义如下:
1 2 3 4 5 6 7 | set-process-sentinel is a built-in function in `process.c'. (set-process-sentinel process sentinel) Give process the sentinel sentinel; nil for none. The sentinel is called as a function when the process changes state. It gets two arguments: the process, and a string describing the change. |
第一个参数是进程,第二个参数是函数,这个函数应该有两个参数,第一个参数是进程,第二个参数是进程的描述串。
由于Emacs中退出gdb的时候,不能自动关闭gdb对应的buffer,所以我也用这个set-process-sentinel实现了自动关闭gdb buffer的功能,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | (defun kill-buffer-when-shell-command-exit () "Close current buffer when `shell-command' exit." (let ((process (ignore-errors (get-buffer-process (current-buffer))))) (when process (set-process-sentinel process (lambda (proc change) (when (string-match "\\(finished\\|exited\\)" change) (kill-buffer (process-buffer proc)))))))) ;; 退出gdb的时候关闭gdb对应的buffer (add-hook 'gdb-mode-hook 'kill-buffer-when-shell-command-exit) ;; 退出term的时候关闭term对应的buffer (add-hook 'term-mode-hook 'kill-buffer-when-shell-command-exit) |
那么怎样用set-process-sentinel实现编译成功的时候自动关闭*compilation* buffer呢?这样:
1 2 3 4 5 6 7 8 9 | (defun kill-buffer-when-compile-success (process) "Close current buffer when `shell-command' exit." (set-process-sentinel process (lambda (proc change) (when (string-match "finished" change) (delete-windows-on (process-buffer proc)))))) ;; 编译成功后自动关闭*compilation* buffer (add-hook 'compilation-start-hook 'kill-buffer-when-compile-success) |
上面这个方法是在compilation-start-hook中增加一个函数来实现的,compilation-start-hook是编译进程开始后调用的,这个hook里面的函数有一个参数process。
那为什么不能像gdb-mode-hook那样,直接把kill-buffer-when-compile-success增加到compilation-mode-hook里面呢?我一开始也是增加到这个hook里面的,但是试了下不行,后来调试了下才知道原来调用compilation-mode-hook的时候,还没有创建编译进程,也就不可能设置进程的sentinel。
那下面我们再来看看通过compilation-finish-functions来实现自动关闭buffer和通过process sentinel来实现有什么不同。
通过看compile.el代码,了解到compilation-finish-functions是在函数compilation-handle-exit里面调用的,而compilation-handle-exit又是在compilation-sentinel又是在compilation-start中这样调用的:
(set-process-sentinel proc 'compilation-sentinel) |
看来compilation-finish-functions最终还是通过process sentinel来实现的,殊途同归阿!

loading...
我有一个函数gud-kill用来关闭gdb,回到编辑状态:
这个函数是好用的,它不会底部我就会直接退出,并且因为gud-comint-buffer被关闭了,所以gdb自动打开的其它几个window(locals, breakpoints, stack等)都会自动关闭。
今天我想通过set-process-sentinel来实现自动开关gud-tooltip-mode,所以写了下面这个hook:
现在启动gdb时会自动打开gud-tooltip-mode,退出时会关闭gud-tooltip-mode,可是现在gud-kill出问题了,除了gud-comint-buffer被关闭了,其它几个window(stack, locals, breakpoints)都还留在那,并且源代码窗口也没回到c++-mode,还保留着gdb的那几个工具栏。set-process-sentinel有什么要特殊注意的地方吗?
[回复]
ahei 回复:
五月 21st, 2010 at 1:34 下午
@Meteor Liu, 话说你为啥要在启动gdb时会自动打开gud-tooltip-mode啊?直接 (gud-tooltip-mode 1)不就行了吗?我就是这样干的
[回复]
Meteor Liu 回复:
五月 21st, 2010 at 2:04 下午
@ahei,
两个原因:1.打开gud-tooltip-mode后鼠标移动关联事件了,鼠标动了highlight-symbol
就不能高亮。2.如果用cua-mode的话,C-c和C-x后如果动下鼠标也会造成拷贝不出字。
其实这两个也不算大问题,不过我希望能完美点所以希望gdb的时候打开gud-tooltip-mode,
退出gdb时禁用gud-tooltip-mode。
其实我已经通过gdb-mode-hook和给gud-kill-buffer-hook加advice已经可以自动打开/禁用
gud-tooltip-mode了。
不过还是希望能搞明白为啥set-process-sentinel方式为啥不好用,感觉好像set-process-sentinel
后,执行这个sentinel后就把kill gud-comint-buffer这个buffer的后续操作中断了似的。
是不是set-process-sentinel需要特殊的返回值?
[回复]
ahei 回复:
五月 21st, 2010 at 4:02 下午
@Meteor Liu, 你对highlight-symbol那么上心啊?我都是设置了光标在某个单词上挺几秒再高亮的. 要想做个真正的Emacser, 表老惦记着CUA啊, 要抛弃Windows那一套, 哈哈. 关键是我还没搞明白为啥kill掉gud-comint-buffer, gdb的其他几个窗口就自动关闭掉了.
[回复]
Meteor Liu 回复:
五月 21st, 2010 at 5:10 下午
@ahei,
我也没找到为啥gud-comint-buffer被kill掉后其它窗口自动关闭,挺奇怪的是默认打开那几个窗口会关闭,如果重新打开几个窗口,像memory之类,就不会随gud-comint-buffer的关闭而关闭。
这个问题反正现在有替代方案了,我就把它先放那吧,以后有机会再深入了解。
[回复]
ahei 回复:
五月 22nd, 2010 at 1:46 上午
@Meteor Liu, 我把gud源码看了半天也没找到,很郁闷
[回复]