Emacs

Table of Contents

1 开始之前

我在大学(2009 年左右)的时候接触 Linux(Ubuntu),选择编辑器时感觉 Vim 入门很难,只是因为好奇就选择了 Emacs。大学期间和我的第一份工作主要的开发环境是 Windows,Emacs 虽然在私下里花了很多精力折腾,但正式使用 Emacs 作为开发环境是 2014 年以后,之后工作环境主要聚焦在 CentOS/Mac/Ubuntu 上,直到现在,Emacs 都是我的主力编辑器:

前端
JavasSript/HTML/CSS/LESS, React(jsx)
后端
C/C++, Python/Django, Go
日常
Shell,YAML,JSON,…
写文章
org-mode,Markdown

从 Emacs 22 到最新的 Emacs 26,Emacs 越来越好用了,常用的插件已经官方集成进去了,生态圈也越来越好了,有像 purcell 这样的牛人来领军配置,还有 spacemacs 这样集大成者开箱即用的配置。以前我写过 Emacs 简易教程,那个时候(2014年)还是主要使用 C/C++ 做游戏服务器开发,那份教程已经很久没有更新了,后面我将集中经历维护这份文档。

另外,我的配置文件在 _emacs.d。我的邮箱是 me@zhangjiee.com ,欢迎与我交流 Emacs 使用心得。

emacs-hello.png

2 基础

2.1 安装 Emacs

2.1.1 Ubuntu

直接使用系统的包管理工具安装的 Emacs 版本都很低,推荐使用源码安装,当前最新的版本是 26.3。源码编译安装即可。下面的 Ubuntu 18.04 上安装流程:

下载源码:http://mirrors.ustc.edu.cn/gnu/emacs/

apt install make
apt install gcc
apt-get install build-essential texinfo libx11-dev libxpm-dev libjpeg-dev libpng-dev libgif-dev libtiff-dev libgtk2.0-dev libncurses-dev
./autogen.sh
./configure
make -j2
sudo make install

打开 .bashrc 添加两行 alias:

alias em="env TERM=xterm-256color emacs -nw"
alias emacs="env TERM=xterm-256color emacs -nw"

注意 如果编译提示 configure: error: The following required libraries were not found: gnutls 需要安装 gnutls 开发包,使用 apt-cache search 'libgnutls.*-dev' 找到对应的包,然后安装 1

2.1.2 MacOS

brew install emacs

2.2 键位约定

Emacs 快捷键约定,新手可能不知道代表的键位,如下:

  • C- :Ctrl 键
  • M- :Meta 键(指的是键盘上的 Alt)
  • S- :Super 键,很少用
  • s- :Shift 键,很少用
  • DEL :键盘上的 Backspace 键,而不是实际的 Delete
  • SPC :空格键

Emacs 几乎所有的快捷键都是以 C- 开始的,也是 Emacs 令人诟病的一部分(伤小拇指,还好我的小拇指健在)。系统默认的绑定键都是 C-x 开始的,用户自定义的大多是 C-c 开始的。

每个初学者建议先阅读 Emacs 的 help-with-tutorialC-h t )。走一遍,基本上就可以流畅的操作 Emacs 了。

3 插件推荐

Emacs 有很多的 插件awesome-emacs 是按照分类整理的。下面列一下我正在使用的插件,以及一些使用心得。

插件名称 插件介绍 备注
Flycheck 强大的语法检查框架 需要编程语言的 lint 工具配合,比如 eslint、golint。编码必备插件
ace-window 多窗口切换 以前一直用 emacs-winum
avy 光标快速定位(按照字符、单词、行等) 以前用的是 ace-jump
company-mode 模块化补全插件(支持各种语言和后端) auto-compete 用了几年,后来切换到了 company,auto-complete 没有 company 精准。编码必备插件
expand-region 快速选中文本(单词、括号内内容、段落等) 比如我常用 M-s s 选中一个单词,用 M-s p 选中括号内内容,然后复制
highlight-symbol 高亮当前单词 在相同单词之间切换,官方使用 F3 绑定;我进行了重新绑定。 M- - 高亮当前单词
ivy 一般 ido+smex,ivy,helm 三者必须选一 helm 用了四五年,最近才切换到 ivy
lsp-mode LSP 的 Emacs 客户端 目前不是特别成熟
magit Git 版本管理 启动速度太慢了,不用了
multiple-cursors 多鼠标操作 Emacs Rocks! Episode 13: multiple-cursors
org-mode 最牛逼的插件,没有之一  
projectile 项目管理框架,在一个 Emacs 下管理多个项目 可以与 ivy 一起使用,counsel-projectile
rainbow-delimiters 彩虹括号  
yasnippet 模板系统,代码片段框架 要与 yasnippet-snippets 一起使用

语言的开发环境配置一直很恼人(很费时间),我记得以前刚配置 C/C++ 的开发环境时,折腾了一个月左右时间才找到一个相对比较满意的开发环境(折腾完之后使用起来可真爽啊): xcscope + etags + c++-mode

写 Python 的时候也折腾了长时间的缩进问题。 Go 就更不用说了···,Go 工具链很完整,但由于 Go 的版本升级很快,工具链根本跟不上,+gocode+ 已经迁移了三次地址了。

后来看到了 LSP(Language Server Protocol) 项目,感觉这个项目才是终极解法:插件化,C/S 模式。目前已经默认支持 Python 和 Go 了,虽然还是有许许多多的 Bug,但比起 2018 年我试的时候已经成熟太多了。有社区的驱动,发展很快。

4 日常工作

使用一个编码工具,无论是 Vim/Emacs,还是 Sublime/Atom/VSCode,甚至是 PyCharm/VS系列,除了生态(社区支持,插件是否完整)之外,最重要的是工作流。单纯快捷键差别在不同的编码环境下切换成本都不大,在我看来核心的成本在于养成的工作流(习惯)不容易改变,内心也会有一定的抵抗情绪。

4.1 项目

使用 Projectile 之后,包含 .svn .git 的项目,Emacs 会自动识别为一个项目,也可以手动添加一个空的 .projectile 文件到项目中。 counsel 本身也提供了很多很好用的快捷键。 Emacs 自带的快捷键就略过了。

快捷键 功能
C-c g 在当前的 git 文件中打开文件,很好用
C-c p b 切换 buffer,类似 ~C-x b~,只不过是针对当前项目的
C-c p b 在当前项目打开的 Buffer 中切换
C-c p f 在打开的项目中打开文件
C-c p i 清空 projectile 当前项目的缓存(有的时候文件被删掉了需要清理一下,不然会造成干扰)
C-c p p 切换项目
C-c p s g 当前项目下面下搜索,使用 grep 命令,不过我更喜欢用 ag => C-c p s s 或者 `C-c k`
C-x C-b 列出所有 Buffer
C-x b 在所有打开的 Buffer 中切换,Emacs 自带
C-x k 关闭当前 Buffer

4.2 多窗口

结合 avy 实现,默认的切换快捷键( C-x o )很鸡肋。

快捷键 功能
C-x 0 关闭当前窗口(并不是关闭 Buffer)
C-x 1 只保留当前窗口
C-x 2 水平分屏
C-x 3 垂直分屏
M-o 1/2/3/4... 切换窗口

4.3 日常编辑

快捷键 功能
C-c j 当前 git 中搜索
C-c k 当前目录下搜索(使用 ag)
C-s 全文搜索
F3 打开 Eshell
F4 插入当前时间
F6 显示当前文件路径
M-- 高亮当前单词, M-p/n 跳转到上一个和下一个
M-m 不断扩大区域的选中
M-s ; 跳转到单词
M-s P 选中括号内的内容
M-s f 选中当前函数
M-s j 快速跳转到行
M-s l 打开/关闭行号
M-s s 选中当前单词
M-y kill-ring
M-y 剪切板历史(很有用)

4.4 版本管理

在不使用任何插件的情况下(内置的 VC),svn 和 git 都支持:

快捷键 功能
C-x v = 文件 diff
C-x v l 当前文件提交记录
C-x v + git pull,svn up
C-x v P git push,svn push

Magit 是最出名的一个插件,也是功能最完善的:只要记得 C-x g 一个快捷键就行了,然后按 ? 引导操作即可。

5 FAQ

5.1 是否要使用 Evil ?

因人而异,如果之前你曾是 Vim 用户,建议你用 Evil。因为我一开始就用的是 Emacs,而不是 Vim,所以我一直用的是 Emacs 的键映射。

5.2 我可不可以用 Emacs 完成 XXX 能做的事情?

没有银弹!纵然 Emacs 可能「可以做任何事情」,比如收发邮件、GTD、阅读 pdf、打开图片等等。然而我以为用合适的工具做合适的事情才是重要的.

5.3 使用 GUI 版本还是 Terminal ?

这个分歧很大,使用 GUI 可以让你很完备的使用 Emacs,不用担心快捷键冲突,具有更好的编辑效果,但是经常会有编码问题,如果使用终端的话,只要设置终端的编码就可以了;而且汉字的字体设置也很繁琐(我使用中英文等宽的 WenQuanYi Zen Hei Mono 解决了这个问题)。

使用终端的问题是快捷键很大概率冲突,因为 Emacs 几乎占了所有的快捷键。比如我会使用 M + 1/2/3/.. 切换 Emacs 中的窗口,而这个往往是很多终端切换页签的快捷键(Ubuntu 和 Windows 下的 Xshell 都是如此)。要自己重新设置快捷键。

不管是使用 GUI 还是 Terminal,全凭个人喜好。我之前一直用终端,最近(2019-06-04)切换到了 GUI。

2019-06-15 10:03:35

在尝试了两周的 GUI 之后,最终还是切换到了 Terminal。主要原因是:

  • 大部分编码等宽字体都不是中英文等宽的,比如我最喜爱的 Source Code Pro~, 你看到的终端等宽大多是因为终端帮你设置了字体的高度和宽度。 ~Ubuntu monoM +1 是中英文等宽的,但不是等高的,编辑的时候跳跃让人很不爽。文泉驿是一个绝对的等宽和等高字体,但是 Mac 下的字体实在太丑了~
  • 以前我最大的快捷键冲突是切换页签的 M - 1/2/3/... ,最近发现了 ace-window ,使用 M - o 1/2/3 切换页签,解决了这个问题。
  • 最后一个问题是工作流,因为工作性质的原因,不管是做服务器开发还是前端开发,都是需要实时的跟终端交互,GUI 和 终端切换,没有终端自身两个页签切换来的流畅。

对 GUI 最大的怀念是可以无缝的跟系统的剪切板融合,比如在别的地方 Ctrl + c 复制的内容,可以在 Emacs 中 M-y 粘贴过来,反之亦然。

2019-06-24 10:29:43

Ubuntu 下文泉驿是 OK 的,还不错,MacOS 有点丑。习惯了之后还有点离不开 GUI(快捷键真舒服啊),准备用终端写代码,GUI 写文档(文章),准备适应一段时间。

2019-07-08 20:57:06

Ubuntu GUI 无法切换搜狗输入法,与默认的 Mark 快捷键冲突,解决办法是修改 locale, /etc/default/locale 中添加 LC_CTYPE=zh_CN.UTF-8 ,然后重启即可(中文桌面情况下是没有这个问题的)。

2019-11-17 16:18:19

试了一下 Microsoft YaHei Mono,发现效果要比文泉驿要好很多,中文还挺好看。决定终端和GUI都使用一段时间。

5.4 如何记忆 Emacs 快捷键?

Emacs 的快捷键非常多,但是除了基本的编辑操作快捷键之外(就是上面描述的内容)并不需要刻意的记忆,只要你掌握了两点:

  1. 在 Emacs 中所有的快捷键对应的都是一个 Lisp 函数,快捷键忘了的话,可以使用 M-x 调用函数来实现相应的功能(如果使用了 helm 或者 ivy,绑定的快捷键会自动显示出来)
  2. 查看当前 mode 的所有快捷键可以使用 C-h m 寻求帮助,正如第一条所述: C-h m 对应的函数是 describe-mode

另外,Emacs 有个 emacs-which-key 的插件,有快捷键提示。比如,在 Markdown 模式下,快捷键前缀都是 C-c C-c 但是后面你可能忘了,当你按下 C-c C-c 时,它会这样提示你:

emacs-wiki-which-key-0.png

5.5 如何去掉空白字符

  • M-\ :删除光标周围的空格和 tab
  • M-<SPC> :删除光标周围的空格和 tab,但是保留一个

另外,我会给文件保存(~C-x s~)时,添加一个 hook:

(setq show-trailing-whitespace t)
(add-hook 'before-save-hook 'delete-trailing-whitespace)

这样在每次保存文件的时候,会自动删掉每行或者段落后面多于的空白字符和空行。

5.6 包安装问题

因为某些原因官方的 elpa 源无法使用怎么办? 如果没有 FQ 的环境,使用清华大学提供了 elpa 镜像源:https://mirror.tuna.tsinghua.edu.cn/help/elpa/

包依赖问题导致安装失败,函数不存在等问题,怎么解决? 这种情况一般是因为依赖的包版本有问题,当安装时存在多个源时,不管源之间的同步速度有多快,总会有版本滞后的问题。所以,安装时尽可能只选一个同一个源,比如:

(setq package-archives
      '(
        ("gnu" . "https://elpa.gnu.org/packages/")
        ("melpa" . "https://melpa.org/packages/")
        ("melpa-stable" . "https://stable.melpa.org/packages/")
        ))

如果发现类似问题,可以注释掉其中的两个,只保留一个,比如 melpta-stable ,一般能解决此问题。之前在安装 helm 的时候经常遇到依赖有问题,刚才(2019-05-31)安装 ivy 也遇到了类似的问题,给 github ivy 提了一个 issue 才解决了这个问题(社区的力量真强大)。

如果有 FQ 环境,也可以在配置文件中设置 proxy :

(setq url-proxy-services
      '(
        ("http" . "127.0.0.1:1088")
        ("https" . "127.0.0.1:1088")
        ))

5.7 Emacs 启动速度太慢怎么办?

M-x emacs-init-time 可以查看 Emacs 启动耗费时间。

多一个插件都会增加启动成本,不信你 emacs -Q 试试,所以要尽可能的减少插件。你可以使用 keyfreq 来查看你常用的快捷键有哪些。筛选出不常用的插件给干掉,这是解决启动速度慢的根本办法。

如何定位插件耗时?

定位之后如何优化?

elisp 比较熟的有自己的办法优化,当然我不熟。我的解决办法是:

  • 基本上不用的插件、UI 上炫酷的插件都干掉
  • 使用 use-package ,use-package 并不是包管理工具,只是一个宏,用来配置和加载包。 你可以设置延迟加载包,以提高第一次打开的速度。还有一个好处是,使用 use-package 组织包更清晰,对于 use-package 我还处于研究阶段,粗暴的使用 :init:config 配置,实际上有很多优雅的方法

5.8 如何软重启 Emacs?

所谓软重启指的是通过命令重新加载配置文件:

  • eval-buffer 会执行当前 buffer 的 lisp,切换到 init.el 文件,然后执行 eval-buffer
  • *scratch* 中输入 (load-file user-init-file) ,然后选中 C-x C-e

两个的原理差不多, C-x C-e 是执行选中的内容, eval-buffer 是执行当前 buffer。

5.9 如何更新所有的依赖包?

  1. M-x list-package
  2. U 标记所有不是最新的包
  3. x 执行更新

5.10 行逆序排序

  1. M-x sort-lines
  2. M-x reverse-region

5.11 如何将 markdown 转成 org-mode

.org 文件可以很轻松的通过 org-md-export-to-markdown 生成 .md 文件。 .md 文件可以借助 Pandoc 生成 .org 2

pandoc -f markdown -t org -o test.org test.md

6 Resource

6.1 Blog/Site/Tutorial

6.2 Article

6.3 Configure reference

社区有很多优秀的配置(我的配置大多都是到处「摘抄」的):

6.4 Org-mode theme

Footnotes:

Date: 2018-06-22 23:24:56

Author: JerryZhang