With Emacs 30.1 defadvice has become obsolete, and one should use advice-add or define-advice instead. The Emacs-manual outlines how to transition from defadvice here. I understand the principle of advice, but a lot of my packages are configured using defadvice-pieces I took up from here and there and are using snippets that aren't really functions. For instance here

(use-package abbrev
  (setq-default abbrev-mode nil)
  (defadvice expand-abbrev (after my-expand-abbrev activate)
   ;; if there was an expansion
   (if ad-return-value
       ;; start idle timer to ensure insertion of abbrev activator
       ;; character (e.g. space) is finished
       (run-with-idle-timer 0 nil
                            (lambda ()
                              ;; if there is the string "@@" in the
                              ;; expansion then move cursor there and
                              ;; delete the string
                              (let ((cursor "@@"))
                                (if (search-backward cursor last-abbrev-location t)
                                    (delete-char (length cursor))))))))

the my-expand-abbrev activate-piece isn't one, right? I tried to evaluate it but Emacs treats the expressions as undefined variables. So I'm confused. How do I translate something like this to advice-add?

  • 2
    FWIW: Obsolete doesn't mean it's not supported. At least it shouldn't mean loss of support. Things should continue to be supported until they're desupported. Does the code in question not still work?
    – Drew
    Commented Sep 23, 2023 at 21:48

2 Answers 2


I agree with Drew’s advice (pun intended); there is no urgent need to convert to advice-add. It’ll be years before defadvice goes away.

Still, might as well point out the obvious. Use C-h f to look up the arguments and documentation of each. First, defadvice:


And then advice-add:

(advice-add SYMBOL HOW FUNCTION &optional PROPS)

It is trivial to rearrange one into the other. Starting with your original:

(defadvice expand-abbrev (after my-expand-abbrev activate)

Rearrange it like this:

(defun my-expand-abbrev ()
  "as long as you are making improvements, add a docstring here to document your advice"
(advice-add 'expand-abbrev :after #'my-expand-abbrev)

Done. Note that I have removed the activate flag from the advice; I don’t know why you used it since it seems unnecessary in this case.

Also, see chapter 13.12.4 Adapting code using the old defadvice in the Emacs Lisp manual.

  • The activate flag is only a defadvice thing; without it you need to call ad-activate to activate the advice. It's not relevant to nadvice.
    – phils
    Commented Sep 25, 2023 at 1:23

See C-hig (elisp)Advice Combinators for the different types of advice available.

Most importantly, each description contains a sentence "More specifically, the composition of the two functions behaves like:" followed by pseudocode demonstrating the new functionality, which gives you the exact requirements for the function you'll write and how it will be used. Each pseudocode example is a function receiving -- in the form of (&rest r) -- all arguments that the advised function would normally accept, and then some code using those arguments r with OLDFUN (being the function being advised), and FUNCTION (being the advice itself: the function you are writing).

For example, the pseudocode for :after is:
(lambda (&rest r) (prog1 (apply OLDFUN r) (apply FUNCTION r)))

From this we can see that the new behaviour is:

  • Firstly the original function OLDFUN is called with the original argument list.
  • Secondly the advice FUNCTION is called with the same argument list.
  • Finally the return value of the original function is returned.

With nadvice you don't have ad-return-value or ad-arg-N accessors available, so you need to pick the type of advice which will allow you to access what you need.

For your example this means we can't port after defadvice to :after nadvice, because (per the pseudocode above) the advice doesn't see the return value of the original function; but :after-while is a good match:

(define-advice expand-abbrev (:after-while () my-double-at)
  "Advice to `expand-abbrev'.  Called if there was an expansion.

If the string \"@@\" exists in the expansion then move cursor there
and delete that string.

To disable:
\(advice-remove \\='expand-abbrev \\='expand-abbrev@my-double-at)"
  ;; start idle timer to ensure insertion of abbrev activator
  ;; character (e.g. space) is finished
   0 nil (lambda ()
           (let ((cursor "@@"))
             (if (search-backward cursor last-abbrev-location t)
                 (delete-char (length cursor)))))))

(I have not tested that code.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.