如何向Elisp函数传递参数?

3

我有一个这样刷新空行的键映射:

(global-set-key (kbd "M-T") (lambda () (interactive) (flush-lines "^[[:space:]]*$")))

但是这不适用于一片区域。超出该区域的无关行也会被合并。但是当我选择一个区域并手动尝试 M-x flush-lines RET ^[[:space:]]*$ RET 时,它按预期工作。

我做错了什么?

1个回答

6
一个函数可以被交互式或非交互式地调用(实际上有一点更复杂,但这已经是一个足够好的近似)。交互式调用模式用于绑定某些函数参数到当前环境中,或通过与用户交互来实现。非交互式模式是写Emacs Lisp代码时的正常调用模式。
通常,使用(interactive ...)形式在docstring之后声明交互行为,但在flush-lines特殊情况下,行为部分自动化了通过interactive规范,并且部分硬编码(对于区域部分)。您可以调用called-interactively-p询问Emacs当前代码是否以交互方式执行,但正如文档所说,建议再添加一个可选参数,指示该调用是否是交互式的。
函数签名为:
(flush-lines REGEXP &optional RSTART REND INTERACTIVE)

RSTARTREND 是当前区域的起始和结束位置,表示为整数,它们也可以是 nil。最后一个参数指示调用是否交互式。

当你从匿名函数中调用 flush-lines 时,你是以非交互方式调用它的,这意味着你需要显式地向函数传递参数。

对于这个函数,你可以简单地按如下方式调用:

(flush-lines "^[[:space:]]*$" nil nil t)

这将使函数的行为表现得像在交互式调用时一样,此时区域会自动计算,因为开始和结束参数为nil。
一般情况下,你需要使用自己的(interactive "r")声明,并向匿名函数添加两个参数。例如,在*scratch*缓冲区中,您可以评估这个表达式:
(call-interactively
  (lambda (beg end)
    (interactive "r")
    (list beg end)))

接下来,结果是一个光标位置的列表,代表您的区域。 例如,在您的情况下,您会调用(flush-lines "^[[:space:]]*$" beg end)

1
就我所知,我不理解你在这个句子中想要表达什么:“通常,交互行为是在文档字符串之后使用(interactive…)形式声明的,但在flush-lines的特定情况下,行为是硬编码的”。据我所知,交互行为并没有硬编码,而且确实使用了(interactive…)规范进行声明。 - Drew
交互式的 "r" 非常有帮助! - maindoor
@Drew 我本来以为代码完全是由交互式规范定义的,但是后来我试图看看它是如何实现的,发现现有的交互式规范中有一个带有两个表达式的 progn(https://github.com/emacs-mirror/emacs/blob/master/lisp/replace.el#L882);一个检查和正则表达式提示。但计算 rstartrend 的部分在下面的 if 中完成。我可能表达得不好,在写作时我关注的是 region 部分。 - coredump
1
是的,你说得对。在我看来,这是糟糕的编码。文档字符串至少应该说明RSTARTRENDnil值意味着/做什么。你可能想要更新你的答案,以便更像你在评论中所说的那样。 (评论可以随时删除。问题和答案应足以让读者学习。) - Drew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接