Emacs编辑器——王垠

本文来源: http://docs.huihoo.com/homepage/shredderyin/emacs.html

更多内容详见 http://docs.huihoo.com/homepage/shredderyin/emacs.html   王垠个人主页

Emacs 是什么的简称?

                       E. M. A. C. S.
                Emacs Makes A Computer Slow
               Escape Meta Alt Control Shift
               Emacs Makers Are Crazy Sickos
              Emacs Makes All Computing Simple
            Emacs Makefiles Annihilate C-Shells
            Emacs Manuals Always Cause Senility
            Emacs May Allow Customized Screwups
           Emacs Manuals Are Cryptic and Surreal
          Eventually Munches All Computer Storage
          Eight Megabytes And Constantly Swapping
          Elsewhere Maybe All Commands are Simple
          Excellent Manuals Are Clearly Suppressed
         Emacs May Alienate Clients and Supporters
         Except by Middle Aged Computer Scientists
         Extended Macros Are Considered Superfluous
        Every Mode Accelerates Creation of Software
       Each Manual's Audience is Completely Stupefied
  Exceptionally Mediocre Algorithm for Computer Scientists
Easily Maintained with the Assistance of Chemical Solutions
Eradication of Memory Accomplished with Complete Simplicity

Emacs 能做什么

很多人不理解为什么 Emacs 的用户那么多,他们是如此的热爱 Emacs,甚至有的人把它当成了“信仰”。虽然我没有这个信仰,但 是它的确很好,我很喜欢用它编辑各种文件,现在来看看 Emacs 能 做什么。

据我所知,Emacs 的能力包括

  1. 编辑文本

    Emacs 编辑文本的能力是非常强的。它的其它很多功能都是依赖 于强大的编辑能力。Emacs 是人性化的设计,它非常可靠,不会莫名 其妙弄坏你的文件,用它编辑重要的文本信心有保障。

    可能你开头觉得它的键绑定用起来非常不顺手。它有很多长长的 “快捷键”,比如按下Ctrl-h再按Ctrl-i…… 比起 VIM 的一个键的 快捷键的确慢很多。但是 Emacs 的优势不在于击键的频率,我们的 目标是用一个键,甚至不按键盘,就完成复杂而智能的操作。面对 Emacs,你感觉自己像是一个魔法师,轻轻一挥魔棒,任务就完成了。

    Emacs 的文本处理能力极强,不管你要处理自然语言还是机器语 言。不管是中文还是英语,不管是小说,HTML, 还是 C 程序, Java 程序,我都可以按 M-e 向前移动一句话(C和Java语句),按 M-k 删 除一句话,按 M-} 移动一段话,按 M-p (我自己设定的按键) 删除 一段话。因为 Emacs 知道,什么样算是一句话 (sentence-end),什 么算是一段话(paragraph-start)。而且它知道在不同的文档里,句 子和段落有什么区别。

    我只要按 M-q 就可以把一段话排的规规矩矩。我只要按一个键就 可以把一个数字列表重新排序。我只要在 HTML 文档里按 C-c backspace 就可以把一组tag删掉,按C-c C-c i 就可以插入一个图 片,按 C-c C-v 就可以马上把这个文档送到一个 Mozilla 窗口去预 览……在 LaTeX 文档里按 C-c C-r RET 就可以把我的文档里选定的 区域送给 LaTeX 处理,马上看到效果。

    现在你是不是感觉自己更像魔法师了?继续!

    Emacs 有非常友好而强大的人机界面。在搜索替换字符串时,你 就能明显感觉到这个优点。它在输入regexp(正则表达式)搜索的时候 还能进行 incremental search,而且对部分匹配的字串都有非常漂 亮的加量。在替换的时候,它独有 recursive edit,可以打断替换 过程,编辑附近的需要纠正的文字,然后继续替换。这个功能对很多 人都是非常重要的,因为,在替换过程中,被替换的文字附近的文字 很可能会因为它被替换而需要修改。看看这个抓图,就是 isearch 输入一个正 则表达式的情况。

    Emacs 的拼写检查能力非常强大。你可以使用 flyspell 随时检 查你的拼写错误,看看这幅图 是 flyspell-mode 的 LaTeX 文档, flyspell 知道段落里的某 些单词是错的,而 \begin{myquotation} 里的 myquotation 被认为是一 个用户自己定义的 LaTeX 环境名字而不认为是拼写错误。

    你还可以利用 ispell 的补全功能输入你容易打错的很长的英语 单词,看看这幅图。

    Emacs 的 hippie-expand 能够非常智能的补全你的文本,文件名, 和其它很多东西,而这个功能都是用同一个按键完成!

    Emacs 能输入和显示多国语言文本 ,是一个真正的国际化程序。Emacs 能识别多种自然语言符号, 它能知道哪些符号是括号,那些是标点。看看这个抓图里,Emacs 显 示了中文书名号的匹配情况。 《正确》,和《错误>

    Emacs 的键是可以任意绑定的。甚至可以使用 Windows 键盘上的 Win 和 Menu 键来控制。我的 Win + 鼠标中键可以弹出一个buffer 里所有引用到的文件和URL的列表,当我点击它们就可以编辑那个文 件或者在 Mozilla 中打开连接。看看这个抓图(ffap-at-mouse)。

    你能想到的和你不能想到的编辑功能,Emacs 都能完成。说到这 里你可能又会说:“这个功能,vi也有”,“这个功能, UltraEdit 都有”……对,这些能力可能很多其它程序也有。对,但是让一个 Emacs 拥有这些功能比起其它程序的确要费少得多的力气。很多热心 的爱好者看到其它编辑器有一个很酷的功能,他就可以想办法把它加 入到 Emacs 里面。Emacs 可以被不断的扩充。其实,你平常用到的 几乎所有功能都是扩展的功能。

    建立一个微小的基本功能的集合,然后把所有复杂功能作为这些 基本功能的组合。这体现了一种程序设计的哲学。不同于 Windows, 也不同于 UNIX,它更好的协调了程序的灵活性和同一性。

    Emacs 编辑文本方面的扩展简直没法计数。folding, narrow, outline, ... 这些给人带来了很 大的方便,已经被许许多多的人采用。

  2. 编辑程序

    程序也是一种特殊的文本文件。因为 Emacs 特别适合编辑程序, 所以特别在这里提出。Emacs 可以方便的编辑任何类型的计算机语言 程序代码,而且为它们提供语法加亮,自动缩进。你可以对程序的语 句进行操作。比如向下移动一个 list, 删除整个 C 函数,…… 你 可以用一个键就把许多行注释重新排的整整齐齐。

    一些扩展可以让你方便的浏览代码,而且它们可以识别代码的语 义。列出函数名,参数和类型,变量名,类,宏,方法,defun, include 的文件。当你编辑程序时,Emacs 可以帮你补全函数名,参 数等等。你可以在代码中间自由的穿梭。看看这个用 Emacs 写程序的介绍。

    你可以使用 Ediff 来比较两个或者三个文件,Emacs 可以把不同 的地方高量,而且修改过的地方有特别明显的颜色不同。我经常用这 个功能很快找到我修改过的代码里新出现的错误的位置。看看这个抓图。

    你用一个指头就可以让 Emacs 调用编译器编译程序,列出错误列 表,启动调试器,在另一个窗口显示当前执行源代码的位置。看看这 个抓图就是 emacs 正在调用 gdb 调试程序。

    Emacs 的 narrow 和 folding 功能使你能够编辑几万行代码在一 个文件里而不会觉得摸不着方向。看看这幅图,你能想像这是 TeX 的源代 码的 24000 多行里的一部分吗?你操作它的时候就像只有几十行。 folding 可以使你的很长的文档折叠起来,看看这幅图就是我的一个很长的网页, 我在需要的时候可以进入其中一个主题进行编辑,当我退出主题时, 整个文档看起来就像只有一页那么长。我可以随意的移动我的主题, 就像只移动了一行。

    加上一些 elisp,Emacs 就可以成为一个代码浏览器。

  3. 作为其它程序的界面

    Emacs 可以作为很多程序的前端。比如w3m, lynx 浏览器,MAXIMA 计算机代数系统, Scheme48 等 Scheme 解释器,shell,……这些文本方式的程序一旦 与 Emacs 接合就拥有了巨大的编辑的能力。看看这幅图,Scheme48 加上 Emacs,就有了语法加亮和方便的热键,可以非常方便的使用Scheme 解释器了。

  4. 作为操作系统

    Emacs 就像一个具有编辑功能的操作系统,你可以用它干很多事 情。比如,阅读email和新闻组, 浏览网页,管理文件目录 (dired),作为网页服务器,远程编辑文件, 记录日程,约会提醒,电子日历 ,查字典,ICQ 聊天, 放 mp3,……

  5. 煮咖啡

    如果你工作的地方有自动咖啡机,Emacs 还可以帮你点咖啡 :) coffee.el :P


Emacs 资料

好书一本

这里有一本书叫做 Sams Teach Yourself Emacs in 24 Hours. 是它把我从彷徨中拉入了 Emacs 神奇的大门。 
[打包下载][例 子elisp 包下载]。

网址

  1. Emacs 主 页

    这里能找到很多信息。

  2. Emacs FTP

    这里能下载到 Emacs 的发行版。

  3. Emacs CVS

    这里能下载到最新的 CVS 版本的 Emacs。

  4. SourceForce.net

    这里有许许多多 Emacs 相关的程序,从提供基本操作方便的, 到浏览代码,编辑程序,…… 内容非常广泛。

  5. EmacsWiki

    一个非常好的 WiKi 网站。里面有很多Emacs的使用技巧。

    从 这里可以进入 Emacs WiKi 的连接列表,非常多的资源!

  6. dotemacs

    一个搜集 .emacs 文件的网站。你可以从这里学习怎么配置 .emacs.

  7. Elisp Tutorial

    一个 elisp 的入门指南。它本身是几个 elisp 文件。用 Emacs 打开,看里面的注释,照着做,就能很快掌握 elisp 的基本用法了。

    这里提供一个本地下载 。

  8. Richard Stallman 有关 Emacs 的论文

    讲述了 Emacs 设计中的各种经验,教训。看了这个你就知道一个 成功的编辑器是怎么设计出来的了。

  9. comp.emacs 上的一个帖子Emacs references: pointers to ports, packages, papers, lisp

    列举了非常多的 Emacs 资源。




Emacs Lisp 功能扩展集锦

Emacs 具有超强的扩展性。这是当今没有任何其它编辑器可以比拟 的强大特点。这里介绍一些很方便的 Emacs Lisp 扩展。

有链接的文件可能不是 Emacs 自带的,你需要下载el文件到你的 load-path 中的一个目录里。

所有文件可以点击链接下载,你也可以点击这里一次性下载所有 el 文件,但是不包括大型的 elisp 包,比如 dictionary. [下载]。

这里提供的下载是我正在用的版本。我没有列出我找到它们的地址, 因为我记不住那么多。如果你需要最新的版本,请查看文件里的网址 到作者的主页。

每个标题后面的lisp代码是需要写到配置文件里的东西。

session.el

  (require 'session)
  (add-hook 'after-init-hook 'session-initialize)

使用了这个扩展之后,你上次离开 Emacs 时的全局变量 (kill-ring,命令记录……),局部变量,寄存器,打开的文件,修 改过的文件和最后修改的位置,…… 全部都会被记录下来。

加载了 session 之后菜单上会多两项:最近访问过的文件和最近 修改过的文件。看这个图 。

desktop.el

(load "desktop") 
(desktop-load-default) 
(desktop-read)

如果你想保存上次打开的文件记录,那么可以使用 desktop。这是 Emacs 自 带的。你只需要加入以上设置,然后 M-x desktop-save。以后 Emacs 启动时就会打开你上次离开时的所有 buffer.

M-x desktop-clear 可以删除记住的内容,你闲现在记住的 buffer 太多就可以采用这个办法。不过我还是建议用 ibuffer(见下) 来管理这些buffer,因为有时你会发现,如果删掉全部记住的buffer, 以后你需要一定的时间来打开你经常编辑的文件!

如果session跟desktop配合,下次启动Emacs 的时候,就像根本 没有关闭 Emacs 一样!多爽!

ibuffer.el

把这几行加入 .emacs 就可以把 C-x C-b 那个普通的 buffer menu 换成非常方便的 ibuffer 啦!

(require 'ibuffer)
(global-set-key (kbd "C-x C-b") 'ibuffer)

使用了 desktop 之后有可能使你同时有几十个 buffer 同时打开 着。有时你想把其中一些关闭,有时你想在某些 buffer 里寻找某个 regexp。这个时候你可以用 ibuffer,它有跟 dired 相似的界面。 可以对 buffer 进行各种标记,排序,隐藏,查找,替换。非常好用。 看看这个抓图就是我限定显示 emacs-lisp-mode 和 c-mode 的 buffer,按 major mode 名称排序, 并做上一些记号的情况。

我常用的一个功能是,用 %-n 标记符合某种名字的 buffer,然 后按 "O" 在这些buffer里寻找我需要的字串。比如在所有打开的后 缀为 html 的 buffer 里寻找 "Emacs" 字样,得到如下结果。

browse-kill-ring.el

(require 'browse-kill-ring)
(global-set-key [(control c)(k)] 'browse-kill-ring)
(browse-kill-ring-default-keybindings)

方便的在 kill-ring 里寻找需要的东西。

你是不是经常按 C-y 放进一个 kill-ring 里的单元。然后 M-y,M-y,…… 来寻找你需要的很久以前剪切下来的东西?很费事吧?用了 browse-kill-ring 就好了。你只需要把它绑定到一个热键,比如 C-c k: 就能出现这样一个buffer。[查看图形]

kill-ring 里的内容都可以方便的浏览,粘贴。具体操作请在这 个 buffer 里 C-h m 或者 ?.

ido.el

你可能用过 iswitchb。但是当有很多buffer时,iswitchb 还是很慢。你可 以使用ido。这是我见过的最方便的切换buffer,寻找文件的扩展了。在你的 .emacs 文件里加入:

(require 'ido)
(ido-mode t)
ido 主要有两个界面:打开文件和切换buffer,它们之间可以随便切换。它的原 始的键设定不是很方便直观,所以我把它另外设置了一下。看 这个列表 。其实你从这个列表就可以看出 ido 的功能。 我这里只叙述一下基本的操作。
  1. 打开文件 (C-x C-f)

    启动 ido-mode 以后,你可以在 C-x C-f 打开文件时得到一个如下的界面: 

    你开始输入文件名,前缀匹配的文件和目录都会列在光标后面的表里。目录 是红色的。

    你可以使用 C-s,C-r 或者左右光标键在匹配的文件之间来回旋转,按上下 光标可以在匹配的目录名之间旋转。你可以按 TAB 补全名字,方括号里的内容 是你按 TAB 将会补全的字符。按 backspace 遇到目录名时,一次会吃掉整个目 录名,并且到达上一级目录。C-k 可以删除一个文件。别担心,当删除文件时, 你会被要求做出明确的回答。

    如果你按 C-p 就会启动部分匹配,而不是严格前缀匹配。这时只要文 件名的一部分与你的输入匹配就会列在表中。

    按 C-t 就会切换到正则表达式匹配方式,你输入的文字将被作为正则 表达式对文件进行匹配。比如我输入 ".*el$" 就可以得到所有以 "el" 结尾的 文件列表。

    另外,C-c 和 C-a 分别切换大小写匹配和忽略某些文件。

    你访问过的目录都会被 ido 记住,你可以按 M-up 和 M-down切换以前访问 过的目录,按 M-left 和 M-right 可以提出以前使用过的文件名。

    按 C-b 就会切换到选择buffer模式。按 C-d 就会进入Dired.

    注意,你如果要创建一个新的文件叫 "ab",而你的目录里有匹配前缀的文件 或目录,RET 就会打开部分匹配的文件。所以这个时候你必须按 C-j 来创建这 样一个文件。如果没有匹配的文件,那么RET也会创建一个新的文件。

    由于方向键,backspace 和 C-f, C-b 都被作为其它用途了,所以如果你想 把光标移动到行首修改一些东西,你必须按 C-e 进入编辑模式。

  2. 寻找文件

    Emacs 打开文件的函数叫 find-file。这是很多人不理解的问题。下面我们 就能看到,有了 ido 之后,这个函数就名副其实了。当你输入了一个文件名或 者正则表达式,而在当前目录没有这样的文件,你就可以利用ido的查找功能了。 我举一个例子好了。

    我经常同时编辑不同种类的文件,它们在系统里不同的目录中。它们的目录 名会被ido记住。这样我打开一个文件时,可以不管它在哪个目录。我只需要输 入它的名字。比如,我刚才已经打开了

    ~/html/emacs_elisp.html
    /usr/local/texlive/texmf-local/tex/latex/CJK/GB/c19fzhc.fd
    ~/.emacs.d/site-lisp/folding.el
    ....
    
    现在我想打开另一些
    /usr/local/texlive/texmf-local/tex/latex/CJK/GB/
    
    目录下的 .cap 文件。我就在 C-x C-f 的界面按输入 "cap$",然后打开正则匹配 和部分匹配,出现以下界面:

    因为当前目录(PWD)下没有后缀是 fd 的文件,所以我按了一下 M-up,这下 成了这个样子:

    显然它找到一个结尾是 "cap" 的文件,但是这不是我想要的,于是我继续按 一下 M-up 到再上面一个目录里去找。就成了这样:

    这就是我需要的文件!想一想,你的当前目录不在那个地方,用别的办法打 开如此深一个目录里的文件需要花多少时间?

    其实我们还有一个窍门,刚才如果在不能匹配的时候按 M-s,ido 就会把所 路径合并,找到匹配文件,然后给出一个列表:

  3. 切换buffer(C-x b)

    切换 buffer 的界面也跟打开文件功能类似。不过少了一些文件特有的功能, 多了一些buffer特有的的功能。 

    打开文件时 C-k 是删除文件,现在 C-k 变成了关闭 buffer。如果你按 C-a 就会显示通常被忽略的 buffer。

    如果不存在这样的的buffer,你可以直接回车新建一个buffer,也可以使用 C-f 切换到打开文件界面。

这个扩展功能实在太多,你看了我的叙述能够体会到它的方便了吧。但是实 际使用时,你很少能记住所有这些功能。最好是自己绑定键。在需要的时候可以 查询这个键绑定。就像我的这个键绑定。

最新的 ido 可以逐步筛选你需要的文件 (ido-restrict-to-matches),你可以把这段代码加入你的 .emacs.

  • swbuff
    (require 'swbuff)
    (global-set-key (kbd "") 'swbuff-switch-to-previous-buffer)
    (global-set-key (kbd "") 'swbuff-switch-to-next-buffer)
    (setq swbuff-exclude-buffer-regexps 
         '("^ " "\\*.*\\*"))
    
    (setq swbuff-status-window-layout 'scroll)
    (setq swbuff-clear-delay 1)
    (setq swbuff-separator "|")
    (setq swbuff-window-min-text-height 1)
    

    使用这个,你就可以方便的在最近编辑的 buffer 之间切换了。 切换的时候会出现一个小窗口显示附近的buffer,就像翻页一样。看 看抓图。

    我把它绑定到了 C-prior 和 C-next,按起来很方便。而且你可 以用变量 swbuff-exclude-buffer-regexps 设定你不想看到的 buffer。它们不会出现在列表里。这个例子中我把所有名称开头是空 格的 buffer 和名字前后都是 "*" 的都排除在轮训以外,这样就可 以在不同的文件之间切换而不被恼人的临时 buffer 扰乱了。

    一个很重要的变量是 swbuff-status-window-layout, 改变它的 值可以改变 swbuff 下面那个窗口的显示方式。

    如果你的 buffer 很多,这个窗口可以长得很大。如果你把 swbuff-status-window-layout设为 'adjust,则不论什么时候都会 出现这样的大窗口,设为 'default 则只有单窗口时才出现大窗口。 虽然这个窗口只显示几秒钟,但是 我很讨厌一个那么大的窗口在我 面前一闪。现在如果你把它设为 'scroll,不论什么时候都只占用一 行。这样buffer太多时,那个窗口不会占用太多空间扰乱视线。

    原来的 swbuff 有一个小小的 bug, 无论 swbuff-status-window-layout 是什么值,都会显示一个很大的窗口 显示所有的 buffer,这个文件是我修改过的版本。

    swbuff-clear-delay 可以控制小窗口消失的延迟。

    swbuff-window-min-text-height 可以控制小窗口的大小。如果 设为 0, 就不会打开小窗口了。

    tabbar.el

    (require 'tabbar)
    (tabbar-mode)
    (global-set-key (kbd "") 'tabbar-backward-group)
    (global-set-key (kbd "") 'tabbar-forward-group)
    (global-set-key (kbd "") 'tabbar-backward)
    (global-set-key (kbd "") 'tabbar-forward)
    

    你曾经觉得 Windows 样式的编辑器上面的 tab 很好用吧?其实 Emacs 的更好,它不但可以把所有 buffer 列在 tab 上,而且根据 major mode 对 tab 进行分组。你可以用 customize-group RET tabbar RET 来设置它的选项。

    我把Windows键盘上的 "Win" 键绑定到了这个功能,因为 tabbar 很像Windows 的风格。按 Win-left 和 Win-right 就会在同组 tab 之间切换,Win-up 和 Win-down 可以在分组之间切换。

    看看下面几个图你就知道它如何好用啦!

    显示 html mode 的分组: 

    分组列表 

    在不同的窗口里可以有独立的 tab。图片太大,要看的话点击这里

    tabbar 有一个小小的 Hack, 可以使你更方便。

    speedbar

    这个是 Emacs 自带的。M-x speedbar 就可以启动它。它可以让你 方便的浏览文件,你还可以显示文件里的标题,函数,变量……

    在你编辑不同的文件时,它可以显示当前文件所在目录里的相关文 件和它们的信息,比如文件是否需要重新编译,elisp文件是否已 经编译,…… 等等。

    你可以看看我的抓图。

    table.el

    (autoload 'table-insert "table" "WYGIWYS table editor")
    

    非常酷的一个扩展。可以“所见即所得”的编辑一个文本模式的 表格。就跟很多字处理程序那样操作,可以识别文本文件里本来就存 在的表格,而且可以把表格输出为 HTML 和 TeX。看看我的抓图吧。

    recentf.el

    这个扩展可以帮你保存一个“最近打开的文件”列表。在 .emacs 文件里加 入:

    (require 'recentf)
    (recentf-mode 1)
    
    你可以把不常用的C-x C-r 绑定到这样一个函数:
    (defun recentf-open-files-compl ()
      (interactive)
      (let* ((all-files recentf-list)
    	 (tocpl (mapcar (function 
    			 (lambda (x) (cons (file-name-nondirectory x) x))) all-files))
    	 (prompt (append '("File name: ") tocpl))
    	 (fname (completing-read (car prompt) (cdr prompt) nil nil)))
        (find-file (cdr (assoc-ignore-representation fname tocpl))))) 
    
    (global-set-key [(control x)(control r)] 'recentf-open-files-compl)
    
    这样你就可以 C-x C-r 然后 TAB 列出最近打开文件列表了。

    rect-mark.el

    你是不是觉得 Emacs 的“矩形区域操作”不太直观?使用这个包之后矩形 region会被真正显示成一个矩形。[查看图形]这 可以使你的矩形操作大大方便。

    如果你已经有了一个 region。你可以使用 C-x r C-x 把它显示为一个矩形。 如果你刚开始设定一个矩形区域,你可以使用 C-x r 作为你的 set-mark-command 的前缀,比如 C-x r C-SPC。那么区域会被显示为矩形。

    hippie-expand

    这个是 Emacs 自带的功能,可是知道它的人不多。很多人用的是M-/ (dabbrev-expand) 这样的东西。 hippie-expand 要强大的多。因为它可以使用 扩展函数任意扩充!你可以把你的 M-/ 绑定到 hippie-expand,马上就可以体 会到它的方便。

    (global-set-key [(meta ?/)] 'hippie-expand)
    
    你可以设置以下 hippie-expand 的补全方式。它是一个优先列表, hippie-expand 会优先使用表最前面的函数来补全。通常我的设置是:
    (setq hippie-expand-try-functions-list 
          '(try-expand-dabbrev
    	try-expand-dabbrev-visible
    	try-expand-dabbrev-all-buffers
    	try-expand-dabbrev-from-kill
    	try-complete-file-name-partially
    	try-complete-file-name
    	try-expand-all-abbrevs
    	try-expand-list
    	try-expand-line
    	try-complete-lisp-symbol-partially
    	try-complete-lisp-symbol))
    

    这是说,首先使用当前的buffer补全,如果找不到,就到别的可见的窗口里寻找, 如果还找不到,那么到所有打开的buffer去找,如果还……那么到kill-ring里, 到文件名,到简称列表里,到list,…… 当前使用的匹配方式会在 echo 区域 显示。

    特别有意思的是 try-expand-line,它可以帮你补全整整一行文字。我很多 时后有两行文字大致相同,只有几个字不一样,但是我懒得去拷贝粘贴以下。那 么我就输入这行文字的前面几个字。然后多按几下 M-/ 就能得到那一行。

    括号匹配

    这是一个很小的函数。你是不是觉得 Emacs 在匹配的括号之间来回跳转的时 候按 C-M-f 和 C-M-b 太麻烦了?vi的 % 就很方便,我们可以把 % 设置为匹配 括号。可是你想输入 % 怎么办呢?

    一个很巧妙的解决方案就是,当 % 在括号上按下时,那么匹配括号,否则输 入一个 %。你只需要在 .emacs 文件里加入这些东西就可以达到目的:

    (global-set-key "%" 'match-paren)
              
    (defun match-paren (arg)
      "Go to the matching paren if on a paren; otherwise insert %."
      (interactive "p")
      (cond ((looking-at "\\s\(") (forward-list 1) (backward-char 1))
    	((looking-at "\\s\)") (forward-char 1) (backward-list 1))
    	(t (self-insert-command (or arg 1)))))
    

    临时记号

    有时你需要跳到另一个文件进行一些操作,然后很快的跳回来。你当然可以 使用 bookmark 或者寄存器。但是这些实在是太慢了。你多想拥有vi那样的 ma, mb, 'a, 'b 的操作。现在你可以用几行 elisp 达到类似的目的:

    (global-set-key [(control ?\.)] 'ska-point-to-register)
    (global-set-key [(control ?\,)] 'ska-jump-to-register)
    (defun ska-point-to-register()
      "Store cursorposition _fast_ in a register. 
    Use ska-jump-to-register to jump back to the stored 
    position."
      (interactive)
      (setq zmacs-region-stays t)
      (point-to-register 8))
    
    (defun ska-jump-to-register()
      "Switches between current cursorposition and position
    that was stored with ska-point-to-register."
      (interactive)
      (setq zmacs-region-stays t)
      (let ((tmp (point-marker)))
            (jump-to-register 8)
            (set-register 8 tmp)))
    
    当你按 C-. 时就做了一个记号。然后你可以到别处,按 C-, 就可以在这两点之 间来回跳转了。这虽然没有 vi 的 26 个 mark 多,但是你仔细想想,vi的26个 mark,你通常能用到几个?

    go-to-char

    (defun wy-go-to-char (n char)
      "Move forward to Nth occurence of CHAR.
    Typing `wy-go-to-char-key' again will move forwad to the next Nth
    occurence of CHAR."
      (interactive "p\ncGo to char: ")
      (search-forward (string char) nil nil n)
      (while (char-equal (read-char)
    		     char)
        (search-forward (string char) nil nil n))
      (setq unread-command-events (list last-input-event)))
    
    (define-key global-map (kbd "C-c a") 'wy-go-to-char)
    

    非常感谢 Oliver Scholz 提供这个函数给 我。

    这个函数是一个 vi 的 "f" 命令的替代品。vi的用户知道,vi有 一个特别好的命令 "f"。当你按 "fx", x 是任意一个字符时,光标 就会移动到下一个 "x" 处。这之后只要按 ";"(分号),光标就到再 下一个 "x"。

    举个例子说明这个命令的用途。比如我们有这样一行字,光标在 行首。

    (setq unread-command-events (list last-input-event)))
                                                   ^^^^^
    

    我们希望迅速的到达最后那个 event 处,于是我在 vi 里按 "fe"。结果光标到了 "setq" 的那个 e 上面,这时候我接着按 ";", 不一会儿就到了我们想要的地方。很方便吧?可能起初不觉得,后来 你发现这真的非常好!

    我一直觉得 Emacs 没有这样一个方便的命令,但是 Oliver 给了 我一个完美的答案:

    有了这段代码之后,当你按 C-c a x (x 是任意一个字符) 时,光 标就会到下一个 x 处。再次按 x,光标就到下一个 x。比如 C-c a w w w w ..., C-c a b b b b b b ...

    我觉得这个方式比 vi 的 "f" 要快。

    setnu和setnu-plus

    这两个包可以用来显示文件的行号。并且根据是否空行和文件行 的语法加亮显示不同的数字颜色。

    看看我的抓图。

    hide-region.el 和 hide-lines.el

    这两个函数可以分别把一个区域和匹配某个regexp的行都藏起来, 就跟不存在一样……这样你就可以对某些内容进行任意的操作而不会 影响到其它的部分。

    hide-lines 在操作某些行的时候用起来特别方便。加一个前缀参 数可以把不匹配的行都藏起来,只看到匹配的!看看我只显示这个文 件里含有global-set-key 的行: 
    Emacs编辑器——王垠_第1张图片

    ;;hide region
    (require 'hide-region)
    (global-set-key (kbd "C-c r") 'hide-region-hide)
    (global-set-key (kbd "C-c R") 'hide-region-unhide)
    
    
    ;; hide lines
    (require 'hide-lines)
    (global-set-key (kbd "C-c l") 'hide-lines)
    (global-set-key (kbd "C-c L") 'show-all-invisible)
    

    folding.el

    我的 folding 配置很长,要看请点击这里。

    我编辑了这么长一个 HTML 文件,你觉得一定很费力,不容易定 位吧?其实我的屏幕看起来就是这 个样子。所有小节都被我折叠了起来,我很容易就可以调换小节 的位置。我刚才把folding的大段的配置代码移动到了另一个文件, 就像移动了一行。

    folding 根据不同的文档有不同的标记,它都是基于注释的。本 网页就是这样一个例子。

    htmlize.el

    把语法加亮的文件输出成彩色 HTML 文件。看看这个例子。

    dictionary


    [本地下载][来源地]
    (autoload 'dictionary-search "dictionary"
      "Ask for a word and search it in all dictionaries" t)
    (autoload 'dictionary-match-words "dictionary"
      "Ask for a word and search all matching words in the dictionaries" t)
    (autoload 'dictionary-lookup-definition "dictionary"
      "Unconditionally lookup the word at point." t)
    (autoload 'dictionary "dictionary"
      "Create a new dictionary buffer" t)
    (autoload 'dictionary-mouse-popup-matching-words "dictionary"
      "Display entries matching the word at the cursor" t)
    (autoload 'dictionary-popup-matching-words "dictionary"
      "Display entries matching the word at the point" t)
    (autoload 'dictionary-tooltip-mode "dictionary"
      "Display tooltips for the current word" t)
    (autoload 'global-dictionary-tooltip-mode "dictionary"
      "Enable/disable dictionary-tooltip-mode for all buffers" t)
    
    (global-set-key [mouse-3] 'dictionary-mouse-popup-matching-words)
    (global-set-key [(control c)(d)] 'dictionary-lookup-definition)
    (global-set-key [(control c)(s)] 'dictionary-search)
    (global-set-key [(control c)(m)] 'dictionary-match-words)
    
    ;; choose a dictionary server
    (setq dictionary-server "localhost")
    
    ;; for dictionary tooltip mode
    ;; choose the dictionary: "wn" for WordNet
    ;; "web1913" for Webster's Revised Unabridged Dictionary(1913)
    ;; so on
    (setq dictionary-tooltip-dictionary "wn")
    (global-dictionary-tooltip-mode t)
    ;(dictionary-tooltip-mode t)
    

    连接到 dict 协议服务器,并且查询单词含义。按 C-c d 查询道当 前单词,按 C-c m 在字典里查找一个匹配的单词,鼠标邮件点击单词 会弹出菜单。还有 tooltip 功能!非常好用!你可以看看我的屏幕截图。

    你可以在自己机器上安装一个 dictd。这样就不需要连到很远的服 务器查字典了。

    color theme

    Emacs 提供给你所有的配色机制,可是由于你的艺术细胞不够,经常为找不 到好的配色方案而烦恼。使用这个包就可以方便的改变你的 Emacs 的配色方案 啦!看看我的屏幕抓图 [1] [2] [3]

    它提供了50种以上的配色方案。你只需要把:

    (require 'color-theme)
    
    加入到 .emacs 文件。使用 M-x color-theme-select 就会出现一个配色方案选 择窗口,在配色方案上按 l 就可以改变当前 frame 的配色,按 i 就可以改变 所有 frame 的配色。

    如果你想选定一个配色方案后就一直用它,而避免每次都加载大量用不着的 lisp代码,按 p 就可以把当前配色方案的 lisp 打印出来,你可以把它加到你 的 .emacs 文件。而不使用 (require 'color-theme) 这样可以加快启动速度。

    thumbs

    这个扩展可以把 Emacs 变成一个图片浏览器。看看抓图吧。

    mmm

    (autoload 'mmm-mode "mmm-mode" "Multiple Major Modes" t)
    (autoload 'mmm-parse-buffer "mmm-mode" "Automatic MMM-ification" t)
    

    你想过一个 buffer 里同时存在很多个 major mode 吗?现在用 MMM 就可以办到了。在不同的区域拥有对应键绑定,变量,缩进 ……

    看看我的抓图,HTML, Emacs Lisp, Perl, C 和 LaTeX 全都都在同一个 buffer 里被正确的语法 加亮了!而且TAB在不同的区域都可以进行正确缩进。在 LaTeX 的区 域还可以使用 AUC TeX 来处理某个部分。

    当然我现在是在滥用这个模式。如果你有嵌入 HTML 的 JavaScript 或 CSS2, PL/SQL,……这个就有用了。

    我可以使用这个扩展来处理 CWEB 程序。最外面的 major mode 用 cweb-mode, 里面的 TeX 和 C 的区域就使用它们各自的TeX-mode 和 c-mode。看起来就像这个样子 。

    AUCTeX

    看到这个名字你还以为是一个 TeX 发行吧?它不是一个 TeX 发 行,而是一个专门用来帮助作者写 TeX/LaTeX 文档的工具。它大大 加强了 LaTeX 用户的能力。你写文档时能够只 TeX 一个你觉得需要 看到效果的 region 的内容,而不必等待漫长的编译过程啦!

    preview-latex

    可以把 LaTeX 文档的效果插入到文档里,形成一个预览,与你的 原文件一一对照。非常方便。你可以看看我的抓图:

    文档里的公式

    文档里的图片

    注意要用这个包,你必须安装 TeX 系统和 AUCTeX.

    ctypes.el

    (require 'ctypes)
    (ctypes-auto-parse-mode 1)
    

    ctypes 可以识别你的 C 文件里的类型定义 (typedef)。自动对 它们进行语法加亮。

    ECB

    ECB(Emacs Code Browser) 是一个代码浏览器,它依赖于Semantic 和Eieio。

    它们组合可以实现很好的文法分析的代码浏览。ECB 在 Semantic 和 Eieio 的支持下,可以识别许许多多种计算机语言,C, C++, Java, Elisp, ...

    看看这个抓图。

    Emacs Wiki

    (require 'emacs-wiki)
    
    (add-hook 'emacs-wiki-mode-hook
    	  (lambda ()
    	    (define-key emacs-wiki-mode-map (kbd "C-c C-h") 'emacs-wiki-preview-html)
    	    (define-key emacs-wiki-mode-map (kbd "C-c C-c") 'emacs-wiki-preview-source)
    	    (define-key emacs-wiki-mode-map (kbd "C-c C-v") 'emacs-wiki-change-project)
    
    ))
    
    ;; (setq emacs-wiki-grep-command "glimpse -nyi \"%W\"")
    
    (setq emacs-wiki-publishing-directory "publish")
    
    (setq emacs-wiki-directories '("~/WiKi"))
    (setq emacs-wiki-meta-charset "gb2312")
    (setq emacs-wiki-style-sheet
          "")
    
    (setq emacs-wiki-inline-relative-to 'emacs-wiki-publishing-directory)
    
    (defun emacs-wiki-preview-source ()
      (interactive)
      (emacs-wiki-publish-this-page)
      (find-file (emacs-wiki-published-file)))
    
    (defun emacs-wiki-preview-html ()
      (interactive)
      (emacs-wiki-publish-this-page)
      (browse-url (emacs-wiki-published-file)))
    
    (setq emacs-wiki-projects
          `(("default" . ((emacs-wiki-directories . ("~/WiKi"))))
            ("work" . ((fill-column . 65)
                     (emacs-wiki-directories . ("~/workwiki/"))))))
    
    

    用来制作 WiKi 的包。非常方便。看看我的抓图。

    这上面的只是我自己的设定,你要根据自己的情况修改。

    我写了一个简要的 WiKi 说明在 这 里。

    更多的信息请参考 http://repose.cx/emacs/wiki/。



    Emacs 笔记

    这里简要记录一些 Emacs 的使用技巧。便于查询。


    Emacs 绑定键盘的技巧。

    万无一失的绑定方法

    很多人绑定一些比较特殊的键的时候,都搞不清楚在

    (global-set-key ... 'my-funtion)
    

    里写些什么。特别是在 xterm 里的时候就更不知所措了。其实有一 个万无一失的办法保证你一定写对。这个办法就是:

    1. M-x global-set-key RET 交互式的绑定你的键。
    2. C-x Esc Esc 调出上一条“复杂命令”。

    好了,你现在就能在 minibuffer 里看到你应该写在 .emacs 的东西 了。

    如果你还是失败了……

    如果你在第1步的时候发现 Emacs 根本对你的按键没有反应,那么应 该怀疑是你的窗口管理器拦截了这个按键。比如,我的 FVWM 设置把 C-f3 设定成了打开一个 FvwmCommand, 所以 Emacs 接收不到这个按键。如果我要绑定一个函数到 C-f3, 我必须 让 FVWM 放过 C-f3。

    绑定新的前缀键

    其实上面的办法只能让你绑定一个已有的前缀。你有可能想绑定一个 save-buffer 到 "C-c C-w C-b a"。上面的办法就不灵了。我们必须 使用另外的办法:

    (global-set-key (kbd "C-c C-w C-b a") 'save-buffer)
    

    一点解释

    上面的那个 "C-c C-w C-b a" 是自动把 "C-c C-w", "C-c C-w C-b" 都定义成了一个 prefix-command. 你可以这样看到它们:

    1. C-h C-b 显示绑定
    2. C-x o 切换到显示绑定的窗口
    3. C-x C-q 消除这个窗口的只读属性
    4. M-x delete-non-matching-lines RET prefix RET 删除所有不含 "prefix" 字样的行。

    现在你清楚的看到了 "C-c C-w", "C-c C-w C-b" 都是 prefix command 吧?

    创建新的 prefix command

    上面这个办法只对开头的键已经是 prefix command 的键序列起作用, 如果你的第一个键不是一个 prefix,那么就会出错。你可以试试:

    (global-set-key (kbd "C-z C-c C-w b") 'find-file)
    

    出现错误:(error "Key sequence C-z C-c C-w b uses invalid prefix characters")

    所以你必须事先把第一个键设定为 prefix:

    (define-prefix-command 'ctl-z-map)
    (global-set-key (kbd "C-z") 'ctl-z-map)
    

    然后再用

    (global-set-key (kbd "C-z C-c C-w b") 'find-file)
    

    就行了。"C-z C-c" 和 "C-z C-c C-w" 都会自动被定义为 prefix command.

    绑定中文命令

    现在我举一个例子来说明 prefix command 是如何工作的。我们可以 把中文的 存盘 两个字绑定到save-buffer. 这样你用中文输入法 敲入“存盘”两个字时,就可以把当前 buffer 保存起来。

    (define-prefix-command '存-map)
    (global-set-key (kbd "存") '存-map)
    (define-key 存-map (kbd "盘") 'save-buffer)
    

    有趣吧?你可以猜到这里面是怎么回事吧?太简单了是不是?当你输 入“存”的时候,看到 minibuffer 是这样:

    ../images/bind-cun.png

    这是因为我们把“存”这个字绑定到了 存-map 这个 prefix-command. 当读到“存”的时候,Emacs 就会等待下一条命令, 这个命令是定义在 存-map 这个 map 里的。它读到“盘”,就会执 行 save-buffer 了。

    不过注意,你真的要在文档里输入“存盘”两个字就得先打 C-q 了。 刚才我就打了好多次 C-q,真累啊。还是用一些不常用的词组比较好, 或者加一个 ctrl 什么的前缀,就像这个,"C-z 存盘"。

    (define-prefix-command 'ctl-z-map)
    (global-set-key (kbd "C-z") 'ctl-z-map)
    (define-key ctl-z-map (kbd "存盘") 'save-buffer)
    

    看我们更 bt 一点:

    (define-prefix-command 'ctl-z-map)
    (global-set-key (kbd "C-z") 'ctl-z-map)
    (define-key ctl-z-map (kbd "给我存盘啦!") 'save-buffer)
    

    嗨哟!yes sir!!

    define-key 会自动建立很多 prefix command. 不过自己显式用 define-prefix-command 定义前缀命令有一个好处,就是你可以在你 的 prefix 里再方便的定义更多的命令,而不用把整个前缀都写一遍。

    (define-prefix-command 'ctl-z-map)
    (define-prefix-command '存-map)
    (define-prefix-command '盘-map)
    
    (global-set-key (kbd "C-z") 'ctl-z-map)
    (define-key ctl-z-map (kbd "存") '存-map)
    (define-key 存-map (kbd "盘") '盘-map)
    
    (define-key 盘-map (kbd "!") 'save-buffer)
    (define-key 盘-map (kbd "到") 'write-file)
    (define-key 盘-map (kbd "退出") 
      (lambda ()
        (interactive)
        (save-buffer)
        (kill-emacs)))
    

    这样,到了“C-z 存盘-” 这个时候,我们定义了3个分支:

    • “C-z 存盘!”,表示保存这个文件;
    • “C-z 存盘到”,表示保存到另一个文件;
    • “C-z 存盘退出”,这个不用解释了吧。

    自定义 prefix command 的另外一个更大的好处就是:你可以修改最 上层对 prefix command 的绑定,从而修改许多键的绑定。比如,我 们可以把 “存盘” 轻而易举的改成 “保存”:

    (define-key ctl-z-map (kbd "保存") '盘-map)
    

    这样一来, “C-z 保存!” ,“C-z 保存到” , “C-z 保存退出” 就分别有了 “C-z 存盘!” , “C-z 存盘到” 和 “C-z 存盘退 出” 的含义了。

    移动光标

    1. 基本操作。
      • C-f, C-b: 以字符为单位移动。
      • M-f, M-b: 以单词为单位移动。
      • C-a, C-e: 移动到行首,行末。
      • M-m: 移动到第一个非空格字符。(back-to-indentation)
      • M-a, M-e: 移动到句子头,句子尾。
      • M-{, M-}: 移动到段落头,段落尾。
      • C-v, M-v: 翻页。
      • M-<, M->: 到文件头和文件尾。
      • M-r: 加参数,移动到窗口里的某一行。不加参数缺省移动到窗口中间。
      • M-x goto-char: 到文件的第 N 字节。
      • M-x goto-line: 到文件第 N 行。
      • C-x C-n: 设定 goal-column.
      • C-u C-x C-n: 取消 goal-column.
    2. 以语法结构为单位移动。

      文档一般都有各种结构,比如LISP里有S表达式,C语言里的函数,LaTeX 里的 \begin{...}...\end{...} ... 如果我们能够已文档的语法单位来移动,就会使操作非常高效。

      1. defun。defun 在 LISP 里就是最高一级的 sexp,而在 C 语言里,它的含义就是函数。
        C-M-a   到 defun 头
        C-M-e   到 defun 尾
        
        这样,我们在C语言程序里可以一个函数一个函数的跳过。也可以从一个函数中间一下跳到函数开头或末尾。
      2. 语法单位(sexp)。语法单位在各种 major mode 有不同的定义:
        • lisp-mode: 一个S表达式。
        • c-mode: 一个变量名,一个 (...), 一个 {...}, 一个 [...], ...

          所以在 Emacs 里,寻找匹配的括号可以在括号处使用 C-M-f 和 C-M-b.

        • Gnus: 一个thread。包括所有的 Re:
        其实 defun 可以被看作最高一级的 sexp。所以范围小一点的移动操作就是在同级 sexp 之间移动。
        C-M-f   到下一个同级语法结构
        C-M-b   到上一个同级语法结构
        

        注意,这种移动不能越过语法结构的边界而进入上一级结构。所以,你如果在

        for (i=0; i<10; i++) { ...}
        
        的 for 循环的括号里向右移动,到达右边括号时,就会被提示到达边界。

        注意,文档中的注释在这两个操作中会被跳过,这是非常方便的。

      3. 进入和退出子结构。

        在LISP中,S表达式是嵌套的括号,进入子结构就是进入到这一级 (...) 里面。而在 C 语言中,进入子结构就是进入 (...), {...}, [...] 的里面。 C-M-d: 进入到下一级结构里。C-M-u: 进入到上一级结构里。

        C-M-a, C-M-e, C-M-f, C-M-b, C-M-d, C-M-u 这几个命令组合起来可以迅速的在程序里移动。往往手可以按住 C-M 不放,所以还是很顺手的。

      4. 段落:
        M-} (forward-paragraph)
        M-{ (backward-paragraph)
        
        段落在不同的mode有不同的含义,它的含义是由 paragraph-start 变量决定的。 这个正则表达式可以告诉 Emacs 那些符号出现被认为是一个段落开始了。
      5. 句子:
        M-e (backward-sentence)
        M-a (forward-sentence)
        

        句子在不同的模式有不同的含义。比如在 c-mode, “句子”成为了“语句”的 代名词,所以 M-a 和 M-e 可以以语句为单位移动。

        句子的含义是由 sentence-end 变量决定的。这个正则表达式可以告诉 Emacs 那些符号出现被认为是一个句子结束了……比如我的 sentence-end 是这样 设置的:

        (setq sentence-end "\\([。!?]\\|……\\|[.?!][]\"')}]*\\($\\|[ \t]\\)\\)[ \t\n]*")
        
        这样中文的句子就可以被正确识别了。
    3. 一些特殊模式专有的移动方式。
      • HTML 模式。
        1. 跳过同一级 tag。
          C-c C-f  向前跳过同一级 tag (sgml-skip-tag-forward)
          C-c C-b  向后跳过同一级 tag (sgml-skip-tag-backward)
          
      • LaTeX 模式。
          C-c C-u   到最近的上一级 \begin{...} 处。
          
          C-c }     到最近的上一级匹配 {...} 处。
          

    Emacs Fill 详解

    Emacs 具有非常智能的文本编辑能力。它可以自动对文字断行,并且在断开 的行首都加入一些 prefix(前缀)。

    你编辑 C 程序多行注释的时候,你想要编辑器能够自动缩进到合适的位置并 且插入一个 "*",就像这样?

    	/* seed the random number generator
    	 * first try the random file /dev/random
    	 * if there isn't such a file in the system
    	 * use current time to seed the RNG.
    	 */
    

    在你写新闻组的文章的时候,你又想让编辑器使你的文档出现这样漂亮的缩 进:

    1. I seed the random number generator first try the random file
       /dev/random if there isn't such a file in the system use current
       time to seed the RNG.
    
    2. I need more powerful randomized binary search tree algorithm to
       store my wavefront elements.
    

    这些 * 和 行首留出的空白就叫做 prefix。每当使用 fill-paragraph 等操 作或者启动了 auto-fill-mode 的时候,文字在断行时,Emacs 可能会在断开的 每行前面加入 prefix(前缀)。这大大方便了编辑类似程序注释这一类文字。

    设置 fill-column

    fill-column就是说到多少列的时候断行。你可以使用

    C-u 70 C-x f
    
    这样的命令把 fill-column 设置为 70. 也可以把光标移动到你想要断行的位置, 然后按
    C-u C-x f
    

    断开的行可能会被自动加上一个前缀(prefix)。设置prefix的方式主要有两 种,手动设置和 adaptive prefix 自动设置。

    手动设置 prefix

    如果把光标放在段落首后面一个位置,使用

    C-x . (set-fill-prefix)
    
    就可以把段落头到光标处的那段字符作为 prefix.

    Adaptive Filling

    但是没有手动设置 prefix 的时候,Emacs 也可以自动识别段落首的一些字 符作为 prefix。这就叫做 Adaptive Filling。

    提取候选前缀

    Emacs 使用变量 adaptive-fill-regexp 来提取前缀。这个变量是一个正则 表达式。它会把fill区域开头的能够匹配的部分作为候选的前缀。很多 major mode 会自动帮你设置好这个变量,所以你通常不用操心。

    但是某些时候,你可能希望能够自己操纵这一切。我们下面就来看一个具体 的例子。假设如果你要达到这种效果,在同一个文本文件里:

    1. 有一些段落每行由3个 * 开头,这可以被作为一小节的标题以及简短的说明。 比如:
      *** Section "Files". The location
          of the RGB database. Note, this
          is the name of the file minus
          the extension (like ".txt" or
          ".db"). 
      
    2. 有一些段落每行由一个 * 号开头,这叫做“强调”。像这样:
      * There is normally no need to
      * change the default. Multiple
      * FontPath entries are allowed
      * (they are concatenated together)
      * By default, Red Hat 6.0 and later
      * now use a font server independent
      * of the X server to render fonts.
      
    3. 有一些段落由数字编号 1. 2. 3. 开头,以后的每一行要求缩进到标号之后。 所有的数字后面的点号要对齐。
      1. I seed the random number generator first try the random file
         /dev/random if there isn't such a file in the system use current
         time to seed the RNG.
      
      2. I need more powerful randomized binary search tree algorithm to
         store my wavefront elements.
      

    这些 "*** ", "* ", "1. ", "2. ", "   " 就叫做前缀。 为了识别这些前缀,我们把 adaptive-fill-regexp 设置为:

    (setq adaptive-fill-regexp "[ \t]+\\|[ \t]*\\([0-9]+\\.\\|\\*+\\)[ \t]*")
    

    这表示前缀可以全是空白字符。或者开头可以有一些空白,接着数字加点 或者一个以上的 *,接着一些空白。那么 Emacs 发现开头有这样的字样时,就 会把这个字符串作为一个“候选前缀”。

    候选前缀的选择

    我们已经轻松提取了可能作为前缀的部分,但是一个候选前缀是否被使用, 还有很多因素。Emacs 的策略是非常聪明的。我们下面来看看 Emacs 是怎样为 用户着想的。

    1. 多行文字的前缀 — 使用第二行的前缀

      首先,我们经常有这样一种想法:如果我两行开头都有符合候选前缀条件的 符号,编辑器应该把第二行的那个候选作为前缀。如果我们输入一些文字:

      1. I seed the random number generator 
         first try the random file   
      /dev/random if there isn't such a file in the system use current time to seed the RNG.
      
      我们第一行输入了一个前缀 "1. ",第二行我们故意退了几格,使得 "1. " 这 种数字标号突出在段落之外。但是其它的文字我们可以先不用管。那么第一行找 到的候选前缀就是 "1. "(1,一个点,一个空格),第二行的候选前缀是"   "(3个 空格)。

      写完一段时,我们按 M-q,这段话就自动采用了第二行的前缀(3个空格)作为 prefix。变成这个样子:

      1. I seed the random number generator first try the random file
         /dev/random if there isn't such a file in the system use current
         time to seed the RNG.
      
      如果我们后来不满意。想把第二行开始的那些行多缩进一些,而且把 fill-column 减小一些。我们可以设置 fill-column,然后在第二行开头再加一 些空格,按 M-q。就成了这样:
      1. I seed the random number generator
            first try the random file
            /dev/random if there isn't such
            a file in the system use
            current time to seed the RNG.
      
      想一下你如果不用 Emacs,如何把上面那段文字变成现在这样!
    2. 单行文字的前缀选择

      那么如果我们只输入了一行字就要求把这行的前缀作为所有断开的行的前缀 呢?比如,我们输入一行,开头以 * 开始。我们希望它在fill的时候断开的行 都以 * 开头。

      这是通过设置 adaptive-fill-first-line-regexp 这个变量实现的。这个变 量是一个正则表达式。如果它能够匹配我们用 adaptive-fill-regexp 提取出来的 前缀,那么这个前缀就被采用。

      (setq adaptive-fill-first-line-regexp "^\\* *$")
      

      注意我们没有使用简单的 "\\* *",而是使用了行首和行尾的匹配符号,因 为我们只希望 "* " 这样的符号作为单行重复前缀,出现在每行的开头,而不希 望 "***" 成为每行的开头。 adaptive-fill-regexp 提取出来的候选前缀被作 为了 adaptive-fill-first-line-regexp 的输入行,它有行首和行尾。

      我们这是在告诉 Emacs,单行文字如果由一个 * 开头,那么断行后每一行都 以 * 作为 prefix。

      如果我们输入一行(麻烦你拖动一下:P) :

      * There is normally no need to change the default. Multiple FontPath entries are allowed (they are concatenated together) By default, Red Hat 6.0 and later now use a font server independent of the X server to render fonts.
      
      按 M-q,它就变成了:
      * There is normally no need to change the default. Multiple FontPath
      * entries are allowed (they are concatenated together) By default, Red
      * Hat 6.0 and later now use a font server independent of the X server
      * to render fonts.
      

      如果adaptive-fill-first-line-regexp 不能匹配从单行取出的前缀,Emacs 会把这个前缀转成同样长度的空格作为前缀,这正是我们想要的结果。比如我们 输入:

      *** Section "Files". The location of the RGB database. Note, this    is the name of the file minus    the extension (like ".txt" or    ".db"). 
      
      由于 "^\\* *$" 不能匹配提取出来的候选前缀 "*** ",所以 Emacs 把跟它同 样长度的4个空格作为了前缀。这样 fill 之后变成了:
      *** Section "Files". The location of the RGB database. Note, this is
          the name of the file minus the extension (like ".txt" or ".db").
      
      这正是我们希望的样子。
    3. 另外一些条件

      前面两种情况有一个前提条件,就是候选前缀不能是用来决定段落开头的字 符,否则不采用。这很好理解:如果采用这个前缀,我们自动断行的时候插入的 字符会把一段话分成好几段话,那么文档的逻辑结构就被破坏了,这是不合理的。

      另外,编辑程序的时候,前缀的选择还跟当前的注释符号有关。这个问题超 出了本文的范围。

    总结

    这个规则看起来挺复杂,不过我们可以用算法描述的方式简单的描述出来:
    1. 使用 adaptive-fill-regexp 把每行开头部分能够匹配的字符提取出来,作
       为“候选前缀”。
    
    2. 如果文字有两行以上,把第二行的候选前缀插入到断开的所有行开头。
    
    3. 如果文字只有一行,看看 adaptive-fill-first-line-regexp 能不能匹配这
       行的候选前缀。如果能匹配,使用这个前缀。否则,把这个前缀转成同样长
       度的空格,把这些空格作为前缀。
    


    Emacs Outline Mode 示例

    Outline mode 是 Emacs 的一个强有力的模式。它可以使你轻松的操纵结构 化的文档。它可以让你只显示文档的某一个分支,只显示主干,只显示一个子树。

    下面就是一个 LaTeX 文档的各种 outline 操作的结果示范。由于 outline-minor-mode 的键绑定前缀 C-c @ 过于复杂,大部分经常使用 outline 的人想把它设置为另一个键,所以以后我在叙述时直接称呼函数名字和简化前缀 的键绑定。具体的键绑定请用 C-h w 查询。

    更改前缀可以在启动 outline-minor-mode 之前,用改变 outline-minor-mode-prefix 变量的办法一次完成。比如:

    (setq outline-minor-mode-prefix [(control o)])
    
    就可以把前缀改成 C-o. 以后我们实例中的键绑定都使用 C-o.

    本文的 Outline

    首先,给大家一个 outline 的总体印象。我们使用 outline 来看看本文的主要内容 :)

    Emacs编辑器——王垠_第2张图片

    原文档

    这是一个非常简单的 LaTeX 文档: outline.tex

    Emacs编辑器——王垠_第3张图片

    我们来把文档的各部分术语解释一下。

    1. heading: 是指文档里的标题,比如 Chapter 1, Chapter 2, Section 1, ... 的那些行。
    2. branch: 是指一颗子树下所有 heading 的集合。想一想“树干”。
    3. entry: 是指文档里不是 heading 的那些内容。比如,"Entry for topmost level", "Entry for Chapter 1", "Entry for Chapter 2", ... 这些才是文档 的主要内容。
    4. leaves: 是指一棵子树里的所有 entry。
    5. body: 是指文档里所有 entry 的集合。注意这个概念跟 leaves 的区别是 范围上的区别。

    启动 Outline

    M-x outline-minor-mode 就可以启动 Outline。还有一个 outline-mode 是 一个 major mode,一般都不用它。

    全局隐藏操作

    光标在任何位置,只要执行这些操作,文档的显示就会变化成需要的样子。

    hide-sublevels(C-o C-q)

    这个操作如果不带参数,隐藏所有文档子结构,只剩最上层。

    M-4 hide-sublevels(M-4 C-o C-q)

    这是参数为4的操作,显示至文档第4层子结构。

    Emacs编辑器——王垠_第4张图片

    hide-body(C-o C-t)

    文档的所有 Entry 都被隐藏。只显示主干。

    Emacs编辑器——王垠_第5张图片

    对一个子树(Chapter 1)的隐藏操作

    hide-subtree(C-o C-d)

    所有文档部分展开时,光标移动到 Chapter 1,执行 hide-subtree。整个 Chapter 1 的子树被折叠起来。

    Emacs编辑器——王垠_第6张图片

    hide-other(C-o C-o)

    所有文档部分展开时,光标移动到 Chapter 1,执行 hide-other。除了 Chapter 1, 其它子树全部被折叠起来。这个操作正好与 hide-subtree 互补。

    Emacs编辑器——王垠_第7张图片

    hide-leaves(C-o C-l)

    所有文档部分展开时,光标移动到 Chapter 1,执行 hide-leaves。所有 Chapter 1 子树下的所有级别的 entry 被隐藏。也就是说,Chapter 1 下,只 显示 branch.

    Emacs编辑器——王垠_第8张图片

    hide-entry(C-o C-c)

    所有文档部分展开时,光标移动到 Chapter 1,执行 hide-entry。Chapter 1 的 Entry 被隐藏,但是所有子树都不动。

    Emacs编辑器——王垠_第9张图片

    全局显示操作

    show-all(C-o C-a)

    显示所有文档。结果就是原文档。

    对一个子树的显示操作

    为了演示,我们从全部隐藏的情况开始:

    show-children(C-o C-i)

    show-children 只显示直接的下一代子树,而不显示间接的下一代。这里, \begin{document} 的直接的下一代就是 \chapter{...}。

    Emacs编辑器——王垠_第10张图片

    show-entry(C-o C-e)

    把光标移动到 Chapter 1,执行 show-entry。Chapter 1 的 Entry 被显示, 但是所有子结构还是保持隐藏。 Emacs编辑器——王垠_第11张图片

    show-branches(C-o C-k)

    把光标移动到 Chapter 1,执行 show-branches。Chapter 1 这棵子树之下 的各级“树干”被显示,但是各级 entry 还是保持隐藏。Chapter 1 自己的 entry,由于我们上一步已经显示,所以保持不变。

    Emacs编辑器——王垠_第12张图片

    show-subtree(C-o C-s)

    把光标移动到 Chapter 1,执行 show-subtree。Chapter 1 及其所有子结构 全部被扩展。

    Emacs编辑器——王垠_第13张图片

    Outline mode 下的移动操作

    在 outline 模式下,有几种特殊方便的移动方式。

    1. C-o C-n (outline-next-visible-heading) 移动到下一个可见标题。
    2. C-o C-p (outline-previous-visible-heading) 移动到上一个可见标题。
    3. C-o C-f (outline-forward-same-level) 移动到下一个同级可见标题。
    4. C-o C-b (outline-backward-same-level) 移动到上一个同级可见标题。
    5. C-o C-u (outline-up-heading) 到上一级标题。
    来源: http://docs.huihoo.com/homepage/shredderyin/emacs.html

    更多内容详见 http://docs.huihoo.com/homepage/shredderyin/emacs.html  王垠个人主页

  • 你可能感兴趣的:(shell,shell,shell,emacs,emacs)