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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.