emacs 根据主模式加载配置

Vim、Emacs配置和使用
回复
kardinal
帖子: 750
注册时间: 2006-03-19 11:39

emacs 根据主模式加载配置

#1

帖子 kardinal » 2011-12-25 0:41

很多人的配置文件,各种主模式的配置写在一起的,可能既有 c 又有 lisp 又有 js 什么的,其实大部分情况下你不会同时用那么多。
于是有了另一种方式,拆分到文件,每种模式用一个单独的文件。在主配置文件里写上 (load 'xxx)
但是这挺麻烦的,你要新建一个文件名为 xxx,然后主配置文件里又得写 xxx,这显然是重复劳动……这是我最痛恨的……因为我一年级就上了好几次
于是我弄了一个文件夹,按顺序自动 load 里面的文件,当然不是随便 load,得符合条件才行。比如我设定的条件是扩展名为 el。
这样一来,不想 load 的文件改下扩展名就可以了……我还弄了一个函数,专门给文件添加/删除扩展名 .bak
但是时间长了也挺麻烦的,因为要手动改,而且下次才能生效…… 你还得自己 load-file

……其实一般情况下,这不是什么问题,你就是给每种模式写上几万行的配置,都写在一个文件里,对于现在的机器而言,压力也不是太大……但是咱追求的不是方便,而是折腾 :em06

好了,开始动手
[lisp]
(let (name
(dir (expand-file-name "_major-mode/" *init-dir*)))
(mapcar
(lambda(x)
(setq name
(file-name-sans-extension (file-name-nondirectory x)))
(add-hook (concat-symbol name "-mode-hook")
`(lambda()(load ,x))))
(directory-files dir t "\\.el\\'")))
[/lisp]
简单的说,假设 ~/init.emacs/_major-mode/ 目录下有 3 个文件 emacs-lisp.el nxml.el js2.el.bak ,则会:
在 emacs-lisp-mode-hook 里添加 (lambda nil (load "~/init.emacs/_major-mode/emacs-lisp.el"))
在 nxml-mode-hook 里添加 (lambda nil (load "~/init.emacs/_major-mode/nxml.el"))
用到的 concat-symbol
[lisp](defun concat-symbol (&rest lst)
(read (apply 'concat (mapcar (lambda(x)(if (symbolp x) (symbol-name x) x)) lst))))
[/lisp]
需要注意的是,这简直就是一个特大号的 hook ,所以不用在里面再写什么 add-hook 'xxx-mode-hook 了,里面所有的东西都相当于在 xxx-mode-hook 里。
如此一来,就产生了另一个问题,每打开一个 a.xxx 文件,xxx-mode-hook 就要运行一次,这比较二,实际上只要运行一次就可以了。

这个问题可以用 require 来解决,但缺点是,你要在新建一个文件为 xxx.el ,还要在里面写 provide 'xxx ,并且还要把当前目录加到 load-path,重复劳动还是比较多。

另一种方案就是 load-once
[lisp]
(defmacro load-once (&rest s)
(let* ((name (file-name-sans-extension
(file-name-nondirectory
(or load-file-name (buffer-file-name)))))
(a (concat-symbol "*load-once--" name "*")))
`(if (boundp ',a)
nil
,@s
(setq ,a t))))
[/lisp]
在 xxx.el 里面只运行一次的部分可以写在 load-once 里面,像这样
[lisp]
(load-once
(foo)
(bar)
)
[/lisp]
基本上,你把所有内容都写在 load-once 里面就对了,不管这个文件被 load 了多少次,都只运行一次。
这种方法的缺点是,需要在文件开头和结尾分别写 (load-once 和 ),而 provide 只要写在一处;
每种模式被 load 后,自动生成了一个值为 t 的变量 *load-once--xxx* 、*load-once--yyy* ……
:em20 所以,我可以给你一个十分专业的建议:你自己看着办
kardinal
帖子: 750
注册时间: 2006-03-19 11:39

Re: emacs 根据主模式加载配置

#2

帖子 kardinal » 2011-12-27 22:54

正在把配置文件里一些不常用的函数转移到 autoload 里面
[lisp]
(defun autoload-directory (dir &optional force loaddefs basedir)
(let* ((path
(expand-file-name dir (or basedir *init-dir*)))
(ldfs
(or loaddefs (expand-file-name "_loaddefs" path))))
(mapcar
(lambda(f)
(if (or force
(null (file-newer-than-file-p ldfs f)))
(update-file-autoloads f t ldfs)))
(directory-files path t "\\.el\\'"))
(load ldfs)))

(autoload-directory "_autoload/")
[/lisp]
kardinal
帖子: 750
注册时间: 2006-03-19 11:39

Re: emacs 根据主模式加载配置

#3

帖子 kardinal » 2011-12-27 22:55

现在的版本
[lisp]
(let* ((dir (expand-file-name "_extensions/" *init-dir*))
(ext (mapcar
(lambda(x)(cons (file-name-sans-extension (file-name-nondirectory x)) x))
(directory-files dir t "\\.el\\'"))))
(add-hook 'find-file-hook
`(lambda ()
(let (mode)
(mapcar (lambda(x)
(and
(string-match (car x)(buffer-name))
(setq mode (symbol-name (cdr x)))))
auto-mode-alist)
;; (setq mode
;; (or mode
;; (and (string-equal "*" (substring (buffer-name) 0 1))
;; (substring (buffer-name) 1 -1))))
(load (or
(cdr (assoc mode ',ext))
(make-temp-name ""))
t))))
(add-hook 'eshell-load-hook
`(lambda ()
(load ,(cdr (assoc "eshell" ext)))))
)
[/lisp]
回复