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 00:00>

Last updated: <2018-04-14 Sat 21:22>

Creator: Emacs 25.3.1 (Org mode 9.1.4)