Emacs 基础操作

Table of Contents

1 文件操作

1.1 打开文件

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

我们学习第一个快捷键是 C-x f,它用来打开指定文件(“f”的表示英文单词“file”),在输入文件路径过程中,可以按 Tab 键补全路径。

C-x_f.gif

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

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

当你按下一组组合键时,它只是调用了绑定在这组键上的 Emacs Lisp 函数(甚至包括上下左右移动操作都是)。比如,当你按下 C-x f时,它实际上调用了 file-file 这个函数而已。

如果你想手动执行这个函数,按下 M-x,然后输入函数名即可。M-x 类似 Vim 中“执行命令”。当按下 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 光标移动

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

字符间移动:

快捷键 动作 Shell对应功能
C-f 向后移一个字符 相同
C-b 向前移一个字符 相同
M-f 向后移一个单词 相同
M-b 向前移一个单词 相同

行间移动:

快捷键 动作 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。

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-%