折腾 Emacs 难免会遇到问题。
论坛里经常可以看到有同学遇到各种配置问题的,但却不知从何入手排查。这里我给大家简单地介绍一些常用的方法,当你下次遇到问题的时候可以先照着排查一下,说不定就能自己解决了。
emacs -Q
启动不加载任何配置的 Emacs
基本每个人都会有自己的配置,在遇到各种 Emacs 内置特性相关问题的时候,如果别人没有问题,那么很可能就是自己的配置引起的。这时可以启用一个干净的环境来验证排查。
具体方法是:
-
Linux/macOS 中打开终端,在终端中执行命令
emacs -Q
就可以了。如果你比较习惯终端界面,或者 Emacs 运行在服务器上,那么可以加一个
-nw
参数,即命令emacs -Q -nw
P.S. 如果你想了解更多的命令行参数,可以通过查看 man 手册(命令
man emacs
)或者查看 emacs help 信息(执行命令emacs --help
),增进对 Emacs 命令行选项的了解。 -
Windows 上打开 cmd ,进入 Emacs bin 目录,然后执行
runemacs.exe -Q
在干净的 Emacs 中加载故障包
通过 emacs -Q
排查 Emacs 内置的特性问题非常有帮助。但另一方面,我们也常常会从 melpa 等仓库安装一些包,而这一块更经常出问题。
那么对于第三方的包如何启动一个干净的 Emacs 来验证呢?只要在 emacs -Q
的基础上增加点东西就可以了,大致有两种方法。
用 package
加载包
例如现在我们要调查 ppcompile 包的问题,那么可以通过这么一段小代码(假如放在文件 /tmp/try-ppcompile.el~ 中)来准备一个干净的环境:
;; 如果你没有手动更改过 package 的包目录,下边这行可以不用
(setq package-user-dir "~/.emacs.local.d/elpa")
;; 初始化 package 包管理器
(require 'package)
(package-initialize)
;; 加载你想要 debug 的包
(require 'ppcompile)
然后在终端中执行命令 emacs -Q -l /tmp/try-ppcompile.el
,这样就在一个干净的 Emacs 环境中“安装”好了 ppcompile 包了。一般来说,目标包已经在 package-user-dir
目录下了,这里并不需要再次下载包, Emacs 会直接加载这个包,因此能够秒开。
直接从源码目录加载包
如果你用某个包的最新版本,而它在包仓库中还没有更新(比如你用 git submodule 来锁定包版本),那么你也可以利用 Emacs -L
启动参数从本地目录来加载那个包。注意你可以指定多个目录,如果必要的话。
假设你的 git clone 了包在 /tmp/ppcompile/
目录下,那么可以这样来加载包及调试:
emacs -Q -L /tmp/ppcompile/ --eval "(require 'ppcompile)"
(感谢 @wzhe 补充这个方法。)
通过 --debug-init
排查启动 Emacs 时就报错的问题
当你更新了配置之后,可能会出现 Emacs 启动的时候直接报错了,比如类似下边这样:
不要着急,其实 Emacs 已经告诉你怎么解决了(注意截图中的第二个红线处)。把当前的 Emacs 关了,打开终端,执行命令 emacs --debug-init
另起一个。她会告诉你报错的位置和调用堆栈,然后顺藤摸瓜,查看对应的文件代码来理解问题原因,再想办法解决就行(截图演示的错误是因为 a-bug-function
这个函数并未定义)。
使用 M-x toggle-debug-on-error
命令打开 debug 开关
(假如你还不知道, M-x toggle-debug-on-error
对应的按键是 ALT+x toggle-debug-on-error 回车
)
有时候,在使用命令或者按键的时候,可能在底部的 echo area 会看到报错:
比如有这么一条 welcome 命令:
(defun welcome ()
(interactive)
(this-func-not-exist))
当你用 M-x welcome
发起命令的时候,就会看到有上述的报错。这时候执行 M-x toggle-debug-on-error
打开调试开关之后,再执行就会弹出出错时的调用栈。
从最上面的部分可以看出来,这个报错是因为 welcome
命令想要调用 this-func-not-exist
,但后者却没有定义,当然就出错啦。
设置 debug-on-message
变量
还有一种情况,你可能只在底部看到有某种 message 但没有报错,但是它的行为与预期不符。此时可以通过设置 debug-on-message
才缩小关注的代码范围,再尝试找到背后的原因。不过,此方法需要对 elisp 具有一定的了解。
比如,调整一下上述的命令实现,让它输出一条讯息 welcome who?
:
(defun welcome ()
(interactive)
(message "welcome who?"))
假设我们现在需要找出到底在哪里输出了 welcome who
这一段莫名其妙的讯息(一般适用于功能比较复杂、调用栈较深的命令),那么就可以利用 debug-on-message
变量设置一个正则表达式 regexp : (setq debug-on-message "welcome who")
。
一旦有人输出, Emacs 就会陷入 debugger ,我们就可以看到在哪里输出了对应的 message :
其他 Emacs 内置的排查问题方法
查看各种帮助的文档:
C-h f
查看函数(C-h f
表示的按键是CTRL+h 再按 f 键
)C-h v
查看变量C-h k
查看某个按键绑定了什么命令C-h m
查看当前 buffer 开启了哪些 major/minor modes ,其中也会汇总它定义了哪些按键C-h l
查看按键历史M-x describe-char
查看光标下的字符相关的属性,比如使用的字体、 face (找到之后可以用于定制颜色、主题等)debug-on-entry
函数,可以设置当某个函数被调用时进入 debugger debug-on-entry (Programming in Emacs Lisp) - www.gnu.orgdebug-on-quit
变量 debug-on-quit (Programming in Emacs Lisp) - www.gnu.org--init-directory
指定配置目录 Emacs 29 增加了--init-directory
来指定配置目录,这样就可以很方便地起一个单独的环境来测试验证特定的配置