Emacs定制与扩展

来源:互联网 发布:人工智能能使死人复活 编辑:程序博客网 时间:2024/06/05 04:19

原文地址http://www.wanglianghome.org/org/programming/emacsbook/emacs.html

 

Emacs定制与扩展

Table of Contents

  • 1 版权
  • 2 前言
  • 3 emacs 和 emacsclient
    • 3.1 CVS Emacs
      • 3.1.1 Daemon
      • 3.1.2 字体
      • 3.1.3 问题
    • 3.2 辅助脚本
    • 3.3 能获得 root 权限的辅助脚本
  • 4 Emacs lisp
    • 4.1 eldoc, paredit, 以及 find-func
      • 4.1.1 eldoc
      • 4.1.2 paredit
      • 4.1.3 find-func
  • 5 通用工具
    • 5.1 加密
    • 5.2 上网
    • 5.3 install-elisp
    • 5.4 Org Mode
      • 5.4.1 简单的 todo (Org Mode)
      • 5.4.2 remember
      • 5.4.3 周期性的任务 (Org Mode)
      • 5.4.4 提醒 (Org Mode)
  • 6 程序员的工具
    • 6.1 filecache 和 anything
      • 6.1.1 filecache
      • 6.1.2 anything
    • 6.2 etags 和 cscope
    • 6.3 hippie-expand 和 auto-complete
      • 6.3.1 hippie-expand
      • 6.3.2 auto-complete
    • 6.4 repository 书签
    • 6.5 yasnippet
  • 7 学习资源
    • 7.1 Emacs 手册
    • 7.2 Emacs Lisp 参考手册
    • 7.3 EmacsWiki
    • 7.4 邮件列表和新闻组
  • 8 后记

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脚本,名字分别为ect和ecc。

所谓更多的场合,目前来讲也只有一个,就是配合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脚本——sudoect和sudoecc,以及一个 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的功能,然而要想显示函数调用关系的话,还需要另外的程序,如Cbrowser或KScope。

然而 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))))))

下面的模板用于在 org-mode 下引用 Emacs Lisp 代码。

(eval-after-load 'yasnippet  '(yas/define-snippets   'org-mode   '(("elisp" "#+BEGIN_SRC emacs-lisp  $0#+END_SRC" "#+BEGIN_SRC emacs-lisp ... #+END_SRC"))))

接下来的模板用于 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

Author: 王亮

Date: 2010-08-06 12:57:01 CST

HTML generated by org-mode 7.01trans in emacs 23

原创粉丝点击