宏 define-minor-mode 提供了一种便捷方式,可在一个独立的定义中实现完整的次要模式。
该宏定义一个名称为 mode(符号)的新次要模式。它会定义一个名为 mode 的命令用于切换该次要模式,并以 doc 作为其文档字符串。
该切换命令接受一个可选(前缀)参数。交互式无参调用时会开启或关闭模式;正前缀参数开启模式,其他前缀参数关闭模式。在 Lisp 中调用时,参数为 toggle 表示切换状态,参数省略或为 nil 则开启模式。
这使得在主模式钩子中启用次要模式变得十分简便。若 doc 为 nil,宏会自动生成一段默认文档字符串说明上述行为。
默认情况下,它还会定义一个名为 mode 的变量,在模式启用或关闭时分别设为 t 或 nil。
keyword-args 由关键字及其对应值组成。部分关键字具有特殊含义:
:global global若非 nil,表示该次要模式为全局模式,而非缓冲区局部模式。默认为 nil。
将次要模式设为全局模式的效果之一,是使 mode 变量成为可自定义变量。通过自定义界面切换它即可开启或关闭模式,其值可保存以便后续 Emacs 会话使用(see Saving Customizations in The GNU Emacs Manual)。
为使保存的变量正常生效,应确保每次 Emacs 启动时该次要模式函数都可用,通常做法是将 define-minor-mode 表达式标记为自动加载。
:init-value init-value该值为变量 mode 的初始化值。除非特殊情况(见下文),否则该值必须为 nil。
:lighter lighter字符串 lighter 用于指定模式启用时在模式行上显示的内容;若为 nil,则不在模式行显示。
:keymap keymap可选参数 keymap 指定该次要模式的按键映射。若非 nil,它可以是一个变量名(其值为按键映射)、一个按键映射,或如下形式的关联列表:
(key-sequence . definition)
其中每个 按键序列 和 定义 都是可直接传给 define-key 的参数(see 修改按键绑定)。
若 keymap 是按键映射或关联列表,宏还会自动定义变量 mode-map。
:variable place用于替换存储模式状态的默认变量 mode。若指定此项,则不会定义 mode 变量,且 init-value 参数无效。
place 可以是另一个命名变量(需自行定义),或任何可与 setf 函数配合使用的对象(see 广义变量)。
place 也可以是一个序对 (get . set),其中 get 是返回当前状态的表达式,set 是接收一个状态参数并应赋值给 place 的单参数函数。
:after-hook after-hook定义一个单独的 Lisp 表达式,在模式钩子运行完毕后执行,无需加引号。
:interactive value次要模式默认为交互式命令。若 value 为 nil,则取消交互性。若 value 是符号列表,则用于说明该次要模式适用于哪些主模式。
其他所有关键字参数会直接传递给为变量 mode 生成的 defcustom。
See 定义自定义变量 可查看这些关键字及其取值的说明。
名为 mode 的命令首先执行标准操作,如设置同名变量,随后执行 body 中的表达式(如有)。
接着运行模式钩子变量 mode-hook,最后执行 :after-hook 中的表达式。
(注意:无论模式开启还是关闭,上述所有步骤包括运行钩子都会执行。)
初始化值必须为 nil,除非满足以下情况:(1) 该模式已预载入 Emacs;或 (2) 即使加载时未经用户请求就启用模式也不会产生任何问题。
例如,若该模式仅在其他功能启用时才生效,且届时必然已加载,那么默认启用是无害的。但这些均属于特殊情况。通常情况下,初始化值必须为 nil。
以下是使用 define-minor-mode 的示例:
(define-minor-mode hungry-mode "切换饥饿删除模式。 交互式无参调用时切换模式状态。 正前缀参数开启模式,其他前缀参数关闭模式。 在 Lisp 中调用时,无参或 nil 开启模式,`toggle' 切换状态。 启用饥饿删除模式时,控制删除键会删除前方所有空白字符,仅保留最后一个。 参见命令 \\[hungry-electric-delete]。" ;; 初始值 nil ;; 模式行指示器 " Hungry" ;; 次要模式按键绑定 '(([C-backspace] . hungry-electric-delete)))
这段代码定义了名为“饥饿删除模式”的次要模式、用于切换它的命令 hungry-mode、标识模式是否启用的变量 hungry-mode,以及模式启用时生效的按键映射变量 hungry-mode-map。
它为 C-DEL 设置了按键绑定。示例中没有 body 表达式——很多次要模式都不需要。
下面是等效的另一种写法:
(define-minor-mode hungry-mode
"Toggle Hungry mode.
...rest of documentation as before..."
;; The initial value.
:init-value nil
;; The indicator for the mode line.
:lighter " Hungry"
;; The minor mode bindings.
:keymap
'(([C-backspace] . hungry-electric-delete)
([C-M-backspace]
. (lambda ()
(interactive)
(hungry-electric-delete t)))))
该宏定义一个名为 global-mode 的全局切换命令,用于在所有(或部分,见下文)缓冲区中开启或关闭缓冲区局部次要模式 mode,同时执行 body 表达式。 它使用函数 turn-on 在缓冲区中开启次要模式;关闭时则以 −1 为参数调用 mode。 (函数 turn-on 独立存在,以便在无法确定是否应始终启用时自行判断是否启用次要模式。)
全局启用模式仅影响后续创建、且使用遵循 run-mode-hooks 规范的主模式的缓冲区。
未遵循该规范的主模式缓冲区不会启用该次要模式。
该宏会定义自定义选项 global-mode(see 自定义设置),可通过自定义界面切换以开启或关闭模式。
与 define-minor-mode 一样,应确保每次 Emacs 启动时都会执行 define-globalized-minor-mode 表达式,例如通过指定 :require 关键字。
可在 keyword-args 中使用 :group group 为该全局次要模式的变量指定自定义组。
默认情况下,标识模式是否启用的缓冲区局部变量与模式名相同。若并非如此,可使用 :variable variable 指定——部分次要模式使用其他变量存储状态。
通常,定义全局化次要模式时,也应同时定义非全局化版本,以便用户可在单个缓冲区中单独启用或禁用它。 这也允许用户在特定主模式中通过该模式的钩子禁用全局启用的次要模式。
若宏指定了 :predicate 关键字,则会创建一个用户选项,名称与全局模式变量相同,但末尾以 -modes 替代 -mode,即 global-modes。
该变量用于谓词函数,判断该次要模式是否应在特定主模式中激活,用户可自定义其值以控制模式生效范围。
:predicate 的合法取值(即其创建的用户选项合法取值)包括:
t(在所有主模式中使用)、nil(不在任何主模式中使用),或由模式名组成的列表,可在前面加上 not(如 (not mode-name …))。
这些元素可混合使用,如下例所示。
(c-mode (not mail-mode message-mode) text-mode)
含义为:“在继承自 c-mode 的模式中启用,不在继承自 message-mode 或 mail-mode 的模式中启用,但在继承自 text-mode 的模式中启用,其他模式均不启用”。
((not c-mode) t)
含义为:“不在继承自 c-mode 的模式中启用,其他所有模式均启用”。
(text-mode)
含义为:“仅在继承自 text-mode 的模式中启用,其他模式均不启用”。(末尾隐含一个 nil 元素。)
次要模式常会设置影响 Emacs 某些功能的缓冲区局部变量。当次要模式关闭时,模式应恢复这些变量之前的状态。
这个便捷宏可简化该操作:其行为与 setq-local 类似,但会返回一个对象,可用于将这些值恢复为之前的取值/状态(通过配套函数 buffer-local-restore-state)。