Emacs

Table of Contents

1. Emacs 简介

Emacs is the extensible, customizable, self-documenting real-time display editor.

GNU Emacs Manual: http://www.gnu.org/software/emacs/manual/html_node/emacs/index.html
GNU Emacs Reference Cards: https://www.gnu.org/software/emacs/refcards/
Richard Stallman 于 1981 年发表的 Emacs 介绍论文:http://www.gnu.org/software/emacs/emacs-paper.html

2. Emacs 基本用法

2.1. 帮助系统

Emacs 帮助文档相关快捷键如表 1, 2, 3, 4 所示。

Table 1: emacs 中查看快捷键的对应功能
hotkey function description
C-h c key describe-key-briefly 简要地查看热键的功能描述
C-h k key describe-key 查看热键的详细功能描述
C-h K key Info-goto-emacs-key-command-node 查看热键对应的文档
Table 2: emacs 中查看命令(或变量)对应的快捷键(或文档)
hotkey function description
C-h w command where-is 查看命令绑定的热键
C-h f command describe-function 查看命令对应的文档
C-h v variable describe-variable 查看变量的当前值以及对应的文档

说明:要设置变量的值,执行 M-x set-variable 即可。

Table 3: emacs 中模糊查找帮助文档(可用正则)
hotkey function description
C-h a apropos-command 查找相关命令
(none) apropos 查找相关的函数或变量
(none) apropos-variable 查找相关的用户设置变量
C-h d apropos-documentation 查找相关的文档
Table 4: emacs 中其它帮助文档
hotkey function description
C-h m describe-mode 查看编辑模式
C-h b describe-bindings 查看绑定热键
C-h ? help-for-help 查看帮助列表
C-h n view-emacs-news 浏览 Emacs 新闻
C-h t help-with-tutorial 查看 Emacs 教程
C-h r info-emacs-manual 以 info mode 方式阅读 Emacs 手册
C-h C-c describe-copying 查看许可证信息

说明:执行 M-x locate refcard 可查找 emacs 自带的 refcard。

2.2. 文件基本操作

Emacs 文件基本操作相关快捷键如表 5 所示。

Table 5: emacs 中文件基本操作
hotkey function description
C-x C-f find-file 打开文件
C-x C-r find-file-read-only 只读方式打开文件
C-x C-v find-alternate-file 关闭当前文件,打开另一文件
C-x 4 f find-file-other-window 在另一个 window 中打开文件
C-x 5 f find-file-other-frame 在另一个 frame 中打开文件
C-x C-s save-buffer 保存当前文件
C-x s save-some-buffers 保存所有文件
C-x C-w write-file 另存文件
(none) write-region 把当前选择区域另存到新文件
C-x C-q toggle-read-only 去掉或加上只读属性
C-x i insert-file 插入另一文件到当前文件
C-x C-c save-buffers-kill-terminal 退出 emacs

2.2.1. 打开远程机器文件

按下 C-x C-f 后,输入 /ssh:yourUser@yourHost:/path/to/file 即可打开远程机器上对应的文件。如果你需要频繁操作远程机器上某个目录下的多个文件,可以按下 M-x dired 后,输入 /ssh:yourUser@yourHost:/path/to/dir

2.3. 移动光标

Emacs 移动光标相关快捷键如表 6 所示。

Table 6: emacs 中移动光标
hotkey function description
C-f forward-char 向前一个字符
C-b backward-char 向后一个字符
C-p previous-line 上移一行
C-n next-line 下移一行
M-f forward-word 向前一个单词(Ctrl+左箭头)
M-b backward-word 向后一个单词(Ctrl+右箭头)
C-a beginning-of-line 移到行首
C-e end-of-line 移到行尾
M-e forward-sentence 移到句首
M-a backward-sentence 移到句尾
M-} forward-paragraph 下移一段
M-{ backward-paragraph 上移一段
C-v scroll-up 下移一屏
M-v scroll-down 上移一屏
C-x ] forward-page 下移一页(没有分页符,会移到文档头)
C-x [ backward-page 上移一页(没有分页符,会移到文档尾)
M-< beginning-of-buffer 移到文档头
M-> end-of-buffer 移到文档尾
M-g g n goto-line 移到第 n 行
(none) goto-char 移到第 n 个字符
C-l recenter 将当前位置放到页面中间(这个实用)
C-M-n forward-list 往前跳转到匹配括号(当前光标应位于“闭括号”后面)
C-M-p backward-list 往后跳转到匹配括号(当前光标应位于“开括号”前面)
C-M-f forward-sexp 往前跳一个 balanced expression
C-M-b backward-sexp 往后跳一个 balanced expression
C-u C-@   回到光标的前一个位置(阅读代码时有用)

2.4. 删除操作

Emacs 删除操作相关快捷键如表 7, 8 所示。

Table 7: emacs 中删除操作
hotkey function description
C-d delete-char 删除光标处字符(相当于 Delete)
Backspace delete-backward-char 删除光标前字符
M-d kill-word 删除光标处一个单词
M-Backspace backward-kill-word 删除光标前的一个单词
C-k kill-line 删除一行(仅从光标处到行尾)
C-S-Backspace kill-whole-line 删除整行(快捷键在 X 下有效,终端下一般无效)
M-k kill-sentence 删除光标起一句
C-x Backspace backward-kill-sentence 删除光标前一句
(none) kill-paragraph 删除光标起段落
(none) backward-kill-paragraph 删除光标前段落
M-z char zap-to-char 删除到字符 char 为止
Table 8: emacs 中删除多余的空格
hotkey function description
M-\ delete-horizontal-space 删除光标前后的所有空格和 tab 字符
M-SPC just-one-space 删除光标前后的所有空格和 tab 字符,但保留一个
C-x C-o delete-blank-lines 删除多余的空白行,仅保留一行
M-^ delete-indentation 合并当前行到上行(两行内容间留且仅留 1 个空格)

2.4.1. 删除重复行(调用外部工具 uniq 对 region 进行操作)

在 shell 中用 uniq 轻松完成。如何在 emacs 中完成呢?
首先选择整个 buffer,再执行下面命令:

C-u M-| uniq file.txt RET

参考:Emacs Manual, 31.3 Running Shell Commands from Emacs

2.4.1.1. delete-duplicate-lines

Emacs 24.4 中引入了函数 delete-duplicate-lines ,可以删除选择区域中的重复行。

2.4.2. 删除空行或匹配某正则表达式的行(flush-lines)

2.4.3. 只留下匹配某正则表达式的行(keep-lines)

参考: C-h f keep-lines

2.5. 搜索和替换

2.5.1. 增量搜索

Emacs 增量搜索相关快捷键如表 9 所示。

Table 9: emacs 中字符串和正则的增量搜索
hotkey function description
C-s isearch-forward 向前增量字符串搜索
C-r iserach-backward 向后增量字符串搜索
M-s w isearch-forward-word 向前增量单词搜索
Esc C-s isearch-forward-regexp 向前增量正则搜索
Esc C-r isearch-backward-regexp 向后增量正则搜索

说明:

  • 增量搜索中“增量”的意思是边输入边查找。
  • 要移动到下一个/上一个匹配处,多次按 C-s 或 C-r 即可。
  • C-s C-w,会把当前光标下的单词自动输入到想要查找字符中。如果光标下的字符为 abc_def_123,按一次 C-w 只会输入 abc,可以连续按 C-w 把后面的部分全部补全!
  • C-s M-c,临时改变本次搜索是否大小写敏感。

2.5.2. 替换

Emacs 替换字符串相关快捷键如表 10 所示。

Table 10: emacs 中字符串和正则的替换
hotkey function description
M-% query-replace 询问字符串替换
(none) replace-string 直接字符串替换
C-M-% query-replace-regexp 询问正则替换(可能有快捷键冲突)
(none) replace-regexp 直接正则替换

2.5.3. 用 grep 搜索

M-x grep
M-x lgrep(仅在本层目录中搜索,不去子目录搜索)
M-x grep-find
M-x find-grep(和 grep-find 一样)
M-x rgrep
M-x zrgrep
M-x kill-grep

比如:

M-x grep
grep -nH -e foo *.el | grep bar | grep toto

参考:
http://www.gnu.org/software/emacs/manual/html_node/emacs/Grep-Searching.html

2.5.3.1. 设置 rgrep 大小写不敏感

我们知道,用 rgrep 查找关键字时,如果关键字都为小写,则大小写不敏感,否则大小写敏感。
如何让 rgrep 大不写不敏感?加 C-u 前缀,这样执行命令前有机会编辑它,增加-i 选项即可。
C-u M-x rgrep

2.5.3.2. 跳转到下一个匹配处

C-x `, 它会执行 next-error.

2.5.4. 显示匹配正则表达式的所有行(occur)

list-matching-lines 是 occur 的别名,可列出当前 buffer 中所有匹配指定正则表达式的行。

如何快速查找当前 buffer 中光标下的单词?
在 isearch 状态中(即输入 C-s 且输入要查找的关键后)执行: M-s o ,即可运行 occur 查找相应单词,非常方便!

参考:
http://blog.zhengdong.me/2011/03/31/emacs-features/

2.6. 正则表达式

emacs 正则表达式中的元字符如下:

  .        any character (but newline)
  *        previous character or group, repeated 0 or more time
  +        previous character or group, repeated 1 or more time
  ?        previous character or group, repeated 0 or 1 time
  ^        start of line
  $        end of line
  [...]    any character between brackets
  [^..]    any character not in the brackets
  [a-z]    any character between a and z
  \        prevents interpretation of following special char
  \|       or
  \w       word constituent
  \b       word boundary
  \sc      character with c syntax (e.g. \s- for whitespace char)
  \( \)    start\end of group
  \< \>    start\end of word
  \_< \_>  start\end of symbol
  \` \'    start\end of buffer\string
  \1       string matched by the first group
  \n       string matched by the nth group
  \{3\}    previous character or group, repeated 3 times
  \{3,\}   previous character or group, repeated 3 or more times
  \{3,6\}  previous character or group, repeated 3 to 6 times
  \=       match succeeds if it is located at point

实例 1: 查找 6 个连续数字
emacs 中的正则表达式,不支持 \d 表示数字,要查询数字请用 [0-9]

Esc C-s [0-9]\{6\}

实例 2: 查找 A 和 B 之间有一个或多个空格
emacs 中,空格对应的正则为 \s- ,注意 \s 后还有个连字符。

Esc C-s A\s-+B

参考:http://www.emacswiki.org/emacs/RegularExpression

2.7. 转换大小写

Emacs 中转换大小写相关快捷键如表 11 所示。

Table 11: emacs 中转换大小写
hotkey function description
M-u upcase-word 光标下单词转大写
M-l downcase-word 光标下单词转小写
M-c capitalize-word 光标下单词第一个字符转大写
C-x C-u upcase-region region 中单词转大写
C-x C-l downcase-region region 中单词转小写

参考:GNU Emacs Manual, 22.6 Case Conversion Commands

2.8. 限制行宽(fill)

The maximum line width for filling is specified by the buffer-local variable fill-column. The default value is 70. The easiest way to set fill-column in the current buffer is to use the command C-x f (set-fill-column).

默认 fill-column 太窄,要设为 80,可把下面这行加入~/.emacs.d/init.el 中:

(setq-default fill-column 80)

要对一个 region 限制行宽,可以执行下面命令:

M-x fill-region

要自动限制行宽,可以打开 auto-fill-mode。

M-x auto-fill-mode

参考:http://www.gnu.org/software/emacs/manual/html_node/emacs/Filling.html

2.9. undo 和 redo

Emacs 中 undo 和 redo 相关快捷键如表 12 所示。

Table 12: emacs 中 undo
hotkey function description
C-/ undo 撤消
C-_ undo 撤消
C-x u undo 撤消
(none) revert-buffer 撤消所有未保存的修改

emacs 中没有 redo,要想实现 redo,可以在 undo 前执行下 C-g

2.10. 交换前后文本

Emacs 中交换前后文本相关快捷键如表 13 所示。

Table 13: emacs 中交换前后文本
hotkey function description
C-t transpose-chars Transpose two characters
M-t transpose-words Transpose two words
C-x C-t transpose-lines Transpose two lines

2.11. 多窗口操作

Emacs 中多窗口操作相关快捷键如表 14 所示。

Table 14: emacs 中多窗口操作
hotkey function description
C-x 2 split-window-vertically 垂直拆分窗口
C-x 3 split-window-horizontally 水平拆分窗口
C-x 0 delete-window 关闭当前窗口
C-x 1 delete-other-windows 关闭其它窗口
C-x o other-window 切换到下一个窗口
C-M-v scroll-other-window 滚动下一个窗口
C-x 4 0 kill-buffer-and-window 关闭当前窗口和缓冲
C-x 4 b switch-to-buffer-other-window 在另一个窗口打开缓冲
C-x 4 C-o display-buffer 在另一个窗口打开缓冲,但不选中
C-x 4 f find-file-other-window 在另一个窗口打开文件
C-x 4 d dired-other-window 在另一个窗口打开文件夹
C-x 4 m mail-other-window 在另一个窗口写邮件
C-x 4 r find-file-read-only-other-window 在另一个窗口以只读方式打开文件
C-x ^ enlarge-window 增高当前窗口
C-x { shrink-window-horizontally 将当前窗口变窄
C-x } enlarge-window-horizontally 将当前窗口变宽
C-x - shrink-window-if-larger-than-buffer 如果窗口比缓冲大就缩小
C-x + balance-windows 所有窗口一样高

2.12. 键盘宏

Emacs 中键盘宏相关快捷键如表 15 所示。

Table 15: 键盘宏基本快捷键
hotkey function description
F3 kmacro-start-macro-or-insert-counter 开始记录,如果正在记录则插入宏计数器值。
F4 kmacro-end-or-call-macro 如果正在记录则停止,否则播放最后一个宏。

注:这种方法记录的只是一个临时的,没有名字的宏,仅当前 session 有效。

2.12.1. 命名键盘宏

给键盘宏命名: C-x C-k n ,其对应的命令是 kmacro-name-last-macro

宏有名字后,就可以用 M-x macro-name 执行宏了。

把已命名的键盘宏写入到文件:
先打开文件(如~/.emacs.d/init.el,这样每次打开 emacs,这个键盘宏都有效), M-x insert-kbd-macro ,输入宏名即可。会在打开的文件中产生一个 fset。

将键盘宏 macro-name 绑定到快捷键(如 F5):

(global-set-key (kbd "<f5>") 'macro-name)

2.12.2. 键盘宏的计数器

每个键盘宏都关联着一个计数器。键盘宏每执行一次计数器就会增加 1。
当你正在定义一个宏时,再次按下 F3 就能插入计数器的值。

实例:插入下列连接的数字到当前 buffer
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

第一步:定义下面键盘宏。
F3 F3 SPC F4
说明:第一个 F3 表示开始定义键盘宏,第二个 F3 表示插入宏计数器。

第二步:执行上面键盘宏 19 遍。
Esc 19 F4

2.13. registers

Emacs 中使用 registers 的相关快捷键如表 16 所示。

Table 16: emacs 中使用 registers
object hotkey(store) hotkey(retrieve) notes
buffer/position C-x r <SPC> R C-x r j R save buffer/position in register R, and jump back to it
rectangle C-x r r R C-x r i R save rectangle into register, and paste it
window C-x r w R C-x r j R save window configuration in register R, and jump back to it
frame C-x r f R C-x r j R save frame configuration in registerR, and jump back to it

注:表中的字母 R 为 register 名字,可以修改为其它任意单个字母(大小写敏感)或数字。

查看 register 内容(以 register R 为例):

M-x view-register RET R

注:好像无法一次查看所有 registers 的内容。

2.14. bookmarks

Emacs 中 bookmarks 相关快捷键如表 17 所示。

Table 17: emacs 中使用 bookmarks
hotkey function description
C-x r m bookmart-set 对当前文件当前位置设置 bookmark
C-x r b NAME bookmark-jump 跳转到名为 NAME 的 bookmark 处
C-x r l list-bookmarks 列出所有的 bookmark
(none) bookmark-delete 删除某个 bookmark
(none) bookmark-save 保存 bookmark 到~/.emacs.bmk(默认位置),退出 emacs 时会自动保存
(none) bookmark-write 保存 bookmark 到文件(可任意指定文件名)
(none) bookmark-load 从文件中加载 bookmark

Emacs 中 bookmarks 和 registers 差不多,也可以保存光标的位置信息,但 registers 不是持久的,而 bookmarks 是持久的,关闭 emacs session 再启动 Emacs 时,bookmarks 仍然存在。

2.15. rectangles

首先选择一个矩形:用 C-@ 设一个 mark,移动光标到另一点(注意,这不会只高亮一个矩阵块)。
然后用表 18 所示命令(都以 C-x r 开头)来进行矩形操作。

Table 18: emacs 中操作 rectangles
hotkey function description
C-x r k kill-rectangle 剪切一个矩形块
C-x r d delete-rectangle 删除一个矩形块
C-x r y yank-rectangle 粘贴一个矩形块
C-x r o open-rectangle 插入一个矩形块
C-x r c clear-rectangle 清除一个矩形块(使其变成空白)
C-x r t string-rectangle 在选定区域的所有列前插入一个相同的字符串(会提示你输入)
C-x <SPC> rectangle-mark-mode 无需提前选择矩形。Emacs 24.4 中引入

参考:http://www.gnu.org/software/emacs/manual/html_node/emacs/Rectangles.html#Rectangles

2.16. 版本控制

Emacs 中版本控制相关快捷键如表 19 所示。

Table 19: emacs 中版本控制
hotkey function description
C-x v d vc-directory 列出目录下修改过的文件
C-x v l vc-print-log 显示改动历史记录
C-x v h vc-insert-headers 插入版本控制信息
C-x v v vc-next-action 进入提交改动状态
C-x v = vc-diff 显示改动
C-x v i vc-register 加入文件到版本控制中
C-x v r vc-retrieve-snapshot 取某一版本文件
C-x v u vc-revert-buffer 取消对当前文件的更改
C-x v ~ vc-revision-other-window 在另外一个窗口中打开文件的指定版本(非常实用)

小技巧:当用 C-x v = 在“*vc-diff*”窗口中显示所做的改动后,可以用 C-c C-a, 或 M-x diff-apply-hunk 取消/应用当前光标处的部分改动。

3. Emacs 高级用法

3.1. 画 ASCII diagrams

Artist mode, toggled by M-x artist-mode, lets you draw lines, squares, rectangles and poly-lines, ellipses, and circles with your mouse and/or keyboard. It is extremely useful when inserting text diagrams or figures in your source comments.

用 artist-mode 在 emacs 中可轻松画出下面图例:

    +---------+
    |         |                        +--------------+
    |   NFS   |--+                     |              |
    |         |  |                 +-->|   CacheFS    |
    +---------+  |   +----------+  |   |  /dev/hda5   |
                 |   |          |  |   +--------------+
    +---------+  +-->|          |  |
    |         |      |          |--+
    |   AFS   |----->| FS-Cache |
    |         |      |          |--+
    +---------+  +-->|          |  |
                 |   |          |  |   +--------------+
    +---------+  |   +----------+  |   |              |
    |         |  |                 +-->|  CacheFiles  |
    |  ISOFS  |--+                     |  /var/cache  |
    |         |                        +--------------+
    +---------+

Reference:
http://www.cinsk.org/emacs/emacs-artist.html
http://unix.stackexchange.com/questions/126630/creating-diagrams-in-ascii

3.2. 编码设置

3.2.1. mode line 中直观显示文件编码

在 emacs 的 mode line 中能直观地显示文件的编码,如图 1 所示。

coding_in_modeline.png

Figure 1: Coding is shown in Emacs mode line

图中 mode line 左侧有编码标记“UU-”,第一、二个编码标记分别指示 coding systems for keyboard input and terminal output(用 X 方式启动 emacs 没有这两个编码标记),第三个编码标记表示打开文件时使用的编码。

常用的编码标记有:
U 表示 utf-8
=表示二进制文件
c 表示 gbk 编码(或 gb2312 等)
1 表示 latin-1 编码

完整的编码标记列表及其含义可以通过下面命令得到。

M-x list-coding-systems

3.2.2. 对某后缀文件使用指定编码

Sometimes a file name indicates which coding system to use for the file. The variable file-coding-system-alist specifies this correspondence. There is a special function modify-coding-system-alist for adding elements to this list.

如设置打开.tt 文件时,使用 iso-8859-15 编码:

(modify-coding-system-alist 'file "\\.tt\\'" 'iso-8859-15)

3.2.3. 以指定编码重新打开当前文件

C-x <RET> r coding <RET>
Revisit the current file using the coding system coding(revert-buffer-with-coding-system).

3.2.4. 自动识别编码(多语言环境下很实用)

Mozilla Universal Charset Detector 有 emacs 对应的移植版本。
参考:http://www.emacswiki.org/emacs/Unicad

uchardet 的原理:http://www-archive.mozilla.org/projects/intl/UniversalCharsetDetection.html

3.2.5. 实现 dos2unix

C-x <RET> f unix <RET>

为方便可以在 init.el 中定义下面函数:

(defun dos2unix ()
    "Not exactly but it's easier to remember"
    (interactive)
    (set-buffer-file-coding-system 'unix 't) )

3.3. 字体设置

By default, Emacs displays text on graphical displays using a 10-point monospace font. There are several different ways to specify a different font:
(1) Click on 'Set Default Font' in the 'Options' menu. This makes the selected font the default on all existing graphical frames. To save this for future sessions, click on 'Save Options' in the 'Options' menu.
(2) Add a line to your init file, modifying the variable default-frame-alist to specify the font parameter, like this:

          (add-to-list 'default-frame-alist
                       '(font . "DejaVu Sans Mono-10"))

This makes the font the default on all graphical frames created after restarting Emacs with that init file.
(3) Add an 'emacs.font' X resource setting to your X resource file, like this:
emacs.font: DejaVu Sans Mono-12
You must restart X, or use the xrdb command, for the X resources file to take effect.
(4) If you are running Emacs on the GNOME desktop, you can tell Emacs to use the default system font by setting the variablefont-use-system-font to t(the default isnil). For this to work, Emacs must have been compiled with Gconf support.
(5) Use the command line option -fn (or --font).

上面介绍了 5 种方法,前两种比较简单。

参考:http://www.gnu.org/software/emacs/manual/html_node/emacs/Fonts.html#Fonts

3.3.1. 怎么描述字体的名字(“font name”)

在 X 系统中,有 4 种方法来描述一个字体名字。

方法1:
The first is to use a Fontconfig pattern. Fontconfig patterns have the following form:
fontname[-fontsize][:name1=values1][:name2=values2]...
In addition, some property values are valid with only one kind of property name, in which case the 'name=' part may be omitted.
实例:
Monospace
Monospace-12
Monospace-12:bold
DejaVu Sans Mono:bold:italic
Monospace-12:weight=bold:slant=italic

方法2:
The second way to specify a font is to use a GTK font pattern. These have the syntax:
fontname [properties] [fontsize]

方法3:
The third way to specify a font is to use an XLFD (X Logical Font Description). This is the traditional method for specifying fonts under X.
The syntax for an XLFD is as follows:
-maker-family-weight-slant-widthtype-style-pixels-height-horiz-vert-spacing-width-registry-encoding
Each XLFD consists of fourteen words or numbers, separated by dashes, like this:
-misc-fixed-medium-r-semicondensed--13---*-c-60-iso8859-1
XLFD 一共 14 个字段,它们的含义参见:http://www.gnu.org/software/emacs/manual/html_node/emacs/Fonts.html#Fonts

方法4:
The fourth and final method of specifying a font is to use a "font nickname". Certain fonts have shorter nicknames, which you can use instead of a normal font specification. For instance, ‘6x13’ is equivalent to -misc-fixed-medium-r-semicondensed--13---*-c-60-iso8859-1

3.3.2. fontsets(字体集合)

3.3.3. 查看光标下字体的设置(describe-char)

要查看光标下字符的字体设置等信息,可以使用快捷键 C-u C-x = ,或者直接执行 M-x describe-char
执行后,得到的信息和下面类似:

             position: 23281 of 54117 (43%), column: 37
            character: a (displayed as a) (codepoint 97, #o141, #x61)
    preferred charset: ascii (ASCII (ISO646 IRV))
code point in charset: 0x61
               script: latin
               syntax: w 	which means: word
             category: .:Base, L:Left-to-right (strong), a:ASCII, l:Latin, r:Roman
             to input: type "C-x 8 RET HEX-CODEPOINT" or "C-x 8 RET NAME"
          buffer code: #x61
            file code: #x61 (encoded by coding system utf-8-unix)
              display: by this font (glyph code)
    uniscribe:-outline-Source Code Pro-bold-normal-normal-mono-21-*-*-*-c-*-iso8859-1 (#x1C)

Character code properties: customize what to show
  name: LATIN SMALL LETTER A
  general-category: Ll (Letter, Lowercase)
  decomposition: (97) ('a')

There are text properties here:
  face                 org-level-3
  fontified            t

[back]

3.4. 拼写检查

M-x ispell-region 对选定区域进行检查
M-x ispell-buffer 对整个当前 buffer 进行检查
M-x ispell-message 写邮件时对邮件进行拼写检查,但不包括引用的内容

M-x flyspell-mode
Enable Flyspell mode, which highlights all misspelled words.

M-$
Check and correct spelling of the word at point (ispell-word).

M-TAB
Complete the word before point based on the spelling dictionary(ispell-complete-word).

M-x ispell-minor-mode
仅在输入文本时,提示单词是否正确。

参考:
GNU Emacs Manual Version 24.1, 13.4 Checking and Correcting Spelling

3.5. 日程管理

执行 M-x calendar 可启动日历。

在日历中移动的相关快捷键如表 20 所示。

Table 20: 在 emacs 中日历中移动
hotkey function description
C-f calendar-forward-day 前进一天
C-b calendar-backward-day 后退一天
C-n calendar-forward-week 前进一周
C-p calendar-backward-week 后退一周
M-} calendar-forward-month 前进一月
M-{ calendar-backward-month 后退一月
C-x ] calendar-forward-year 前进一年
C-x [ calendar-backward-year 后退一年

查看农历的相关快捷键如表 21 所示。

Table 21: 在 emacs 中日历中查看农历
hotkey function description
p c calendar-iso-print-date 以 ISO 形式查看(可知第几周)
p C calendar-chinese-print-date 以农历形式查看
p d calendar-print-day-of-year 查看某天是一年中第多少天
C-p calendar-backward-week 后退一周

其它命令参考表 22

Table 22: 在 emacs 中日历相关的其它命令
hotkey function description
q calendar-exit 退出日历
. calendar-goto-today 回到当天
d diary-view-entries 显示某天的日历
s diary-show-all-entries 显示日历文件
m diary-mark-entries 标记设置有日历的那些天
u calendar-unmark 取消标记
H m cal-html-cursor-month 以 html 形式导出某月的日历
H y cal-html-cursor-year 以 html 形式导出某年的日历

设置提醒:

M-x appt-active

3.6. 查看和设置环境变量

process-environment
该变量保存着进程的所有环境变量。
initial-environment
该变量保存着从父进程继承过来的环境变量。

注:
M-x getenv,可查看 process-environment 中保存的环境变量
M-x setenv,设置环境变量,会保存在 process-environment 中。

3.7. emacs 中使用 terminal

M-x ansi-term
M-x term
M-x shell
M-x eshell

注:

  • 请使用 M-x term 或 M-x ansi-term,它们功能更强大(如果使用的是 M-x shell,可能连 man ls 等命令都无法使用!)
  • 进入到 ansi-term 后,想直接执行命令时,按 M-x 无效了,应该按 C-x M-x

3.8. emacs 中内置游戏

gomoku
五子棋
tetris
俄罗斯方块
5X5
游戏的目的是用#号填满整个方框。按下空格后,以光标为中心的十字会在空格和#号间变换

4. Emacs 小技巧

4.1. 对文件内容排序

首先选择要操作的内容,再用表 23 所示命令对其进行排序。

Table 23: emacs 中排序相关命令
hotkey function description
(none) sort-lines 按字母序排序,如 12 会排在 9 前面
(none) sort-fields 加 numeric argument 即可指定对第几个 field 进行排序
(none) sort-numeric-fields 按数字序排序,如 9 会排在 12 前面
(none) sort-columns 选择哪列就对哪那排序
(none) reverse-region 反向排序

4.2. 插入特殊字符(Unicode)

24 是在 Emacs 中插入特殊字符的例子。

Table 24: emacs 中插入特殊字符
hotkey produces meaning
C-x 8 Y ¥ 日元符号,人民币符号
C-x 8 L £ 英镑符号
C-x 8 o ° Degrees
C-x 8 R ® Registered
C-x 8 C © Copyright

直接插入 Unicode 对应字符:
输入 C-x 8 后紧接着按回车键(对应函数 insert-char )后可以直接输入 Unicode 对应字符。

4.3. 空格和 tab 键互换

The commands tabify and untabify do just that.

参考:http://www.gnu.org/software/emacs/manual/html_node/emacs/Just-Spaces.html

4.4. emacs 命令行参数

常用的 emacs 命令行参数:

-nw
不启动 GUI,以终端形式启动。
-q
不加载启动文件。
-Q
Similar to -q --no-site-file --no-splash. Also, avoid processing X resources.
-l file
指定启动时要加载的文件。
-u user
Load user's init file.
--debug-init
This is useful for debugging problems in the init file.

4.4.1. emacs batch 模式(--batch)

指定 --batch 命令行选项,emacs 将进入 batch 模式(noninteractive 模式)。这时,emacs 不会加载“~/.emacs”, “~/.emacs.el”, “~/.emacs.d/init.el”文件(即相当于指定了 --no-init-file )。

$ emacs --batch --eval '(message "Hello Emacs")'
Hello Emacs

参考:http://www.emacswiki.org/emacs/BatchMode

4.4.1.1. Batch 模式实例:批量格式化 C 代码

第一步,新建文件 format-code.el

(defun format-me ()
    (c-set-style "linux")                        ; preferred c style
    (indent-region (point-min) (point-max) nil)  ; format it
    (untabify (point-min) (point-max))           ; untabify
    (save-buffer))

第二步,在 shell 中执行下面命令

for i in `find -type f -regex ".*/.*\.\(c\|cpp\|cc\|h\|hh\)"`; do
    emacs --batch $i -l ~/format-code.el -f format-me;
done

参考:http://redbrain.co.uk/2014/06/27/emacs-reformatting-a-huge-code-base/

4.4.2. 以脚本方式执行 elisp 文件(--script)

指定 --script 选项,可以让 emacs 以脚本方式执行 elisp 文件。如:

$ echo '(message "The current directory is %s" default-directory)' > 1.elisp
$ emacs --script 1.elisp
The current directory is ~/test/

4.4.3. 启动和关闭 emacs 服务端

下面方法可以启动 emacs 服务端。
方法 1:使用 --daemon 参数启动服务端。

emacs --daemon

方法 2:第一次启动 emacsclient 时自动启动服务端,这是利用 emacsclient 的 -a 参数。
emacsclient 的-a 参数用于指定连接不上服务器时使用的别的编辑器(alternate editor),当把-a 指定为空串时,它会自动启动服务端。

emacsclient -a "" file    #指定了-a "",当emacs服务端没启动时会自动启动服务端

下面方法可以关闭 emacs 服务端。
方法 1:使用 pgrep emacs ,找到 emacs --daemon 进程,再 kill 它。
用上面这种方式关闭,如果 buffer 有未保存的内容,则不会保存到原文件,而是会生成备份文件#filename#。

方法 2:使用 emacsclient -e "(kill-emacs)"
这种方式的行为和前面一样,不会自动保存 buffer 到原文件,会生成备份文件。

方法 3:使用 emacsclient -e "(save-buffers-kill-emacs)"
退出前会在 emacsclient 的 minibuffer 区域提示你要不要保存 buffer,但如果所有的 emacsclient 都关闭了,则命令一直暂停在控制台没有自动退出。

Tips: 为方便地启动和关闭 emacs,可以在.bashrc 中定义相关的辅助函数。如:

function emacs-start {
    LC_CTYPE=zh_CN.UTF-8 /usr/bin/emacs --daemon
}

function emacs-stop {
    emacsclient --eval "(progn (setq kill-emacs-hook 'nil) (kill-emacs))"
}

4.4.4. emacsclinet 的-c 和-t 选项

emacsclinet 的-c 和-t 选项
-c,会启动 X 窗口。
-t(或者--tty 或者-nw),Open a new Emacs frame on the current terminal。

注:如果既不加-c 又不加-t,会在之前已启动的 emacsclient 中编辑文件,这次启动的 emacsclient 仅显示 Waiting for Emacs...

4.5. 让 init.el 的改动立刻生效

如果修改了 emacs 启动文件 init.el,如何不重启 emacs 的情况下使更改生效?有下面几种方法:

  • C-x C-e 执行光标前面的一条语句(如果改动比较少的话很方便)。
  • 选择改动的 region, M-x eval-region
  • M-x load-file ~/.emacs.d/init.el
  • M-x eval-buffer

4.6. Linux 英文系统中输入中文

首先要安装 ibus 的 emacs 插件(Ubuntu 中可以这样安装 sudo apt-get install ibus-el)。

在启动 emacs 前,先设置 LC_CTYPE=zh_CN.UTF-8 即可,emacs 已经很智能了,它会根据 LC_CTYPE 来取消“Ctrl + Space”的内置绑定。
如果还有问题,可以在/etc/default/locale 中设置 LC_CTYPE=zh_CN.UTF-8

4.7. 遇到错误时进入调试器

有时 Emacs 遇到错误只会打印简单的错误信息,要想此时进入调试器,可以通过 M-x toggle-debug-on-error 设置。

参考:http://emacs.stackexchange.com/questions/7698/how-to-make-org-export-give-more-messages-when-it-fails

4.8. 把 Caps Lock 改为 Ctrl

在 Emacs 中使用 Ctrl 的频率非常高,把 Caps Lock 键改为 Ctrl 键可以提高使用 Emacs 的舒适度。

参考:https://www.emacswiki.org/emacs/MovingTheCtrlKey

5. Emacs 常用 mode

5.1. emacs 如何自动选择 Major Mode

emacs 在打开文件时可以自动地设置 Major Mode,它的规则是什么呢?

有几种方式(描述顺序不代表其优先级,详细优先级可以查看 emacs manual):
1.根据文件名自动设置
如打开.c 结尾的文件,就进入 C mode。文件名和 major mode 的对应关系保存在变量 auto-mode-alist 中。

2.根据#!后的解释器设置
当文件的内容以“#!”开头时,这时 Emacs 使用文件中指定的解释器的名字来选择 major mode,解释器和 major mode 的对应关系保存在变量 interpreter-mode-alist 中。
2.1 根据文件开头内容判断
把判断规则写入到 magic-mode-alist 中,如:

(add-to-list 'magic-mode-alist '("<!DOCTYPE html .+DTD XHTML .+>" . nxml-mode) )

2.2 根据定制函数返回值判断
magic-mode-alist 还有一种用法,如:

(add-to-list 'magic-mode-alist '(MATCH-FUNCTION . nxml-mode) )

只要 MATCH-FUNCTION 返回 non-nil 就会设置为 nxml-mode。
显然,这种方法的可定制性更强。

3.根据 File-local variables 设置
什么是 File-local variables?就是把变量写在文件中,Emacs 打开该文件时自动加载其中的变量。当然 File-local variables 要符合一定的规则,以使 emacs 正确识别其中的变量。
File-local variables 有两种形式:
3.1 使用-*-标记
应该成对出现。只能出现在文件第一行(如果第一行以#!开头,可以出现在第二行)。如:

/*  -*- mode:lisp; tab-width: 4; indent-tabs-mode: t; -*-  */

3.2 使用 Local Variables:和 End:标记
Local Variables:和 End:必须成对出现,可以在文件的开头或结尾位置。如:

;; Local Variables: ;;
;; mode:lisp ;;
;; tab-width:4 ;;
;; End: ;;

前缀;;和后缀;;的要求比较宽松,用/*和*/也行,都用##也行。

注:File-local variables 这种办法很强大,因为它仅仅可以定义 mode,还可以设置变量或运行函数等其它事件!但是不太安全!

参考:
Emacs Manual For Emacs 24.1, 20.3 Choosing File Modes.
Emacs Lisp Reference Manual For Emacs 24.1, 23.2.2 How Emacs Chooses a Major Mode.

5.1.1. 定制 emacs 的 mode 自动识别规则

正如前面所说,auto-mode-alist 决定着文件名和 Major mode 的对应关系。所以修改它即可定制 emacs 的 mode 自动识别规则。

把下面内容加入在 init.el 文件中,就可以使以点开始的文件名自动为 fundamental-mode,.C 结尾的文件自动打开为 c++-mode。

(setq auto-mode-alist
      (append
       ;; File name (within directory) starts with a dot.
       '(("/\\.[^/]*\\'" . fundamental-mode)
      ;; File name ends in '.C'.
      ("\\.C\\'" . c++-mode))
     auto-mode-alist))

5.2. dired

Dired makes an Emacs buffer containing a listing of a directory, and optionally some of its subdirectories as well. You can use the normal Emacs commands to move around in this buffer, and special Dired commands to operate on the listed files.

参考:
http://www.gnu.org/software/emacs/manual/html_node/emacs/Dired.html
http://www.gnu.org/software/emacs/manual/html_node/dired-x/index.html
http://www.emacswiki.org/DiredExtra

5.2.1. 标记文件

* *
标记所有可执行文件。
* @
标记所有符号链接。
* /
标记所有目录(不包括 . 和 .. )。
* s
标记所有文件(不包括 . 和 .. )。
* .
标记具有给定扩展名的文件。
% m REGEXP <RET> 或 * % REGEXP <RET>
标记所有匹配到给定的正则表达式的文件。
% g REGEXP <RET>
标记所有文件内容(而不是文件名)匹配到给定的正则表达式的文件。

5.2.2. 常用操作

+
创建目录
y
查看当前文件类型
^
访问父目录
j
快速跳到指定文件
C
拷贝文件。把 dired-recursive-copies 设为非 nil 的值可以递归拷贝目录,通常我们设定为 top ,这表示对于顶层目录 dired 会先进行询问是否要递归拷贝,而其中的子目录则不再询问。如果嫌询问太麻烦,可以直接设置为 always 。
D
删除文件。类似的有一个 dired-recursive-deletes 变量可以控制递归删 除。
R
重命名文件,也就是移动文件。
H
创建硬链接。
S
创建软链接。
M
修改权限位,即 shell 里面的 chmod 命令。
G
修改文件所属的组。
O
修改文件的所有者。
T
修改文件的修改时间,类似于 shell 命令 touch 。
P
打印文件。
Z
压缩或解压文件。
L
把 Elisp 文件加载进 Emacs 。
B
对 Elisp 文件进行 Byte compile 。
A
对文件内容进行正则表达式搜索,搜索会在第一个匹配的地方停下,然后 可以使用 M-, 搜索下一个匹配。
Q
对文件内容进行交互式的正则表达式替换。

参考:
http://www.cnblogs.com/leohxj/archive/2012/02/20/2360606.html
http://lifegoo.pluskid.org/wiki/EmacsAsFileManger.html

5.2.3. 使用更强大的 Dired Extra

(add-hook 'dired-load-hook
            (function (lambda () (load "dired-x"))))

Tips: Dired Extra 中可以方便打开当前文件所在的目录:
C-x C-j

5.2.4. dired 小技巧

5.2.4.1. 临时改变 ls 的选项

在 dired 中输入 C-u s,回车后,再输入新的 option 即可。

5.2.4.2. 快速重命名文件

使用快捷键 C-x C-q(dired-toggle-read-only),然后就可以修改 buffer 内容了,保存修改可以按 C-c C-c(wdired-finish-edit),取消修改可以按 C-c Esc(wdired-abort-changes)

注意:
C-c C-c 和 C-c Esc 快捷键只能连续按一次!因为它们仅在“可编辑状态”时才可用。而且按一次 C-c C-c 或 C-c Esc 就马上进入到了 read-only 状态。

5.3. 高亮关键字

5.3.1. hi-lock-mode

Emacs 自带 hi-lock-mode (hi-lock.el),里面提供了一下几个很有用的命令:
highlight-phrase (M-s h p)
highlight-regexp (M-s h r)
highlight-lines-matching-regexp (M-s h l)

使用时直接输入上面命令或快捷键即可,不用先打开 hi-lock-mode ,Emacs 会自动打开。
使用时 Emacs 会先问你要高亮什么内容,如果你不想手工输入的话,得事先拷贝好内容再在这里粘贴。
然后 Emacs 会问你使用那种风格(face)来显示高亮,除了 hi-lock.el 提供的 hi-yellow, hi-pink, hi-green 等颜色之外,你也可以使用 Emacs 里面其它的 face。
要去除高亮,用 M-x unhighlight-regexp (M-s h u),它会自动提供当前已有条目(刚才通过上 述三个命令输入的内容)供你选择。

参考:
http://www.cnblogs.com/bamanzi/archive/2012/12/03/emacs-find-modify-all-occurences.html

5.3.2. idle-highlight-mode

emacs 自动高亮光标处字符
可利用 idle-highlight-mode,但它不是 Emacs 自带的,需要自己下载。

参考:
http://www.emacswiki.org/IdleHighlight

5.4. 根据后缀名或主模式插入模板(auto-insert-mode)

如果新建一个.pl 文件,就自动在文件最前面输入“#!/usr/bin/env perl”等内容该多好啊。 auto-insert-mode 就是为这个功能而设计的。

(require 'autoinsert)
(auto-insert-mode +1)         ; enable auto-insert-mode
(setq auto-insert-query nil)  ; No prompt before insertion

(eval-after-load 'autoinsert
  '(define-auto-insert '("\\.pl\\'" . "Perl skeleton")
     '(nil
       "#!/usr/bin/env perl" \n
       \n
       "use strict;" \n
       "use warnings;" \n \n
       _ \n)))

模板语法基于 Skeleton Mode,比如 _ 表示光标位置等,详情可参考:Emacs Skeleton Language

5.5. diff mode

打开 patch 文件,会自动进入 diff 模式。下面 diff 模式中是常见功能对应的快捷键。

M-o             diff-goto-source

M-n             diff-hunk-next
M-p             diff-hunk-prev

M-N             diff-file-next
M-P             diff-file-prev
M-{             diff-file-prev
M-}             diff-file-next

5.6. ediff mode

Ediff provides a convenient way for simultaneous browsing through the differences between a pair (or a triple) of files or buffers (which are called 'variants' for our purposes).

参考:http://www.gnu.org/software/emacs/manual/html_node/ediff/index.html
ediff 比 KDiff3,Diffuse,Vimdiff Meld 强在哪?见图:http://jixiuf.github.io/emacs/ediff.html

Table 25: Ediff Control Panel 常用快捷键
hotkey function description
n 或 Space ediff-next-difference 下一个差异处
p 或 Del ediff-previous-difference 上一个差异处
j 或 [n]j ediff-jump-to-difference 有数字前缀[n]修饰,第 n 个差异处,n可为负数
v 或 C-v ediff-scroll-vertically 所有缓冲区同步向下滚动
V 或 M-v ediff-scroll-vertically 所有缓冲区同步向上滚动
< ediff-scroll-horizontally 所有缓冲区同步向左滚动
> ediff-scroll-horizontally 所有缓冲区同步向右滚动
| ediff-toggle-split 切换缓冲区布局方式为水平或竖直
a ediff-copy-A-to-B 把 Buffer-A 的内容复制到 Buffer-B
b ediff-copy-B-to-A 把 Buffer-B 的内容复制到 Buffer-A
ra 或 rb ediff-restore-diff 恢复 Buffer-A 或 Buffer-B 差异区域中的被修改的内容
A 或 B ediff-toggle-read-only 切换 Buffer-A 或 Buffer-B 的只读状态
C-l ediff-recenter 恢复先前的所有缓冲区比较的高亮差异区。
! ediff-update-diffs 重新比较并高亮差异区域
wa 或 wb ediff-save-buffer 保存 Buffer-A 或 Buffer-B 到磁盘
E ediff-documentation 打开 Ediff 帮助文档
z ediff-suspend 关闭 ediff control buffer,只是挂起,可在以后恢复 ediff 状态
q ediff-quit 关闭 ediff control buffer,并退出 ediff

5.6.1. ediff-directories 比较两个目录

假设/home/newdir 下是最新的代码,/home/olddir 下是老代码,我们需要把老代码中的部分修改合入最新的代码。

首先启动 emacs,运行 M-x ediff-directories 命令,选择上述两个目录,第一个目录是“newdir”,我们认为他是“a”,第二个目录是“olddir”,我们叫他是“b”。

接着会进入“Ediff Session Group Panel”窗口,目录中的每个文件都是一个 session,在当前窗口可以选择对哪个 session 做什么操作。这里我们先使用“=h”来隐藏所有相同的文件,然后在相应的 session 上用“h”来隐藏你不想合并的文件,每一个待隐藏的 session 前都有一个“H”标记。运行“x”执行,所有的“H”标记的 session 都不见了。

把光标移动到每一个待合并的 session 上回车,进入比较画面,“?”可以显示命令帮助,“n”和“p”可以在差异之间跳转,如果当前差异需要从 b 合入 a,用“b”命令,完成后用“wa”保存在 a 窗口做的修改,用“q”退出当前 session。如果用“M”可以回到“Ediff Session Group Panel”窗口,但是当前打开的 session 前有“+”标识,这个时候即使你用“h”和“x”去隐藏也隐藏不了。如果是用“q”退出的 session,前面有用“-”做标识。完成后用“q”一路退出。

参考:
http://www.iamwuyang.com/wuyang/wordpress/?p=504

6. 编程相关

6.1. cedet

CEDET stands for “Collection of Emacs Development Environment Tools”.
CEDET 它包含很多子工具。

M-x cedet-version

会显示类似下面的结果:

CEDET Version:	2.0
  			Requested	File		Loaded
  Package		Version		Version		Version
  ----------------------------------------------------------
  cedet:		2.0		nil		ok
  eieio:		1.4		nil		ok
  semantic:		2.2		nil		ok
  srecode:		1.2		nil		Not Loaded
  ede:			1.2		nil		Not Loaded
  speedbar:		1.0.4		nil		1.0
  cogre:		1.2		nil		Not Loaded
  cedet-contrib:	1.2		nil		Not Loaded

6.1.1. semantic

Semantic is a suite of Emacs libraries and utilities for parsingsource code. At its core is a lexical analyzer and two parsergenerators (Bovinator and Wisent) written in Emacs Lisp.

Bovinator 是一个 LL generator,而 Wisent 是一个 LALR generator。

The Wisent parser is a port of bison to Emacs Lisp written by David Ponce.

参考:
http://www.gnu.org/software/emacs/manual/html_mono/semantic.html
http://www.randomsample.de/cedetdocs/semantic/
http://www.randomsample.de/cedetdocs/semantic-langdev/index.html
http://www.randomsample.de/cedetdocs/grammar-fw/index.html

How To Set Up Semantic Bovinator For A New Language: http://www.emacswiki.org/emacs/HowToSetUpSemanticBovinatorForANewLanguage

6.1.2. speedbar

Speedbar is a program for Emacs which can be used to summarize information related to the current buffer.

参考:http://www.gnu.org/software/emacs/manual/html_node/speedbar/index.html

speedbar 可以使用 etags,imenu,semantic 等工具做后台。

如果想显示在同一个 frame 中,可以用 sr-speedbar。

6.2. ecb (Emacs Code Browser)

ecb 不是 emacs 内置的,Ubuntu 中可以这样安装:sudo apt-get install ecb

参考:
http://www.xemacs.org/Documentation/packages/html/ecb.html
http://ecb.sourceforge.net/docs/index.html

6.3. jdee (Java Development Environment for Emacs)

JDEE stands for Java Development Environment for Emacs.

参考:
http://www.emacswiki.org/JavaDevelopmentEnvironment
http://blog.csdn.net/csfreebird/article/details/19033939
http://blog.csdn.net/csfreebird/article/details/7028174

安装步骤:
测试环境 Ubuntu 14.04 + Emacs 24.3
第一步,从http://sourceforge.net/projects/jdee/files/ 下载 jdee 最新版本文件,如 jdee-bin-2.4.1.tar.bz2
第二步,解压到~/.emacs.d
第三步,添加下面两行到~/.emacs.d/init.el 中

(add-to-list 'load-path "~/.emacs.d/jdee-2.4.1/lisp")
(load "jde")

重启 Emacs,打开 java 文件,可发现菜单栏多了几个 java 相关的菜单。

6.4. emacs-eclim

什么是 Eclim?
Eclim provides the ability to access Eclipse code editing features (code completion, searching, code validation, and many more) via the command line or a local network connection, allowing those features to be integrated with your favorite editor. Eclim provides an integration with Vim, but third party clients have been created to add eclim support to other editors as well (emacs, sublime text 2, textmate).

说明:Eclim 能把 eclipse 的功能集成到 vim 中,通过第三方插件如 Emacs-eclim 也能把 eclispe 的功能集成到 emacs 中。

什么是 Emacs-eclim?
通过 Emacs-eclim 能使 Emacs 使用 eclim 的功能,而通过 eclim 可以使用 eclipse 的功能。
功能介绍可参考:http://www.skybert.net/emacs/java/

Emacs-eclim 非常的强大,代码自动补全,实时语法检查,重构等 eclispe 的功能都不在话下。
唯一的缺点是依赖太重了! Emacs-eclim 依赖于 eclim,而 eclim 依赖于 eclipse。

安装 eclim
http://eclim.org/install.html
安装配置 Emacs-eclim
https://github.com/senny/emacs-eclim

6.5. SLIME (Emacs mode for Common Lisp development)

SLIME stands for The Superior Lisp Interaction Mode for Emacs.
SLIME is a Emacs mode for Common Lisp development. Inspired by existing systems such Emacs Lisp and ILISP, we are working to create an environment for hacking Common Lisp in.

Reference:
SLIME homepage: https://common-lisp.net/project/slime/
SLIME User Manual: https://common-lisp.net/project/slime/doc/html/

6.5.1. 执行 Shortcuts

"Shortcuts" are a special set of REPL commands that are invoked by name. To invoke a shortcut you first press , (comma) at the REPL prompt and then enter the shortcut’s name when prompted.
在 repl 提示符中输入逗号后,可执行一些 shotcuts

Table 26: Shortcuts in SLIME
shotcuts aka description
, cd , !d Change the current directory
, pwd   Show the current directory
, quit   Quit the current Lisp
, sayoonara   Quit all Lisps and close all SLIME buffers
, compile-and-load , cl Compile (if necessary) and load a lisp file
, change-package , !p or , in-package Change the current package
, defparameter , ! Define a new global, special, variable
, help , ? Display the help

参考:https://common-lisp.net/project/slime/doc/html/Shortcuts.html#Shortcuts

6.5.2. SLIME 中常用快捷键

Table 27: SLIME 中常用快捷键
hotkey function description
C-RET slime-repl-closing-return 补全括号并执行
C-c M-o slime-repl-clear-buffer 清空 buffer
C-c C-o slime-repl-clear-output 清空上次结果
C-c C-l slime-load-file 加载文件
C-c C-k slime-compile-and-load-file 编译加载当前文件
C-c C-] slime-close-all-parens-in-sexp 补全剩余的括号
C-c Tab slime-complete-symbol Complete the symbol at point

参考:https://common-lisp.net/project/slime/doc/html/Key-Index.html#Key-Index

6.6. emacs 中调试 perl

6.7. emacs 中使用 gdb

直接用 M-x gdb 启动 gdb(不要用 M-! gdb 的方式启动 gdb)。

M-x gdb-many-windows
可以打开很多窗口(默认 6 个)来辅助调试,这个命令只有在启动 gdb 后才能使用。

6.8. 注释源码

M-;
Insert or realign comment on current line; if the region is active, comment or uncomment the region instead (comment-dwim).
C-u M-;
Kill comment on current line (comment-kill).
C-x ;
Set comment column (comment-set-column).

参考:http://www.gnu.org/software/emacs/manual/html_node/emacs/Comment-Commands.html#Comment-Commands

6.8.1. 自动格式化注释(M-q)

选择注释后,按 M-q 可以重新格式化注释,它可以自动使选择的注释不超过变量 fill-column 指定的列数。

参考:https://mrmichaelwill.wordpress.com/2009/03/04/how-to-reformat-a-paragraph-of-sgml-source-to-80-column-width-for-easier-editing-emacs-kung-fu-comes-to-help/

6.9. etags

etags

参考:
http://www.gnu.org/software/emacs/manual/html_node/emacs/Tags.html#Tags

生成 tag 文件
如:对当前文件夹的 perl 文件生成 tag

$ find . -name "*.pl" -o -name "*.pm" |etags -

常用操作
M-. tag RET
转到 tag 的定义处(如果不指定 tag,就默认为光标处的 tag)。

M-*
回到上次运行 M-.时的位置。

C-x 4 . tag RET
转到 tag 的定义处,但在新窗口中显示。

C-u M-.
查找下一个 tag 的位置。

C-u - M-.
回到上一个 tag 的位置。

C-M-. pattern RET
Find a tag whose name matches pattern (find-tag-regexp).

C-u C-M-.
Find the next tag whose name matches the last pattern used.

etags 缺点
etags 生成的 TAGS 文件中仅保存了函数的定义,所以无法利用 etags 找到一个函数的所有引用。
下面命令可以实现这个功能:
M-x rgrep
默认把光标处的单词作为查找目标。(搜索内容时,支持一些预定义的别名,比如 ch 表示所有的 c++代码文件,hh 表示所有的 c++头文件等等)
M-x lgrep
仅在本层目录进行搜索,不去子目录中搜索,l是指 local 的意思。

6.10. imenu

Imenu is a feature that lets users select a definition or section in the buffer, from a menu which lists all of them, to go directly to that location in the buffer.

参考:
http://www.gnu.org/software/emacs/manual/html_node/elisp/Imenu.html
http://emacswiki.org/emacs/ImenuMode

用 imenu 比用 etags 更好的地方——不用生成 TAGS 文件。

M-x imenu
提示输入一个名称(按 table 键会列出所有合法名称),emacs 将跳转到这个名称的定义处。

M-x imenu-add-to-menubar
运行命令后,会提示输入一个名称,确定后 emacs 会生成一个菜单,事先定义好的文件的结构会列在这个菜单下。

6.10.1. 对某个 mode 增加“Index”菜单

如何对某个 mode 增加“Index”菜单(菜单中会列出当前文件的所有函数等信息)呢?
以 c-mode 为例,可以这样做:

(add-hook 'c-mode-hook 'imenu-add-menubar-index)

6.10.2. 怎么对一个新语言进行定制呢?

方法一
imenu-generic-expression
imenu-case-fold-search
imenu-syntax-alist

方法二
imenu-prev-index-position-function
imenu-extract-index-name-function

方法三
imenu-create-index-function

6.11. flycheck-mode

Flycheck is a modern on-the-fly syntax checking extension, and a modern alternative to Flymake.
Flycheck-mode 不是 emacs 内置的,要手动安装。

参考:http://www.emacswiki.org/emacs/Flycheck

6.12. cscope

Emacs 中 cscope 相关的常用快捷键如表 28 所示。

Table 28: Emacs 中 cscope 相关的常用快捷键
Shotcuts Description
C-c s a 设定初始化目录,一般指定为代码码根目录
C-c s I 对目录中的相关文件建立列表并进行索引
C-c s s Find symbol.
C-c s d Find global definition.
C-c s g Find global definition (alternate binding).
C-c s G Find global definition without prompting.
C-c s c Find functions calling a function. (看看指定函数被哪些函数所调用)
C-c s C Find called functions. (看看指定函数调用了哪些函数)
C-c s t Find text string.
C-c s e Find egrep pattern.
C-c s f Find a file.
C-c s i Find files #including a file. (看看指定的文件被哪些文件 include).
C-c s b Display cscope buffer.
C-c s B Auto display cscope buffer toggle.
C-c s n Next symbol.
C-c s N Next file.
C-c s p Previous symbol.
C-c s P Previous file.
C-c s u Pop mark.

7. Elisp

Emacs Lisp is a dialect of the Lisp programming language used by the GNU Emacs and XEmacs text editors. It is used for implementing most of the editing functionality built into Emacs, the remainder being written in C (as is the Lisp interpreter itself). Emacs Lisp is also referred to as Elisp.

Elisp Cookbook: http://emacswiki.org/emacs/ElispCookbook
Emacs Lisp Reference Manual: http://www.gnu.org/software/emacs/manual/html_node/elisp/index.html
An Introduction to Programming in Emacs Lisp: https://www.gnu.org/software/emacs/manual/eintr.html
Practical Emacs Lisp: http://ergoemacs.org/emacs/elisp.html

7.1. 调试工具

7.1.1. trace-function, untrace-function

trace-function 可以方便地查看 elisp 函数执行时的参数和返回值(默认显示在*trace-buffer*中),这要调试函数时非常有用;要取消对某个函数或所有函数的“监视”,可以使用 untrace-functionuntrace-all

M-x trace-function
M-x trace-function-background
M-x untrace-function
M-x untrace-all

效果演示:

;;  (defun fact (n)
;;    (if (= n 0) 1
;;      (* n (fact (1- n)))))
;;  fact
;;
;;  (trace-function 'fact)
;;  fact
;;
;;  Now, evaluating this...
;;
;;  (fact 4)
;;  24
;;
;;  ...will generate the following in *trace-buffer*:
;;
;;  1 -> fact: n=4
;;  | 2 -> fact: n=3
;;  | | 3 -> fact: n=2
;;  | | | 4 -> fact: n=1
;;  | | | | 5 -> fact: n=0
;;  | | | | 5 <- fact: 1
;;  | | | 4 <- fact: 1
;;  | | 3 <- fact: 2
;;  | 2 <- fact: 6
;;  1 <- fact: 24

7.1.2. Edebug

把光标放到想要调试的 Elisp 函数的定义处,按 M-x edebug-defun RET 即可调试该函数。

单步执行:space
设置断点:b
取消断点:u
运行到断点处:g
退出调试:q

参考:https://www.gnu.org/software/emacs/manual/html_node/elisp/Using-Edebug.html

7.2. advice

The advice feature lets you add to the existing definition of a function, by advising the function. This is a cleaner method for a library to customize functions defined within Emacs—cleaner than redefining the whole function.

一句话总结:advice 可以改变现有函数的行为,而不必重新定义它。

7.2.1. advice 实例:让 align-regexp 仅使用空格

align-regexp 可以用来对齐代码,但它默认可能使用 tabs 字符,如果想仅用空格做对齐,可以用 advice 对其进行修改。把下面代码加入 init.el 中即可。

;; Align with spaces only
(defadvice align-regexp (around align-regexp-with-spaces)
  "Never use tabs for alignment."
  (let ((indent-tabs-mode nil))
    ad-do-it))
(ad-activate 'align-regexp)

参考:
http://stackoverflow.com/questions/915985/in-emacs-how-to-line-up-equals-signs-in-a-series-of-initialization-statements

7.3. load-file, load, require, autoload 区别

变量 features 中保存着当前已经加载的 feature(系统中可能存在能加载但没有加载的 feature)。

Table 29: load-file, load, require, autoload 区别
Function Purpose Tech Detail Comment
load-file Load a file Load one specific file. Use this if you have one very SPECIFIC file at one particular file path.
load Load a file Load a file by searching thru var "load-path". Also, tries to load a compiled version (.elc) if exists. Use this if the path for the file is not known in advance, and you are using a file name without full path, such as "undo" or "undo.el", and you want to load the compiled version if it exists (undo.elc).
require Load a package if it has not already been loaded Checks the var "features", if symbol is not there, then call load to load it. Best used in elisp source code, similar to other lang's "require" or "import".
autoload Load a file only when a function is called Associate a function name with a file path. When the function is called, load the file, and execute the function. If you are writing a major mode, it's good to have your package installation go by autoload (if possible). It saves startup time.

参考:http://ergoemacs.org/emacs/elisp_library_system.html

7.4. 检查函数,变量,feature 等是否定义或可用

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Check if a function is defined
(fboundp 'info)                         ; t
(fboundp 'setq)                         ; t

(fboundp 'xyz)                          ; nil

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Check if a variable is defined
(boundp 'auto-mode-alist)               ; t
(boundp 'default-input-method)          ; t
(boundp 'nil)                           ; t

(boundp 'xyz)                           ; nil

;; The fboundp actually check a symbol's function cell.
;; Similarly, the boundp checks a symbol's value cell.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Check if a "feature" (package) has been loaded
(featurep 'expand-region)

Reference: http://ergoemacs.org/emacs/elisp_check_defined.html

7.5. 增加自定义菜单

Here's a example of how to add your own menu. Suppose you want a menu named "MyMenu" that comes after the "Tools" menu. Suppose your menu will have 2 menu items: "Next Line" and "Previous Line", each should call the emacs command next-line and previous-line. Here's the elisp code to do it:

;; Creating a new menu pane in the menu bar to the right of "Tools" menu
(define-key-after
  global-map
  [menu-bar mymenu]
  (cons "MyMenu" (make-sparse-keymap "hoot hoot"))
  'tools )

;; Creating a menu item, under the menu by the id “[menu-bar mymenu]”
(define-key
  global-map
  [menu-bar mymenu nl]
  '("Next Line" . next-line))

;; creating another menu item
(define-key
  global-map
  [menu-bar mymenu pl]
  '("Previous Line" . previous-line))

;; code to remove the whole menu panel
;; (global-unset-key [menu-bar mymenu])

参考:
http://ergoemacs.org/emacs/elisp_menu.html
http://www.gnu.org/software/emacs/manual/html_node/elisp/Keymaps.html

7.6. set, setq, setq-default 区别

setq 和 set 有什么区别?下面两个设置等价:

(set 'some-variable t)
(setq some-variable t)   ; same as above

setq 和 setq-default 有什么区别?
Some variables in Emacs are “buffer-local”, meaning that each buffer is allowed to have a separate value for that variable that overrides the global default. tab-width is a good example of a buffer-local variable.
If a variable is buffer-local, then setq sets its local value in the current buffer and setq-default sets the global default value.
If a variable is not buffer-local, then setq and setq-default do the same thing.

Reference:
http://stackoverflow.com/questions/18172728/the-difference-between-setq-and-setq-default-in-emacs-lisp

7.7. 在 minibuffer 中执行 elisp 代码(M-:)

对于简短的 elisp 代码可以直接在 minibuffer 中执行。
如生成 1 到 20 的数字序列:

M-: (dotimes (i 20) (insert (format "%2d\n" (1+ i))))

参考:http://stackoverflow.com/questions/1509688/emacs-macro-to-generate-a-sequence

7.8. 如何为新语言创建 mode

参考:
An Emacs language mode creation tutorial: http://emacswiki.org/emacs/ModeTutorial
How to Write a Emacs Major Mode for Syntax Coloring: http://ergoemacs.org/emacs/elisp_syntax_coloring.html

7.9. customization

Emacs 中,可以通过菜单中的[Options] -> [Customize Emacs]来方便定制 Emacs,可定制的项非常多,它们通过 groups 来组织。
大部分 Emacs package 都有自己的 main customization group,在 main customization group 可能有很多 subgroups。

参考:
http://www.gnu.org/software/emacs/manual/html_node/emacs/Easy-Customization.html
http://www.gnu.org/software/emacs/manual/html_node/elisp/Customization.html

7.9.1. 增加 user options

如何给自己新写的包增加 user options(又称 Customizable variables),以方便用户配置呢?使用 defgroupdefcustom 可完成任务。

第一步:先用宏 defgroup 定义一个 group,在定义时可以把这个 group 放入到合适的 Customization group 下。
执行 M-x customize-browse 可以查看 Emacs 中存在的所有 Customization Groups:

[-]-\ [Group] Emacs
   [+]-- [Group] Editing
   [+]-- [Group] Convenience
   [+]-- [Group] Files
   [+]-- [Group] Text
   [+]-- [Group] Data
   [+]-- [Group] External
   [+]-- [Group] Communication
   [+]-- [Group] Programming
   [+]-- [Group] Applications
   [+]-- [Group] Development
   [+]-- [Group] Environment
   [+]-- [Group] Faces
   [+]-- [Group] Help
   [+]-- [Group] Multimedia
   [+]-- [Group] Local

下面语句定义了一个新 group,其名为 tabbar,并且它位于 group convenience 下。

(defgroup tabbar nil
  "Display a tab bar in the header line."
  :group 'convenience)

第二步:定义好 group 后,用宏 defcustom 在这个 group 下定义 user options。
下面语句定义了一个 user option: tabbar-auto-scroll-flag,并且位于 group tabbar 下。

(defcustom tabbar-auto-scroll-flag t
  "*Non-nil means to automatically scroll the tab bar.
That is, when a tab is selected outside of the tab bar visible area,
the tab bar is scrolled horizontally so the selected tab becomes
visible."
  :group 'tabbar
  :type 'boolean)

Author: cig01

Created: <2012-08-21 Tue>

Last updated: <2018-04-14 Sat>

Creator: Emacs 27.1 (Org mode 9.4)