16.7 功能

providerequireautoload 之外的另一种自动加载文件的方式。它们基于命名的 功能(feature) 工作。自动加载由调用特定函数触发,而功能则是在其他程序首次按名称请求时被加载。

功能名是一个符号,代表一组函数、变量等的集合。定义它们的文件应当 提供(provide) 该功能。使用这些功能的其他程序可以通过 需求(require) 该功能来确保其已被定义。如果文件尚未加载,这会加载对应的定义文件。

要需求某个功能存在,以功能名作为参数调用 requirerequire 会检查全局变量 features,判断所需功能是否已经提供。 如果没有,它会从对应的文件加载该功能。 该文件应当在顶层调用 provide,将该功能加入 features; 如果没有这样做,require 会发出错误信号。

例如,在 idlwave.el 中,idlwave-complete-filename 的定义包含如下代码:

(defun idlwave-complete-filename ()
  "Use the comint stuff to complete a file name."
   (require 'comint)
   (let* ((comint-file-name-chars "~/A-Za-z0-9+@:_.$#%={}\\-")
          (comint-completion-addsuffix nil)
          ...)
       (comint-dynamic-complete-filename)))

表达式 (require 'comint) 会在尚未加载时加载 comint.el 文件,确保 comint-dynamic-complete-filename 已被定义。 功能通常以提供它的文件命名,因此 require 不需要指定文件名。 (注意,require 语句必须放在 let 体之外,这一点很重要。 在变量被 let 绑定期间加载库可能产生意外后果,即变量在 let 退出后会变为未绑定。)

comint.el 文件包含如下顶层表达式:

(provide 'comint)

这会将 comint 加入全局的 features 列表,此后 (require 'comint) 就知道无需执行任何操作。

require 在文件顶层使用时,它不仅在加载时生效,在对该文件进行字节编译时也会生效(see 字节编译)。 这是为了应对被需求的包中包含字节编译器必须知晓的宏的情况。 这也可以避免字节编译器对由 require 加载的文件中定义的函数和变量发出警告。

虽然顶层的 require 调用会在字节编译期间执行,但 provide 调用不会。 因此,你可以通过先写 provide、再对同一功能写 require,确保定义文件在字节编译前已被加载,如下例所示。

(provide 'my-feature)  ; Ignored by byte compiler,
                       ;   evaluated by load.
(require 'my-feature)  ; Evaluated by byte compiler.

编译器忽略 provide,然后通过加载对应文件来处理 require。 加载文件时会执行 provide 调用,因此后续的 require 在文件加载时不执行任何操作。

Function: provide feature &optional subfeatures

该函数宣告 feature 已加载或正在加载到当前 Emacs 会话中。 这意味着与 feature 相关的功能已经或将要可供其他 Lisp 程序使用。

调用 provide 的直接效果是:如果 feature 不在 features 列表中,则将其添加到列表前端,并执行所有等待该功能的 eval-after-load 代码(see 加载相关钩子)。 参数 feature 必须是一个符号。 provide 返回 feature

如果提供了 subfeatures,它应当是一个符号列表,表示此版本 feature 提供的一组特定子功能。 你可以使用 featurep 检查某个子功能是否存在。 子功能的设计意图是:当一个包(对应一个 feature)足够复杂,需要为其不同部分或功能分别命名时使用,这些部分可能加载或未加载,可能存在或不存在于某个版本中。 see Testing Availability of Network Features 提供了一个示例。

features
     ⇒ (bar bish)

(provide 'foo)
     ⇒ foo
features
     ⇒ (foo bar bish)

当加载文件以满足自动加载需求,且因执行文件内容出错而中断时,加载过程中产生的所有函数定义或 provide 调用都会被撤销。See 自动加载

Function: require feature &optional filename noerror

该函数检查 feature 是否已存在于当前 Emacs 会话中(通过 (featurep feature) 实现;详见下文)。参数 feature 必须是一个符号。

如果该功能不存在,则 require 会通过 load 加载 filename 文件。若未提供 filename,则将符号 feature 的名称作为要加载的基础文件名。 但在此情况下,require 要求文件必须带有 ‘.el’ 或 ‘.elc’ 后缀(可附加压缩后缀);仅以 feature 为名的文件不会被使用。 (变量 load-suffixes 规定了确切的 Lisp 文件后缀要求。)

如果 noerror 为非 nil 值,则会抑制文件实际加载过程中产生的错误。此时若文件加载失败,require 返回 nil。 默认情况下,require 返回 feature

如果文件加载成功但未提供 featurerequire 会针对缺失的功能发出错误信号。

Function: require-with-check feature &optional filename noerror

该函数的行为与 require 类似,但存在一个例外情况:当 feature 已加载时(即已存在于 features 列表中;详见下文)。 若 feature 已加载,该函数会检查提供此功能的文件是否与 filename 不同;若不同,默认会发出错误信号。 如果可选参数 noerror 的值为 reload,函数不会报错,而是强制重新加载 filename; 若 noerror 为其他非 nil 值,函数会发出警告,提示 feature 已由另一个文件提供。

Function: featurep feature &optional subfeature

如果 feature 已在当前 Emacs 会话中被提供(即 featurefeatures 的成员),该函数返回 t。 若 subfeature 为非 nil 值,则仅当该子功能也被提供时(即 subfeaturefeature 符号的 subfeature 属性的成员),函数才返回 t

Variable: features

该变量的值是一个符号列表,代表当前 Emacs 会话中已加载的功能。列表中的每个符号都是通过调用 provide 加入的。 features 列表中元素的顺序无实际意义。

use-package 宏提供了一种便捷的方式来加载功能并配置其使用方式。 它既能像 require 一样需求某个功能,又能指定功能实际加载时要运行的代码(类似加载时钩子;see 加载相关钩子)。 use-package 的声明式语法使其在用户初始化文件中使用起来格外简便。

Macro: use-package feature &rest args

该宏指定如何加载命名为 feature 的功能,以及如何配置和自定义其使用方式。参数 args 为若干键值对。 其中重要的关键字及其取值如下:

:init forms

指定在加载 feature 之前要执行的 forms(表单)。

:config forms

指定在加载 feature 之后要执行的 forms(表单)。

:defer condition

condition 为非 nil 值,表示延迟加载 feature,直到首次使用该功能的任意自动加载命令或变量时才加载。 若 condition 为数字 n,表示在空闲 n 秒后加载 feature

:commands commands

指定要为 feature 自动加载的命令。

:bind keybindings

指定 feature 相关命令的 keybindings(按键绑定)。每个绑定的格式为:

(key-sequence . command)

or

(:map keymap (key-sequence . command))

其中 key-sequence(按键序列)的格式需符合 kbd 宏的要求(see Key Sequences)。

关于 use-package 的更多细节,参见 use-package 用户手册


emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike