如何从另一个 Emacs Lisp 函数中带有前缀参数调用交互式的 Emacs Lisp 函数?

15

我想编写一个 Emacs Lisp 函数,无论模式的当前状态如何,都能打开 flyspell-mode。函数 flyspell-mode-on 已经被弃用。文档建议使用正数前缀参数来打开flyspell-mode,但是运行以下命令不起作用:

(flyspell-mode 1)

导致一个错误消息:

Wrong number of arguments: (lambda (flyspell-mode 1)), 0
如果我能找出如何使用前缀参数调用flyspell-mode,我相信我可以解决这个问题。
在Emacs Lisp手册中,我能找到的最相关的部分是标题为“交互调用”的部分,其中描述了call-interactively等命令。这绝不是我想要的。
(我试图解决的终极问题是创建一个模式钩子,无论其当前状态如何都会打开该模式。)
注:问题标题为emacs lisp call function with prefix argument programmatically,但那个问题询问如何创建一个交互式命令,并且问题最终通过使用call-interactively解决。

编辑:这个问题没有意义;我已经找到了原问题的替代解决方案:

(add-hook 'text-mode-hook
          (function (lambda ()
                      (require 'flyspell)
                      (if flyspell-mode nil (flyspell-mode)))))
我仍然想知道如何在另一个Emacs Lisp函数中无需交互就使用前缀参数调用Emacs Lisp函数。
更新: 或许我应该问为什么我会收到那个错误信息...

1
嗨,Norman。真正的问题在于你写成了(lambda (flyspell-mode 1))而不是(lambda () (flyspell-mode 1)),因此Emacs将(flyspell-mode 1)视为参数列表而不是要评估的表达式。 - Stefan
6个回答

10
看起来你的Flyspell模式版本不遵循次要模式约定,这需要你可以使用(name-of-mode t)或任何正前缀参数打开一个次要模式,使用(name-of-mode 0)或任何负前缀参数关闭它,并使用(name-of-mode nil)切换它。
如果你有最新版本的Flyspell,可能需要提交一个错误报告。我在我的机器上安装了GNU Emacs 23.2附带的版本,它遵守这个约定。我的版本还定义了两个函数turn-on-flyspellturn-off-flyspell,它们都是围绕flyspell-mode的简单包装器;这样命名的函数很常见,但不是官方约定。函数flyspell-mode-onflyspell-mode-off显然是为内部使用而设计的。
作为一般规则,命令从current-prefix-arg变量中读取当前前缀参数。不要将其与prefix-arg混淆,后者是下一个命令的值(只有少数命令,如universal-argument,才会接触此变量)。因此,如果您需要在调用函数时传递前缀参数,请绑定或设置current-prefix-arg
(let ((current-prefix-arg t))
  (flyspell-mode))

我正在使用emacs 21,因为emacsen 22和23在我使用但不再维护的一些传统模式中导致了一些损失。 - Norman Ramsey
1
哇,那真是老掉牙了。至于为什么会出现这个错误消息,原因就在于你的版本中的flyspell-mode函数不接受任何参数,而是直接读取current-prefix-arg;这是一个设计缺陷,在后来的版本中已经得到了修复。 - Gilles 'SO- stop being evil'

5
如果您不是交互式调用函数,则不需要使用 (interactive) 声明来获取参数。
在绝大多数情况下,您无需担心非交互式调用是否需要使用"前缀参数";只需查看函数文档,并传递您需要的值以及您想要执行的操作即可。
如果出于某种原因,在非交互式环境中需要复制发送前缀参数,则需要检查该函数的 (interactive)声明,并确定它如何使用该参数,并确保您复制了传递的参数的行为。
有关完整详细信息,请参见:
  • C-hf interactive RET
  • M-: (info "(elisp) Prefix Command Arguments") RET
在更复杂的情况下,如果函数根据 current-prefix-arg 变量更改其行为,则可以直接设置该变量。
(let ((current-prefix-arg '(4)))
  (foo current-prefix-arg))

3
我可以考虑这个.. 应该更好些。
(call-interactively (lambda ()
                       (interactive)
                       (flyspell-mode '(4))))

更新:我可以直接运行这个程序...问题在哪里?
(flyspell-mode '(4))

编辑:去掉了lambda表达式的引用(我添加了这个说明是因为SX强制编辑至少要六个字符,所以这可以被删除)。


意思是在代码中删除了 lambda 表达式的引用。

1
请查看我的评论,以获取解决您问题源的修复方法。至于您的问题的答案,前缀参数转换为某种Lisp参数的方式取决于interactive规范,因此可靠地执行它的唯一方法(即没有先前的知识,例如它是一个较小的模式函数)是交互式调用该函数:
(let ((current-prefix-arg '(4)))
  (call-interactively 'flyspell-mode))

1

顺便说一下,“flyspell-mode”函数自Emacs-21起已接受参数(如“(flyspell-mode 1)”),所以我不知道你是怎么得到那个错误的。

但在这里,我也可以指出,在Emacs-24中,“(add-hook 'text-mode-hook 'flyspell-mode)”的含义已经改变:它不再表示“在文本模式下切换flyspell-mode”,而是表示“在文本模式下启用flyspell-mode”。这是一个不向后兼容的更改,但我相信它会修复比引入更多的潜在错误。


0

虽然我还不是Emacs和Elisp的大师(但我会努力的 ;)),但在这种情况下,我认为你可以使用 Ctrl-u 1 Alt-x flyspell-mode


5
那将是一次互动通话。 - phils

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