如何模块化 emacs 配置?

Posted

技术标签:

【中文标题】如何模块化 emacs 配置?【英文标题】:How to modularize an emacs configuration? 【发布时间】:2011-01-05 22:49:52 【问题描述】:

我决定从头开始重写我的 .emacs,并且我想设置一些模块化的东西,以避免可怕的 1k+ LoC init.el 文件...

我认为每个配置都需要解决一些基本问题:

全局选项 编辑功能 导航(帧和缓冲区) 键绑定 模式自定义

虽然我仍在思考结构,但我正在寻找一些关于如何实现这一点的指针。 我在 github 等上查看了一些 .emacs,似乎有不同的方法,并且没有首选的方法,这有点令人困惑。 我有兴趣阅读有关如何构建此类设置的一些想法,尤其是一些相关的 elisp 代码。


edit : 一直在忙,还没有太多时间玩这个。 几天后将尝试提出的方法,看看哪种方法最好,同时感谢所有建议!


edit2 :我一直在使用literate init file with org-mode,这绝对很棒! 我还没有设置一个特定的加载机制,我一直在使用这个代码,递归地加载我的 elisp 目录,然后 require 或任何设置说明。

  (if (fboundp 'normal-top-level-add-subdirs-to-load-path)
  (let* ((my-lisp-dir "~/.emacs.d/elisp/")
  (default-directory my-lisp-dir))
  (setq load-path (cons my-lisp-dir load-path))
  (normal-top-level-add-subdirs-to-load-path)))

我仍然需要完善这个,也许使用自动加载,如果修改了一些字节重新编译技巧;很想听听这方面的建议。

【问题讨论】:

【参考方案1】:

我的 .emacs 文件加载 ~/.emacs.d/init.el,它定义了以下函数,首先为 XEmacs 编写,但对 Emacs 运行良好这几天:

(defconst user-init-dir
  (cond ((boundp 'user-emacs-directory)
         user-emacs-directory)
        ((boundp 'user-init-directory)
         user-init-directory)
        (t "~/.emacs.d/")))


(defun load-user-file (file)
  (interactive "f")
  "Load a file in current user's configuration directory"
  (load-file (expand-file-name file user-init-dir)))

然后文件的其余部分会加载大量具有如下格式的单个文件:

(load-user-file "personal.el")

我目前的文件集如下:

personal.el platform.el cygwin.el 变量.el paths.el mail-news.el misc-funcs.el bbdb.el 日历.el gnus-funcs.el c-and-java.el lisp.el clojure.el go.el markdown.el sgml-xml.el tex.el spelling.el org.el packages.el fonts.el color-theme.el frame.el server.el keys.el aquamacs.el

顾名思义,其中一些在意图上比其他更具体。文件的粒度越细,在重新安装包或库时禁用表单集群就越容易。这在“移入”新系统时特别有用,您将配置文件拖过来但尚未安装所有支持包。

【讨论】:

正是我的目标。不错。 这就是为什么我倾向于将支持包藏在我的 .emacs 文件夹中的某个地方。 文件名不会和真实包冲突吗?或者您正在加载完整路径而不是简单的要求? 请注意上面显示的对load-file 的调用:它提供了由user-init-dir 中的文件基名形成的路径。也就是说,它不会尝试从 load-path 加载名为 markdown.el 的第一个文件,而是专门加载文件 /home/myusername/.emacs.d/markdown.el . 今晚我发现了 Aaron Bedra 巧妙地使用 org-mode 将他的配置定义为一个“识字程序”:github.com/abedra/emacs.d。在此处查看导出的 org-as-html 文档:aaronbedra.com/emacs.d。我受到启发尝试更改我的配置方法以使用这样的一个文件,尽管当我只更改上面列出的文件之一中的一些相关内容时,我会失去更精确的版本控制跟踪。编写文档的能力是否值得颠倒正常的文档内代码排列?【参考方案2】:

要将自定义拆分为不同的文件,您可以使用initsplit.el。此外,该 wiki 在modular .emacs layout 上有一个页面。

【讨论】:

【参考方案3】:

您可以执行(require 'foo),这将加载在您的加载路径中找到的第一个“foo.el”elisp,或者(require 'foo "/home/user/experimental/foo.el") 用于加载路径之外的内容。如果“foo.el”不包含表达式(provide 'foo),它会报错。在不正常的情况下,您可以使用(require 'foo "bar.el"),只要“bar.el”有(provide 'foo) 子句,这将起作用。

【讨论】:

【参考方案4】:

使用load-file有什么问题?

您可能会发现一些有用的附加位包括

file-exists-p file-expand-wildcards

使用C-h f <function> 获得帮助,您可能会发现An Introduction to Programming in Emacs Lisp 有用(或download the PDF)

【讨论】:

【参考方案5】:

如果你想拆分你的custom-set-variables,但又想 比initsplit.el 更轻的东西,然后试试这个:

(defmacro custom-set-variable (var value)
  "Call `custom-set-variables' with a warning shown
when customizing using the customize GUI.

XXX: does not support setting the optional NOW and
REQUEST (dependency) fields."
  (custom-set-variables
    ;; `load-file-name' is set by `load-file':
    ;; http://***.com/a/1971376/470844
    `(,var ,value nil nil
      ,(format "!!! CAREFUL: CUSTOM-SET IN %s !!!" load-file-name))))

现在,将您的配置模块放在单独的文件中,然后加载它们 正如其他人所建议的那样,在您的~/.emacs 中使用load-file。每个 配置模块,使用

(custom-set-variable var value)

而不是

(custom-set-variables '(var value))

(setq-default var value)

设置由自定义管理的变量时。然后,如果你需要 要使用自定义界面自定义变量,您会看到一个 警告告诉您自定义应存储在哪个文件中:

"!!! CAREFUL: CUSTOM-SET IN <path to module file> !!!"

如果您对配置文件进行版本控制,那么差异会告诉您哪个 ~/.emacs 的行移入自定义模块。

天真地使用多个问题的讨论 custom-set-variables 来电:http://www.dotemacs.de/custbuffer.html

编辑:回答艾伦的问题

上面custom-set-variable宏解决的问题

Emacs 中的自定义界面将所有自定义设置变量保存在 每当您编辑任何自定义设置变量时,您的~/.emacs。如果你 模块化您的自定义设置变量,通过将它们分散到 多个文件,那么你需要意识到emacs何时插入重复 .emacs 中的设置,然后将其删除。 custom-set-variable 上面的宏可以帮助你做到这一点。

我的配置文件中的一个真实示例

我自定义设置与空格相关的变量 ~/.emacs.d/extensions/whitespace.el 由我加载的文件 ~/.emacs。例如,我设置我的search-whitespace-regexp 像这样的变量:

(nc:custom-set-variable search-whitespace-regexp "[ \t\r\n]+")

现在,发生了两件事。首先,当我在 自定义界面我收到警告:

其次,如果我使用自定义界面编辑任何变量,我的 ~/.emacs 很明显我已经掩盖了一些定义。 例如,在更改search-whitespace-regexpglobal-whitespace-mode在自定义界面,我得到:

$ svn di --diff-cmd "diff" -x "-U0" ~/v/conf
Index: /home/collins/v/conf/dot.emacs
===================================================================
--- /home/collins/v/conf/dot.emacs  (revision 267)
+++ /home/collins/v/conf/dot.emacs  (working copy)
@@ -55,0 +56 @@
+ '(agda2-include-dirs (\` ("." (\, (file-truename "~/local/opt/agda-std-lib/src")))) nil nil "!!! CAREFUL: CUSTOM-SET IN /home/collins/.emacs.d/extensions/agda.el !!!")
@@ -58,0 +60,3 @@
+ '(global-whitespace-mode t)
+ '(indent-tabs-mode nil nil nil "!!! CAREFUL: CUSTOM-SET IN /home/collins/.emacs.d/extensions/whitespace.el !!!")
+ '(indicate-empty-lines t nil nil "!!! CAREFUL: CUSTOM-SET IN /home/collins/.emacs.d/extensions/whitespace.el !!!")
@@ -72,0 +77 @@
+ '(search-whitespace-regexp "" nil nil "!!! CAREFUL: CUSTOM-SET IN /home/collins/.emacs.d/extensions/whitespace.el !!!")
@@ -74,0 +80 @@
+ '(tab-width 2 nil nil "!!! CAREFUL: CUSTOM-SET IN /home/collins/.emacs.d/extensions/whitespace.el !!!")

宏无法帮助我记住我改变了 search-whitespace-regexp,只是它已经设置在其他地方, 但它确实帮助我知道我应该从中删除哪些变量设置 我的~/.emacs(所有有警告的人),我应该离开哪些 在我的~/.emacs 中,或移动到其他单独的文件(所有那些没有 警告)。

结论

这个宏可能有点矫枉过正,但是一旦它存在,它就很容易使用,并且可以实现 使用阴影自定义设置更容易避免在脚上射击自己 变量。

【讨论】:

那么,一旦你有了这段代码,自定义界面有什么用呢?您是否应该通过界面保存更改,然后将 .emacs 文件中产生的更改传输到自定义文件中,从 .emacs 文件中删除它们? 另外,我想你是说你要改变这样的东西:(custom-set-variables '(column-number-mode t) '(display-time-mode t )) to this: (custom-set-variable column-number-mode t) (custom-set-variable display-time-mode t) 对吗? @Alan:请参阅上面的新示例。主要用途是帮助您避免在使用自定义界面时在两个不同的文件中意外设置两次变量。您可以通过界面或直接在配置文件中进行更改,但实际上我发现界面更友好,例如当我不知道我更改为更改的变量被称为什么时,所以我正在自定义浏览。是的,我按照你的建议使用它。 感谢您非常清晰明确的解释。我现在明白它的工作原理了。 关于通过自定义 UI 更改 any 选项的要点,会导致 all 以编程方式设置自定义选项(带有custom-set-variables)在提名中重复自定义文件是关键 - 我也在这里可能过于详细地强调了这一点,通过逐步演示 setqcustom-set-variables 的区别,如果它可以帮助人们:emacs.stackexchange.com/questions/55018/…【参考方案6】:

看看优秀的emacs starter kit,它组织得非常好,还包括 ELPA 包。我使用该结构作为我自己的更简单设置的灵感。

【讨论】:

【参考方案7】:

感谢@JoeCasadonte 提及EmacsWiki page。当我使用 Debian 和 Ubuntu 时,它的这个特殊的 sn-p 工作得很好(放在 .emacs.d/init.el 中):

(setq dotfiles-dir (file-name-directory (or load-file-name (buffer-file-name))))

(let ((user-site-start-dir (concat dotfiles-dir "/site-start.d")))
    (debian-run-directories user-site-start-dir))

然后,我只需将我的 .el 文件放入 ~/.emacs.d/site-start.d(例如 ~/.emacs.d/site-start.d/50cedet.el),它们都会自动加载。

【讨论】:

【参考方案8】:

这是一个库,您只需将新模块保存在指定目录中即可安装它们。可以从http://github.com/tripleee/my-site-start/下载安装

    ;;; my-site-start.el --- set up personal .emacs.d/site-start.d/
    ;;
    ;; Copyright (C) era eriksson <http://www.iki.fi/~era/> 2008-2009
    ;; License: GPL v2
    ;; Version: see `my-site-start-version' below
    ;;
    ;;; Commentary:
    ;;
    ;; The purpose of my-site-start is to simplify maintenance of user libraries.
    ;; Instead of indefinitely tweaking your .emacs, just create a site-start.d
    ;; directory and add symlinks to the libraries you want to load into Emacs.

这种架构确实强加了一些您可能喜欢或不喜欢的约定。完整的独家新闻,请参阅文件顶部附近的评论部分的其余部分。

我是这段代码的作者,感谢任何反馈(虽然不是直接通过这个公共论坛)。

【讨论】:

【参考方案9】:

我所做的是对我可能想要使用的每一件事 X,创建一个名为 configure-X 的文件,并将相应的 require 放入 .emacs 中。然后可以通过注释掉一行来删除此功能。

例如,要选择最简单的(如果命名最糟糕的话),我有 configure-picture-and-artist-mode.el。这个文件很简单:

(global-set-key (kbd "C-M-P")
                'picture-mode)

(provide 'configure-picture-and-artist-mode)

在 .emacs 中:

(require 'configure-picture-and-artist-mode)

整理出来之后,我的 .emacs 中没有任何内容,除了 requires 的大列表和对 load-path 的一些更改,给定的包可以很容易地删除,所有配置每个包都在一个地方。

(我还有一个包罗万象的 .el 文件,我用它来存储没有任何明显归宿的东西(我的全局键绑定、辅助函数等),但理想情况下仍想远离 .el 文件。 emacs。这个位不是非常模块化,并且可能会引入对我的方案应该轻松卸载的包的隐式依赖,但实际上它似乎没什么大不了的。)

【讨论】:

您能否分享更多您如何哄骗“要求”以查找包含“提供”表单的文件?您是否为require 的第二个参数指定了文件名? @seh 只要“提供的”文件在加载路径中,您就不需要这样做。我可以有一个文件 foo.el,在其中我有 (provide 'foo) 或 (provide 'foobar) 或其他什么,然后在我的初始化文件中我可以说 (require 'foo) (或 foobar 或其他) 它会加载文件。 我在提供的符号后命名了每个文件,确保包含的文件夹位于加载路径中......并且它正常工作。 (我不确定这种机制的详细来龙去脉,但我怀疑如果没有加载的文件已经提供了请求的符号,则 require 只知道尝试加载以该符号命名的文件。无论如何这就是我注意到其他 elisp 文件在做,所以我复制了它。) 谢谢。今天我将尝试在我自己的配置文件中进行设置。 请参阅C-h f require RET,了解该机制如何工作的详细信息。另请注意 NOERROR 参数,它根据是否可以加载相关功能来促进条件行为。【参考方案10】:

你可以试试init-loader.el,它是一个配置文件的加载器。 init-loader.el 从指定目录加载配置文件。它使您能够对配置进行分类并将它们分成多个文件。

Fore more info, please visit init-loader GitHub Repo

【讨论】:

以上是关于如何模块化 emacs 配置?的主要内容,如果未能解决你的问题,请参考以下文章

Elisp 11:动态模块

Linux 下配置 Python IDE——Emacs

更新:SICP+Emacs+Lisp学习的抽象认识

用于 Python 编程的 Emacs:模块/类大纲/浏览器

emacs系列文章目录——更新ing

转 在emacs中绘图