Emacs的日常生活

Emacs的日常生活
robinh

October 15, 2003

Chapter 1

前言

有 很多很多现成的文章介绍 Emacs 的。大致有那么两种:一种介绍说, Emacs 是一个无比强大的文本编辑器,但是不管谁用了一下都会觉得,这个文本编辑器真是难用了,所有的命令都是组合出来的怪物。甚至 Emacs 自己的帮助文档里面也说,用 Emacs 多的了用户会希望终端的输入设备加上两个脚踏板);另一种介绍说 Emacs 是一个无比强大的 IDE ,但是对于象我们这样见过 Visual Studio 之类市面的新新人类来说,这“无比强大”大致上和“刀枪不入”是一个档次的广告。

Emacs 在我的概念中到底是个什么样的呢?它是一个环境。 Emacs 可以什么都是,也可以什么都不是,因为环境本身不创造什么。 Emacs 的强大是因为前人已经在这个环境中作了很多尝试,所以你不用从头发明轮子。 Emacs的强大是因为它能够将各种软件统一到同样一个界面底下来,你就可以以相仿的方式,操作各种其实并不相仿的程序。 Emacs的最强大之处在于它本身并不强迫你接受什么,不就是个Lisp程序嘛,不满意的地方你可以改,哪怕你其实不怎么懂Lisp。

以下的文字,说起来有一些混乱,因为我常常引用一些在介绍的同时并没有给出说明的概念。这又有什么关系呢?Lisp不是C++,它并不要求你在真正开始用这个概念之前就已经掌握了它的实际含义。而你真正想到用这个概念的时候,你肯定已经掌握了它。

Chapter 2

Emacs基础

Emacs最好的入门教材,不是 Emacs 自己带的 toturial 。实际上就是这个 tutorial 给好多人非常恶劣的印象, Emacs 是一个操作复杂的变态编辑器。人性化一点的 Emacs 教材,大致有这一些:

  1. Sams Teach Yourself Emacs in 24 Hours ,可以从这里下载。

  2. 《如何使用Emacs编辑器》,这是已经出了中文版的了,不过不值得推荐,因为是在是太贵太陈旧了。

关 于 Elisp 的入门教材是《GNU Emacs Lisp编程入门》,这本书实际上就是elisp introduction这个info文件的翻译,翻译质量不错,而且纸版的书看起来确实比较舒服。Emacs自带的有一个Elisp手册,真的就只能当 手册用,全无可读性可言。

使用Emacs的技巧在http://www.emacswiki.org 上有很多,常去翻翻很长见识。

因 为 Emacs 是一个单线程的应用程序,所以有可能一个操作占用了太长的时间,让使用者觉得很不爽。实际上很多 Emacs 的使用者,同时都起若干个 Emacs 进程,其中一个专门做一些耗时的操作,比如收信。如果你只愿意起一个进程,并且实在不耐烦等下去,C-g可以直接将进行到一半的操作停止下来。 Emacs 里面所有作到一半的事情,只要 Emacs 没有崩溃,就可以用C-g停止掉。

Emacs 对自己的描述非常完备,C-h可以带你进入 Emacs 的文档世界。常见比如:

  1. C-h k可以告诉你按下某一键的时候到底有什么函数被调用;

  2. C-h m可以告诉你当前的模式到底有什么特别之处;

  3. C-h f可以告诉某一个函数到底有一些什么作用。

习惯这一些你会发现, Emacs 里面查帮助其实比 MSDN 还要方便。

Chapter 3

如何安装Emacs

是 的是的,我知道你会有自己安装 Emacs 的方法。你要是 RedHat 的用户,你肯定是下载 rpm 包回来装;你要是用的 debian ,你多半是apt-get;你要是用 gentoo ,你就 emerge 一把;你要是用 Windows ,你会上网去找安装文件;你要是用BSD的,你多半就去make port了;再大不了你会用 cvs 把最新的 Emacs 代码拉回来,然后在自己的机器上重新编译。

但是,这些方法只能帮助你安装 Emacs 本身。 Emacs 绝不仅仅是一个大大的tar包,或者是一个大大的安装文件能够包括的。你下回来的 Emacs ,相当于是一个Java的虚拟机,你真正面对的,是互联网上elisp资源的汪洋大海。所以我们关心的,其实是怎么安装这些零零碎碎从网络上 download 回来的资源。

Emacs 的软件,一般都是一个压缩包。因为 Emacs 没有象 Python 那样完善的安装机制,所以这个安装过程一般都要看着说明文档自己一步一步来。作为一个 Emacs 平台下的软件,它总得有这样几个组成部分:

  1. 有部分是用 elisp 写的,不然的话没法给 Emacs 用;

  2. 可能有部分是平台相关的代码,一般都是些可执行的二进制文件或者脚本。

  3. 可能还有一些info格式的相关文档。

可执行的二进制文件只要放在$PATH环境变量所包含的目录下,就可以被调用了,这和一般的程序并无区别。

而 elisp写就的 Emacs 软件则稍微有一些不同。你如果想在 Emacs 里面调用一个函数,你得先有这个函数的定义;为了有这个函数的定义,你必须显式地将拥有函数定义的elisp文件载入 Emacs 环境;为了将 elisp文件正确载入 Emacs , Emacs 会在load-path这个变量所包含的所有目录下寻找同名文件。所以,说到底,安装elisp软件的过程,就是将它拷贝到某一个 load-path目录下,并且在.emacs文件显式载入这个文件的过程。

一 般FAQ上给出的建议,是将自己下回来的elisp包,放在某一个名叫 site-lisp的目录之中。这是个不错的建议,不过问题在于大多数Linux的发布版厂商,习惯把他们自己维护的一些elisp资源包也扔到 site-lisp这个目录里面去,这造成的结果就是,有一天你突然想备份自己的Emacs资源的时候,你突然发现分不清那些是自己想要的,哪些是不小心 让系统装上去的垃圾。

所以,建议你还是在自己的$HOME目录底下建一个专门给Emacs用的目录,名字嘛,可以就叫 emacs。$HOME/emacs这个目录里面至少可以分出两个子目录来,一个是config,我们从此就可以把所有Emacs配置的内容放到这个目录 底下,并且分门别类起来;另一个叫package,里面就可以堆放各种从网络上下载回来的elisp资源。

所有扔到site-lisp目录里面的elisp文件夹,在emacs起动的时候都会自动被加入load-path列表,这是site-lisp/subdirs.el的功劳。我们的package/subdirs.el也要有这个功能。

(defun my-add-subdirs-to-load-path (dir)
(let ((default-directory (concat dir "/")))
(setq load-path (cons dir load-path))
(normal-top-level-add-subdirs-to-load-path)))

(my-add-subdirs-to-load-path "~/emacs/packages")

然后在.emacs文件中显式载入这个文件。

(load "~/emacs/packages/subdirs")
 
   

Chapter 4

让Emacs变得轻快

Emacs最为人诟病的一点,就是它起动太慢。也难怪,光看可执行文件的尺寸,一个 emacs 差不多就是一个 vim 的5倍了,起动起来能快得了嘛。更何况大多数人都把以前用 vi 的习惯照搬到用 emacs 的情况下面来,每次要编辑一个文件的时候,就起动一个新的 Emacs 进程。您要是也习惯这种 Vi 风格,那您用 Emacs 可就得慢慢等着啦。因为 Emacs 的风格,是所有事情都在 Emacs 里面完成,包括浏览目录,打开文件。当然, Emacs 也不是完全排斥 Vi 风格,至少 Unix 环境下的 Emacs 就自带了一个叫 emacsclient 的程序,是个完全轻量级的东东,让你可以象起动 vi 那样轻松起动 emacs 。还有一个独立的程序,叫 gnuserv 的,更尽一步,支持 Windows 平台下的 Emacs ,而且功能也更多一些。反正是要用,干脆就用个好的吧。

Unix下的gnuserv可以从 http://www-uk.hpl.hp.com/people/ange/gnuserv/ 下载, Windows下的gnuserv建议在 http://www.wyrdrune.com/gnuserv.html 里面找。 gnuserv的安装并不复杂,无非是将几个可执行文件放到%PATH%变量提及的地方,然后将 gnuserv.el 放到 load-path 所包含的目录中去,最后在配置文件中加入两句配置:

(require 'gnuserv)
(gnuserv-start)

这是Emacs安装插件的标准做法。以下还有一些代码,是从David Vanderschel的帖子上抄来的,偶一般将其中的dv-close-client-frame绑定到C-上,估计也会有人喜欢绑定到C-F4上。

(defvar dv-initial-frame 
(car (frame-list))
"Holds initial frame.")

(defun dv-focus-frame (frame)
"pop to top and give focus"
(make-frame-visible frame)
(raise-frame frame)
(select-frame frame)
(w32-focus-frame frame))

(defvar dv-mail-frames ()
"Frames created by dv-do-mailto")

(defun dv-focus-initial-frame ()
"Make the initial frame visible"
(dv-focus-frame dv-initial-frame))

(defun dv-do-mailto (arg)
"For handling mailto URLs via gnudoit"
(dv-focus-frame (make-frame))
(message-mail (substring arg 7))
(delete-other-windows)
(setq dv-mail-frames
(cons (selected-frame) dv-mail-frames)))

(defun dv-close-client-frame ()
"Close frame, kill client buffer."
(interactive)
(if (or (not (member (selected-frame) dv-mail-frames))
(and (> (length (buffer-name)) 4)
(equal (substring (buffer-name) 0 5)
"*mail")
(not (buffer-modified-p))))
(kill-buffer (current-buffer)))
(setq dv-mail-frames
(delete (selected-frame) dv-mail-frames))
(if (equal (selected-frame) dv-initial-frame)
(iconify-frame)
(delete-frame)))

(defun dv-paste-to-temp ()
"Load clipboard in a temp buffer"
(dv-focus-frame (make-frame))
(switch-to-buffer (generate-new-buffer "temp"))
(clipboard-yank))

再如果,你和我一样是个win32底下的懒人,那么估计你还需要这个。

(defun w32-restore-frame (&optional arg)
"Restore a minimized frame"
(interactive)
(w32-send-sys-command 61728 arg))
(defun w32-maximize-frame (&optional arg)
"Maximize the current frame"
(interactive)
(w32-send-sys-command 61488 arg))
(w32-maximize-frame)
(add-hook 'after-make-frame-functions 'w32-maximize-frame)

这可以使得每一个新打开的frame都自动最大化。

Chapter 5

安排自己的时间

如果你已经是一个善于管理自己时间的人,那么Emacs的这些功能可能就全都没有必要了。不过,如果你和我一样在这个方面拎不清,Emacs 就显得很帮忙了。

以下的内容加入到.emacs文件中,会有不少帮助。

(setq display-time-24hr-format t)
(setq display-time-day-and-date t)
(display-time)

(setq todo-file-do "~/emacs/todo/do")
(setq todo-file-done "~/emacs/todo/done")
(setq todo-file-top "~/emacs/todo/top")

(setq diary-file "~/emacs/diary")
(setq diary-mail-addr "[email protected]")
(add-hook 'diary-hook 'appt-make-list)

将所有emacs里面用到的文件都放到~/emacs目录中去,偶觉得是个好习惯。

对于大多数预期要做的事情,使用todo模式是最方便的,偶的习惯是访问do文件的时候,作一个bookmark(使用M-x bookmark-set命令),这样以后访问起来就很方便了。bookmark是Emacs很有用的功能,偶一般就把 list-bookmark绑定到F12上去,随手就能钩着。想起什么事情的时候,随手就切到todo那边去,找一个catalog,用I命令插入一个新的entry;做完了一件事情,随手切到todo那边去,用d或者f就能把entry去掉。每个星期结束的时候,看看done文件,就会有“日子过得好充实阿”酱紫的感叹。

也有的事情没有那么重要,写不成todo的。这个时候就用 appointment 。用 (setq appt-issue-message t)确认打开了约会提醒功能,然后用appt-add命令就可以加入新的约会提醒。比如在电话里和mm吵架了,挂了电话以后,在 Emacs 里面使用M-x appt-add命令加一条记录,估计一下半个小时以后,给mm打个电话赔礼道歉。半个小时以后, Emacs 就会跳出一个小框来提醒说,该打电话了。当然,如果半个小时之内,mm主动打电话回来修好,那就用 M-x appt-delete命令删掉提醒好了。

对于那些周期性比较长的事情,可以用diary。我刚开始用diary的时候,以为diary是用来帮助写日记的,所以试了一下觉得好难用阿。仔细看看,发现diary其实是用来做行程管理的。

单独用 diary 没什么意思的,所以info里面 diary 也是和 calendar 放在一起的。偶把 calendar 绑定到F8上 (global-set-key [(control f8)] 'calendar)。启动calendar会出现一个小窗口,显示当前日历。calendar 模式底下命令很多,但是常用的就那么几个,.命令可以跳回当今天,o命令可以跳到某一个月。g系列命令表示goto,可以跳到指定的某一天,g d 是跳到某年某月某日,g c是跳到某年某星期的星期几,g C可以跳到阴历的某一天。p系列命令表示print,可以按照某格式显示当前日期,比如p C就可以显示当前的阴历日期。偶们现在比较关心的是i系列命令,i d是加入当前这一天的行程安排,d表示的是day,显然依此类推还有m,y,w。

比如mm打个电话来说,星期天要陪她去颐和园。用Emacs记一下吧,免得忘了。打开calendar,跳到星期天上面,i d,陪mm去颐和园。又比如老板说,以后每个星期一都给我交一份报告上来,打开calendar,跳到某个星期一上面,i w,交报告给老板。还有一个常用的,比如mm生日是 9/29,打开calendar,跳到9/29,i a,a代表anniversary,mm生日。养成用diary的习惯以后,经常性的打开calendar,跳到某一天,按一下d,就可以看那一天有哪些安排了。相比之下,h命令对偶来说就很垃圾。

diary文件里面可以写一些更详细的内容,比如直接把一个约会提醒写到diary里面,偶觉得这个不是很方便。diary文件不过是一个纯文本,有什么不满意的话,可以直接去修改,记得打开diary文件的时候,切回基本模式,不然很多东西是看不到啦。

有的更完整一些的模式,比如plan模式,需要额外从网上下载,可以将作出的plan转换成pp的个人主页。不过我是不会用的啦。

Chapter 6

用Emacs写东西

虽然写程序也叫做写东西,可是我们现在要讨论的不是它。我们大部分的时间里写的,是拿到bbs上灌的,是留到以后查的。Emacs有一些不错的特点,可以方便上面说的这些事情。

大部分的时间里,我们要写的文章写成TEX挺困难,因为不是那么有条理;但是直接当纯文本处理,似乎又太没有条理。这种时候,用Emacs 的outline-mode就很合适。outline模式最大的好处是简单:只要在行首放上几个星号“*”,就可以表达文章的条理关系。比如,看《爱丽丝漫游奇境》是做一点摘录,先打开一个新文件C-x C-f;然后打开outline模式M-x outline-mode;再然后呢,第一章的内容,先来一个*吧,写上第一章的题目;第一章的写完了,C-c C-c就可以把前面写的内容都折起来,只留下一个标题,看起来很是清爽。过一段时间想起来翻查的时候,C-x C-f打开文件,还是进入outline-mode,C-c C-o就把所有内容都折起来了,只留下标题,文件里面到底有些什么内容,也都一目了然了。

这样做有一个缺点,就是很容易把所有内容都堆到同一个文件里面,眼看着文件越来越大,很容易就要上兆了。不过Emacs是支持直接编辑压缩文件的,用auto-compression-mode就可以。

另外一个经常让人头疼的问题,就是文章的排版。特别是要拿到bbs 上贴的文章,bbs虽然号称支持自动换行,但是那也只是个聊胜于无的东西,要想给人看,还是得手动换行才行。Emacs可以把一句长长的话自动分成若干行,而且这个效果一般都很让人满意。只要把point移动到需要重新整理的段落之中,M-q,就可以完成排版的工作。如果这样还嫌麻烦,那么做成宏也是个不错的注意。如果文章是自己在写,那么打开 auto-fill-mode,是个不错的注意。auto-fill模式可以在写的同时,帮你做换行的工作。

如果写的东西大部分是英文的,还可以考虑打开flyspell-mode。不过这个东西需要后面有ispell支持。win32底下有一个native的ispell v4,不过用起来挺不爽的:v4是一个已经放弃的版本,这是一;和现有的其他模式搭配也不愉快,这是二。v3里面现成的win32版本有两个,建议装一个 cygwin。 另外也有一个win32 native的,可以从 http://www.fsci.fuk.kindai.ac.jp/ kakuto/win32-ptex/web2c75-e.html 找到,日本人做的,我也没有试过。打开flyspell-mode之后,写东西的过程当中,ispell认为写错了的单词,就会自动高亮,很是显眼。 M-$可以让Emacs提示你写错的单词到底应该怎么拼。win32的Emacs 21还没支持 tooltip,所以有些看起来很炫的功能不能用。不过偶觉得就上面说的几种模式,一般过日子也就足够了。

尝试过 wiki 的同学,也可以试用一下 Emacs 下的 wiki 模式,不过偶以为 wiki 模式不适合给说中文的人用。

Chapter 7

管理自己的地盘

我习惯没事情干的时候,就逛自己的硬盘。相信很多人有和我类似的毛病,上网的时候看到好的文章就存下来,时间一长,硬盘的各个角落里面就堆满了各种各样的html,txt文件。只能常常抽空遍历一下自己的目录,看看又多了一些什么东西。这件事情用cmd.exe可以做,用资源管理器也能做,不行话还有wincmd,totalcmd之类的软件。当然用惯命令行的还是觉得用sh.exe最好。

Emacs既然是个八卦,就会八卦到底,它至少提供了另外两种选择, eshell 和 dired 。

eshell 看起来就很象一个 shell 了,不过拿它就做一个 shell 那也太委屈它了, eshell 带的 pcomplete 自动补全功能比 bash 之类的shell,还是有差距的。但是eshell的特长在于,可以直接使用 emacs lisp 的函数做命令。比如偶比较喜欢的 (defalias 'vi 'find-file) (前提是系统里面没有vi的可执行文件),这样在eshell里面,vi一个文件,就会弹出一个新的 emacs buffer 。充分发挥想象力吧。唯一要注意的是, eshell 里面不能用C-c取消一个输入了一半的命令,我的做法一般是C-a C-k,其实也不太麻烦的。

说到自动补全,不知道什么时候开始,突然发现几乎所有的shell,所有的编辑器都支持用tab来做自动补全了。Emacs当然也可以这样设定,不过有的时候,我们还是会怀念用tab来indent,不是吗?用这样一个函数吧:

(defun my-indent-or-complete ()
"如果在词尾,那就hippie-expand,否则就indent"
(interactive)
(if (looking-at ">")
(hippie-expand nil)
(indent-for-tab-command)
))
(global-set-key [(tab)] 'my-indent-or-complete)

hippie-expand虽然已经很不错了,不过我们可以让它更强一点的,

(autoload 'senator-try-expand-semantic "senator")

然后再

(setq hippie-expand-try-functions-list
'(
senator-try-expand-semantic
try-expand-dabbrev
;;........
))

当然前提是要装一个semantic,这个以后再说。

Dired看起来就更象一个wincmd。常用的命令也就是v(查看),e (编辑),d(标记删除),x(执行删除)。以前一度,我喜欢再dired里面做el文件的byte compile,只要在需要compile的文件上面按B就可以了,不过现在发现,那样还不如用这样一行命令合算:

(byte-recompile-directory "/path/to/somewhere" 0 t)

自动重新编译一个目录下面所有的el文件。Emacs和python不一样, elc 和el执行速度会差的很多,mule-ucs就是一个典型,编译之前启动一次要半分钟,编译之后启动就是一眨眼的事情了。当然编译也是一个很费时的事情。真是的。

Chapter 8

用Emacs写程序

Emacs号称是一个强大的IDE,可是往往被人误解。甚至常常有人以为,让VS.net的热键设置和Emacs一样,VS.net就算可以模仿Emacs了,这个基本上是比天只小一点点的笑话。

还是从写还是吧。

基本上那些常见的编程语言,Emacs都有支持。我们现在说的支持,基本上就限于有一种对应于这种语言的major mode。最常见的就是cc-mode了,甚至还专门拿出来,在sourceforge上做了一个项目的说。cc-mode可以不错的支持各种语法上有些类似c的语言,甚至于idl。

不过, cc-mode 不支持c Sharp 。 google 上能搜到一些给 Emacs 用的 c sharp major mode ,我觉得 http://davh.dk/script上的那个不错;别的大都需要对 Emacs 自带的 cc-mode 做替换,让人觉得很不爽。前面提到的那个,在 Emacs 21.3.xx上可能需要作一点小的修改,大概621行左右的位置, (c-common-init)改成(c-common-init 'c-mode) ,大致如此。

Python mode需要到http://www.python.org上去下载。

有了这些以后,基本上不愁写程序的时候的语法加亮的问题了。(其实本来也没什么好愁的,毕竟这是最基本的要求)。

有人偏爱ue那样,把当前行高亮的样子,那就先打开M-x hl-line-mode

有人看到 ThisIsASimpleVarInJava 就觉得郁闷,那就打开 M-x glasses-mode

关于补全,上次提到过,hippie-expand加上semantic是现在最好的选择了。dabbrev-expand完全不懂语义的,常常给扩展出一些莫名其妙的内容来,semantic就不一样了。它至少是懂的语义的,expand的结果看起来就合理的多,有的时候甚至能够认出某一个变量的类型来,让我激动了老半天,当然,只是有时候阿。semantic可以从 http://sf.net/projects/cedet上下载。别的还有一些东西,比如jde或者ecb都是建立在semantic的基础上的,写java的话,也可以用jde,比elipse之类当然是要轻的多了。ecb偶没用过。 http://www.xref-tech.com上的xref 支持更出色,不过那就不是自由软件的范畴了。

关于宏,c语言最麻烦的可能是宏了。常常是面对嵌套了若干层的宏,看不出一个所以然来。 这个时候,可以直接用C-c C-e,对已经选定的区域做预处理,预处理的结果会显是在另外的buffer里面。这项工作缺省使用cpp来做,不过只要编译器支持从标注输入读入代码,好像都可以正常工作。另外有一些简单的宏,比如用来做平台选择的,直接用 hide-ifdef-mode就可以摆平,都免去了调用预处理器的麻烦。

关于代码隐藏,其实偶一般只用一个C-c @ C-c,hs-troggle-hiding。基本上能够满足要求了。 不过用之前记得先打开M-x hs-minor-mode。

关于文件,有一个很方便的命令。取个名字叫my-find-related-file。这个命令可以打开当前.c文件中所有include了的文件。

(defun my-find-related-file ()
"Find all related files in this buffer"
(interactive)
(save-excursion
(let ((my-buffer (current-buffer)))
(goto-char (point-min))
(while (search-forward-regexp " *# *include" (point-max) t)
(progn
(ff-find-other-file)
(switch-to-buffer my-buffer)
)))))

还有就是注意,ff-search-directories早一点设定,用过一次ff函数以后,再setq就晚了。 ;-) 以上。

关于查看帮助,有的选的有info和woman。woman可以用来看man能查到的帮助。一般可以把下面这点代码绑定到f1上。

(global-set-key [(f1)] (lambda() 
(interactive)
(let ((woman-topic-at-point t))
(woman))))

也可以用C-h C-i在info里面查看函数。这些都很简单了。如果你还不满足,那就直接google吧,可以考虑利用browser-url。 ;-)

关于自动排版。自动排版算是一个比较重要的功能,特别是对于那些版式和程序结构没有影响的语言,让代码的排版比较迎合个人的喜好,这个不过分吧。对于整个文件做重排,一般要C-x h选定整个buffer,然后 C-M-重新排版。 这个一般比较耗时间,如果代码文件确实很长的话。折中的做法是在一个代码端开始的地方,就是{处,用C-c C-q,酱紫可以排版一个代码段。排版的风格可以用c-set-style来设定,偶一般用 stroustrup,表达偶对他的仰慕。如果对于具体的某一个设置不满意,可以在不满意的地方用C-c C-s看一下,这里缩进的设置是取决于什么的;然后可以用C-c C-o修改之。

关于移动。程序代码里面移动来移动去,也是一个问题。最直接的办法当然是用鼠标了。不过各种模式里面一般也都会对常见的移动键位做修订,可以自己试一试,找出比较常用的键来。几乎所有的模式都会支持以函数定义为单位移动,这个一般都很好用。

关于tag,Emacs附带的etags可以用来生成TAGS文件。在某个源文件中 M-.,Emacs会询问访问哪一个TAGS文件,这个基本上和vi差不多。更强悍一点的是ebrowser,生成一个BROWSER文件,只要find-file这个文件,就会进入ebrowser的模式,有一点类似于cscope。speedbar在这个时候可以来帮忙,试试看就知道了,不过好不好使就完全是一个见仁见智的问题。上次说到的ecb(sf.net/projects/ecb),也着眼于解决这一类问题,有兴趣试试?

关于grep,Emacs自己没有grep功能(ft)。不过好在grep这个东西不管哪个平台都有,win32底下叫做findstr(ft again)。可以尝试一下 grep,grep-find。 前者是直接把参数传递给grep的,后者是把参数传递给find + grep,两者都会在当前buffer对应文件所在的目录下面进行。反正和直接在console下面玩没什么两样的。

关于diff,ediff模式很不错的。比较爽的做法是在eshell里面用。 ediff a.c.orig a.c。 特别注意的是,ediff的session控制区是一个小窗口,那个小窗口关掉,就算是退出ediff了哦。ediff可以忽略空格的。对应的epatch较之直接用patch的优点就在于,patch完了以后就直接进入 ediff模式,什么地方做了改动一目了然。

关于注释,虽然最简单的做法是用C-c C-c注释掉整块已经选定的区域,但是这种做法不一定是最好的。如果是想将暂时不需要的代码抹掉,还是用#if 0/#endif比较合适,因为我一般用flyspell-prog-mode来检查注释里面有没有单词拼写错误。如果你不想看到代码里面被指出很多拼写错误来,那还是不要滥用C-c C-c的好。

xref 是一个用起来挺不错得 refactory 包,它也可以用来完成大多数例如 symbol 补全一类得工作。唯一让人觉得有点不爽的是,它是一个版权软件,是需要 license 的。 当然,它也提供了免费试用版本下载的;同时也提供了源代码下载,你也可以试着用xref重构它自己的代码。

Chapter 9

编译你的程序

如果Emacs只能拿来写东西,那是不够的。至少,因为Emacs可以通过 M-!直接运行外部程序,所以理论上我们是可以在Emacs里面做一切事情的。嗯,理论上。

写了一点代码以后就会急急忙忙的去考虑该编译一下了。 M-x compile 就可以了。 compile 缺省命令是make -k, Emacs 会在 minibuffer 里面跳出来问,compile的命令到底应该用什么呢?如果把 compilation-read-command 设成nil,它就不会那么罗嗦了。compile使用的命令是由 compile-command ,这个变量可以自己调整。有的IDE习惯每次文件存盘的时候就会做一次编译,比如 eclipse ;如果你也有这样的偏好的话,可以吧 compile 命令放到 after-save-hook 里面去。

编译一切顺利那当然很好了,不过一般都不会是酱紫的。这个时候用 C-x `就可以跳到错误地点。 Emacs是通过对错误信息做正则匹配来找相关信息的,所以让Emacs支持特定某一个编译器是比较容易的。比如为了让 Emacs支持csc,就是csharp的compile,我们只需要:

(setq compilation-error-regexp-alist
(append
;;; tt.cs(5,14): error CS1585: Member modifier 'static' must precede the member type and name
(cons '("(.*)(([0-9]+),([0-9]+)): (error|warning) CS[0-9]+:" 1 2 3) ())
compilation-error-regexp-alist
)
)

注意到其中的1,2,3;分别表示的是文件名,行号,列号。

Compile命令的扩展能力几乎是不受约束的,想得到的,就能支持的到。偶的想象能力比较贫乏,这里留白给更有研究精神的人来补全吧。

Chapter 10

电子邮件

10.1 收邮件,发邮件

邮件系统很早就在Emacs里面占有重要地位了。从最早的 rmail 到 vm 到 gnus ,各种各样的 mail mode ,乱花迷人眼。VM似乎是界面最友好的了,还记得第一次用 xemacs 的时候,那还是4年前,VM就已经支持很好看的图标按钮了。虽然如此,我当时对 xemacs 是如此白痴,还是没法把VM好好的用起来。

不过我转回到Emacs收信也是最近的事情了。要知道能让我从becky转过来,这个诱惑一定要不一般才可以。

gnus就提供了这样的诱惑。这个号称世界上最好的news客户端,也能作一个世界上最好的maillist客户端。如果你不上news,不混maillist,那么gnus的强大对你来说并没有意义,不如早点放弃吧。hehe

设置用gnus收信,或者发信,其实是很容易的。gnus支持多个pop3服务器, Emacs 支持smtp发信认证(需要升级你的 Emacs lisp 部分,21.3.1的还没包括这部分的内容),如果你希望 pop3 收信的时候不删除服务器上的邮件,Emacs现在还不行,你要自己动手去安装一个 epop3.el 的扩展。偶没用装过,因为偶没有这样的需求。在开始M-x gnus之前,记得在.emacs文件里面添上一些东西:

(require 'gnus-load)
(setq gnus-startup-file "~/emacs/config/newsrc")
(setq gnus-init-file "~/emacs/config/gnus-config")

把gnus-config从.emacs中分出来的做法比较清楚,因为你以后会经常有改gnus-config的需要的。在gnus-config中需要加上这些内容:

(setq gnus-select-method '(nnfolder ""))
(setq gnus-secondary-select-methods '((nntp "news.gnus.org")))
(add-to-list 'gnus-secondary-select-methods '(nntp "perl.org" (nntp-address "nntp.perl.org")))

上面的第一行指明的是gnus对邮件使用的backend,偶觉得nnfloder比较好,你可以先这样用着,反正backend以后还是可以换的,等你熟悉了 gnus以后。然后就可以给secondary-select-methods加各色的news服务器了,

(setq mail-sources '(
(pop :server "263.net" :user "huxwcn" :password "pighead")
(pop :server "knight.6test.edu.cn" :user "huxw" :password "pighead")
))

然后设置 backend 去哪里收信,除了 pop3 ,还支持 imap ,或者本地的 maildir 之类的冬冬,以此类推,还能加很多。还有就是希望你不会naive 的以为上面的密码是真的。

最后,就是发信的设置。

(setq smtpmail-auth-credentials '(("smtp.263.net" 25 "huxwcn" nil)))
(setq smtpmail-smtp-server "smtp.263.net")
(setq user-full-name "Robin Hu")
(setq user-mail-address "[email protected]")

前两行是说发信服务器的位置,后两行是给收信人看的。人家回信就会回到那个user-mail-address 上去。以后会看到这种东西都可以很灵活的修改。

这些都搞定的时候,你就可以尝试开始gnus之旅了。我最后offer一点不好的消息,你要是觉得不爽的话,现在停止尝试gnus还来得及。因为 Emacs不是一个多线程的程序,而gnus也没有打算和别的程序合作,你打开 gnus的时候,Emacs的所有frame都会失去相应一段时间,时间的长短视 gnus把所有的邮件收回来需要的时间而定;如果你在家慢慢拨号的话,而且信有很多的话,这段时间可能会长达15分钟。事实上,news.gnus.org里面的同学似乎都是开了两个Emacs的,一个专门起gnus,另一个干活,酱紫。

好了,如果你连这个也不在乎的话,那就开始吧。M-x gnus。你会什么也看不见。这是因为gnus缺省以为,你收的邮件,都是属于某一个列表的,就好像bbs上的版面一样,你不订阅,就什么也看不见。订阅很容易,用^可以进入*Server Buffer*,在nnfolder上按RET,看到mail.misc以后按u就可以订阅它。 缺省所有没有分类的邮件都会跑到mail.misc里面去。你以后在慢慢改吧。

10.2 删除邮件

如何删除邮件,是GNUS的一个特点。

我们上bbs的时候,帖子怎么处理,不是我们说了算的,而是版主说了算了的。换句话说,帖子的生命周期是不受你掌握的,如果你想保存某一封帖子,你得把它下载到你自己的硬盘上去。gnus认为邮件也是酱紫的。在gnus里面,主动删除邮件是不提倡的,你应该让gnus自己处理邮件,被称为过期(expire)。

在缺省的设置中,你可以在一封邮件上按E,告诉gnus,这封邮件,过期了。 但是过期并不等同于删除,gnus会将这封邮件放入expire的队列中,然后等待一个特定的时间,差不多就是你真的已经忘了这封邮件的时候,gnus悄悄的删除了它。

你也可以采用auto expire模式,gnus会认为,所有你“读”过的信都是过期的,于是那些信都悄悄的自动进了过期队列,等着被删除。你还可以采用total expire模式,gnus会认为,所有你“标记为读”的信,都是过期的,于是那些信也都悄悄的进了过期队列,等着被删除。

这样的做法看起来比较诡异,但是处理邮件列表的时候却让人觉得非常自然。 所有看过就忘的信,不用去管什么时候要删除它,而且这些信在被删除之前,不会主动跳出来骚扰你;如果看到列表中某一个thread,记不起来之前这个thread到底是怎么样的,可以把这个thread没有真正删除的邮件都翻出来,显示一个完整的thread。相当于说,你总能在本地保留这个maillist最近若干天的snapshot。

对于其他信件,处理也是一样的。除非是mm发给你的一万年都不能删除的信件,别的信都可以让它自己悄悄跑进过期队列,悄悄消失。你甚至可以改变那些过期邮件的去向,不删除他们,而是把他们按时期打个包,已备若干年后写回忆录用(joking)。如果你万分肯定,这封信必须当即删除,比如不能给mm看到的别人写给你的情书,那就B del,不过这个情况很少很少,而且会导致gnus一些诡异的行为,以后再说。

罗里罗嗦了很多。expire具体的设置,都和邮件的分类联系在一起,下次再说吧,手累了。 ;-(

10.3 邮件分类

收到很多信的时候,需要分一下类。所有能用的mail客户端都支持对收到的mail做分类,包括gnus。在gnus当中,你可以通过指定的规则,将某一些邮件归成一类;在gnus看来,这样的一类邮件,基本上等同于news 服务器上的一个讨论组。

给邮件分组的工作,是通过设置nnmail-split-methods来实现的。 gnus里面所有的设置名字都很长,容易敲错,一个简单的判别方法,是在变量上面C-h v看看有没有文档。 有文档的至少能够说明这个变量名没有敲错。

我推荐使用nnmail-split-fancy来实现邮件分类,因为从个人经验来看,用fancy分类,至少可以把判别规则写的好看一点。这里先给一个sample。

(setq nnmail-split-fancy
'(|
;; (: gnus-group-split-fancy)
(any ".*-?current@(freebsd|FreeBSD).org" "maillist.freebsd.current")
(any ".*-?ipfw@(freebsd|FreeBSD).org" "maillist.freebsd.ipfw")
(any "[email protected]" "maillist.emacs.emacs-devel")
"mail.misc"))
(setq nnmail-split-methods 'nnmail-split-fancy)

其实就是一些正则表达式。需要注意的是,如果将 nnmail-crosspost设置为nil,那么就不会出现“一稿多投”的情况,也就是说一封邮件在这些判别规则上遇到符合的就会直接break了。还有就是不建议在这里用太复杂的正则表达式,偶曾经试图在一行里面对所有 freebsd的邮件列表进行分类,结果死的挺难看的。

一旦邮件分类了。你应该也期望对不同类别的邮件作出区别对待。林林总总的需要都可以通过设置group parameter完成。对group parameter 的设置可以在gnus里面完成,使用G p或者G c:前者是字符界面的,后者是能跳出一个类似于cunstomize的界面来。也可以通过setq gnus-parameters,在.gnus文件里面手工设定。典型的例子比如:

(setq gnus-parameters ;;别写错了名字
'(
("maillist.freebsd.(.*)$"
(to-list . "[email protected]")
(posting-style
(name "Me me me")
(address "[email protected]")
(signature "Smile and Retain Smile.")) ;;签名档也可以是文件
(total-expire . t)
(expiry-wait . 7)
(broken-reply-to . t)
(subscribed . t))
))

注意,这里也可以用正则表达式哦 ;-)

group parameter 中的 to-list ,可以被自动收集起来;酱紫如果你在 nnmail-split-fancy 里面用 gnus-group-split-fancy 来自动分类,免去自己重写一遍分类规则的麻烦。不过设置gnus-parameters,是没办法利用这种能力。

设置好了 group parameter 能够简化很多事情。比如现在在 freebsd 的某个list里面按一下a,收件人地址就自动设成to-list了。很多时候比 bbdb还要方便的多。

10.4 SCORING

Chapter 11

支持中文

何曾几时,中文支持是Emacs的强项。在Emacs里面开一个shell上水木几乎是我唯一的选择。 时代变了,Emacs的中文支持就渐渐的落后了。

Emacs内部有一套表示多国的方法,就是所谓的emacs-mule。我们能够在同一个emacs buffer里面能够同时看中日韩文字,能够同时看到阿拉伯文,能够同时看到德文,丹麦文;这都拜emacs-mule所赐。不幸的是 emacs-mule并没有成为事实上的编码标准。emacs-mule除了emacs自己能够认识以外,其他的编辑器都不支持。所以Emacs必须在和其他文本处理器交互的时候重新编码内部的emacs-mule。这里没有必要谈太多的细节, Emacs是一个self-document的编辑器,上面这些细节都可以在 coding.[ch]和charset.[ch]中找到。

为了能和键盘交互(可以认为键盘是一个文本处理器,Emacs从键盘中读入文本),Emacs将从键盘中读入的文本“解码”为emacs-mule(我们这里说到编码解码,都是从Emacs的角度来看),为了文件能被其他的文本编辑器打开,比如vi,Emacs在存盘的时候将emacs-mule编码为 chinese-iso-8bit。这就是我们平常用到的各种coding system起到的作用。

为了让Emacs支持gbk,我们需要做的,就是让所有的gbk编码字符,都能够在emacs-mule中找到自己的座位。虽然实际上emacs-mule里面所有的座位都已经被人坐满了,我们还是可以假设那些很少有人出现的座位依然是空的。前面给出的chinese-gbk就强占了cns11643-5, 6, 7的座位。这些座位的汉字几乎不可能出现在我们这些人的屏幕上,所以这种做法基本上是可行的。所以如果有一天,你在使用chinese-gbk的时候,又试图用 cns11643的编码来保存,还请你回到这里来想想可能会发生的事情。

因为Emacs已经开始支持unicode了,所以让utf-8或者utf-16编码的 gbk汉字的文件在Emacs中显示并不是麻烦。而且Emacs已经在这里预留了 hook,只需要给一个 translate-table ,那就一切ok了。

让 Emacs 支持从X拷贝过来的gbk汉字,很难直接在 lisp 代码中实现。因为X和 emacs 一样是个历史悠久的软件,所以它同样也有一套自己的多字节编码格式。在 Emacs 中,缺省是采用 compound-text-with-extension 来处理这种编码格式的。从作者当初开发的思路来看,我们让它支持gbk编码,只需要添加一项 ("GBK-0" . chinese-gbk) 到 non-standard-icccm-encodings-alist中去,就可以简单的扩展支持gbk编码了。然而,因为实现上bug,和X本身的问题,这终究只是一个美好的愿望。如果你坚持的话,可以尝试一下这个补丁。注意这个时候就不能再使用 compound-text-with-extensions 作为 selection 的 coding system 了,而应该用chinese-gbk。

--- /home/huxw/src/Resp/emacs/src/xselect.c     2003-04-07 04:35:06.000000000 +0800
+++ xselect.c 2003-05-26 11:17:42.966829744 +0800
@@ -1496,6 +1496,11 @@
Lisp_Object target_type; /* for error messages only */
Atom selection_atom; /* for error messages only */
{
+ // by huxw start here
+ XTextProperty text_prop;
+ char** local_list;
+ int local_number = 0;
+ // by huxw end here
Atom actual_type;
int actual_format;
unsigned long actual_size;
@@ -1554,12 +1559,70 @@

/* It's been read. Now convert it to a lisp object in some semi-rational
manner. */
+ //by huxw start here
+ if (XSupportsLocale()) {
+ int local_status;
+
+ text_prop.value = (char*)data;
+ text_prop.encoding = actual_type;
+ text_prop.format = actual_format;
+ text_prop.nitems = actual_size;
+
+ local_status = XmbTextPropertyToTextList(display, &text_prop, &local_list, &local_number);
+ if (local_status < Success || !local_number || !*local_list ) {
+ } else {
+ xfree((char*)data);
+ data = strdup(*local_list);
+ XFreeStringList(local_list);
+ }
+ } else {
+ }
+ //by huxw end here
+
+#if 0
val = selection_data_to_lisp_data (display, data, bytes,
actual_type, actual_format);
+#else
+ val = selection_data_to_lisp_data (display, data, strlen(data), actual_type, actual_format);
+#endif

/* Use xfree, not XFree, because x_get_window_property
calls xmalloc itself. */
- xfree ((char *) data);
+
+ // by huxw start here
+// xfree ((char *) data);
+ if (local_number == 0) { // Xmb is not used or not successed
+ xfree((char*)data);
+ } else {
+ free(data);
+ }
+ // by huxw end here
return val;
}

Windows的国际化一向做的很好,可是Emacs没有打算依赖它。在 Windows里面跑的Emacs看起来很像在X里面跑的Emacs,很像,还是有一些要注意的地方。

Windows 里只定义了 GB2312_CHARSET ,这把Emacs搞糊涂了。如果在 Emacs里面列出可用的所有字体,会发现没有字体是以gbk结尾的,这也使得Emacs无法处理所有gbk编码的汉字,只能显示一个方框代替。处理方法一样简单,只要把 ("gbk" w32-charset-gb2312 . 936) 加到 w32-charset-info-alist 中去就可以了。另外一个问题是Emacs有时无法正确处理字体名称中的中文编码,这种时候很是少见,找出问题之前,绕开就是了。

Windows下的粘贴拷贝并没有额外的编码,所以把 clipboard-coding-system 设成 chinese-gbk 就可以了,没有X底下的困扰。

Emacs的中文支持虽然看起很繁琐,但是它却是最完善的。比如我们知道动感超人的口号是“啊哈{6,8}”,这种正则表达除了在Emacs里面,还能在哪里用呢?

以上所说,都是针对FSF Emacs,而不是XEmacs。XEmacs的X11版本虽然也是用的 mule,但是做法稍有不同.

你可能感兴趣的:(Ubuntu)