这两天受到 @SteamedFish@Locez 的鼓动,而且本人的 doom 配置也积累到一定程度了,是时候把自己的 doom-private-config 进行模块化拆分,以方便管理维护了。

本来预想是挺简单的事,把集中在一起的配置,按照不同的类型,新建对应的 module 目录,然后建立 config.el 和 packages.el,把配置代码对应部分剪切过来就行了。结果拆分完毕一启动,一堆报错信息。汗~

冷静,冷静。查看错误信息,发现都是无效函数和参数为空的信息。这就怪了,配置代码完全没变,只是放在了不同的位置。莫非……跟执行的顺序有关?尝试把所有与已有 package 有关的配置代码都用 after! 括起来,确保配置代码在对应的 package 加载之后才执行。再次重新启动,果然可以了!看来之前的 ~/.doom.d/config.el 文件里的代码是在各个 modules 加载之后才执行的。

至此以为,万事大吉。而且可喜的是, emacs 的启动时间竟然减少了 1 秒,真是意外的收获。可运行一段时间以后,总觉得有些不对劲,仔细观察发现是 theme 的部分颜色有异常,由于我是使用 theme-changer 包根据日出日落时间自动切换 theme 的,已经在 use-package! 的 :after 中指定在 doom-themes 和 solaire-mode 之后加载了,怎么还会有问题呢?

没有出错提示,那就说明是正常执行了,还有异常说明有些配置被别的代码修改了,应该还是执行顺序的问题。我尝试让此 package 延迟 5 秒加载,发现问题依旧,那看来最后执行是不可以的,肯定在这之前 doom 有一些 ui 方面的处理。从 ~/.emacs.d/init.el 开始查找,在 core.el 里的 doom-initialize 函数中找到了说明, doom 的加载顺序为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  ~/.emacs.d/init.el
  ~/.emacs.d/core/core.el
  ~/.doom.d/init.el
  Module init.el files
  `doom-before-init-modules-hook'
  Module config.el files
  ~/.doom.d/config.el
  `doom-init-modules-hook'
  `after-init-hook'
  `emacs-startup-hook'
  `doom-init-ui-hook'
  `window-setup-hook'

果然发现有一个 doom-init-ui-hook,这个 hook 看名字就是和 ui 初始化有关,加上看到 ~/.doom.d/config.el 的次序也在它之前,八成是这样没跑了。最后选中 emacs-startup-hook 作为注入点,再次重新启动。

完美~

[1 月 13 日更新]

无意中发现 tabnine 没生效,根据前一日的经验,应该也是执行顺序的事, tabnine 要修改 company-backends 的值,估计是 doom 里的 company 之后又有修改 company-backends 的操作,哪怕是在 tabnine 中指定 after company 也不行。依据前述办法,把增加 company-backends 操作加在后面次序的 hook 中,我选定的是 after-init-hook,重启后生效。最终 tabnine 的配置代码是:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
(use-package! company-tabnine
  :when (featurep! :completion company)
  :hook
  (kill-emacs . company-tabnine-kill-process)
  (lsp-after-open . (lambda ()
                      (add-to-list 'company-transformers 'company//sort-by-tabnine t)
                      (add-to-list 'company-backends '(company-lsp :with company-tabnine :separate))))
  (after-init . (lambda ()
                  (add-to-list 'company-backends #'company-tabnine)
                  (set-company-backend! 'text-mode
                    'company-tabnine 'company-dabbrev 'company-yasnippet 'company-ispell)
                  (set-company-backend! 'conf-mode
                    'company-tabnine 'company-capf 'company-dabbrev-code 'company-yasnippet)
                  (set-company-backend! 'prog-mode
                    'company-tabnine 'company-capf 'company-yasnippet)))
  :config
  (set company-idle-delay 1)
  (map! (:leader
          :desc "Use company default backend" "clo" #'company-other-backend
          :desc "Use company tabnine backend" "clt" #'company-tabnine))
  ;; Integrate company-tabnine with lsp-mode
  (defun company//sort-by-tabnine (candidates)
    (if (or (functionp company-backend)
            (not (and (listp company-backend) (memq 'company-tabnine company-backend))))
        candidates
      (let ((candidates-table (make-hash-table :test #'equal))
            candidates-lsp
            candidates-tabnine)
        (dolist (candidate candidates)
          (if (eq (get-text-property 0 'company-backend candidate)
                  'company-tabnine)
              (unless (gethash candidate candidates-table)
                (push candidate candidates-tabnine))
            (push candidate candidates-lsp)
            (puthash candidate t candidates-table)))
        (setq candidates-lsp (nreverse candidates-lsp))
        (setq candidates-tabnine (nreverse candidates-tabnine))
        (nconc (seq-take candidates-tabnine 3)
               (seq-take candidates-lsp 6))))))