A fast and incredible Emacs config
Table of Contents
个人Emacs配置
仿 Centaur Emacs 的个人配置.
git clone --depth 1 https://github.com/condy0919/.emacs.d ~/.emacs.d
仅包含C/C++/Rust/OCaml/Haskell相关配置,且全线使用lsp
。当前由于haskell-ide-engine
水土不服,故haskell
没有采用lsp
。
保持着尽量使用Emacs
自带功能的原则,能用自带的就用自带的。
需要的依赖
hunspell
(optional) 拼写检查,目前仅在git-commit-mode
下默认启用rg
更快的grep
pandoc
(optional) 文本转换工具,markdown-mode
渲染需要markdown
(optional) 文本转换工具,markdown-mode
渲染需要cmake
c++
项目的构建工具git
这个就不用说了吧?gcc
这个就不用说了吧?fd
(optional) 更现代的find
,projectile
会自动检测
基础配置
最基础的配置包含了那些在所有mode
下都不会变更的配置,包含了:
包名 | 功能 |
---|---|
align | align-regexp 可以自动对齐选择的符号 |
appt | 任务提醒,可以与org-mode 结合 |
hippie-expand | 用来展开文本 |
hl-line | 高亮当前行 |
newcomment | 注释、反注释功能 |
paren | 高亮匹配的括号 |
saveplace | 自动记录上次打开文件的位置 |
simple | 在modeline 里显示行号、列号以及当前文本的大小 |
so-long | 打开长行的文件不再痛苦 (Emacs 27+ 自带) |
tab-bar | 窗口布局管理 (Emacs 27+ 自带) |
tramp | 远程编辑就靠它 |
而这几个包也是Emacs
自带的。
为了保持界面的整洁,禁用了菜单栏、工具栏和滚动条。
插件配置、升级
package.el
(自带的)来安装包、use-package
来管理配置。对于elpa
, melpa
里没有的包,使用quelpa
辅助下载。
为什么我会从straight.el
切换至quelpa
呢?
主要是straight.el
不支持单个文件的下载、配置,为了使用llvm-mode.el
而 clone 整个 llvm repo 就显得有点得不尝失了。相关配置见init-cpp.el
内的llvm-mode
配置项。另外由于quelpa
与 package-quickstart
冲突,llvm-mode
和tablegen-mode
需要人工执行对应的quelpa
代码块来提前安装,而不是通过use-package
自动检测、下载。不过因为quelpa
安装过后的包也会在~/.emacs.d/elpa/
里放一份,所以最终效果跟package.el
是一样的。
Emacs 29 引入了 package-upgrade-all
,需要更新直接 M-x package-upgrade-all 即可。
界面
使用了doom-themes
和doom-modeline
,简直惊艳!doom-one
的界面非常好看!
趁手的工具
rg
是比较常用的工具,更有projectile
管理项目,让项目编译、测试、运行变得更加方便。
avy
用来代替vim-easymotion
。而且avy
还提供了goto-line
的功能,这下都不用开相对行号8k
9j
这样跳了。
以前是ivy
用户,现在则是仅使用vertico
, embark
, consult
和 marginalia
了。
Emacs
下的org-mode
/markdown-mode
让人惊艳,突然觉得写文档也会这么快乐。与之相辅相成的还有separedit
,让人在代码里写documentation comments
不再烦恼。
valign 提供了像素级别的表格对齐,终于不用再靠西文半宽的字体了!
从neovim
迁移过来的我,自然是常开evil-mode
,相关的evil
套件有:
evil
evil-collection
(已包含evil-magit
)evil-surround
按键绑定
evil-mode
开启了evil-collection-want-unimpaired-p
(由evil-collection
提供) 而获得了如下键绑定:
key | function |
---|---|
[b | previous-buffer 切换至上一个 buffer |
]b | next-buffer 切换至下一个 buffer |
[e | evil-collection-unimpaired-move-text-up 将当前行移动至上一行 |
]e | evil-collection-unimpaired-move-text-down 将当前行移动至下一行 |
[l | evil-collection-unimpaired-previous-error 上一个错误 |
]l | evil-collection-unimpaired-next-error 下一个错误 |
[ SPC | evil-collection-unimpaired-insert-newline-above 在上方插入一空行 |
] SPC | evil-collection-unimpaired-insert-newline-below 在下方插入一空行 |
[u | evil-collection-unimpaired-url-encode 对所选内容进行url 参数编码 |
]u | evil-collection-unimpaired-url-decode 对所选内容进行url 参数解码 |
此外,凭借 avy 模拟了 evil-snipe 的 s
和 f
功能。
key | function |
---|---|
s | evil-avy-goto-char-timer |
f | evil-avy-goto-char-in-line |
本配置里使用hideshow
来fold
代码块。由于hideshow
本身提供的快捷键非常长,非常推荐使用evil-mode
在normal
状态下定义的键绑定。
key | function |
---|---|
zm | evil-close-folds 隐藏所有代码块 |
zr | evil-open-folds 显示所有被隐藏的代码块 |
zo | evil-open-fold 隐藏当前代码块 |
zO | evil-open-fold-rec 递归地隐藏当前以及之内的代码块 |
zc | evil-close-fold 显示当前被隐藏的代码块 |
zC | evil-close-fold-rec 递归地显示当前以及之内的代码块 |
za | evil-toggle-fold 来切换是否隐藏代码 |
与文件相关的Leader
键绑定如下:
key | function |
---|---|
ff | find-file 打开文件, f.有相同效果 |
fF | find-file-other-window 同上,不过是在另一窗口打开, f/有相同效果 |
f/ | 同上 |
fD | +delete-current-file 删除当前文件 |
fC | +copy-current-file 拷贝当前文件至其他地方 |
fy | +copy-current-filename 拷贝当前文件的绝对路径 |
fR | +rename-current-file 重命名当前文件 |
fr | recentf-open-files 访问最近使用过的文件 |
fl | find-file-literally 采用朴素模式打开文件 |
fj | dired-jump 进入当前文件的目录 |
fJ | dired-jump-other-window 同上,不过是在另一窗口打开 |
与buffer
、bookmark
相关的键绑定:
key | function |
---|---|
bb | switch-to-buffer 切换buffer |
bB | switch-to-buffer-other-window 同上,不过是在另一窗口打开 |
bc | clone-indirect-buffer 将当前buffer 克隆至另一buffer ,它们可以使用不同major-mode |
bC | clone-indirect-buffer-other-window 同上,不过是在另一窗口打开 |
bv | revert-buffer 重新读取当前buffer 对应的文件 |
bx | scratch-buffer 直接跳转到 *scratch* buffer |
by | +copy-current-buffer-name 复制当前buffer 的名字 |
bz | bury-buffer 退出当前buffer 的显示,当前buffer 未被 kill |
key | function |
---|---|
bj | bookmark-jump 跳转至书签 |
bJ | bookmark-jump-other-window 同上,不过是在另一窗口打开 |
bm | bookmark-set 设置书签 |
bM | bookmark-set-no-overwrite 同上,但是不会覆盖同名的书签 |
bd | bookmark-delete 删除书签 |
bi | bookmark-insert 插入书签的内容 |
bl | bookmark-bmenu-list 打开书签列表 |
br | bookmark-rename 重命名书签 |
bs | bookmark-save 保存书签 |
bw | bookmark-write 将书签保存至其他文件 |
打开其他程序的Leader
键绑定:
key | function |
---|---|
ot | ansi-term 打开ansi-term |
oe | eshell 打开eshell |
os | shell 打开shell |
ol | org-store-link 存储URL |
oc | org-capture 随时记录一些想法、URL等 |
打开一些看起来像是独立的应用:
key | function |
---|---|
aa | org-agenda 日程 |
ac | calendar 日历 |
ag | gnus 查看新闻组 |
ai | rcirc 上 IRC |
搜索相关的Leader
键绑定:
key | function |
---|---|
si | imenu |
sj | evil-show-jumps |
sm | evil-show-marks |
sr | evil-show-registers |
sp | consult-ripgrep |
ss | consult-line |
与代码相关的Leader
键绑定:
key | function |
---|---|
cc | compile 编译 |
cC | recompile 重新编译 |
ck | kill-compilation 打断当前的编译过程 |
cx | quickrun 快速运行当前程序 |
cX | quickrun-shell 在eshell 里查看输出 |
cd | rmsbolt-compile 查看编译器的输出,如汇编、IR表示 |
cw | delete-trailing-whitespace 删除行末空白字符 |
Emacs
key | function |
---|---|
M-` | 打开一个弹出式shell 以临时执行一些命令 |
M-; | comment-or-uncomment 注释与反注释 |
C-c ' | 通过separedit 在注释中快乐地写代码 |
C-c p | projectile 调用前缀 |
C-x g | 呼出 magit |
M-g M-l | 调用avy-goto-line |
因为projectile比较常用,故把它单独拿出来说。
key | function |
---|---|
C-c p f | projectile-find-file 在项目内查找其他文件 |
C-c p b | projectile-switch-to-buffer 切换至其他buffer (限定在本project 下) |
C-c p C | projectile-configure-project 配置当前项目 |
C-c p c | projectile-compile-project 编译当前项目 |
C-c p u | projectile-run-project 运行当前项目 |
C-c p P | projectile-test-project 测试当前项目 |
C-c p p | projectile-switch-project 切换至其他项目 |
C-c p s r | projectile-ripgrep 使用ripgrep 来搜索当前项目内的文本。 |
基于同样的理由,把flycheck
单独拎了出来。
key | function |
---|---|
C-c ! l | flycheck-list-errors 列出所有lint 错误 |
C-c ! n | flycheck-next-error 下一个lint 错误 |
C-c ! p | flycheck-previous-error 上一下lint 错误 |
更详细的按键绑定请直接看代码. :-)
C-c h是所有hydra
的前缀,目前有 2 个,分别是:
background-opacity-menu
方便执行调整真透明度scroll-other-window-menu
在不改变焦点的情况下移动另一窗口的buffer
通用开发设置
- 显示行末空白字符
- 高亮TODO FIXME等关键字
dumb-jump
作为lsp-find-definition
失败后的备份手段magit
作为git
客户端hideshow
来显示/隐藏结构化的代码块,如 "{ }" 函数体等rmsbolt
作为一个本地的 Compiler Explorer 相比于godbolt
快速一点ispell
拼写检查器,evil
用户可以快速通过z= (ispell-word
) 来检查flyspell
拼写检查器,仅在magit
写提交信息时启用quickrun
作为一个能够执行部分区域内的代码块,方便快速验证函数功能tempo
作为代码片段展开工具,spdx
然后再M-x tempo-expand-if-complete即可。也可以通过hippie-expand
来触发
prog-mode
cc-mode
使用lsp-mode
作为补全、符号查找的工具,默认后端使用clangd
,一般发行版的源里都会有对应的包。如果想使用ccls,可以customize
对应的变量:
(setq lsp-clients-clangd-executable "ccls"
lsp-clients-clangd-args nil)
如果想使用ccls
的lsp
扩展功能,需要安装ccls扩展。
此外,
cmake-mode
可使用company-mode
进行符号补全- 启用了
hide-ifdef-mode
,可以令#if 0
到#endif
之间的代码看起来像注释一样。也可以#define
一些宏,放入hide-ifdef-env
中即生效。 - 部分常用
snippet
,如ifndef
,main
等等。详细列表见init-cpp.el
文件 cmake-mode
增加了一个简单 lib 的snippet
,可以通过lib
关键字展开
rust-mode
使用lsp-mode
作为补全、符号查找的工具,默认后端使用rust-analyzer
,需要额外安装rust-analyzer
的包。lsp-mode
会首先考虑rust-analyzer
,如果未在exec-path
中找到则会转而使用rls
。rls
通常与rust
这个包捆绑在一起。
rust-mode
开启了保存时格式化文件,需要确保rustfmt
二进制包存在- 使用了cargo来提供深度集成化的
cargo
命令
ocaml-mode
使用lsp-mode
作为补全、符号查找的工具。在Arch Linux
上,可以使用 ocaml-lsp-git 这个包。
由于ocaml-lsp-git
目前只实现了lsp-format-buffer
且额外依赖ocamlformat
。
所以这里额外使用了ocp-indent,通过ocp-indent-region
, ocp-indent-buffer
来提供格式化代码的功能。
同时也集成了dune。
ocp-indent
和dune
都依赖系统级别的包。
如果你是Arch Linux
可以直接通过如下命令安装:
yay -S ocaml-ocp-indent dune
haskell-mode
非常纯粹, 其实是平常不怎么写 haskell。
截图
FAQ
dashboard 图标显示异常
依赖 nerd-icons, 请确保 M-x nerd-icons-install-fonts
安装对应的字体以显示图标。
更新时提示对应包版本不存在
如果您在使用国内镜像源时出现这个问题,多数情况都是由镜像源同步不一致导致的,可以切换成上游来规避这个问题。
(setq package-archives
'(("melpa" . "https://melpa.org/packages/")
("gnu" . "https://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")))
Emacs 配置挂了
可以使用 init-mini.el 这个最小配置来临时救急一下。
emacs -Q -l init-mini.el
虽然咱都是直接开 nvim 的
其他
欢迎提issue
给出建议,感谢!