On Defadvice

Most people familiar with emacs lisp, have come across defadvice – a macro which alters existing functions. It’s an alternative to redefinition, better in terms of organization, brevity and forward compatibility. I suspect even those who have used them, might be oblivious (and curious) as to some details. I will only cover the functionality I’ve found useful – an extensive and easy to read documentation can be found in the beginning of advice.el

A basic advice

The basic syntax of defadvice is this:

1
2
3
4
(defadvice NAME-OF-ORIGINAL-FUNCTION
  ([before|after|around] NAME-OF-THE-ADVICE activate)
  "Optional docstring"
  BODY)

For instance, one could write:

1
2
3
4
(defadvice flymake-mode (after auto-activate-mouse activate)
  "Activate mouse highlights, when flymake becomes active."
  (when flymake-mode
    (setq-local mouse-highlight t)))

The before advice is similar, but is executed before the main function.

The around advice

A very useful type of advice, is the around advice. As the name suggest, it wraps around the main function. It will call it at specific point(s), or maybe not at all. One can define it like this:

1
2
3
4
5
6
7
(defadvice kill-buffer (around dont-kill-scratch activate)
  "Make scratch immortal."
  (unless (equal (buffer-name
                  (when (ad-get-arg 0)
                    (get-buffer (ad-get-arg 0))))
                 "*scratch*")
    ad-do-it))

ad-do-it will be replaced by a call to the original function. (ad-get-arg 0) returns the first argument. Arguments can also be accessed by name.

The return value

Even if the body of the advice is executed last, the return value will be that of the original function. However the return value is accessible through ad-return-value:

1
2
3
4
5
6
7
8
(defadvice dired-do-kill-lines (after always-kill activate)
  "Kill the current line when none is marked."
  (when (zerop ad-return-value)
    (let (( inhibit-read-only t))
      (delete-region
       (line-beginning-position)
       (1+ (line-end-position)))
      (setq ad-return-value 1))))

If one wants to set a new value, he has to do it after the main function executes - either in an after or an around advice.

How it works

defadvice works by re-writing functions’ definitions. Interestingly enough, one can create advices for functions that haven’t been defined yet. As far as I was able to tell, this part of defadvice is implemented in C.

defadvice stores various parts for constructing a definition in the function symbol’s plist. You can see them by evaluating (get 'ad-advice-info 'FUNCTION-NAME). The original, unadviced definition is moved to a function such as ad-Orig-kill-buffer.

Inspecting adviced functions

By default, adviced functions are compiled. If one wants to inspect the new definition, he can (setq ad-default-compilation-action 'never), re-evaluate the advice, and get the result of (symbol-function 'FUNCTION-NAME).

Removing advices

defadvice also provides methods to undo possible damage.

To remove a specific advice:

1
2
3
(ad-remove-advice 'kill-buffer 'around 'dont-kill-scratch)
;; Rebuilds the function using the latest "parts"
(ad-update 'kill-buffer)

To remove all advices from a function:

1
(ad-unadvise 'flymake-mode)