16.5 自动加载

自动加载(autoload) 机制允许你先登记一个函数或宏的存在, 但推迟加载定义它的文件。 第一次调用该函数时,会自动加载对应的库,以安装真正的定义及其他相关代码, 然后运行真正的定义,就像它早已被加载一样。 自动加载也可以通过查看函数或宏的文档(see Documentation Basics) 以及变量和函数名的补全触发(见下文 see 按前缀自动加载)。

配置自动加载函数有两种方式:一是调用 autoload 函数,二是在源码中真正的定义之前编写一条「魔法注释」。 autoload 是实现自动加载的底层原语,任何 Lisp 程序都可在任意时机调用它。 对于随 Emacs 一同安装的软件包而言,魔法注释是让函数支持自动加载最便捷的方式。 这些注释本身不执行任何操作,但会作为 loaddefs-generate 命令的指引—— 该命令会构造对 autoload 的调用,并安排在 Emacs 构建时执行这些调用。

Function: autoload function filename &optional docstring interactive type

该函数定义名为 function 的函数(或宏),使其从 filename 自动加载。 字符串 filename 指定要加载的文件,以获取 function 的实际定义。

filename 既不包含目录名,也无 .el.elc 后缀, 此函数会强制添加其中一个后缀,且不会加载仅为 filename 无附加后缀的文件。 (变量 load-suffixes 规定了具体要求的后缀。)

参数 docstring 是该函数的文档字符串。 在调用 autoload 时指定文档字符串,可在不加载函数实际定义的情况下查看文档。 通常,此处的文档字符串应与函数定义本身的文档字符串完全一致; 若不一致,函数定义中的文档字符串会在加载时生效。

interactivenil,表示 function 可被交互式调用。 这使得 M-x 中的补全功能无需加载 function 的实际定义即可工作。 此处无需给出完整的交互式规范——除非用户实际调用 function, 而此时正是加载实际定义的时机。

interactive 是一个列表,则会被解析为该命令适用的模式列表。

除普通函数外,你也可以为宏和按键映射表设置自动加载: 若 function 实际是宏,需将 type 指定为 macro; 若 function 实际是按键映射表,需将 type 指定为 keymap。 Emacs 的多个功能模块需要在不加载实际定义的情况下知晓这些信息。

当前缀键的绑定为符号 function 时, 自动加载的按键映射表会在按键查找过程中自动加载。 而以其他方式访问该按键映射表时,不会触发自动加载—— 尤其是当 Lisp 程序从某个变量的值中获取该映射表并调用 keymap-set 时, 即便变量名与符号 function 相同,也不会触发自动加载。

function 已有非空的函数定义(且该定义并非自动加载对象), 此函数不执行任何操作并返回 nil。 否则,它会构造一个自动加载对象(see 自动加载类型), 并将其存储为 function 的函数定义。 自动加载对象的格式如下:

(autoload filename docstring interactive type)

For example,

(symbol-function 'run-prolog)
     ⇒ (autoload "prolog" 169681 t nil)

在这个例子中,"prolog" 是待加载文件的名称, 169681 指向 emacs/etc/DOC 文件中的文档字符串(see Documentation Basics), t 表示该函数可交互式调用,nil 表示它既非宏也非按键映射表。

Function: autoloadp object

如果 object 是一个自动加载对象(autoload object),该函数返回非 nil 值。例如,要检查 run-prolog 是否被定义为自动加载函数,可执行以下代码:

(autoloadp (symbol-function 'run-prolog))

自动加载文件通常包含其他定义,且可能会引入(require)或提供(provide)一个或多个功能特性(feature)。如果文件未完全加载(因执行其内容时出错),加载过程中产生的所有函数定义或 provide 调用都会被撤销。这样做是为了确保下次调用任何从该文件自动加载的函数时,会重新尝试加载该文件。若不执行此操作,文件中的部分函数可能会因加载中断而被定义,但由于文件后续未成功加载的某些子例程缺失,这些函数将无法正常工作。

如果自动加载文件未能定义所需的 Lisp 函数或宏,则会触发错误,错误数据为 "Autoloading failed to define function function-name"

自动加载魔术注释(通常称为 自动加载标记(autoload cookie))由 ‘;;;###autoload’ 组成,需单独写在一行,且紧跟在其可自动加载的源文件中函数的实际定义之前。函数 loaddefs-generate 会将对应的 autoload 调用写入 loaddefs.el 文件。 (用作自动加载标记的字符串以及 loaddefs-generate 生成的文件名可修改,默认值如上,详见下文。) 构建 Emacs 时会加载 loaddefs.el,从而调用 autoload 函数。

相同的魔术注释可将任意类型的表单(form)复制到 loaddefs.el 中。紧跟魔术注释的表单会被原样复制,但 例外情况 是:若该表单属于自动加载机制会特殊处理的类型(例如转换为 autoload 调用),则不会原样复制。不会原样复制的表单类型如下:

函数或类函数对象的定义:

defundefmacro;还包括 cl-defuncl-defmacro(see Argument Lists in Common Lisp Extensions), 以及 define-overloadable-function(参见 mode-local.el 中的注释)。

主模式或次模式的定义:

define-minor-modedefine-globalized-minor-modedefine-generic-modedefine-derived-modedefine-compilation-modedefine-global-minor-mode

其他定义类型:

defcustomdefgroupdefthemedefclass (see EIEIO in EIEIO),以及 define-skeleton (see Autotyping in Autotyping)。

你也可以使用魔术注释,让某个表单仅在构建阶段执行,而 在加载文件本身时执行。要实现此效果,需将该表单与魔术注释写在 同一行。由于该表单位于注释中,加载源文件时不会执行任何操作;但 loaddefs-generate 会将其复制到 loaddefs.el 中,在构建 Emacs 时执行该表单。

以下示例展示如何通过魔术注释为 doctor 函数配置自动加载:

;;;###autoload
(defun doctor ()
  "Switch to *doctor* buffer and start giving psychotherapy."
  (interactive)
  (switch-to-buffer "*doctor*")
  (doctor-mode))

这是在 loaddefs.el 中生成的内容:

(autoload 'doctor "doctor" "\
Switch to *doctor* buffer and start giving psychotherapy.

\(fn)" t nil)

虽然 loaddefs.el 并非用于编辑,但我们仍尽量使其对用户具有一定可读性。例如,对 defvar 值中的控制字符进行转义,并在文档字符串的双引号后面立即插入反斜杠与换行符,以控制行长度。 各种帮助函数(see Help Functions)在显示时,会将文档字符串用法部分中的 ‘(fn)’ 替换为函数名。

如果你使用非常规宏(不属于已知并被识别的函数定义方式)来编写函数定义,普通的自动加载魔术注释会将整个定义复制到 loaddefs.el。这并不可取。你可以改用下面的写法,将所需的 autoload 调用写入 loaddefs.el

;;;###autoload (autoload 'foo "myfile")
(mydefunmacro foo
  ...)

你可以使用非默认字符串作为自动加载标记,并将对应的自动加载调用写入文件名不同于默认 loaddefs.el 的文件中。 Emacs 提供了两个变量来控制这一点:

Variable: lisp-mode-autoload-regexp

该常量的值是一个匹配自动加载标记的正则表达式。 loaddefs-generate 会将标记后面的 Lisp 表单复制到它生成的自动加载文件中。 它可以匹配类似 ‘;;;###autoload’ 和 ‘;;;###calc-autoload’ 这样的注释。

Variable: generated-autoload-file

该变量的值指定存放自动加载调用的 Emacs Lisp 文件名。 默认值为 loaddefs.el,但你可以覆盖它,例如在 .el 文件的局部变量区段中设置(see 文件局部变量)。 自动加载文件被假定包含以换页符开头的尾部内容。

下面的函数可用于显式加载自动加载对象所指定的库:

Function: autoload-do-load autoload &optional name macro-only

该函数执行由 autoload 指定的加载操作,autoload 必须是自动加载对象。 可选参数 name 若非 nil,则应为一个符号,其函数值为 autoload; 此时该函数的返回值是该符号新的函数值。 如果可选参数 macro-only 的值为 macro,则该函数只加载宏,不加载普通函数。


emacs

Emacs

org-mode

Orgmode

Donations

打赏

Copyright

© 2025 Jasper Hsu

Creative Commons

Creative Commons

Attribute

Attribute

Noncommercial

Noncommercial

Share Alike

Share Alike