首页 > 中级, 其他 > Emacs的process sentinel

Emacs的process sentinel

2010年1月25日 ahei 发表评论 阅读评论

今天早上去水木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来实现的,殊途同归阿!

分享家:Addthis中国
GD Star Rating
loading...
Emacs的process sentinel, 6.0 out of 10 based on 1 rating 标签:buffer, Emacs, gdb, lambda, lisp, mode, process sentinel, se, term, windows, 编译, 调试, 鼠标

相关日志

分类: 中级, 其他
  1. Meteor Liu
    2010年5月20日00:30 | #1

    我有一个函数gud-kill用来关闭gdb,回到编辑状态:

    1
    2
    3
    4
    5
    6
    
    (defun gud-kill ()
      "Kill gdb process."
      (interactive)
      (with-current-buffer gud-comint-buffer (comint-skip-input))
      (set-process-query-on-exit-flag (get-buffer-process gud-comint-buffer) nil)
      (kill-buffer gud-comint-buffer))

    这个函数是好用的,它不会底部我就会直接退出,并且因为gud-comint-buffer被关闭了,所以gdb自动打开的其它几个window(locals, breakpoints, stack等)都会自动关闭。

    今天我想通过set-process-sentinel来实现自动开关gud-tooltip-mode,所以写了下面这个hook:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    (defun gdb-tooltip-hook ()
      (gud-tooltip-mode 1)
      (let ((process (ignore-errors (get-buffer-process (current-buffer)))))
        (when process
          (set-process-sentinel process
                                (lambda (proc change)
                                  (let ((status (process-status proc)))
                                    (when (or (eq status 'exit)
                                              (eq status 'signal))
                                      (gud-tooltip-mode -1))))))))

    现在启动gdb时会自动打开gud-tooltip-mode,退出时会关闭gud-tooltip-mode,可是现在gud-kill出问题了,除了gud-comint-buffer被关闭了,其它几个window(stack, locals, breakpoints)都还留在那,并且源代码窗口也没回到c++-mode,还保留着gdb的那几个工具栏。set-process-sentinel有什么要特殊注意的地方吗?

    [回复]

    ahei 回复:

    @Meteor Liu, 话说你为啥要在启动gdb时会自动打开gud-tooltip-mode啊?直接 (gud-tooltip-mode 1)不就行了吗?我就是这样干的

    [回复]

    Meteor Liu 回复:

    @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 回复:

    @Meteor Liu, 你对highlight-symbol那么上心啊?我都是设置了光标在某个单词上挺几秒再高亮的. 要想做个真正的Emacser, 表老惦记着CUA啊, 要抛弃Windows那一套, 哈哈. 关键是我还没搞明白为啥kill掉gud-comint-buffer, gdb的其他几个窗口就自动关闭掉了.

    [回复]

    Meteor Liu 回复:

    @ahei,
    我也没找到为啥gud-comint-buffer被kill掉后其它窗口自动关闭,挺奇怪的是默认打开那几个窗口会关闭,如果重新打开几个窗口,像memory之类,就不会随gud-comint-buffer的关闭而关闭。
    这个问题反正现在有替代方案了,我就把它先放那吧,以后有机会再深入了解。

    [回复]

    ahei 回复:

    @Meteor Liu, 我把gud源码看了半天也没找到,很郁闷

    [回复]

  1. 本文目前尚无任何 trackbacks 和 pingbacks.
:wink: :-| :-x :twisted: :) 8-O :( :roll: :-P :oops: :-o :mrgreen: :lol: :idea: :-D :evil: :cry: 8) :arrow: :-? :?: :!: