我对启动时间不太在意,2011 年的老 Mac,需要 10 多秒(不过我这时间是实实在在的,没有半点偷渡),如果刚好遇到什么任务冲突,100 多秒也是有的,无所谓。
所以我的配置没有认真在 Daemon 模式下跑过,刚才试了一下,发现背景色不太对劲,很快找到了问题配置所在:
(defun edit-server-edit-frame-setup (frame)
(unless window-system
(with-selected-frame frame
;;...
)))
(add-hook 'after-make-frame-functions #'edit-server-edit-frame-setup)
这个函数用途是,在终端模式下对主题&背景做些调整。常规方式启动是没有问题的,但 emacsclient 启动 GUI,函数也生效了,这是不应该的。于是加了两行 message:
(defun edit-server-edit-frame-setup (frame)
(unless window-system
(message ">>> edit-server-edit-frame-setup 1 %S" window-system)
(with-selected-frame frame
(message ">>> edit-server-edit-frame-setup 2 %S" window-system)
;;...
)))
(add-hook 'after-make-frame-functions #'edit-server-edit-frame-setup)
果然问题出在 window-system
:
edit-server-edit-frame-setup 1 nil
edit-server-edit-frame-setup 2 ns
在 emacsclient 启动 GUI 的过程中,window-system
有一段时间其实是 nil,待 frame 创建完成之后,它又变成了 ns,所以这段代码的判断就出错了。改成一下写法解决问题:
(defun edit-server-edit-frame-setup (frame)
(with-selected-frame frame
(unless window-system
;;...
)))
由于存在这么一个从 nil 到有
的过程,我如果打算长期使用 Daemon,整个配置可能要重新检视一遍,比如,有些代码区分是终端和 GUI 版本的:
(if window-system
(defun foo-setup ()
;; aaa
)
(defun foo-setup ()
;; bbb
))
常规启动,无论 GUI 还是终端,只有一套代码生效。如果 emacsclient 启动,则必须这么写:
(defun foo-setup ()
(if window-system
(progn
;; aaa
)
(progn
;; bbb
)))
因为要应对 emacsclient 随时都可能启动 GUI 或终端的情况,所以两种实现都得有效。仅这个例子来看,每次执行 foo-setup
都需要判断一次 (if window-system ...)
,而不是原来的只在初始化判断一次。
更严重的不是多了几次判断,而是由于 emacsclient 启动过程中,frame 创建的时机跟常规方式不同,所以也导致配置当中有用到 window-system (或其他跟 frame 相关的变量 )的代码没有按照预期执行:该执行的没执行,不该执行的偏又执行。