Emacs 基础操作
Table of Contents
1. 文件操作
1.1. 打开文件
我从 Emacs 源码中拷贝了文件 emacs.c 作为练习,请<点击此处下载>到本地。
学习的第一个快捷键是 C-x f,用于打开指定的文件(“f”即“file”)。寻找文件的过程中,也能靠 Tab 键补全路径。
实际上,打开一个文件是将文件内容加载到了一个新的 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 过程:
我们亦能随意绑定函数给某组快捷键上。有时,你按下组合键后,却未达到你预期的结果,很可能因为这组键绑定给了其他函数!
两步助你查看快捷键绑定了哪个函数:
- C-h k(或 M-x describe-key)
- 按下要查看的快捷键
这方法你需要牢记,可能时不时会用上,比如你想把某个操作绑定在某组快捷键上,你得先知道这组快捷键操作是否已被占用。
另外,在实际操作中如果你觉得某组快捷键按起来太复杂、按的键又多、又记不住,那就用这个方法看它是调用了哪个函数,以后就直接通过 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-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 行:
默认情况下,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 了:
如上图,按下 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 了:
6. 查找和替换
从当前位置向下查找 | C-s |
从当前位置向上查找 | C-r |
这是最基本的文本查找操作。也可以通过 list-matching-lines 列出匹配到的所有行,然后 C-x o 切换到匹配结果的 buffer 中,按回车就可以跳转到相应的行中:
M-x list-matching-lines
操作演示:
list-matching-lines 强大之处在于它支持正则表达式。
两个 C-s/C-r 有关的实用小技巧:
- 如何查找当前光标指到的单词(类似 Vim 中 Ctrl+n):按下 C-s,然后按 C-w 选中要查找的单词;
- 查找最近查找过的内容:C-s C-s 或 C-r C-r。
如果要替换文本,可以用:
M-%