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
拼写检查,目前仅在git-commit-mode
下启用rg
更快的grep
pandoc
(optional) 文本转换工具,markdown-mode
渲染需要markdown
(optional) 文本转换工具,markdown-mode
渲染需要cmake
c++
项目的构建工具git
这个就不用说了吧?gcc
这个就不用说了吧?fd
(optional) 更现代的find
,projectile
会自动检测languagetool
(optional) 更好的拼写检查、语法纠错工具
基础配置
最基础的配置包含了那些在所有mode
下都不会变更的配置,包含了:
包名 | 功能 |
---|---|
align | align-regexp 可以自动对齐选择的符号 |
appt | 任务提醒,可以与org-mode 结合 |
autorevert | 当文本被其他编辑器修改后,可自动更新 |
delsel | 选中文本可以直接覆盖着写,一般编辑器都默认开这个功能 |
hippie-expand | 用来展开文本 |
hl-line | 高亮当前行 |
newcomment | 注释、反注释功能 |
paren | 高亮匹配的括号 |
saveplace | 自动记录上次打开文件的位置 |
simple | 在modeline 里显示行号、列号以及当前文本的大小 |
so-long | 打开长行的文件不再痛苦 (Emacs 27+ 自带) |
speedbar | 侧边栏,可以显示当前目录下的文件、打开的buffer |
tab-bar | 窗口布局管理 (Emacs 27+ 自带) |
tramp | 远程编辑就靠它 |
而这几个包也是Emacs
自带的。
为了保持界面的整洁,禁用了菜单栏、工具栏和滚动条。
在跳转之后会闪烁一下当前行,这样就比较容易知道当前光标在哪里了。这个功能也是基于自带的pulse。 太卡了,禁用了。
插件配置、升级
选用use-package
来管理插件。对于elpa
, melpa
里没有的包,使用quelpa
辅助下载。为什么我会从straight.el
切换至quelpa
呢?主要是straight.el
不支持单个文件的下载、配置,见init-cpp.el
内的llvm-mode
配置项。
而自动升级选择了auto-package-update
这个包。如果需要更新,M-x auto-package-update-now即可。需要注意的是,更新是同步的。
注意:
llvm-mode
和tablegen-mode
下载通过raw.githubusercontent.com
,而这个域名在国内几乎不可达,需要科学上网。因此现在已使用gitee
的镜像服务来中转。
界面
使用了doom-themes
和doom-modeline
,简直惊艳!doom-one
的界面非常好看!
趁手的工具
which-key
,rg
是比较常用的工具。更有projectile
管理项目,让项目编译、测试、运行变得更加方便。
avy
用来代替vim-easymotion
。而且avy
还提供了goto-line
的功能,这下都不用开相对行号8k
9j
这样跳了。
自然 ivy
, counsel
是要上的,补全功能太好用了。没有 counsel
加持的 M-x 根本无法让人按下去。这里推荐尽量使用 isearch
,因为 swiper
下方占用空间过大(继承于 ivy
的设置),搜索时必然是比较在意上下文的。而自带的 isearch
在稍加设置之后,效果也还可以接受。当 evil-search-module 设置成 isearch
后,也可以使用相同的快捷键来触发 occur
。而 occur
自带也是可以配合 wgrep
来获得快速的编辑能力。
曾经有段时间切换至了 selectrum, embark 和 consult, 虽然多数情况下没有问题,但是没有 counsel-projectile
还是挺影响工作效率的。
Emacs
下的org-mode
/markdown-mode
让人惊艳,突然觉得写文档也会这么快乐。与之相辅相成的还有separedit
,让人在代码里写documentation comments
不再烦恼。
valign 提供了像素级别的表格对齐,终于不用再靠西文半宽的字体了!
从neovim
迁移过来的我,自然是常开evil-mode
,相关的evil
套件有:
evil
evil-collection
(已包含evil-magit
)evil-surround
按键绑定
evil-mode
normal
状态下增加了如下键绑定:
key | function |
---|---|
gs | evil-avy-goto-char-timer 来跳转到目标字符 |
go | evil-avy-goto-word-or-subword-1 来跳转至目标单词处 |
gl | evil-avy-goto-line 来跳转到对应行 |
avy
真乃神器也!
同时,因为开启了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 参数解码 |
本配置里使用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
键,绑定在了SPC
键上。
special-mode
专用。通常在special-mode
里会定义一个SPC
用来翻页 (如Info
),但是由于SPC
已经作为Leader
键了。这里曲线救国,用SPC SPC
来代替原来的SPC
单键行为,并且会进入transient
状态,接下来仅按SPC
即可触发原函数。
key | function |
---|---|
SPC | 调用当前mode 下原来绑定至SPC 上的函数 |
与文件相关的Leader
键绑定如下:
key | function |
---|---|
ff | find-file 打开文件, f.有相同效果 |
f. | 同上 |
fF | find-file-other-window 同上,不过是在另一窗口打开, f/有相同效果 |
f/ | 同上 |
fg | rgrep 递归地在目录下grep 给定字符串 |
fj | counsel-file-jump 打开由find 在当前目录下搜索到的文件 |
fD | my/delete-current-file 删除当前文件 |
fC | my/copy-current-file 拷贝当前文件至其他地方 |
fy | my/copy-current-filename 拷贝当前文件的绝对路径 |
fR | my/rename-current-file 重命名当前文件 |
fr | recentf-open-files 访问最近使用过的文件 |
fl | find-file-literally 采用朴素模式打开文件 |
与目录相关的Leader
键绑定如下:
key | function |
---|---|
dj | dired-jump 进入当前文件的目录 |
dJ | dired-jump-other-window 同上,不过是在另一窗口打开 |
dM | make-directory 创建目录 |
dC | copy-directory 拷贝目录 |
dD | delete-directory 删除目录 |
dl | list-directory 列出目录下的文件 |
与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 对应的文件 |
by | my/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 | my/ansi-term 打开ansi-term |
oT | my/ansi-term-other-window 在其他窗口打开ansi-term |
oe | eshell 打开eshell |
oE | my/eshell-other-window 在其他窗口打开eshell |
os | shell 打开shell |
od | osx-dictionary-search-word-at-point 打开 MacOS 自带的词典 |
ow | dictionary-search 搜索单词定义 |
ol | org-store-link 存储URL |
oc | org-capture 随时记录一些想法、URL等 |
打开一些看起来像是独立的应用:
key | function |
---|---|
aa | org-agenda 日程 |
ac | calendar 日历 |
ag | gnus 查看新闻组 |
ai | rcirc 上 IRC |
aj | jblog 管理博客文章 |
an | newsticker 查看RSS订阅 |
ap | proced 查看进程 |
搜索相关的Leader
键绑定:
key | function |
---|---|
ss | swiper-isearch 使用isearch 的方式来搜索符号 |
sS | swiper-isearch-thing-at-point 与上面类似,默认输入是当前光标处的文本 |
sg | counsel-ripgrep 在当前目录中使用rg 搜索 |
si | imenu |
sj | evil-show-jumps |
sm | evil-show-marks |
sr | evil-show-registers |
与文本相关的Leader
键绑定:
key | function |
---|---|
x TAB | indent-rigidly 在一个region 上统一进行缩进 |
xw | delete-trailing-whitespace 删除行末空白字符 |
xj | set-justification 文本对齐 |
与代码相关的Leader
键绑定:
key | function |
---|---|
cc | compile 编译 |
cC | recompile 重新编译 |
ck | kill-compilation 打断当前的编译过程 |
cx | quickrun 快速运行当前程序 |
cX | quickrun-shell 在eshell 里查看输出 |
cd | rmsbolt-compile 查看编译器的输出,如汇编、IR表示 |
ca | add-change-log-entry-other-window 打开ChangeLog 文件 |
Emacs
key | function |
---|---|
M-; | comment-or-uncomment 注释与反注释 |
C-c ' | 通过separedit 在注释中快乐地写代码 |
C-c x | 调用quickrun 来运行当前buffer 内的代码。eval 快人一步! |
M-= | 在下方弹出一个ansi-term 终端 |
C-c p | projectile 调用前缀,方便地在项目内跳转、编译等其他功能 |
C-x g | 呼出 magit |
C-M-; | 在git-commit 时会有flyspell 检查单词是否错误,通过此按键自动修正 |
C-x o | 开启repeat-mode , 方便原生C-x o 切换window |
C-c = | 调用align-regexp 提供以一个对齐符号的功能 |
因为projectile比较常用,把它单独拿出来
说。本配置中还使用了counsel-projectile
来令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 错误 |
更详细的按键绑定请直接看代码. :-)
定义了一组hydra
,通过调用avy
与thing-at-point
函数,快速复制对应光标处的内容。为了在编辑模式中也能够使用,将其绑定在了C-c h c上。C-c h是所有hydra
的前缀,目前有3个,分别是:
hydra-copy
通过avy
用来快速拷贝hydra-macro
方便执行kmacro
hydra-other-window-scroll
在不改变焦点的情况下移动另一窗口的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即可
prog-mode
cc-mode
使用lsp-mode
作为补全、符号查找的工具,默认后端使用clangd
,一般发行版的源里都
会有对应的包。如果想使用ccls,可以customize
对应的变量:
(setq lsp-clients-clangd-executable "ccls"
lsp-clients-clangd-args nil)
如果想使用ccls
的lsp
扩展功能,需要安装ccls扩展。
禁用了flycheck
自带的3个checker
(分别为c/c++-clang
, c/c++-cppcheck
,
c/c++-gcc
),因为它们都无法正确包含自定义的头文件路径。
此外,
c++-mode
启用了modern-cpp-font-lockcmake-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
作为补全、符号查找的工具,默认后端使用rls
,一般发行版会把它直接跟rust
绑在一起,也可以使用rustup
来安装。对于rust-analyzer
用户而言,通过设置
(setq lsp-rust-server 'rust-analyzer)
来切换至rust-analyzer
。
当然,
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
- 使用
dante
截图
FAQ
- dashboard 里图标显示不正确?
依赖 all-the-icons, 请确保
M-x all-the-icons-install-fonts
安 装对应的字体以显示图标。 - 更新时提示对应版本的包不存在? 这多是因为国内镜像源同步慢导致的。如果出错,可以临时禁用镜像源。
(setq package-archives
'(("melpa" . "https://melpa.org/packages/")
("gnu" . "https://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")))
- 如果升级包遇到了错误,可以使用 init-mini.el 这个最小的配置来 启动。
emacs -Q -l init-mini.el
其他
欢迎提issue
给出建议,感谢!