Emacs定制与扩展

Table of Contents

1 版权

本文采用创作共用署名-非商业性使用-相同方式共享 2.5 中国大陆版许可证 (Creative Commons Attribution-Noncommercial-Share Alike 2.5 China Mainland)

如需查看许可证全文,请访问如下网址: http://creativecommons.org/licenses/by-nc-sa/2.5/cn/

2 前言

在使用 GNU Emacs 的第六个年头上,仍然发现有很多新鲜的东西需要学习,仍 然有这样那样的配置或者扩展可以提高工作效率。在 Emacs 的世界里,真可谓 学无止境啊!

曾经对手册有很强烈的排斥心理,是在学习 Emacs (以及 Perl)的过程中,渐渐 感觉到阅读手册的必要性和重要性,也慢慢明白,并未只有印刷在书上的文字才 充满智慧,屏幕上的手册也可以妙趣横生、引人入胜。

当有一天,手册也不能满足学习的饥渴,便只好在网络上收集、整理、加工乃至 升华。过程虽然枯燥,但是当发现新的工具学会新的用法时,宛如攀上山的顶 峰,一路的艰辛早已抛之脑后,只有无尽的愉悦在心中荡漾。怎一个爽字了得!

写一本书,是我的一个梦想。一度以为,也只能想想而已,因为没啥好写的。最 近几个月的积累,让我有了尝试的冲动。即便写不成上百页的著作,弄个几十页 甚至十几页的小册子也好啊。只要对他人有帮助。

于是,便有了以下的内容。

这本小册子并非是 Emacs 的入门读物,它是接受了 Emacs 的设计理念,认真阅 读了操作手册,能够熟练运用其基本技巧之后的进阶读物。本文大部分内容留给 了我钟爱的 Org Mode 和适合程序员使用的专有工具。

3 emacs 和 emacsclient

3.1 CVS Emacs

使用CVS Emacs的理由很简单,它具有一些已经发布的 GNU Emacs 不具备的功能。就目前来讲,一个是 Emacs daemon,另一个就是可以享受漂亮的字体。

访问CVS Emacs的方法参见 http://savannah.gnu.org/cvs/?group=emacs

3.1.1 Daemon

从前只有Emacs server的概念,即可以让已经启动的Emacs进程作为server,然后使用emacsclient连接它;而 Emacs daemon 则是让Emacs进程作为daemon存在。

两者的区别是,Emacs server至少具有一个frame,即使不使用emacsclient程序,我们仍然可以使用这个Emacs进程;而daemon没有任何交互界面存在,必须通过emacsclient创建frame才能使用它。

使用Emacs daemon的好处之一是在重新启动 X Window 的时候无须关闭Emacs进程;另外就是可以远程启动,Emacs daemon可以在登录退出时仍然运行在服务器端。

CVS里面的emacsclient程序拥有两个新的选项: -c-t ,分别用于创建X frame和终端上的frame。

3.1.2 字体

CVS Emacs的另一个特性是可以使用更加漂亮的字体,正是这一特性,使我一直坚持使用 CVS Emacs,而不是正式发布的稳定版本,屏幕截图参见 http://emacs-fu.blogspot.com/2009/01/emacs-23.html

编译方法如下:

 cd emacs
 ./configure --prefix=/usr --enable-font-backend
 make bootstrap && make
 sudo make install

设置字体方法是运行 M-x customize-face RET default RET ,我基本都是使用 Liberation Mono 。

3.1.3 问题

因为是开发版,CVS Emacs 难免有这样那样的问题,尤其是对于刚刚开发出来的新特性,但是对于基本特性来说,质量还是相当过硬的,这也是为什么我使用 CVS Emacs 一年多来再也没有回头使用已经发布的稳定版本的原因。如果在使用过程中确信是 CVS Emacs 的问题,建议报到emacs-devel邮件列表上,以便开发者及时发现并解决问题。

在同时使用 Emacs daemon 和漂亮字体的时候我曾遇到过一个问题,至今不得要领,解决方法是很偶然发现的,即将自己的配置全都放在 custom-set-faces 语句之后。我的 .emacs 文件是如下的样子:

(custom-set-variables
  ;; custom-set-variables was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
 )
(custom-set-faces
  ;; custom-set-faces was added by Custom.
  ;; If you edit it by hand, you could mess it up, so be careful.
  ;; Your init file should contain only one such instance.
  ;; If there is more than one, they won't work right.
 '(default ((t (:inherit nil :stipple nil :background "white"
                :foreground "black" :inverse-video nil :box nil 
                :strike-through nil :overline nil :underline nil 
                :slant normal :weight normal :height 140 :width normal 
                :foundry "unknown" :family "Liberation Mono")))))

(add-to-list 'load-path "~/elisp")
(require 'wl-fedora-init)

3.2 辅助脚本

作为一名 GNU Emacs 重度用户,却一度使用vi做些系统管理的工作,因为vi的启动速度快。命令行上输入vi加文件名,修改三五字符,便存盘退出,就这层意义上说,Emacs慢了。当然也可以使用 Emacs server,但是那就复杂了些,如果是远程登录,就更复杂了,搞不好便要启动多个Emacs进程。

这个问题随着multi-tty乃至 Emacs daemon 的出现,便迎刃而解了。首先,我 们可以在命令行上使用 emacsclient -t 的用法,在终端上开启一个frame, 速度堪比vi。如果喜欢图形界面,请使用 emacsclient -c 。不过,敲这么一长串字符,比起vi两个字符而言,还是繁琐了不少。

解决这个问题也很简单,要么做一个alias,要么创建一个短名字的脚本,为了能够用在更多的场合,我选择创建两个bash脚本,名字分别为ectecc

所谓更多的场合,目前来讲也只有一个,就是配合firefox的扩展 It's All Text! 使用。通过这一套工具的组合,便可以使用Emacs编辑原本需要在textarea里面编辑的内容。

3.3 能获得 root 权限的辅助脚本

前面提到曾使用vi做些系统管理的工作,速度快是原因之一,根本原因还是如何 获得root权限,就这一点来说, sudo emacs 根本没法和 sudo vi 相提并论。幸好,我们有TRAMP。

TRAMP本是为了编辑远程文件而设计的,仔细阅读它的文档会发现,它还提供了 使用root权限打开本地文件的功能,方法是 C-x C-f /sudo:: ,后面加上本 地文件名,如 C-x C-f /sudo::/etc/X11/xorg.conf ,此外,对文件名还提 供 TAB 键补全功能,第一次按 TAB 键会要求输入密码。

为了方便在命令行上使用,我创建了两个bash脚本——sudoectsudoecc,以及一个 Emacs Lisp 辅助函数,如下:

(defun wl-sudo-find-file (file dir)
  (find-file (concat "/sudo:localhost:" (expand-file-name file dir))))

4 Emacs lisp

定制、扩展 Emacs 难免要写一些 Emacs Lisp 代码,下面简单介绍几个后面会 用到的特性,Emacs Lisp 手册参见 http://www.gnu.org/software/emacs/manual/elisp.html

  • `autoload'. 很多 Emacs 功能模块都可以通过 autoload 动态加载,即 第一次使用的时候,这样可以减少 Emacs 的启动时间。
  • `require'. 对于没有提供 autoload 特性的模块,就要使用 require 在启动时加载。
  • `eval-after-load'. 可以用于模块加载之后对其进行定制和扩展,这样可 以避免重复加载一个模块导致定制或扩展丢失的现象。
  • `hook'. 许多模块会在特定的时机调用 hook ,给用户一个机会做一些 个性化的设置。
  • `advice'. 在没有 hook 可以使用的情况下,编写 advice 可以把个 性化设置塞进模块。

当找不到相应模块时,使用一个参数的 require 会报错,从而导致 Emacs 启 动失败,使用如下的宏(引自using packages/functions only if they are available ),可以避免这种情况。

(defmacro require-maybe (feature &optional file)
  "*Try to require FEATURE, but don't signal an error if `require' fails."
  `(require ,feature ,file 'noerror)) 

4.1 eldoc, paredit, 以及 find-func

这三个模块使得浏览和编写 Emacs Lisp 程序的过程更加流畅和便捷。

4.1.1 eldoc

eldoc能够在echo area显示函数的参数列表,它对系统自带函数和用户自定义函数一视同仁。例如当输入

 (dolist

的时候,eldoc显示如下帮助信息:

 dolist: ((VAR LIST [[RESULT]]) BODY...)

其中各个参数部分还会随着用户的输入而逐个高亮显示。

4.1.2 paredit

paredit是为了对付 Lisp 程序里无处不在的括号,下面列出它的部分功能:

  • 在用户输入左括号后,自动输入相应的右括号
  • 如果光标在左括号前,那么 C-k 删除整个括号表达式,即使它跨越多行;否则删除到当前一级括号表达式的最后,但保留括号
  • M-s 删除当前括号表达式的左右括号
  • C-S-) 将当前一级的右括号向右扩展一位,即将下一个表达式包含进来

更多功能参见paredit.el中的变量 paredit-commands

4.1.3 find-func

使用 find-func ,可以快速定位emacs lisp函数定义,配置如下:

(require 'find-func)
(find-function-setup-keys)

快捷键如下(以下函数由find-func提供,无需自行定义):

(defun find-function-setup-keys ()
  "Define some key bindings for the find-function family of functions."
  (define-key ctl-x-map "F" 'find-function)
  (define-key ctl-x-4-map "F" 'find-function-other-window)
  (define-key ctl-x-5-map "F" 'find-function-other-frame)
  (define-key ctl-x-map "K" 'find-function-on-key)
  (define-key ctl-x-map "V" 'find-variable)
  (define-key ctl-x-4-map "V" 'find-variable-other-window)
  (define-key ctl-x-5-map "V" 'find-variable-other-frame))

以前都是傻傻地使用 C-h f ,然后把光标移到 *Help* buffer里面的相应链接上,最后按回 车。有了 find-func ,无需移动光标了。摘一段注释

 The funniest thing about this is that I can't imagine why a package so
 obviously useful as this hasn't been written before!!

5 通用工具

5.1 加密

EasyPG把用gnupg加密解密的过程集成的Emacs里面,CVS版Emacs自带EasyPG,如果是 Emacs 22的话,要自己到http://www.easypg.org/ 下载。

使用EasyPG很简单,只需在.emacs里添加如下语句:

(require 'epa)

如果是单独下载的EasyPG,还需要添加一条:

(require 'epa-setup)

这条语句的主要目的就是调用 (epa-file-enable) 使得Emacs遇到后缀名为gpg的文件会自 动解密。

如果希望使用minibuffer输入passphrase,而不是弹出对话框的话,可以将环境变量 GPG_AGENT_INFO 清空。

(setenv "GPG_AGENT_INFO" nil)

然后就可以在Emacs里面直接使用加密文件了,比如使用加密过的bbdb文件数据库:

(require 'bbdb)
(setq bbdb-file "~/bbdb.gpg")

5.2 上网

Emacs-w3m 可以使你用 Emacs 浏览网页,它使用 w3m 程序把网页抓下来,显示 在 Emacs buffer 里面。Emacs-w3m 虽然不支持 CSS 和 javascript,但是对于 大多数只有文字和图片的静态网页,它足够用了;或者,可以访问网站的手机 版,如 http://m.delicious.com/ 。我的配置很简单,主要就是显示图片和记 录 cookie (很多网站用cookie记录登录信息)。

(or (require-maybe 'w3m-load) (require-maybe 'w3m))
(eval-after-load 'w3m
  '(progn
     (setq w3m-default-display-inline-images t)
     (setq browse-url-browser-function 'w3m-browse-url)
     (setq w3m-use-cookies t)
     (setq w3m-use-title-buffer-name t)))

如果使用 CVS Emacs 的话,也要相应地从 Emacs-w3m 的 CVS 服务器上拿开发 版才能使用。

5.3 install-elisp

EmacsWiki上有好多强大的工具可以下载,甚至有一个专门的工具用于下载elisp,这就是 install-elisp.el。如果你还没有下载过,那么需要手工下载、编译、安装和加载,之后 就可以利用它的强大功能完成自动化操作。具体配置参见该文件里面的注释。

如果你像我一样,曾经安装过多个elisp工具,那么批量更新就是个问题,又或者有了一 台新机器,重新安装一遍也很麻烦。使用如下一段小程序,可以解决这个问题。

(defun wl-install-elisp-from-emacswiki ()
  (interactive)
  (let ((install-elisp-confirm-flag nil)
        (emacs-lisp-mode-hook nil))
    (dolist (m '(anything
                 anything-config
                 auto-complete
                 browse-kill-ring
                 htmlize
                 install-elisp))
      (install-elisp-from-emacswiki (concat (symbol-name m) ".el")))
    (dolist (u '("http://www.davep.org/emacs/boxquote.el"
                 "http://code.jblevins.org/markdown-mode/markdown-mode.el"
                 "http://mumble.net/~campbell/emacs/paredit.el"
                 "http://homepage1.nifty.com/bmonkey/emacs/elisp/cldoc.el"
                 "http://www.xsteve.at/prg/emacs/psvn.el"))
      (install-elisp u))))

在批处理过程中不希望用户使用 C-c C-c 逐个确认,所以暂时把 install-elisp-confirm-flag 设为 nil ,当然这样做可能会有很严重的安 全问题,因为根本不知道下载的是什么就运行了。

另外,加载这些模块可能导致以前的配置被覆盖,所以关于这几个模块的配置都 会使用 eval-after-load 保护起来,使得这些配置在模块被重新加载之后仍 然有机会运行。

5.4 Org Mode

5.4.1 简单的 todo (Org Mode)

就我个人来看,Org Mode是近年来最震撼人心的 Emacs Mode 之一,它的出现,使得大量用户更加紧密地团结在Emacs周围。

主页: http://orgmode.org/

代码: git://repo.or.cz/orgmode.git

管理代办事项是Org Mode的核心功能之一,使用起来非常简单。在 Outline Mode 的基础上,Org Mode提供了 C-c C-t 切换任务状态。第一次将人物切换 到 TODO 状态,第二次切换到 DONE 表示完成。下面是一个简单的例子。

 * TODO 写一篇关于Emacs的blog
 * DONE 确认测试全部通过

另外还可以通过 C-c C-s 设置任务开始日期, C-c C-d 设置截止日期。更多功能参见Org Mode手册。

5.4.2 remember

Org Mode 最简单的功能不是管理代办事项,而是记笔记,不需任何配置,就想使 用 Outline Mode 那样即可。除此之外,如果想记录每次记笔记的时间,可以使 用快捷键 C-c ! 来插入一个日期。

Org Mode功能很强大,只是在开始记录的时候稍嫌麻烦,比如要先打开相应的 org文件,选择合适的位置,才能插入内容。如果配合remember使用Org Mode,则 对于那些上下文无关的内容,可以大大减轻辅助工作量。更花哨地,可以配合使 用remember,不仅可以记录日期,还能插入链接。下面是一些配置:

(autoload 'remember "remember" nil t)
(autoload 'remember-region "remember" nil t)
(setq org-reverse-note-order t)
(when (file-exists-p "~/gtd/")
  (define-key global-map [(f8)] 'remember)
  (setq remember-annotation-functions '(org-remember-annotation))
  (setq remember-handler-functions '(org-remember-handler))
  (add-hook 'remember-mode-hook 'org-remember-apply-template)

  (setq org-directory "~/gtd/")
  (setq org-remember-templates
        `((?t "* TODO %?\n  %i"
              ,(expand-file-name "todo.org" org-directory) "Tasks")
          (?m "* %U\n\n  %?%i\n  %a"
              ,(expand-file-name "notes.org" org-directory) "Notes")))

  (let ((todo (expand-file-name "todo.org" org-directory)))
    (when (file-exists-p todo)
      (add-to-list 'org-agenda-files todo))))

想要创建任务,先按 F8 键,然后按 t ,之后输入任务标题、时间、标签或 者更详细的描述,输入完毕之后按 C-c C-c ,将这个任务保存在 ~/gtd/todo.org 文件的 Tasks 大类下面。

5.4.3 周期性的任务 (Org Mode)

只要对任务开始日期稍加修改,Org Mode 就能够管理周期性代办事项。比如周四要开会,可以设置如下代办事项:

 * TODO 开会
 SCHEDULED: <2009-01-22 四>

如果是每周四都开会,就改写成如下的样子:

 * TODO 开会
 SCHEDULED: <2009-01-22 四 +1w>

1w表示每周,另外1d表示每天,1m表示每月。对于周期性的任务, C-c C-t 每次将开始日期修改为相应的下一次开始日期,并保持 TODO 状态不变。

通常情况下,任务开始日期总是严格地按照预定间隔变动,但是当我们需要忽略 掉已经过期的日期时,就可以使用 ++ 或者 .+ 来修饰时间间隔,如

 <2009-01-22 四 ++1w>

的下一次日期一定是今天之后的第一个星期四,而

 <2009-01-22 四 .+1w>

的下一次日期是按今天算起的下一个星期,也就是说,不一定是星期四;如果今天是星期二,那么下一次开始日期就是星期二。

5.4.4 提醒 (Org Mode)

Org Mode本身并没有提供提醒功能,需要配合appt使用。下面是一个简单的配置:

(defun wl-org-agenda-to-appt ()
  ;; Dangerous!!!  This might remove entries added by `appt-add' manually. 
  (org-agenda-to-appt t "TODO"))

(wl-org-agenda-to-appt)
(defadvice  org-agenda-redo (after org-agenda-redo-add-appts)
  "Pressing `r' on the agenda will also add appointments."
  (progn
    (let ((config (current-window-configuration)))
      (appt-check t)
      (set-window-configuration config))
    (wl-org-agenda-to-appt)))

(ad-activate 'org-agenda-redo)

在 Org Mode 的 Agenda View 下,按 r 或者 g ,就可以把有具体时间的 任务添加到appt的任务提醒列表里面。需要注意的是,手工使用 appt-add 添 加的提醒将被清除,无法恢复。所以,当使用本节的配置时,请将任务添加到相 应的org文件里,而不是使用 appt-add

6 程序员的工具

6.1 filecache 和 anything

6.1.1 filecache

经过配置,filecache 能够根据用户输入的文件名,找到该文件的实际位置,省 去了用户回忆和查找的过程。下面是我的配置(忽略git相关目录及文件):

(require 'filecache)
(add-to-list 'file-cache-filter-regexps "\\.git\\>")
(file-cache-add-directory-recursively "/path/to/project")

filecache 没有独立的文档,用法记录在源文件头上的注释里,不过已经足够 了,本来也不是很复杂的东西。使用时有一个小窍门,在使用 C-x C-f 打开文 件时,不用管前面的目录名是什么,直接在后面输入文件名,然后用 C-TAB 补 全,目录名会自动被替换,无需手工修改。

6.1.2 anything

使用filecache可以快速打开项目里的某个文件,但是它的文件名补全功能有一个小小的 局限,就是必须从头开始匹配,不像iswitchb那样可以匹配buffer名的任意部分。配合使 用anything可以解决这个问题。

anything不仅仅可以配合filecache使用,之所以叫anything,就是因为它可以快速打开 anything,而且高度可配置、可扩展。在anything模式下有几个快捷键,左右方向键在不 同分类之间切换; C-nC-p 在不同条目之间切换; C-vM-v 上下翻页。下面是我的配置, 使用 F9 作为快捷键启动anything模式。

(eval-after-load 'anything
  '(progn
     (setq anything-enable-digit-shortcuts t)
     (global-set-key (kbd "<f9>") 'anything)))

(eval-after-load 'anything-config
  '(add-to-list 'anything-sources anything-c-source-file-cache))

(require-maybe 'anything)
(require-maybe 'anything-config)

6.2 etags 和 cscope

etags是GNU Emacs的标配,程序员通常用它来定位函数、变量或其它实体的定义。 对于每个名字之只有一处定义的项目,etags足够用了;如果某个名字对应多出定 义,那么etags会根据用户请求逐个遍历这些定义位置,直到用户找到自己想要的 东西而终止遍历。

cscope提供的功能比etags更强大,它的使用很简单,只需在项目根目录下运行

 find . -name "*.[hc]" -type f >cscope.files
 cscope -b -q -k

即可。

而在GNU Emacs里面也只需一行代码

(require-maybe 'xcscope)

在GNU Emacs里面可以使用绝大多数cscope的功能,然而要想显示函数调用关系 的话,还需要另外的程序,如CbrowserKScope

然而 etags 也有它的优点——速度快,所以,我们不妨同时使用 etags 和 cscope。有了 cscope.files ,生成 TAGS 文件也简单了,如下:

 cat cscope.files | etags -

6.3 hippie-expand 和 auto-complete

6.3.1 hippie-expand

hippie-expand是个非常强大的补全工具,虽然其中的补全文件或路径功能很少用 到,但是在添加 load-path 的时候就很方便了,感觉就像在mini-buffer里输 入路径一样。当然用得最多的还是在写程序的时候了。使用下面的配置可以通过 M-/ 快捷键调用该补全功能。:

(global-set-key (kbd "M-/") 'hippie-expand)

(setq hippie-expand-try-functions-list
      '(try-expand-all-abbrevs try-expand-dabbrev
        try-expand-dabbrev-all-buffers try-expand-dabbrev-from-kill
        try-complete-lisp-symbol-partially try-complete-lisp-symbol
        try-complete-file-name-partially try-complete-file-name))

6.3.2 auto-complete

auto-complete.el提供了与hippie-expand完全不同的补全方式,通过弹出菜单的 形式让用户在候选列表中选择,它的作者用一段视频展示了auto complete提供怎 样的功能。

以下配置,使用 F1 键打开自动补全功能,补全过程中候选列表随着输入的变 化随之更新,选择或取消后,自动补全功能关闭。:

(require-maybe 'auto-complete)
(eval-after-load 'auto-complete
  '(progn
     (global-auto-complete-mode t)
     (define-key ac-complete-mode-map "\C-n" 'ac-next)
     (define-key ac-complete-mode-map "\C-p" 'ac-previous)
     (setq ac-auto-start nil)
     (defun wl-ac-start ()
       (interactive)
       (setq ac-auto-start 6)
       (ac-start))
     (defadvice ac-cleanup (after wl-ac-cleanup ())
       (setq ac-auto-start nil))
     (ad-activate 'ac-cleanup)
     (define-key global-map (kbd "<f1>") 'wl-ac-start)
     (add-hook 'emacs-lisp-mode-hook
               (lambda ()
                 (make-local-variable 'ac-sources)
                 (setq ac-sources
                       '(ac-source-words-in-buffer ac-source-symbols))))

     (defvar ac-source-etags
       '((candidates
          . (lambda () (all-completions ac-target (tags-completion-table))))))
     (defun wl-add-ac-source-etags ()
       (make-local-variable 'ac-sources)
       (add-to-list 'ac-sources 'ac-source-etags))

     (add-hook 'c-mode-common-hook 'wl-add-ac-source-etags)))

6.4 repository 书签

psvn 提供了一个书签功能,可以快速定位 subversion repository,很强大。 模仿它的实现,可以将该书签功能扩充到支持 CVS 和 Git 。

(defvar wl-vc-bookmark-list nil)

(defvar wl-vc-status-completing-read-function 'wl-iswitchb-completing-read)

(defun wl-vc-status-via-bookmark (bookmark)
  (interactive
   (list
    (let ((completion-ignore-case t))
      (funcall wl-vc-status-completing-read-function
               "VC status bookmark: " wl-vc-bookmark-list))))

  (unless bookmark
    (error "No bookmark specified"))

  (let ((directory (cdr (assoc bookmark wl-vc-bookmark-list))))
    (if (file-directory-p directory)
        (cond ((file-exists-p (expand-file-name "CVS" directory))
               (cvs-examine directory nil))
              ((file-exists-p (expand-file-name ".svn" directory))
               (svn-status directory))
              ((file-exists-p (expand-file-name ".git" directory))
               (magit-status directory))
              (t
               (dired-x-find-file directory)))
      (error "%s is not a directory" directory))))

(define-key global-map (kbd "<f7>") 'wl-vc-status-via-bookmark)

(defun wl-vc-add-to-bookmark-list (bookmark &rest repos)
  (dolist (repo repos)
    (when (and (file-exists-p repo)
               (file-directory-p repo))
      (when (= 0 (length (file-name-nondirectory repo)))
        ;; remove trailing slash to get the last directory name
        (setq repo (substring repo 0 -1)))
      (add-to-list bookmark (cons (file-name-nondirectory repo)
                                  (file-name-as-directory repo))))))

接下来使用 wl-vc-add-to-bookmark-list 添加 repository 。如:

(wl-vc-add-to-bookmark-list 'wl-vc-bookmark-list
                            "~/project/proj1"
                            "~/project/proj2")

之后,就可以通过 F7 快捷键管理注册过的 repository 了。

6.5 yasnippet

主页: http://code.google.com/p/yasnippet/

演示:YouTube,下载高清晰度版本

以下配置选择性的在某些 major mode 下打开该功能,由于 org-mode 使用了 TAB 键,所以在 org-mode 里面使用 F4 展开模板。:

(when (file-exists-p "~/elisp/3rd-party-lib/yasnippet")
  (add-to-list 'load-path "~/elisp/3rd-party-lib/yasnippet")
;;; Workaround: do not show menu until I find a way to show menu only
;;; on certain window.
  (setq yas/use-menu nil)
  (require-maybe 'yasnippet)
  (eval-after-load 'yasnippet
    '(progn
       (yas/initialize)
       (yas/load-directory "~/elisp/3rd-party-lib/yasnippet/snippets/")
       (remove-hook 'after-change-major-mode-hook
                    'yas/minor-mode-auto-on)
       (add-hook 'cperl-mode-hook
                 'yas/minor-mode-auto-on)
       (add-hook 'perl-mode-hook
                 'yas/minor-mode-auto-on)
       (add-hook 'c-mode-common-hook
                 'yas/minor-mode-auto-on)
       (add-hook 'html-mode-hook
                 'yas/minor-mode-auto-on)
       (add-hook 'org-mode-hook
                 (lambda ()
                   (make-local-variable 'yas/trigger-key)
                   (make-local-variable 'yas/next-field-key)
                   (setq yas/trigger-key (kbd "<f4>")
                         yas/next-field-key (kbd "<f4>"))
                   (yas/minor-mode-auto-on))))))

下面的模板用于 GCC 开发时编写遍历 basic block 和 edge 的循环。

(eval-after-load 'yasnippet
  '(progn
     (yas/define-snippets
      'c-mode
      '(("bb" "basic_block ${1:bb};

FOR_EACH_BB (${1:bb})
  {
    $0
  }" "FOR_EACH_BB (...) { ... }")
        ("bsi" "for (${1:si} = bsi_start (${2:bb});
                     !bsi_end_p (${1:si});
                     bsi_next (&${1:si}))
  {
    $0
  }" "bsi_start (...) { ... }")
        ("ee" "edge ${1:e};
edge_iterator ${2:ei};

FOR_EACH_EDGE (${1:e}, ${2:ei}, ${3:bb->succs})
  {
    $0
  }" "FOR_EACH_EDGE (...) { ... }")))))

编写模板的方法参见 http://code.google.com/p/yasnippet/wiki/HowtoDefineSnippet 或者 How to define a snippet

7 学习资源

7.1 Emacs 手册

Emacs 手册是学习 Emacs 最主要的资源,对于初学者尤其如此。手册内容很多, 可以先从感兴趣的部分开始。通读一遍是不够的,要反复阅读,而且要边读边实 践。更重要的是,要反思自己的操作方式是否合理,有无可能的改进。

7.2 Emacs Lisp 参考手册

基本的语法和概念要掌握,否则没有办法去配置和扩展 Emacs 。幸运的是, Emacs Lisp 语言的核心部分非常简单,无需花费多大功夫。不要被 Lisp 吓倒 了,其实 Lisp 属于易学难精的语言,上手还是很容易的。

7.3 EmacsWiki

http://www.emacswiki.org/ 是各种 Emacs 工具的网上聚集地。

7.4 邮件列表和新闻组

提问可以选择邮件列表或新闻组。

订阅邮件列表参见 http://savannah.gnu.org/mail/?group=emacs

新闻组可以在 Google Groups 查看;或者用 Gnus 通过 nntp 方式订阅,地址可 以在 http://dir.gmane.org/index.php?prefix=gmane.emacs 上找到。

8 后记

本文在 Emacs 里完成写作,使用 Org Mode 管理组织结构、样式风格,以及写 作进度,并使用版本控制工具 Subversion 管理原稿。有 HTML 和 PDF 两种文 件格式可供阅读,其中 PDF 使用文泉驿正黑字体。

获得原稿请安装 Subversion ,并运行如下命令:

 svn co http://www.wanglianghome.org/svn/emacsbook/trunk emacsbook

欢迎一同探讨、学习与 Emacs 相关的知识、工具。对于本文的内容如有任何意 见和建议,也非常欢迎提出。 Patches welcome!

相对于文字,我更善于写代码,因此本文的内容,尤其是其中涉及到的配置,可 能与我实际使用的有出入。如需获得我的最新配置,可以使用如下命令:

 svn co http://www.wanglianghome.org/svn/elisp

Date: 2012-05-14 13:12:00 CST

Author: 王亮

Org version 7.8.06 with Emacs version 24

Validate XHTML 1.0