讨论Package管理与加载流程

elisp的package管理和其他语言的相比,好像没有版本依赖性这一块。

java有pom.xml,perl有cpan,ruby有Gemfile,go有Gopkg.toml。它们都能很好的解决代码间的依赖关系

而由于历史和现实的原因,elisp对版本的依赖需求并不是很高。

  1. 底层的版本依赖,emacs已经保证了。

  2. elisp的package大部分是由一两个文件组成,接口一般相对稳定,并且规模不是很大,导致package对外接口和版本的对应关系不是严格绑定。

  3. emacs自身是一个整体的运行环境,而不像其它语言,开发出的软件是各自独立运行。独立的运行环境内,要求版本之间是不能冲突的。

  4. 即使emacs有版本链这一说,只要emacs的版本决定下来了,在其上的各package就基于它来保证正常运行。也就是emacs的一个版本决定一个版本链。

我说的是package的版本依赖,不是package依赖,比如我写的elisp必须xxx package的某某版本以上,没有的话相应的工具就帮着自动下载。其他语言都有这种版本链。现在的状况是,只要都取最新的package,好像就不会出现冲突问题。

欢迎大家来讨论!我是新手,难免理解有误,欢迎指正。

怎么可能没有呢?ELPA 上几千个包不可能个个都相互独立、没有依赖关系。

比如 Magit 就有不少依赖:MELPA

  1. Package的加载有多种写法,最终是不是都由require来加载?

  2. init.el的第一行是不是emacs启动之后,加载外部包时执行的第一个代码?

  3. flymake被吸收到emacs中,它应该在init.el之前执行吧?这样的话,在init.el中对它的各种配置岂不是无效了。

  4. 对于设置,我一直没搞清楚。比如 (setq js-indent-level 2)这句,若使用它的package在它之前被加载,岂不是已经执行过了,定义它不就没用了?若在它之后加载,package是不是必须检查它是否被设置,没设置的话才用默认值?问题又来了,一般是先require,后设置

除了特殊情况,用户配置与 require 的时机是没关系的,之前或者之后都没有影响

init.el和各package中代码执行的先后关系呢?

没有关系,没有影响

一般不需要关心setq的先后,但是如果有的插件作者在代码里把配置项作为右值赋值给其他变量的话,就需要在require之前setq。比如早期projectile的前缀键就需要在(require 'projectile)之前(setq projectile-prefix-key "C-x p"),因为在require/autoload之后,前缀键已经用来生成完整快捷键了。

此外,对于defvar来说,如果被定义的名字已经存在,则不会重新初始化。setq会生成一个名字,defvar不会覆盖它。

总结一下,大部分插件require/autoload之后再设置配置项,特殊情况需要在require之前配置某些配置项。

好的,谢谢!

在这之前,我理解有误,以为所有package加载时,像projectile的前缀键绑定那样,都要在启动时,用到手动设置的参数。

正确的理解是不是,大部分的设置是在当前mode的各种hook触发时才被用到?

和hook是没有关系的,你可能想复杂了,这些配置项是在执行某个函数时用到的。

所有的配置项都是全局变量,我们setq就是在修改这些全局变量,插件模块根据这些全局变量调整自身行为,如此而已。

你可以在xxx-mode-hook里写个函数去setq,也可以require这个包之后再setq,也可以require之前setq,因为即使这个变量没有被定义,setq会创建这个变量。

但是一般为了缩短启动时间,大家倾向避免直接require,而是在xxx-mode-hooksetq,或者在(with-)eval-after-load某个包之后setq。当某个autoload的函数(比如所有的xxx-mode函数都是autoload的)被执行了,emacs才会require整个包。

顺便给一个eval-after-loadxxx-mode-hook的区别:

xxx-mode-hook每打开一个buffer都执行一次,eval-after-load只在第一次require包时执行一次。

对于buffer-local的配置,eval-after-load可以用来设置默认值(用setq-default),而xxx-mode-hook可以设置在特定mode下,某个配置项的值,也因此xxx-mode-hook会在eval-after-load之后执行。

1 个赞