interactive ¶本节介绍如何编写 interactive 形式,使 Lisp 函数成为可交互式调用的命令,
以及如何检查命令的 interactive 形式。
该特殊形式声明一个函数是命令,因此可以被交互式调用(通过 M-x 或输入绑定到它的按键序列)。 参数 arg-descriptor 声明命令在交互式调用时如何计算参数。
命令可以像其他函数一样从 Lisp 程序中调用, 但此时由调用者提供参数,arg-descriptor 不起作用。
interactive 形式必须位于函数体的顶层,
或在函数符号的 interactive-form 属性中(see 符号属性)。
它之所以有效,是因为命令循环在调用函数之前会查找它(see 交互式调用)。
一旦函数被调用,其所有体形式都会被执行;
此时,如果 interactive 形式出现在函数体内,
该形式仅返回 nil,甚至不计算其参数。
modes 列表用于指定命令适用于哪些模式。 关于指定 modes 的效果及使用时机,详见 为命令指定适用模式。
按照惯例,应将 interactive 形式放在函数体中,作为第一个顶层形式。
如果符号的 interactive-form 属性和函数体中同时存在 interactive 形式,
前者优先级更高。
interactive-form 符号属性可用于为已有函数添加交互式形式,
或在不重新定义函数的情况下修改其交互式参数处理方式。
参数 arg-descriptor 有三种可能:
nil;此时命令不带参数调用。
如果命令需要一个或多个参数,这会迅速导致错误。
interactive 的代码字符)和可选的提示字符串组成
(某些代码字符会使用提示,有些则忽略)。示例:
(interactive "P\nbFrobnicate buffer: ")
代码字符 ‘P’ 将命令的第一个参数设为原始命令前缀(see 前缀命令参数)。 ‘bFrobnicate buffer: ’ 以 ‘Frobnicate buffer: ’ 提示用户输入已有缓冲区的名称, 该名称成为第二个也是最后一个参数。
提示字符串中可以使用 ‘%’ 将之前的参数值(从第一个参数开始)包含到提示中。
这通过 format-message 实现(see 格式化字符串)。
例如,下面可以读取一个已有缓冲区的名称,再读取要赋予该缓冲区的新名称:
(interactive "bBuffer to rename: \nsRename buffer %s to: ")
如果字符串以 ‘*’ 开头,则当缓冲区为只读时会发出错误信号。
如果字符串以 ‘@’ 开头,并且用于调用命令的按键序列包含任意鼠标事件, 则在运行命令前会选中与这些事件中的第一个相关联的窗口。
如果字符串以 ‘^’ 开头,并且命令是通过shift 转换调用的,
则在运行命令前设置标记并临时激活区域,或扩展已激活的区域。
如果命令不是通过 shift 转换调用,且区域处于临时激活状态,
则在运行命令前取消激活区域。
shift 转换在用户层面由 shift-select-mode 控制;
详见 Shift Selection in The GNU Emacs Manual。
可以同时使用 ‘*’、‘@’ 和 ‘^’,顺序无关紧要。 实际的参数读取由提示字符串的剩余部分(从第一个不是 ‘*’、‘@’ 或 ‘^’ 的字符开始)控制。
将光标位置或标记作为参数值也很常见, 但如果这样做**并且**读取输入(无论是否使用小缓冲), 务必在读取后获取光标或标记的整数值。 当前缓冲区可能接收子进程输出; 如果命令在等待输入时子进程输出到达,可能会改变光标和标记的位置。
下面是 不应 这样做的示例:
(interactive
(list (region-beginning) (region-end)
(read-string "Foo: " nil 'my-history)))
下面是正确做法,在读取键盘输入后再获取光标和标记:
(interactive (let ((string (read-string "Foo: " nil 'my-history))) (list (region-beginning) (region-end) string)))
警告:参数值不应包含任何无法被打印和读取的数据类型。
某些工具会将 command-history 保存到文件中,以便在后续会话中读取;
如果命令的参数包含以 ‘#<…>’ 语法打印的数据类型,这些工具将无法工作。
不过也有少数例外:可以使用有限的表达式,
如 (point)、(mark)、(region-beginning) 和 (region-end),
因为 Emacs 会特殊识别它们,并将表达式(而非其值)存入命令历史。
要判断你写的表达式是否属于这些例外,运行命令后查看 (car command-history)。
该函数返回 function 的 interactive 形式。
如果 function 是可交互式调用的函数(see 交互式调用),
返回值是命令的 interactive 形式 (interactive spec),
它指定如何计算参数。否则返回 nil。
如果 function 是符号,则使用其函数定义。
当作用于 OClosure 时,工作会委托给泛型函数 oclosure-interactive-form。
与 interactive-form 类似,该函数接收命令并返回其交互式形式。
区别在于它是泛型函数,且仅在 function 是 OClosure 时被调用(see 开放式闭包)。
其目的是让某些 OClosure 类型可以动态计算它们的交互式形式,
而不是在某个槽位中保存它。
例如,该函数用于 kmacro 函数以减少内存占用,
因为它们共享同一个交互式形式。
它也用于 advice 函数,其中交互式形式从其组件的交互式形式计算而来,
从而使计算更惰性,并在某个组件被重新定义时正确调整交互式形式。