很多人已经报了,顺手修的时候又引入新的bug,无语。。。
方便截个图看看吗?
我也退回 28 了,最近 nix-community/emacs-overlay 加上了一个 emacsUnstableGcc
,就是开启了 native-comp 的 emacs 28.0.90。我发现 doom 在最新的 Emacs 上根本跑不了,只能在 emacs 28 用。
怪不得,看来要想用得舒服还是得守旧一点
这个已经不用了,改成了 emacsGcc ,也是指向 emacs 28.0.90
哦,那么 master 版本呢?
master 版本要自己 override 一下 emacsGit
启动过一段时间变成这样是已知的 bug 么,好像看到了 [Bug] Modeline Background gets Broken Up · Issue #486 · seagle0128/doom-modeline · GitHub
是Emacs29吗?29改了东西,不兼容了
是,我把几个face都设了貌似没问题了,再观察下
29的开发者最近在瞎改,等改完了再说吧
最新消息,他们准备先改回 monospace.
Message-ID: [email protected] To: [email protected] Subject: Variable pitch mode line From: Lars Ingebrigtsen [email protected] Date: Wed, 22 Dec 2021 14:14:22 +0100 X-Now-Playing: Daniel Brandt’s Erased Tapes: 1 + 1 = X (1): “Blackpool Sands Forever”
It’s been almost a month since it was switched to “on”, so I think it’s about time to take stock.
I think that, long term, we want to go in that direction. But we’ve uncovered some usability problems, mainly in the “how do I click those things in “U:–” anyway?” area, and we’ve got a plan to fix that, but haven’t yet.
So I think the way forward is to revert the trunk back to monospaced fonts, then fix the usability problems, and then do another test in a few months.
– (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no
谢天谢地,否则兼容性破坏很多包都要改的。等方案成熟了再merge。
(defmacro doom-modeline-def-segment (name &rest body)
"Defines a modeline segment NAME with BODY and byte compiles it."
(declare (indent defun) (doc-string 2))
(let ((sym (intern (format "doom-modeline-segment--%s" name)))
(docstring (if (stringp (car body))
(pop body)
(format "%s modeline segment" name))))
(cond ((and (symbolp (car body))
(not (cdr body)))
(add-to-list 'doom-modeline-var-alist (cons name (car body)))
`(add-to-list 'doom-modeline-var-alist (cons ',name ',(car body))))
(t
(add-to-list 'doom-modeline-fn-alist (cons name sym))
`(progn
(fset ',sym (lambda () ,docstring ,@body))
(add-to-list 'doom-modeline-fn-alist (cons ',name ',sym))
,(unless (bound-and-true-p byte-compile-current-file)
`(let (byte-compile-warnings)
(byte-compile #',sym))))))))
最后那个 unless 是不是写错了?只有在当 byte-compile-current-file 为 t 时才会编译吧
如果编译整个文件就不需要单独编译这部份。
最近发现在 Github 上使用 snapshot 的 Emacs 总会遇到这个错误
Debugger entered--Lisp error: (error "bar is not a defined segment")
signal(error ("bar is not a defined segment"))
error("%s is not a defined segment" bar)
doom-modeline--prepare-segments((bar workspace-name window-number modals matches follow buffer-info remote-host buffer-position word-count parrot selection-info))
doom-modeline-def-modeline(main (bar workspace-name window-number modals matches follow buffer-info remote-host buffer-position word-count parrot selection-info) (objed-state misc-info persp-name battery grip irc mu4e gnus github debug repl lsp minor-modes input-method indent-info buffer-encoding major-mode process vcs checker))
byte-code("\300\301!\210\300\302!\210\303\304\305\306#\210\303\307\310\311#\210\303\312\313\314#\210\303\315\316\317#\210\303\320\321\322#\210\303\323\324\325#\210\303\326\327\330#\210..." [require doom-modeline-core doom-modeline-segments doom-modeline-def-modeline main (bar workspace-name window-number modals matches follow buffer-info remote-host buffer-position word-count parrot selection-info) (objed-state misc-info persp-name battery grip irc mu4e gnus github debug repl lsp minor-modes input-method indent-info buffer-encoding major-mode process vcs checker) minimal (bar matches buffer-info-simple) (media-info major-mode) special (bar window-number modals matches buffer-info buffer-position word-count parrot selection-info) (objed-state misc-info battery irc-buffers debug minor-modes input-method indent-info buffer-encoding major-mode process) project (bar window-number modals buffer-default-directory) (misc-info battery irc mu4e gnus github debug minor-modes input-method major-mode process) dashboard (bar window-number buffer-default-directory-simple) (misc-info battery irc mu4e gnus github debug minor-modes input-method major-mode process) vcs (bar window-number modals matches buffer-info buffer-position parrot selection-info) (misc-info battery irc mu4e gnus github debug minor-modes buffer-encoding major-mode process) package (bar window-number package) (misc-info major-mode process) info (bar window-number buffer-info info-nodes buffer-position parrot selection-info) (misc-info buffer-encoding major-mode) media (bar window-number buffer-size buffer-info) (misc-info media-info major-mode process vcs) message (bar window-number modals matches buffer-info-simple buffer-position word-count parrot selection-info) (objed-state misc-info battery debug minor-modes input-method indent-info buffer-encoding major-mode) pdf (bar window-number matches buffer-info pdf-pages) (misc-info major-mode process vcs) org-src (bar window-number modals matches buffer-info-simple buffer-position word-count parrot selection-info) (objed-state misc-info debug lsp minor-modes input-method indent-info buffer-encoding major-mode process checker) helm (bar helm-buffer-id helm-number helm-follow helm-prefix-argument) (helm-help) timemachine (bar window-number modals matches git-timemachine buffer-position word-count parrot selection-info) (misc-info minor-modes indent-info buffer-encoding major-mode)] 4)
doom-modeline-mode()
run-hooks(after-init-hook)
(let ((url-show-status nil) (user-emacs-directory default-directory) (user-init-file (expand-file-name "init.el")) (load-path (delq default-directory load-path))) (load-file user-init-file) (run-hooks 'after-init-hook) (run-hooks 'emacs-startup-hook))
(progn (require 'package) (require 'url-vars) (let ((url-show-status nil) (user-emacs-directory default-directory) (user-init-file (expand-file-name "init.el")) (load-path (delq default-directory load-path))) (load-file user-init-file) (run-hooks 'after-init-hook) (run-hooks 'emacs-startup-hook)))
eval((progn (require 'package) (require 'url-vars) (let ((url-show-status nil) (user-emacs-directory default-directory) (user-init-file (expand-file-name "init.el")) (load-path (delq default-directory load-path))) (load-file user-init-file) (run-hooks 'after-init-hook) (run-hooks 'emacs-startup-hook))) t)
command-line-1(("--eval" "(progn\n (require (quote package))\n (require (q..."))
command-line()
normal-top-level()
但是明明代码里是有 (require 'doom-modeline-segments)
的,我比较好奇为啥只有 Emacs snapshot 有问题,而 26/27 就没问题。
原来有人在前几天已经提了一个 issue 了
我在 github actions 里的机器上使用 emacs 29.0.50 测试了一下,发现只有在第 1 次运行的时候会出现 bar is not a defined segment
错误,在第 2 次进行时就没问题了。
如下代码即可精准复现。
;; 假设本文件名 a.el, 则通过 emacs -Q -l a.el 即可复现
(require 'package)
(setq package-archives
'(("melpa" . "https://melpa.org/packages/")
("gnu" . "https://elpa.gnu.org/packages/")))
(package-initialize)
(unless (package-installed-p 'doom-modeline)
(package-refresh-contents)
(package-install 'doom-modeline))
;; 删除所有的 .elc 文件,再模拟安装时的编译过程
(cl-loop for path in load-path
when (string-match-p "doom-modeline" path)
do (let ((default-directory path))
(shell-command "find . -iname '*.elc' -delete")
(byte-recompile-directory "." 0 t)))
(require 'doom-modeline) ;; 这里会挂
(load-theme 'doom-one t)
下面探索了一下为什么会出现这种情况。
xxx is not a defined segment
错误是在 doom-modeline--prepare-segments
里抛出的。
(defun doom-modeline--prepare-segments (segments)
"Prepare mode-line `SEGMENTS'."
(let (forms it)
(dolist (seg segments)
(cond ((stringp seg)
(push seg forms))
((symbolp seg)
(cond ((setq it (cdr (assq seg doom-modeline-fn-alist)))
(push (list :eval (list it)) forms))
((setq it (cdr (assq seg doom-modeline-var-alist)))
(push it forms))
((error "%s is not a defined segment" seg))))
((error "%s is not a valid segment" seg))))
(nreverse forms)))
例如 bar
这个 segment 是这样定义的:
(doom-modeline-def-segment bar
"The bar regulates the height of the mode-line in GUI."
(if doom-modeline-hud
(doom-modeline--hud)
(doom-modeline--bar)))
再继续深入下去看一下 doom-modeline-def-segment
的实现,
(defmacro doom-modeline-def-segment (name &rest body)
"Defines a modeline segment NAME with BODY and byte compiles it."
(declare (indent defun) (doc-string 2))
(let ((sym (intern (format "doom-modeline-segment--%s" name)))
(docstring (if (stringp (car body))
(pop body)
(format "%s modeline segment" name))))
(cond ((and (symbolp (car body))
(not (cdr body)))
(add-to-list 'doom-modeline-var-alist (cons name (car body)))
`(add-to-list 'doom-modeline-var-alist (cons ',name ',(car body))))
(t
(add-to-list 'doom-modeline-fn-alist (cons name sym))
`(progn
(fset ',sym (lambda () ,docstring ,@body))
(add-to-list 'doom-modeline-fn-alist (cons ',name ',sym))
,(unless (bound-and-true-p byte-compile-current-file)
`(let (byte-compile-warnings)
(byte-compile #',sym))))))))
由于 bar
segment 不满足「在去掉 docstring 之后至少还有 2 个元素,且满足第一个元素为 symbol」,说明 bar
segment 会被放在 doom-modeline-fn-alist
中。在 package-install
时默认就会编译所有的 .el
文件,所以后面的 byte-compile
不会被执行。
再回过头去看错误抛出的函数 doom-modeline--prepare-segments
, 由于已经已知对应的的元素必定会在 doom-modeline-fn-alist
中,所以只需要知道什么原因使得
(setq it (cdr (assq seg doom-modeline-fn-alist)))
返回了 nil
就行了.
打开 emacs 查看 doom-modeline-fn-alist
的值,发现是这样的:
...
(#<symbol hud at 64346> . doom-modeline-segment--hud)
(#<symbol bar at 64178> . doom-modeline-segment--bar)
...
然后在 scratch 里观察 (assq 'bar doom-modeline-fn-alist)
的值,发现表达式的值为 nil
…
查阅 emacs lisp reference 得知 #<symbol bar at 64178>
叫作 “bare symbol”,通常由 byte compiler 产生。
于是开始猜想是不是 byte compiler 最近有变动导致生成的字节码不同?
于是就开始了漫长的 git bisect
, 附 git bisect log
:
git bisect start
# good: [2dad332a1439b59a62cd5ed0d8e3626d9e91e3e5] (hack-local-variables--find-variables): Use `user-error`
git bisect good 2dad332a1439b59a62cd5ed0d8e3626d9e91e3e5
# bad: [82aa5be7ce1d5f508d42a4bb394760198a1c6e62] ; Merge from origin/emacs-28
git bisect bad 82aa5be7ce1d5f508d42a4bb394760198a1c6e62
# good: [41846901e22e824f02796012164c51df0297c6ec] Improve dired-do-create-files slightly
git bisect good 41846901e22e824f02796012164c51df0297c6ec
# bad: [067e84116dde36a2e058e3915fe81c818a21e40a] ; * src/bytecode.c (exec_byte_code): Silence GCC warning
git bisect bad 067e84116dde36a2e058e3915fe81c818a21e40a
# bad: [d0f3de72b678608677e1021f3e3c4dd42935b537] Allow using outline minor mode in `M-x apropos-value'
git bisect bad d0f3de72b678608677e1021f3e3c4dd42935b537
# bad: [df49e3a3ab4cddf1e3c0f5482c7fdd809d8a8884] Merge branch 'master' of /home/acm/emacs/emacs.git/master
git bisect bad df49e3a3ab4cddf1e3c0f5482c7fdd809d8a8884
# bad: [bdd9b5b8a0d37dd09ee530c1dab3a44bee09e0f8] Miscellaneous amendments to the scratch/correct-warning-pos branch
git bisect bad bdd9b5b8a0d37dd09ee530c1dab3a44bee09e0f8
# bad: [4e77177b063f9da8a48709aa3ef416d0ac21837b] Try to make scratch/correct-warning-pos build on Windows and not segfault
git bisect bad 4e77177b063f9da8a48709aa3ef416d0ac21837b
# bad: [8f1106ddf2a3861e9c1ebb9d8fa3d4087899de81] Several amendments to scratch/correct-warning-pos.
git bisect bad 8f1106ddf2a3861e9c1ebb9d8fa3d4087899de81
# bad: [368570b3fd09d03ac5b9276d1ca85ae813c3f385] First commit of scratch/correct-warning-pos.
git bisect bad 368570b3fd09d03ac5b9276d1ca85ae813c3f385
# first bad commit: [368570b3fd09d03ac5b9276d1ca85ae813c3f385] First commit of scratch/correct-warning-pos.
一看就知道,368570b3fd09d03ac5b9276d1ca85ae813c3f385
是跟 bare symbol 相关的。尝试着 git revert
但是有太多冲突了,只能仔细看这个 commit 的改动是啥了。
由于这个提交太大了,源码理解在最后再进行,这里先从库的角度看是否可以规避。
再次观察 doom-modeline-def-segment
的定义
(defmacro doom-modeline-def-segment (name &rest body)
"Defines a modeline segment NAME with BODY and byte compiles it."
(declare (indent defun) (doc-string 2))
(let ((sym (intern (format "doom-modeline-segment--%s" name)))
(docstring (if (stringp (car body))
(pop body)
(format "%s modeline segment" name))))
(cond ((and (symbolp (car body))
(not (cdr body)))
(add-to-list 'doom-modeline-var-alist (cons name (car body)))
`(add-to-list 'doom-modeline-var-alist (cons ',name ',(car body))))
(t
(add-to-list 'doom-modeline-fn-alist (cons name sym))
`(progn
(fset ',sym (lambda () ,docstring ,@body))
(add-to-list 'doom-modeline-fn-alist (cons ',name ',sym))
,(unless (bound-and-true-p byte-compile-current-file)
`(let (byte-compile-warnings)
(byte-compile #',sym))))))))
发现出现了两行 add-to-list
调用。根据这里的说明
没有用 backquote 包起来的 add-to-list
在编译时会执行一次。之后再运行 elc 文件时这行 add-to-list
已经不存在了。实际上去掉这一行也是没问题的,因为下面的另一个 add-to-list
总会被调用,无论是在解释执行 el 文件或是编译成了 elc 再执行。
所以这里可以安全地将不正确的 add-to-list
调用给注释掉。经验证, bar is not a defined segment
错误不再出现。
容易得到以下结论:
在 首次 安装 doom-modeline
进行 byte-compile 的时候
(add-to-list 'doom-modeline-fn-alist (cons name sym))
此处的 name
会是一个 bare symbol #<symbol bar at 64178>
,所以此时 doom-modeline-fn-alist
里的符号都是 #<symbol xxx at yyy>
这种形式。又因为 'bar
与 #<symbol bar at 64178>
满足 equal
,add-to-list
通过 equal
来判断元素是否相等,所以第二个 add-to-list
相当于没做什么。
但是在第二次运行时,由于已经编译完成,不会将 bare symbol 给添加到 doom-modeline-fn-alist
中,所以在执行第二个 add-to-list
时会把普通的 'bar
给插入至链表。
而这里真正的问题是 assq
里使用的 eq
来判断符号是否相等,但是
(car (car doom-modeline-fn-alist))
;;=> #<symbol follow at 127713>
(eq (car (car doom-modeline-fn-alist)) 'follow)
;;=> nil
(equal (car (car doom-modeline-fn-alist)) 'follow)
;;=> t
显示带有 position 信息的符号与普通的符号不满足 eq
.
所以目前的解决方案有 2 个:
- 将
这一行和上面的那行 add-to-list
给删掉。
- 将
doom-modeline--prepare-segments
函数内的assq
换成assoc
,不过倒是有一个奇怪的现象,明明assoc
默认就是用的equal
,但是在不显式指明equal
的时候居然找不到
(car (car doom-modeline-fn-alist))
;;=> #<symbol follow at 127713>
(equal (car (car doom-modeline-fn-alist)) 'follow)
;;=> t
(assoc 'follow doom-modeline-fn-alist)
;;=> nil
(assoc 'follow doom-modeline-fn-alist 'equal)
;;=> (#<symbol follow at 127713> . doom-modeline-segment--follow)