Emacs 入门指南:Why & How

发布: 2020-11-25   上次更新: 2024-07-06   分类: 效率工具   标签: Emacs

文章目录

提到 Emacs,每个程序员应该或多或少听过其大名,毕竟 Emacs已经有将近四十多年的悠久历史。不过由于 Emacs "入道"门槛较高, 导致很多初学者还没领略其精髓就弃之而去。在“熟练”使用Emacs 之前,我也被其虐的体无完肤,因此在使用 Emacs 四年后, 觉得可以把个人经验分享出来,供有兴趣使用Emacs 的读者参考。

本文将分两部分来介绍如何入门 Emacs:

  1. 核心理念,很多初学者在简单尝试 Emacs 之后就放弃的原因是没了解其设计理念。Emacs 是如此独特,以至于从其他编辑器转过来时有很多不适。 通过了解其理念,读者可以判断 Emacs 是否符合自己的品味,是否值得花精力去掌握它
  2. 学习建议,笔者在真正把 Emacs 作为主力编辑器前也失败了多次,走了不少弯路,这里就会把我的经验分享给大家;其次会介绍日常使用的扩展。 很多用户选择 Emacs 就是因为某个插件的某个功能,即使现在,我时常也会因为发现某些高级用法而感叹 Emacs 的威力。

虽然本文会用 ELisp 来举例,但读者并不需要学习 ELisp,具备基本编程技能即可。其次本文以现在较为流行的 VSCode 编辑器来对比 Emacs 的特点,换做其他编辑器也是成立的。

Emacs is Emacs. VSCode/Vim/Sublime… is yet just another editor. —– 出处

https://img.alicdn.com/imgextra/i4/581166664/O1CN0156nQHc1z6A1VldH2x_!!581166664.png
常用编辑器的学习曲线

核心理念

GNU Emacs 官方网站是这么介绍的:

An extensible, customizable, free/libre text editor — and more.

前一部还算直接,"可扩展、可定制、自由的文本编辑器",至于后面的 more 部分,则仁者见仁,智者见智。坊间一度传闻,Emacs 是伪装成编辑器的操作系统。 这一节就来揭开 Emacs 的神秘面纱。

文本编辑

不论 Emacs 的追捧者再怎么神化它,首先 Emacs 是一个文本编辑器,然后才是其他。与 VSCode 不同的是,Emacs 是为纯键盘操作而设计的, 与之类似的编辑器只有 Vi(m)。纯键盘操作无疑比鼠标点击更高效。不过对于初学者来说,使用鼠标点击菜单栏中的功能可能会更方便些。

https://www.gnu.org/software/emacs/images/teaser.png
Emacs 图形界面

单就文本编辑而言,Emacs 就提供了很多贴心的功能,这里列两举:备份与撤销。

备份

作为一名成熟的程序员,每时每刻都需要有备份的意识,毕竟错误在所难免。在 Emacs 中主要提供两种备份方式:

  1. 静态备份,发生在第一次打开文件时,备份的文件名结尾有 ~ 标示;而且支持多版本备份
  2. 自动备份(auto saving)。顾名思义,周期性保存当前正在编辑的文件,文件名头尾有 # 标示。 在保存时,自动备份的文件会被删掉,而当因意外原因(如死机)导致 Emacs 崩溃时,文件则会保留,此时可通过 recover-this-file 命令来恢复。
静态备份的文件
!Users!liujiacai!.emacs.d!README.org.~1~
!Users!liujiacai!.emacs.d!README.org.~2~
!Users!liujiacai!.emacs.d!customizations!editing.el.~1~*
!Users!liujiacai!.emacs.d!customizations!editing.el.~44~*
!Users!liujiacai!.emacs.d!customizations!editing.el.~45~*
!Users!liujiacai!.emacs.d!customizations!editing.el.~46~*
!Users!liujiacai!.emacs.d!customizations!editing.el.~47~*
!Users!liujiacai!.emacs.d!customizations!editing.el.~48~*

自动备份的文件
#!Users!liujiacai!.emacs.d!customizations!misc.org#
#!Users!liujiacai!.emacs.d!customizations!navigation.el#*
#!Users!liujiacai!.emacs.d!elpa!lsp-java-20201105.1758!lsp-java.el#
#!Users!liujiacai!.emacs.d!init.el#

上面是笔者电脑中部分的备份文件,得益于这两个功能,好多次把笔者从崩溃的边缘救回来。

Undo/Redo

在编辑文件时,撤销(Undo)和恢复(Redo)是很重要的功能,传统编辑器这两个操作都是线性的,而在 Emacs 中,则是树状的,这里通过一组文本编辑时的状态图来说明这两者的区别。

  • 依次输入 a b c 三个字符,进入到下图中的 current 状态
;;                                o  (initial buffer state)
;;                                |
;;                                |
;;                                o  (first edit)
;;                                |
;;                                |
;;                                o  (second edit)
;;                                |
;;                                |
;;                                x  (current buffer state)
  • 此时,撤销两步,回到 first edit 状态(即只输入了字符 a ),传统编辑器的此时的状态为
;;                                o  (initial buffer state)
;;                                |
;;                                |
;;                                x  (current buffer state)
;;                                |
;;                                |
;;                                o
;;                                |
;;                                |
;;                                o

而 Emacs 则不然,其状态为

;;                                o  (initial buffer state)
;;                                |
;;                                |
;;                                o  (first edit)
;;                                |
;;                                |
;;                                o  (second edit)
;;                                |
;;                                |
;;                                x  (buffer state before undo)
;;                                |
;;                                |
;;                                o  (first undo)
;;                                |
;;                                |
;;                                x  (second undo)

其状态是追加的,一次撤销意味着返回上次的状态,所以下面的状态图可能更合适些:

;;        (initial buffer state)  o
;;                                |
;;                                |
;;                  (first edit)  o  x  (second undo)
;;                                |  |
;;                                |  |
;;                 (second edit)  o  o  (first undo)
;;                                | /
;;                                |/
;;                                o  (buffer state before undo)
  • 此时,如果进行一次新的插入(比如字符 d ),此时文本上的字符虽然都为 a d ,但编辑器的状态图是不同的,如下所示:
;;            Undo/Redo:                      Emacs' undo
;;
;;               o                                o
;;               |                                |
;;               |                                |
;;               o                                o  o
;;               .\                               |  |\
;;               . \                              |  | \
;;               .  x  (new edit)                 o  o  |
;;   (discarded  .                                | /   |
;;     branch)   .                                |/    |
;;               .                                o     |
;;                                                      |
;;                                                      |
;;                                                      x  (new edit)
  • 此时,如果再进行两次撤销,传统编辑器返回到 initial 状态(无任何字符),而 Emacs 则回到 second 状态(有 a b 两个字符)。

初次接触 Emacs 中的撤销与恢复,十分容易让人迷惑,我时常也被这个搞得晕头转向,不过幸好有 Undo Tree 这个插件来可视化撤销状态,上面的状态图就取自 UndoTree 代码中的注释,感谢其作者的贡献。

扩展、定制

At its core is an interpreter for Emacs Lisp, a dialect of the Lisp programming language with extensions to support text editing.

上面一小节介绍了文本编辑中两个非常实用的基本功能,其实这只是冰山一角,Emacs 可扩展、可定制的特性营造了一个富有创造力的社区,拥有无数功能强大的插件。也许读者会疑问,VSCode 也有丰富的插件市场,那 Emacs 与它们有什么不同呢?这与 Emacs 设计架构有关。

Emacs 本身可以看作是一个虚拟机(Lisp Machine),核心是一个用 C 实现的 ELisp 解释器(interpreter)。除了与操作系统交互的部分使用 C 语言实现外,其余部分均由 ELisp 实现,由核心的 ELisp 解释器解析执行。

在 Emacs 中的所有操作,相当于在解释器中进行函数调用。比如字符输入会调用 self-insert-command 函数。这意味着用户自己定义的代码与 Emacs 的源码(ELisp 部分)是平等的,比如 Emacs 源码里面定义了 foobar 这么一个变量,那么用户自己编写的函数可以直接修改它。

如果读者对 Lisp 不熟悉,可以这么类比,打开 Emacs 的界面对应着在终端输入 python 进入的交互式 REPL,键盘输入、鼠标点击均对应一个个函数调用。

基于核心的 ELisp 解释器,对 Emacs 的扩展变得十分简单(熟悉 ELisp 的前提下),比如在 init.el 中定义下面几行代码即可在 Emacs 中打开浏览器,对指定关键词进行 Google 搜索。

1
2
3
4
5
6
7
8
9
(defun my/google-search ()
  "Googles a query or region if any."
  (interactive)
  (browse-url
   (concat
    "http://www.google.com/search?ie=utf-8&oe=utf-8&q="
    (if mark-active
        (buffer-substring (region-beginning) (region-end))
      (read-string "Google: ")))))

而 VSCode,即使是个 Hello World 级别的扩展,步骤也复杂的多,可参考:

基于 Emacs 功能完备的 Lisp 解释器,社区开发出了各式各样的插件,让一切都尽可能在 Emacs中完成。 比如听音乐玩游戏看EPUB 电子书聊Telegram,甚至任何应用都能运行在 Emacs 里!

Emacs, "a great operating system, lacking only a decent editor"

https://img.alicdn.com/imgextra/i2/581166664/O1CN01ePMFmU1z6A1W5J6a4_!!581166664.png https://img.alicdn.com/imgextra/i2/581166664/O1CN012SFAW41z6A1WeRU9M_!!581166664.png https://img.alicdn.com/imgextra/i4/581166664/O1CN01hJslQh1z6A1VLFP7y_!!581166664.png https://img.alicdn.com/imgextra/i4/581166664/O1CN01DWl21B1z6A1UNsI4U_!!581166664.jpg https://img.alicdn.com/imgextra/i1/581166664/O1CN01vpjexS1z6A1PICqIh_!!581166664.gif

自由

提到 Emacs,不得不提的人是 Richard Stallman,江湖人称“教主”。早期的 Emacs 有很多版本,而现在 GNU Emacs 基本上已经统一江湖了。

https://img.alicdn.com/imgextra/i2/581166664/O1CN01VH3Txp1z6A1WcQQ05_!!581166664.jpg
Richard Stallman

教主极力推崇自由软件,关于自由软件的定义,可以在 GNU 官方网站上找到,这里不再赘述。读者需要明确的是, GNU 社区中的 free 代表但是自由,而非免费。

自由软件毫无疑问极大促进了软件行业的发展,它让程序员有机会了解所用软件的实现机制,而 Emacs 作为教主早期的作品之一, 毫无疑问继承这种思想。每一个操作都可以追根溯源,喜欢这种自由感觉。

更多 Emacs Hackers 可参考:

入门指南

经验分享

我接触 Emacs 是因为学习 Clojure,作为一门 Lisp,Emacs 毫无疑问是最佳的编辑器。不过由于原生 Emacs 功能“简陋”, 多次尝试不得要领,直到遇到 braveclojure 上 Emacs 教程,把 emacs-for-clojure 的配置 clone 到本地, 这才觉得 Emacs 也没那么“难用”。出于对 Lisp 的热爱,强迫自己尽量在 Emacs 中去编码,大概花了一两个月,度过了最艰难的适应期,以后就离不开它了。

到如今,我的配置文件已经丰富了许多,也有很多自己编写的函数。学习一门新语言之前,会把相关 Emacs 扩展先配置上, 让一切都在 Emacs 中运行。这里我想着重强调一点:

论单个功能来说,Emacs 可能不是最好的,但如何有机的把各个功能组合起来,减少切换,Emacs 无意是最优的。

这里结合自己的踩坑、使用经验,给出下面几条建议:

  • 找个成熟的配置先把 Emacs 用起来,一开始不用去纠结细节,等有感觉后再去尝试自己定制。SpacemacsDoom Emacs 是社区内最流行的两个,建议初学者两个都去尝试下,找最合适自己
  • 找一个月重点突击,不要断断续续的使用 Emacs,否则很难适应它,一但过了这一个月,后面就是无限的“春风得意”
  • 在各种插件满足不了自己的需求、有 BUG 时(我大概是在两到三年后),学习 ELisp,毕竟这才是它的精髓。我自己是参考了

  • 接触社区,Emacs China 是我见过的质量最高的中文论坛,里面有很多资深的 Emacs Hacker 来解决初学者遇到的各种问题。
  • 截止到 2020/11 月初,笔者使用 Emacs 比较追求“原汁原味”,尽量用 Emacs 自己的快捷键( C-x C-s 之类), 虽然一年前小指就开始疼痛了,当时是把 CAPS 键映射为 Ctrl,这样用了一年左右,问题不能根治。虽然社区有推荐使用 evil (具有 Vim 的操作模式)来解决这个问题,但之前我觉得这不够“忠贞”,就一直没去用,直到最近发现了 viper mode 才意识到这种想法的幼稚,Emacs 的理念就是可以自己根据自身需求来定制,没有所谓的标准答案。对于其他编辑器的优点, Emacs 会吸收过来。于是立刻把 evil 配置上,从此彻底解放了我的小指。用了四年多的时间了,还是能从 Emacs 中学到些人生经验, 估计这是其他软件做不到的。这点也促使我写这篇文章,防止初学者陷入这种思维定势。

当然每个人的学习道路都不一样,读者可根据自身情况来调整,尽读书不如不读书。一些优质的中文学习资料:

插件推荐

Org-mode

https://img.alicdn.com/imgextra/i4/581166664/O1CN01gzJi7t1z6A1OkLTZ9_!!581166664.png
在 org-mode 中编辑 UML

Org-mode 这是很多非程序员选择 Emacs 的主要原因,简单来说它是一种类似 markdown 的标记语言,很多用户用它来记笔记、 管理 GTD,借助于 Emacs 强大的扩展能力,程序员用它来进行文学编程(literate programming),当之无愧的排在插件榜的第一名。🥇

目前我对 org-mode 使用比较简单,只是把它当时 markdown 来用,单就这一点,配合上 Emacs 的快捷键,就已经甩各种 md 编辑器好几条街了,org-mode 对表格的支持也非常棒,比如可以使用 org-table-transpose-table-at-point 命令将表格的行列调换过来。

https://img.alicdn.com/imgextra/i1/581166664/O1CN01VDVZEM1z6A1UOtSm0_!!581166664.gif
表格行列转换

Magit

https://img.alicdn.com/imgextra/i1/581166664/O1CN01GeC6rw1z6A1VdWuEW_!!581166664.png

Magit 为 Emacs 用户提供操作 git 的接口,是笔者日常重度依赖的插件,也是社区内排行第二的插件。Magit 深度整合到 Emacs 的快捷键中,一切 git 操作都如流水行云般,没有它的话,我连 rebase 都不会。

Evil

https://img.alicdn.com/imgextra/i3/581166664/O1CN01TjEFRp1z6A1U4MagS_!!581166664.png

上面在个人经验里面以及提到过 evil,它并不“邪恶”,而是 Extensible VI Layer for Emacs。除了把 Vi 的模态编辑移植过来外, 还增加了 emacs state,用于禁用所有的 vi 功能。由于是在 Emacs 里面,我们完全可以自定义快捷键来覆盖 vi 的默认快捷键, 达到 Emacs/Vi 的最佳融合,可以同时拥有 h j k lC-a, C-e, M-s, M-f, M-b 。 拷贝 7 行的文本, 在 evil 的 normal state 下,只需要

7 yy

而原生 Emacs 则需要

C-a C-SPC C-u 6 C-n C-e M-w

The best editor is neither Emacs nor Vim, it's Emacs with Vim binding! —-出处

Dired

Dired 是 directory editor 的缩写,是 Emacs 内置的插件,类似 macOS 上的文件管理器 Finder。 在 Dired 界面中,可以很方便的移动/删除/创建文件,更强大是可以直接批量修改文件,下图展示了如何批量把 test_foo_*.dat 重命名为 test_bar_*.dat ,操作文件就像操作文本一样流畅。(图片来源

https://img.alicdn.com/imgextra/i2/581166664/O1CN01QIzFM91z6A1TiEdB0_!!581166664.gif
dired 批量重命名文件

Ivy/Counsel/Swiper

Ivy/Counsel/Swiper 三件套是补全框架,它可以很方便的把当前操作的候选项以交互的方式展示出来,类似 VSCode 中的 Command Palette、 Intellj 中的 Double Shift

https://img.alicdn.com/imgextra/i1/581166664/O1CN01BnQ5pp1z6A1NIcJrL_!!581166664.png

虽然其他编辑器也有类似的功能,但是其功能不是局限,就是与其他功能分割,没有统一的体验。Emacs 则不同,再多的插件也能够有统一的体验, 这一点是非常影响用户体验的。这里通过 ivy-occur + wgrep + evil 批量修改多个文件中的内容来说明 ivy 套件的强大功能。

演示的目录有 1.txt 2.txt 两个文件,内容都是 hello world,这里批量修改为 hello emacs。

https://img.alicdn.com/imgextra/i1/581166664/O1CN01dS73W31z6A1Tk5UwK_!!581166664.gif

操作步骤:

  1. counsel-ag world 搜索当前目录下搜索含 world 的文件
  2. C-c C-o ivy-occur 打开 occur 界面
  3. C-x C-q ivy-wgrep-change-to-wgrep-mode 进入编辑模式
  4. :%s/world/emacs/g 借助 evil 修改内容
  5. C-c C-c wgrep-finish-edit 保存内容

当然,上面操作可以根据自己的习惯定义快捷键,上述五个步骤一气呵成,"挥一挥衣袖,不带走一片云彩"。

LSP

https://img.alicdn.com/imgextra/i1/581166664/O1CN01EeQOpy1z6A1U5lWzk_!!581166664.png

LSP 出现之前,没有统一的框架来解决不同语言的高亮、补全等现代 IDE 的基本功能,微软推出的 LSP 无疑已经成为业界标准, 无需使用正则这种既不准又耗自由的方式了。Emacs 中有两个扩展支持 LSP,分别是

  • Lsp-mode,主打开箱即用,默认提供传统 IDE 的所有体验
  • Eglot,主打小巧精致

目前我使用的是 lsp-mode,初学者可以都去尝试下,然后选择符合自己品味的那一款。

已经叛逃到 eglot 阵营,写 Rust 时还会用业界最快的 lsp-bridge

https://img.alicdn.com/imgextra/i4/581166664/O1CN01gjdchA1z6A1T3EaUP_!!581166664.png

More

除了上面介绍的几个扩展外,日常使用的扩展还有如下几个,当然还有很多有趣的扩展等着读者自己去发现。

  • company 补全框架,可配合 lsp-mode 使用 https://img.alicdn.com/imgextra/i3/581166664/O1CN01F3lxtC1z6A1RlueN7_!!581166664.png
  • multiple-cursors 多光标点操作 https://img.alicdn.com/imgextra/i4/581166664/O1CN01ceUOar1z6A1OW1MMp_!!581166664.gif
  • ace-jump-mode 根据字符,快速移动光标。下图为根据 p 进行快速跳转的示例 https://img.alicdn.com/imgextra/i1/581166664/O1CN019sHvUm1z6A1R9QtHn_!!581166664.gif
  • yasnippet 模板系统,通过定义代码片段的缩写来简化输入 https://img.alicdn.com/imgextra/i2/581166664/O1CN01lQVIpx1z6A1Wz6Th3_!!581166664.png
  • flycheck 语法实时检查 https://img.alicdn.com/imgextra/i4/581166664/O1CN01gL8IST1z6A1WZt3Dk_!!581166664.png
  • treemacs 文件目录树导航 https://img.alicdn.com/imgextra/i3/581166664/O1CN01oTtvbz1z6A1RykfJ8_!!581166664.png
  • projectile 项目工作空间管理 https://img.alicdn.com/imgextra/i3/581166664/O1CN01yHD9GD1z6A1Tmi66V_!!581166664.gif 上面图示展示了如何在一个项目中查找文件、在实现与测试中切换、切换不同的项目

总结

也许 Emacs 的普及程度远远比不上 VSCode,但这也并不算件坏事,比如那些伸手党们就不适合使用 Emacs,让他们进来只会拉低社区的整体水平; 而且 Emacs 是个开放的系统,会借鉴 VSCode 中优秀的设计,Emacs 与其他编辑器并不是互斥的。 之前论坛就有台湾的 Emacs Twitter 账户维护者“叛逃”到 VSCode 的讨论,这类具有争议话题毫无疑问会引起大家的关注, 但是别忘了 Emacs 自由的精神,符合自己的才是最好的,没必要一味沉迷在某个事物中,说到底,Emacs/VSCode 只是些工具而已, 解决实际问题才是最重要的,当然一个舒心的操作系统编辑器,会让这个枯燥的过程变得乐趣无穷。 最后,借 Mastering Emacs 中的一句话结束本文

Your patient mastery of Emacs is well-rewarded. I assure you.

参考资料

评论

欢迎读者通过邮件与我交流,也可以在 MastodonTwitter 上关注我。