All information you need is included in C-h f add-function
which
describes the underlying mechanism of advice-add
.
The new advice system basically acts like replacing the current
definition of a function by the function described in the table in
C-h f add-function
, depending on your choice of the WHERE
argument, only cleaner for the sake of tracking what behaviour has
been defined in what source file.
An example with the :around
option
The most general case is the :around
option, so I give an example
for that. (It is probably better to use dedicated WHERE
parameters
when possible, but you can replace every other by an equivalent
:around
function).
Just as an example, lets say you want to debug some use of find-file
and want to print
its argument list every time it is called. You
could write
(defun my-find-file-advice-print-arguments (old-function &rest arguments)
"Print the argument list every time the advised function is called."
(print arguments)
(apply old-function arguments))
(advice-add #'find-file :around #'my-find-file-advice-print-arguments)
With this new implementation, everything the advice needs is passed as
argument. ad-get-args
becomes unnecessary, because the arguments are
passed to the advice function as normal function arguments (for
WHERE
arguments for which it makes sense). ad-do-it
becomes
unnecessary as :around
advice gets as arguments the function and the
arguments, so (ad-do-it)
is replaced by the form
(apply old-function arguments)
or when you have named the arguments
(funcall old-function first-arg second-arg)
which is cleaner as there are no magic forms involved. Modifying the
arguments simply happens by passing modified values to OLD-FUNCTION
.
Other WHERE
values
The docstring of add-function
contains a table of all advice places (or "combinators"),
and what they are equivalent to, and explains the functionality in
terms of a lambda
behaving equivalent to the advised function:
`:before' (lambda (&rest r) (apply FUNCTION r) (apply OLDFUN r))
`:after' (lambda (&rest r) (prog1 (apply OLDFUN r) (apply FUNCTION r)))
`:around' (lambda (&rest r) (apply FUNCTION OLDFUN r))
`:override' (lambda (&rest r) (apply FUNCTION r))
`:before-while' (lambda (&rest r) (and (apply FUNCTION r) (apply OLDFUN r)))
`:before-until' (lambda (&rest r) (or (apply FUNCTION r) (apply OLDFUN r)))
`:after-while' (lambda (&rest r) (and (apply OLDFUN r) (apply FUNCTION r)))
`:after-until' (lambda (&rest r) (or (apply OLDFUN r) (apply FUNCTION r)))
`:filter-args' (lambda (&rest r) (apply OLDFUN (funcall FUNCTION r)))
`:filter-return'(lambda (&rest r) (funcall FUNCTION (apply OLDFUN r)))
(cited from `C-h f add-function')
where FUNCTION is the advice function and OLDFUN the function where
the advice is added. Don't try to understand all of them at once, just
select a WHERE
symbol that sounds fitting and try to understand that
one.
Or just use :around
. As far as I can tell the only advantage of
using specialized WHERE
s over :around
for everything is that you
get a bit more information from looking up C-h f ADVISED-FUNCTION
prior to reading the docstring of the advice. Unless you plan to
publish the code containing the advice it probably doesn't matter.
Named advice functions
I recommend using named functions as advice since it provides many
advantages (some of them also apply to using named functions for hooks):
It shows up in C-h f find-file
as
:around advice: `my-find-file-advice-print-arguments'
linking to the definition of the advice function, which as usual
contains a link to the file where it was defined . If the advice
had been defined as a lambda
form directly in the advice-add
form the docstring would be shown inline (a mess for long
docstrings?) and nothing would indicate where it was defined.
You can remove the advice with
(advice-remove #'find-file #'my-find-file-advice-print-arguments)
You can update the definition of the advice without rerunning
advice-add
or risking to keep the old version active (as running
advice-add
with a changed lambda
will be recognized as new
advice, not as an update to the old one).
Side remark The #'function
notation is basically equivalent to
'function
, except that it help the byte compiler identify symbols
as function names and thus to identify missing functions (e.g. due to
typos).
M-x report-emacs-bug
. Some developers sometimes prefer developing over documenting. ;-) It is important that Emacs document itself.nadvice
from my config: :after, :filter-return, :around, :before-until