@qqiseeu
2014-06-18T07:10:19.000000Z
字数 6532
阅读 41602
Emacs
用Emacs好一段时间了,由于自己一向懒得折腾,以前用的配置文件直接就是从Steve Purcell那里fork过来的。但是由于Steve Purcell主要做前端,所以配置文件里各种针对HTML、JavaScript、Ruby之类的插件(虽然绝大部分都被我注释掉了),还有万恶的flymake等我完全用不上的东西。身为一个强迫症患者,这样存在大量冗余的配置文件我实在是忍无可忍了,最近又刚好有时间,遂决心自己从头配过一个简单点的版本:
本人的系统环境如下:
首先,Emacs的初始化文件可以有两种设置方法:
~/.emacs。这种方法把所有初始化函数放在一个文件里,设置起来简单,但是一旦插件多了这个文件就会变得很长很乱。~/.emacs.d/。所有配置文件都放在该目录下,并且Emacs启动时会自动执行该目录下名为init.el的文件。虽说只有一个文件会被自动执行,但可以在init.el里执行其它的函数,所以init.el可以变得很简洁;使用Emacs的Feature机制,可以很方便地把具体的初始化工作按类别分在其余文件中。这也是我选择的方法我自己的配置放在这里,目录结构如下(注意其中elpa/目录没有被push到github上):
~/.emacs.d/README.md #请无视该文件init.el #Emacs会自动从init.el开始执行snippets/ #yasnippet的自定义模板保存的位置,不重要elpa/ #通过ELPA下载的插件所保存的位置lisp/ #就是加载各个插件的初始化文件的位置啦init-xxx.el #某初始化文件editing-utils/ #文本编辑用的一些小工具custom-themes/ #自定义的主题,不重要custom-dicts/ #自定义的auto-complete词典,不重要
主要就是下面几句
;; init.el;; 把目录lisp/添加到搜索路径中去(add-to-list'load-path(expand-file-name "lisp" user-emacs-directory));; 下面每一个被require的feature都对应一个lisp/目录下的同名;; elisp文件,例如init-utils.el、init-elpa.el(require 'init-utils) ;; 为加载初始化文件提供一些自定义的函数和宏(require 'init-elpa) ;; 加载ELPA,并定义了require-package函数(require 'init-fonts) ;; 以Server-Client模式启动时需额外设置字体(require 'init-editing-utils) ;; 一些顺手的小工具...(require 'init-markdown)(require 'init-auctex)(provide 'init)
最主要的作用是提供了一个宏after-load,供后续的各初始化函数使用。这个函数来自Purcell,目的是把一些相互依赖的feature的加载顺序理顺,例如feature A依赖于feature B,则可以写成(after-load 'B 'A),这样如果错误地在B之前require了A也不会影响正常启动。
;; after-load(defmacro after-load (feature &rest body)"After FEATURE is loaded, evaluate BODY."(declare (indent defun))`(eval-after-load ,feature'(progn ,@body)))
这个文件我也是从Purcell那里截取过来的,但是去掉了很多用不上的函数。该文件主要工作是初始化Emacs的包管理系统ELPA,你可以把ELPA类比为Emacs的软件源,就像Debian、Ubuntu之类的软件源一样。需要注意的是,仅从Emacs24开始默认支持ELPA,如果Emacs版本过低,建议升级一下;但如果使用的是Emacs23,也可以从这里下载package.el放在lisp/目录下,然后在init-elpa.el中加入如下代码:
(require 'package)(add-to-list 'package-archives'("marmalade" ."http://marmalade-repo.org/packages/"))....;;这一句放在(provide 'init-elpa)之前(package-initialize)
init-elpa的关键内容如下:
;; init-elpa.el(require 'package);; 增加软件包仓库(add-to-list 'package-archives '("org" . "http://orgmode.org/elpa/"))(when (< emacs-major-version 24)(add-to-list 'package-archives '("gnu" . "http://elpa.gnu.org/packages/")))(add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/"))(add-to-list 'package-archives '("melpa-stable" . "http://melpa-stable.milkbox.net/packages/"));; 定义require-package函数(defun require-package (package &optional min-version no-refresh)"Install given PACKAGE, optionally requiring MIN-VERSION.If NO-REFRESH is non-nil, the available package lists will not bere-downloaded in order to locate PACKAGE."(if (package-installed-p package min-version)t(if (or (assoc package package-archive-contents) no-refresh)(package-install package)(progn(package-refresh-contents)(require-package package min-version t)))));; 强行提前初始化ELPA。因为默认情况下Emacs在init.el加载完之后才开始初始化ELPA,;; 而我们把大多数包的初始化函数都放在init.el中,如果不提前初始化ELPA会导致后面的;; 初始化过程出错(对应的包文件还没有加载进来)。(package-initialize)(provide 'init-elpa)
require-package函数的作用是,判断某个包是否已经安装,如果没有则自动从ELPA中安装它(需要联网)。当然,对于自定义的插件或是没有被ELPA收录的插件,这个函数就不起作用了。
有了ELPA,给Emacs装插件就变的非常容易了。比方说你需要一个叫example的插件,那么可以在lisp/目录下增加一个文件init-example.el:
;; init-example.el(require-package 'example);; ELPA中的插件一般都提供一个"autoloads"方法,可以帮用户自动加载插件并做相应;; 的配置。不过如果涉及到细节的配置,那请自己看该插件的帮助文档。(require 'example-autoloads)(provide 'init-example)
然后在init.el中加入一句(require 'init-example)(注意这一句要放在(require 'init-elpa)之后)即可。
auto-complete是必不可少的Emacs插件之一,它的问题主要集中在设置触发补全动作的快捷键及添加ac-sources上。默认情况下补全动作是自动触发的,但是如果用了clang之类的扩展,第一次触发可能需要比较久的时间(在构造ac-source),于是给人一种卡出翔的错觉。所以需要将补全动作改为手动触发。ac-sources是auto-complete在补全时自动搜索的模板空间,被补全的半单词会自动在ac-sources里面进行匹配。根据所编辑文件类型的不同,ac-sources最好也不同(例如我在补全C语言关键字的时候auto-complete就不应该去匹配Python的关键字),因此常见的做法是给每个主模式的hook上挂一个函数,由该函数来执行修改ac-sources的工作(具体可见init-ac-source.el中的函数ac-latex-mode-setup)。
;; init-auto-complete.el...(require 'auto-complete-config)(global-auto-complete-mode t);; 把自定义的dict加到auto-complete的字典中去(add-to-list 'ac-dictionary-directories(expand-file-name "lisp/custom-dicts" user-emacs-directory));; 按下TAB时首先缩进所在行,然后尝试补全(setq tab-always-indent 'complete);; 阻止自动触发补全动作(setq-default ac-expand-on-auto-complete nil)(setq-default ac-auto-start nil);; 用TAB作为手动触发补全动作的快捷键(ac-set-trigger-key "TAB");; 使用after-load来确保ac-source-yasnippet已经完成加载(after-load 'init-yasnippet(set-default 'ac-sources'(ac-source-dictionaryac-source-words-in-bufferac-source-words-in-same-mode-buffersac-source-words-in-all-bufferac-source-functionsac-source-yasnippet)))(require 'init-ac-source)(provide 'init-auto-complete)
yasnippet是auto-complete的最好搭配。它的触发快捷键包括TAB,这和之前设置的auto-complete的键位冲突,所以做如下配置:
(require-package 'yasnippet)(require 'yasnippet);; 使用Ctrl-c k作为唯一的触发快捷键(define-key yas-minor-mode-map (kbd "<tab>") nil)(define-key yas-minor-mode-map (kbd "TAB") nil)(define-key yas-minor-mode-map (kbd "C-c k") 'yas-expand)(yas-global-mode t)(provide 'init-yasnippet)
clang是一个C/C++、Obj-C/Obj-C++的编译器前端,利用它可以auto-complete能够补全C/C++的各种标准库函数。首先需要有auto-complete-clang,然后把系统中各头文件的路径告诉它:
(require-package 'auto-complete-clang)(require 'auto-complete-clang)(setq ac-clang-flags(mapcar (lambda (item) (concat "-I" item))(split-string"/usr/include/c++/4.8/usr/include/x86_64-linux-gnu/c++/4.8/usr/include/c++/4.8/backward/usr/lib/gcc/x86_64-linux-gnu/4.8/include/usr/local/include/usr/lib/gcc/x86_64-linux-gnu/4.8/include-fixed/usr/include/x86_64-linux-gnu/usr/include")))
上述路径可以通过命令echo "" | g++ -v -x c++ -E -来得到(详细说明见这里)。
插件多了Emacs的启动速度就会很慢,为了掩盖(没错,就是掩盖,不是解决...)这个问题,可以用Server-Client的方法营造一种启动很快的错觉,用开机速度换Emacs的启动速度。
具体原理是,开机的时候以daemon模式启动emacs(这时就会进行插件的加载,并执行初始化文件),让它作为server运行在后台,之后再打开Emacs的时候就以client的形式调用,此时它会直接连接到在后台运行的server上,然后server给你一个窗口编辑文件。由于初始化过程已经在启动server时完成了,启动client的时候几乎是秒开。
需要注意的问题是,GTK版本的Emacs以daemon模式启动时,由于一个Gtk+的Bug,emacsclient可能会时不时地崩溃。解决这个问题的办法是换用emacs24-lucid(可以从Debian的源里安装)绕过它。
具体为,开机的时候启动加载下述脚本:
#! /bin/shLC_CTYPE=zh_CN.utf8 emacs24-lucid --daemon
然后给emacs命令加条alias:emacsclient -a "" -c即可。另外,根据这篇文章,由于启动server的时候是以daemon模式启动的,初始化文件中所有与X有关的设置都失效了。这是因为这些设置是在X下的frame创建时才有效的,而启动服务器的时候是没有创建frame的。在我的机子上,就表现为使用client创建frame后字体奇丑无比,因此使用下述函数来设置字体:
;; init-fonts.el(setq window-system-default-frame-alist'((x (font . "文泉驿等宽微米黑 11")) ;; 若frame在X下创建(nil))) ;; 若frame在terminal中创建(provide 'init-fonts)
本文只是介绍了我认为的配置过程中几个比较重要的文件。全部配置文件可以在我的github repository中查看。如果你懒得配置,可以直接clone或fork它。