Re: Advicing the `interactive' form of a command
Stefan Monnier <[email protected]>
| Newsgroups | gmane.emacs.help |
|---|---|
| Message-ID | <[email protected]> |
Roland Winkler [2026-05-04 10:04:27] wrote:
> On Sun, May 03 2026, Stefan Monnier via Users list for the GNU Emacs text editor wrote:
>> So if you want to apply the let binding to the interactive spec, you
>> can do:
>>
>> (advice-add 'switch-to-buffer :before
>> (lambda (&rest _)
>> (interactive
>> (lambda (ispec)
>> (let ((confirm-nonexistent-file-or-buffer t))
>> (advice-eval-interactive-spec ispec))))
>> nil))
>
> Thank you, this answers my questions! Reading the brief discussion of
> advice-eval-interactive-spec (that I had missed previously), I would
> not have guessed that the above two nested lambdas can modify the interactive
> spec of a command. Maybe one such example can be added to the elisp manual.
Actually, there is already such an example in the manual, where
`advice-eval-interactive-spec` is documented:
(defun my-compose-mail-advice (orig &rest args)
"Read From: address interactively."
(interactive
(lambda (spec)
(let* ((user-mail-address
(completing-read "From: "
'("one.address@@example.net"
"alternative.address@@example.net")))
(from (message-make-from user-full-name
user-mail-address))
(spec (advice-eval-interactive-spec spec)))
;; Put the From header into the OTHER-HEADERS argument.
(push (cons 'From from) (nth 2 spec))
spec)))
(apply orig args))
(advice-add 'compose-mail :around #'my-compose-mail-advice)
Eli, what do you think about a patch like the one below?
[ Clearly "See @code{advice-eval-interactive-spec} for an example"
needs some real xref, but my Texinfo-fu is weak. ]
=== Stefan
diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index 5444cea7fa9..c6cedd9346b 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -2112,7 +2112,7 @@ Core Advising Primitives
could say something like this:
@example
-(defun my-compose-mail-advice (orig &rest args)
+(defun my-compose-mail-advice (&rest _)
"Read From: address interactively."
(interactive
(lambda (spec)
@@ -2126,9 +2126,10 @@ Core Advising Primitives
;; Put the From header into the OTHER-HEADERS argument.
(push (cons 'From from) (nth 2 spec))
spec)))
- (apply orig args))
+ ;; This body is not used.
+ nil)
-(advice-add 'compose-mail :around #'my-compose-mail-advice)
+(advice-add 'compose-mail :interactive-only #'my-compose-mail-advice)
@end example
@end defun
@@ -2148,8 +2149,8 @@ Advising Named Functions
to named functions, offers the following extra features compared to
@code{add-function}: they know how to deal with macros and autoloaded
functions, they let @code{describe-function} preserve the original docstring as
-well as document the added advice, and they let you add and remove advice
-before a function is even defined.
+well as document the added advice, and they let you add and remove
+pieces of advice before a function is even defined.
@code{advice-add} can be useful for altering the behavior of existing calls
to an existing function without having to redefine the whole function.
@@ -2338,8 +2339,18 @@ Advice Combinators
@example
(lambda (&rest r) (funcall @var{function} (apply @var{oldfun} r)))
@end example
-@end table
+@item :interactive-only
+While the @var{where} option controls how the body of the two functions
+are composed, it does not actually affect the way interactive forms are
+composed. So, in a sense, this does the opposite of @code{:override}:
+call only the old function as if no advice was applied. But it still
+affects the interactive form like any other @var{where} value would: The
+interactive form of @var{function}, if any, overrides that of
+@var{oldfun} and if it is a lambda expression, it receives
+@var{function}'s interactive form as argument.
+See @code{advice-eval-interactive-spec} for an example.
+@end table
@node Porting Old Advice
@subsection Adapting code using the old defadvice
diff --git a/lisp/emacs-lisp/nadvice.el b/lisp/emacs-lisp/nadvice.el
index 7421ce079ab..80467caeb40 100644
--- a/lisp/emacs-lisp/nadvice.el
+++ b/lisp/emacs-lisp/nadvice.el
@@ -74,7 +74,8 @@ advice--how-alist
(:before-until (or (apply car r) (apply cdr r)))
(:before-while (and (apply car r) (apply cdr r)))
(:filter-args (apply cdr (funcall car r)))
- (:filter-return (funcall car (apply cdr r))))
+ (:filter-return (funcall car (apply cdr r)))
+ (:interactive-only (apply cdr r)))
"List of descriptions of how to add a function.
Each element has the form (HOW OCL DOC) where HOW is a keyword,
OCL is a \"prototype\" function of type `advice', and