Emacs中的命令有“flet”吗?
我想动态地将一个命令重定向到 使用around advice的某些函数,如:Emacs中的命令有“flet”吗?,emacs,elisp,Emacs,Elisp,我想动态地将一个命令重定向到 使用around advice的某些函数,如: (defun f1 (arg) (interactive (list (read-from-minibuffer "F1: "))) (message "f1: %S" arg) arg) (defun f2 (arg) (interactive (list (read-from-minibuffer "F2: "))) (message "f2: %S" arg) arg) ;; Functi
(defun f1 (arg)
(interactive (list (read-from-minibuffer "F1: ")))
(message "f1: %S" arg)
arg)
(defun f2 (arg)
(interactive (list (read-from-minibuffer "F2: ")))
(message "f2: %S" arg)
arg)
;; Function that invokes the f1 command
(defun myfunc ()
(call-interactively 'f1))
;; I want myfunc to invoke f2 instead whenever it would invoke f1
(defadvice myfunc (around f1-to-f2 activate)
(flet ((f1 (&rest args) (interactive) (call-interactively 'f2)))
ad-do-it))
(myfunc)
但是,这会产生一个错误(错误的类型参数commandp f1)
,
表明当flet
重新定义f1
函数时,它没有
处理交互式表单并将其视为命令,因此它不能
可由交互调用调用
是否有一种flet
的变体可以以这种方式用于命令
(以下是我想做的实际重新定义:)
(编辑:cl-letf
宏可以在现代emacs中本机完成此操作。下面的答案对于旧版本可能仍然有用。)
如果以前没有,现在有了:
(require 'cl)
(require 'cl-lib)
(defmacro command-let (bindings &rest body)
"Like `flet', but works for interactive commands.
In addition to the standard `(FUNC ARGLIST BODY...)' syntax from
`flet', this also supports `(FUNC NEW-FUNC)' as a shorthand for
remapping command FUNC to another command NEW-FUNC, like this:
(defun FUNC (&rest ignored)
(interactive)
(call-interactively NEW-FUNC))
\(fn ((FUNC ARGLIST BODY...) ...) FORM...)"
(declare (indent 1))
(cl-loop for binding in bindings
collect (list (car binding) nil) into empty-bindings
collect (if (symbolp (cadr binding))
;; Remap one command to another
`(defun ,(car binding) (&rest args)
(interactive)
(call-interactively ',(cadr binding)))
;; Define command on the fly
(cons 'defun binding))
into defun-forms
finally return
`(flet (,@empty-bindings)
,@defun-forms
,@body)))
在行动中:
(defadvice myfunc (around f1-to-f2 activate)
(command-let ((f1 f2))
ad-do-it))
(myfunc)
该代码现在根据需要使用交互式调用调用调用
f2
命令。您在flet中遇到了一个愚蠢的错误:flet的宏扩展将有:(lambda(&rest args)(progn(interactive)(交互式调用“f2”)
。注意这里添加的伪progn
,它“隐藏”了交互
要获得更多控制(同时避免cl.el),您可以执行以下操作:
(defadvice myfunc (around f1-to-f2 activate)
(cl-letf (((symbol-function 'f1)
(lambda (&rest args)
(interactive) (call-interactively 'f2))))
ad-do-it))
(需要'cl-lib)
是不够的,因为flet
不是由cl-lib提供的(而是由cl.el提供的)。感谢您的提醒cl-lib
用于循环宏,我忘记了flet
不是内置的。而且,我总是忘记哪个flet
变体使用动态绑定,哪个使用词汇绑定。显然我需要动态绑定。@ryanthonson:是的,它相当混乱。它们背后的故事很有帮助,因为:cl-lib
较新,因此cl-flet
比flet
更新,所以cl-flet
是词法的“自然”(因为词法范围界定在Emacs中较新)。至于letf
,f
来自setf
,而不是来自“let function”,因此它实际上只是一种“设置…恢复”的方式,因此是动态范围。
(defadvice myfunc (around f1-to-f2 activate)
(cl-letf (((symbol-function 'f1)
(lambda (&rest args)
(interactive) (call-interactively 'f2))))
ad-do-it))