Emacs 基础操作

Table of Contents

1. 文件操作

1.1. 打开文件

我从 Emacs 源码中拷贝了文件 emacs.c 作为练习,请<点击此处下载>到本地。

学习的第一个快捷键是 C-x f,用于打开指定的文件(“f”即“file”)。寻找文件的过程中,也能靠 Tab 键补全路径。

C-x_f.gif

实际上,打开一个文件是将文件内容加载到了一个新的 buffer 中。

1.2. 当按下快捷键时,Emacs 干了什么

当按下一组键时,Emacs 只不过调用了绑定在这组键上的 Emacs Lisp 函数(连上下左右移动也是)。按下 C-x f,调用了 file-file 函数而已。

若想手动执行这个函数,按下 M-x,输入函数名,回车。当按下 M-x 后,Mini buffer 会提示输入,要么输入内容,要么按 C-g 取消操作。

重要知识点:

M-x 执行命令
C-g 中断操作

下面图演示了执行 find-file 过程:

M-x_find_file.gif

我们亦能随意绑定函数给某组快捷键上。有时,你按下组合键后,却未达到你预期的结果,很可能因为这组键绑定给了其他函数!

两步助你查看快捷键绑定了哪个函数:

  1. C-h k(或 M-x describe-key)
  2. 按下要查看的快捷键

这方法你需要牢记,可能时不时会用上,比如你想把某个操作绑定在某组快捷键上,你得先知道这组快捷键操作是否已被占用。

另外,在实际操作中如果你觉得某组快捷键按起来太复杂、按的键又多、又记不住,那就用这个方法看它是调用了哪个函数,以后就直接通过 M-x 调用这个函数好了。至少函数名比快捷键要好记得多。其实我也没记多少快捷键,很多复杂功能还是通过 M-x 调用函数来解决的。

1.3. 只读模式

用惯了 Vim 的同学一定习惯了 Vim 的输入模式和命令模式,当处于命令模式时,你无论是按哪个键移动光标,都不会担心误修改文本内容,而 Emacs 没有区分输入模式和命令模式,比如你想按 C-f 移动光标时,偏偏 Ctrl 键没按好,却在当前 buffer 中插入了字母“f”。这种情况在当年我才用 Emacs 时会时常发生的,编译代码时才发现莫名奇妙多了一些字母。为了安全起见,一般浏览文本时,你可以先按 C-x C-q,让 buffer 进入只读模式后再来操作,这样就不会修改 buffer 内容了。等你需要修改文本时,也按 C-x C-q 退出只读模式。

顺带说一句:作为一名养了两只猫的猫奴,深感只读模式的好处,经常一不留神就让两只猫从键盘上“开火车”路过。

1.4. 保存文件

修改文件后,按 C-x s 就可以把Buffer的内容写入到文件。如果想要“另存为”,按 C-x C-w,输入新的文件路径即可。

如果编辑了多个文件,想同时保存:

M-x save-some-buffers
或
C-x s

1.5. 关闭文件

前面说过,“打开文件”实质上是把文件读到 buffer 中。关闭文件也就是关闭 buffer,使用 C-x k。操作 buffer 我们后面会讲到,你先记得 C-x k 即可。

2. 光标移动

对于初学者,先掌握下面几个快捷键基本就够用了。值得注意的是,GNU/Linux 的 shell 一般默认快捷键就是 Emacs 风格,所以一些移动操作同样可以在 shell 打命令时中使用。

字符间移动:

快捷键 动作 Shell对应功能
C-f 向后移一个字符 相同
C-b 向前移一个字符 相同
M-f 向后移一个单词 相同
M-b 向前移一个单词 相同
M-a 移到句子开头  
M-e 移到句子结尾  
C-{ 移到段落开头  
C-} 移到段落结尾  

行间移动:

快捷键 动作 shell对应功能
C-a 移动到行首 相同
C-e 移动到行尾 相同
C-n 下一行 下一条历史命令
C-p 上一行 上一条历史命令
M-r 光标移动到当前屏幕中间行行首

当然,上下左右的移动也可以用方向键,只是这太低效了。

3. 删除

快捷键 动作
M-d 删除后面一个字符
M-Backspace 移除光标前一个词
退格 删除前面一个字符
C-k 从当前光标之后一个字符一直删到行尾(不包括换行符)

删除一整行该怎么做呢?答案是:C-a C-k,首先 C-a 移动光标到行首,然后 C-k 删除后续所有字符(除了换行符)。

如何把下一行合并到上一行尾,比方说下面这段代码:

1: if (!NILP (Vinvocation_directory)
2:     && NILP (Ffile_name_absolute_p (Vinvocation_directory)))

我想把第二行的内容合并到第一行中,可以把光标移到第二行任意位置,再按 M-^(Alt+Shift+6),就成这样了:

if (!NILP (Vinvocation_directory) && NILP (Ffile_name_absolute_p (Vinvocation_directory)))

M-^_c.gif

如果想删除匹配到的行,可执行:

M-x delete-matching-lines

有个和 delete-matching-lines 相反功能的命令:delete-non-matching-lines;删除正则未匹配到的行还可以用 keep-lines。

如果想撤销删除,用 C-_(同时按下 Ctrl+Shift+_)键,或使用 M-x undo,撤销、重做的快捷键整理如下:

功能 快捷键
C-x u 或 C-_ 撤销
C-g C-x u 重做

4. 跳转和翻页

4.1. 跳到指定行号

按下 M-g g,输入行号即可

如下图,跳转到 emacs.c 第 100 行:

M-g-g-10.gif

默认情况下,Emacs 是不显示行号的,要显示行号,得:M-x linum-mode。

还可以在启动 Emacs 时加”+“参数,让 Emacs 启动后自动跳转到指定那行上。比如我想知道 emacs.c 中 main 函数的位置,于是用 grep 命令搜索:

$ grep 'main (' emacs.c -n
639:/* malloc can be invoked even before main (e.g. by the dynamic
699:main (int argc, char **argv)

找到 emacs.c 第 699 行定义了 main 函数,然后让 Emacs 启动后直接定位到 699 行:

$ emacs +699 emacs.c

4.2. 翻页

4.2.1. 跳转到 buffer 起始和结束位置

快捷键 动作
M-< 跳到 buffer 起始
M-> 跳到 buffer 结尾

4.2.2. 上下翻页

快捷键 动作
C-v 向下翻页
M-v 向上翻页

5. buffer

Emacs 的文本编辑区域就是 buffer,可以有多个 buffer 同时存在,每个 buffer 可以对应一个文件,也可以不对应文件。每个 buffer 都有自己独一无二的名字(如果 buffer 对应文件,buffer 的名字就是文件名)。

比如默认的 Emacs 启动后,会有三个 Buffer“*GNU Emacs*”、“*scratch*”和“*Messages*”。我们按下 C-x C-b,就可以列出当前有哪些 buffer 了:

list-buffer.png

如上图,按下 C-x C-b 后 Emacs 新建了一个名叫“*Buffer List*”的 buffer,这个 buffer 中列出了当前所有 buffer。

在 buffer 之上,还有个术语——window,buffer 只是一个文本区域,负责显示 buffer 的是 window。上图中同时显示了两个 buffer,实际上就是 Emacs 切割了两个 window,每个 window 里显示了一个 buffer。在Emacs里,一个 window 同时只显示一个 buffer。

现在光标仍停留显示“*GNU Emacs*”这个 buffer 的 window 里,我想把光标移到显示“Buffer List”的 window 里,只需要按 C-x o 切换 window 就可以了。

在“Buffer List”里,选中你想切换的 buffer 摁下回车,当前的 window 就显示你选中的 buffer 了:

C-x_C-b_RET.gif

6. 查找和替换

从当前位置向下查找 C-s
从当前位置向上查找 C-r

这是最基本的文本查找操作。也可以通过 list-matching-lines 列出匹配到的所有行,然后 C-x o 切换到匹配结果的 buffer 中,按回车就可以跳转到相应的行中:

M-x list-matching-lines

操作演示:

M-x_list-matching-lines.gif

list-matching-lines 强大之处在于它支持正则表达式。

两个 C-s/C-r 有关的实用小技巧:

  1. 如何查找当前光标指到的单词(类似 Vim 中 Ctrl+n):按下 C-s,然后按 C-w 选中要查找的单词;
  2. 查找最近查找过的内容:C-s C-s 或 C-r C-r。

如果要替换文本,可以用:

M-%