暗无天日

=============>DarkSun的个人博客

读:Protesilaos 的 Emacs 合理默认配置 —— 兼与 newcomers-presets 对比

上次整理了 newcomers-presets theme 的配置清单。几天后,Protesilaos(Prot)在和 Sacha Chua 的 livestream 上聊了这个话题,随后分享了一份自己的"sensible defaults"配置。

Prot 在 Emacs 社区以 themes(Modus/ef 系列)、icon 等包闻名。他这份配置不是个人完整配置,而是"跟不同水平的人交流后,觉得有用的基础配置"。

这篇笔记把他的配置逐段过一遍,顺带对比两种思路的差异。

两种思路的根本差异

方面 newcomers-presets Prot 的方案
形式 Emacs theme, load-theme 加载 配置片段,合并到 init.el
开关 随时 disable-theme 关闭 修改 init.el 移除
作用域 只设 setopt / add-hook 可设任何 Elisp
包依赖 纯内置,不装包 推荐 6 个第三方包
改动范围 30+ 项细微调整 更激进(如 custom-safe-themes t)

newcomers-presets 的定位是"开箱即友好的默认值":不改用户的配置习惯,不满意可以关掉。Prot 的思路则是"给一份我觉得对的配置",他认为对新手来说,一开始就用对的设置比"错了再改"更省事。

custom-file 分离

(setq custom-file (locate-user-emacs-file "custom.el"))
(load custom-file :no-error-if-file-is-missing)

Prot 第一件事就是把 custom.elinit.el 里拆出来,单独用一个文件存 M-x customize 产生的配置。没有 custom-file 时,Emacs 把这些自动生成的配置直接写进 init.el ,跟手写配置混在一起。想删掉某个 customize 项得在 init.el 里翻半天,分不清哪些是自动写的、哪些是自己手写的。

这一条和 newcomers-presets 不冲突,无论哪种方案,都应该设 custom-file

包仓库优先级

(setq package-archives
      '(("gnu-elpa" . "https://elpa.gnu.org/packages/")
        ("nongnu" . "https://elpa.nongnu.org/nongnu/")
        ("melpa" . "https://melpa.org/packages/")))
(setq package-archive-priorities
      '(("gnu-elpa" . 3)
        ("nongnu" . 2)
        ("melpa" . 1)))

Prot 把 GNU ELPA 的优先级设为最高(3),NonGNU 其次(2),MELPA 最低(1)。 gnu-elpa 这个名字比默认的 gnu 更直观(新手容易混淆 gnuelpa )。

newcomers-presets 没动包仓库配置,只设了 package-autosuggest-mode 提醒用户装包。

通用设置(General options)

(defun prot/keyboard-quit-dwim ()
  "Do-What-I-Mean behaviour for a general `keyboard-quit'."
  (interactive)
  (cond
   ((region-active-p)
    (keyboard-quit))
   ((derived-mode-p 'completion-list-mode)
    (delete-completion-window))
   ((> (minibuffer-depth) 0)
    (abort-recursive-edit))
   (t
    (keyboard-quit))))

C-g 被绑定到一个 DWIM 函数:激活了区域就取消选择,打开了 minibuffer 就关闭,Completions buffer 被选中就关闭,否则退出的常规行为。这个函数把 C-g 改成了一个通用的"取消当前操作"按钮,非常实用。

(set-face-attribute 'default nil :family "Aporetic Sans Mono" :height 160)
(set-face-attribute 'fixed-pitch nil :family "Aporetic Serif Mono" :height 1.0)
(set-face-attribute 'variable-pitch nil :family "Aporetic Sans" :height 1.0)

字体设的是 Prot 自己做的 Aporetic 系列字体。读者换成自己的字体即可。

(setq custom-safe-themes t)
(setq use-short-answers t)
(setq read-answer-short t)
(setq help-window-select t)
(setq help-window-keep-selected t)
(setq window-combination-resize t)
(setq save-interprogram-paste-before-kill t)
(setq completion-category-defaults nil)

几个值得注意的:

  • custom-safe-themes t :最激进的一行。不再询问每个 theme 是否安全。Prot 的理由是"理论上任何 Elisp 文件都能跑任意代码,theme 安全提示在安全层面没实际作用"。这个看个人风险偏好。
  • use-short-answers t :把 yes/no 改成 y/n 。Emacs 28 引入,省事。
  • help-window-select t + help-window-keep-selected t :打开帮助窗口时自动聚焦,且在帮助 buffer 中继续按快捷键不会跳回原来的窗口。newcomers-presets 没设这些。
  • save-interprogram-paste-before-kill t :从系统剪贴板粘贴后,下次 kill 不会覆盖粘贴的内容。防止粘贴后不小心删掉东西找不回来。

会话与编辑

(savehist-mode 1)
(delete-selection-mode 1)

savehist-mode 和 newcomers-presets 里的 savehist-mode t 一回事。 delete-selection-mode (选中文本后输入直接替换)newcomers-presets 没设。

书签

(setq bookmark-fringe-mark nil)
(setq bookmark-save-flag 1)

关闭 fringe 上的书签图标(很多人不知道那是什么),且每增删一次书签就立即保存文件,而不是等 Emacs 退出时再写。

Dired

(setq dired-kill-when-opening-new-dired-buffer t)
(setq dired-auto-revert-buffer #'dired-directory-changed-p)
(setq dired-recursive-copies 'always)
(setq dired-recursive-deletes 'always)
(setq delete-by-moving-to-trash t)
(setq dired-create-destination-dirs 'ask)

Prot 的 Dired 配置比 newcomers-presets 丰富得多:

  • 打开新目录时复用当前 Dired buffer,不产生一堆 buffer
  • 递归复制和删除不需要确认(但删除走回收站,所以安全)
  • delete-by-moving-to-trash t :删除文件不进回收站的人应该不多

Isearch

(setq isearch-lazy-count t)
(setq lazy-count-prefix-format "(%s/%s) ")
(setq lazy-count-suffix-format nil)

搜索时实时显示"当前匹配 /总匹配数"。Emacs 30 开始默认就有,但 Prot 把格式改成了更紧凑的 (%s/%s) 前缀。

Ediff

(setq ediff-split-window-function 'split-window-horizontally)
(setq ediff-window-setup-function 'ediff-setup-windows-plain)

Ediff 的默认窗口布局在平铺窗口管理器上几乎没法用:控制面板会单独开一个 frame。Prot 把它改成水平分割的窗口内布局,这也是社区常见的优化。

辅助窗口布局(display-buffer-alist)

让帮助窗口、occur/grep 结果等出现在合适的位置,而不是随机占一个窗口。

(add-to-list 'display-buffer-alist
             '((or . ((derived-mode . occur-mode)
                      (derived-mode . grep-mode)
                      (derived-mode . Buffer-menu-mode)
                      (derived-mode . log-view-mode)
                      (derived-mode . help-mode)))
               (display-buffer-reuse-mode-window display-buffer-below-selected)
               (body-function . select-window)))

上例让 occur-mode、grep-mode 等辅助 buffer 优先复用一个同类型的窗口,否则在当前窗口下方打开,并自动聚焦。 display-buffer-alist 是 Emacs 窗口管理的高级话题,初学者可以直接照抄。

推荐的包

Prot 把推荐的包分了两级:

ESSENTIAL:vertico + marginalia

(vertico-mode 1)
(marginalia-mode 1)

vertico 提供垂直的补全候选列表(类似 Emacs 30 的 minibuffer-visible-completions 但更成熟), marginalia 在 minibuffer 中给每个候选显示额外信息(如文件大小、函数签名)。

有意思的是,newcomers-presets 选择了 Emacs 30 原生的补全增强,而 Prot 推荐了第三方包。两种方案都能用,但 vertico 的生态更成熟、社区用户更多。

VERY USEFUL:orderless + consult + embark + trashed

  • orderless — 灵活的补全匹配方式(输入空格分隔的关键词,匹配包含所有关键词的候选),替代 Prot 配置中的 completion-styles
  • consult — 增强版搜索/跳转命令( consult-bufferconsult-line 等)
  • embark — 对任意候选执行操作(比如在 minibuffer 中对文件候选执行复制、重命名等)
  • trashed — 回收站的文件管理器,跟 delete-by-moving-to-trash 配合使用

总结

Prot 这份配置和 newcomers-presets 不是替代关系。newcomers-presets 的目标是"给 Emacs 新手一个更好的默认值,不满意随时关",改动谨慎、可逆。Prot 的配置更接近"一个 Emacs 老手认为初学者应该用的设置",有些选择比较个人化( custom-safe-themes t 、DWIM 的 C-g )。

对读者来说,两个可以同时用:先加载 newcomers-presets 获得基础改进,再从 Prot 的配置里挑自己需要的片段加上。newcomers-presets 的完整代码在 Emacs 源码的 etc/themes/newcomers-presets-theme.el ,Prot 的这份配置在 他的博客

emacs : protesilaos : newcomers-presets