How to Indent Your Custom Emacs Lisp Code

Today I want to share a small but useful tip for anyone writing Emacs Lisp code and wanting Emacs to indent it as if it were native to the language.

Our Example

In PHP Mode we use a testing library that defines with-php-mode-test, a macro that we use like so:

(ert-deftest php-mode-test-issue-124 ()
  "Proper syntax propertizing when a quote appears in a heredoc."
  (with-php-mode-test ("issue-124.php" :indent t)
     (search-forward "Heredoc")
     ;; The heredoc should be recognized as a string.
     (dolist (syntax (c-guess-basic-syntax))
       (should (eq (car syntax) 'string)))
     (search-forward "function bar")
     ;; After the heredoc should *not* be recognized as a string.
     (dolist (syntax (c-guess-basic-syntax))
       (should (not (eq (car syntax) 'string))))))

The important thing to note here is the indentation for the with-php-mode-test macro and its forms. If we wrote our own macro, e.g. my-test-macro, and used it like the example above we would see Emacs indent it like this:

(my-test-macro ("issue-124.php")
               (search-forward "Heredoc")
               (dolist (syntax (c-guess-basic-syntax))
                 (should (eq (car syntax) 'string))))

How can we make Emacs indent my-test-macro as if it were a macro native to Elisp?

Introducing Declarations

To get our desired indentation we use declare. As the manual says:

declare is a special macro which can be used to add “meta” properties to a function or macro: for example, marking it as obsolete, or giving its forms a special indentation convention in Emacs Lisp mode.

We can use declare to give Emacs meta-information about how it should indent our macro (this also works for functions). Here is a simple example:

(defmacro my-test-macro ((test-filename) &rest body)
  (declare (indent defun))
  ;; The rest of the macro goes here.

This declaration tells Emacs that it should indent all uses of my-test-macro the same way it indents defun. Now Emacs will indent our previous example like this:

(my-test-macro ("issue-124.php")
  (search-forward "Heredoc")
  (dolist (syntax (c-guess-basic-syntax))
    (should (eq (car syntax) 'string))))

That’s a lot more pleasing on the eyes, right? There are also other indent values that we can use. You can see an example in with-php-mode-test.

There is nothing more to it than that. To make the indentation of our functions and macros look more ‘native’ all we need to do is use a simple declaration. A useful an easy way to improve the readability of our Emacs Lisp.


Add Your Thoughts

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s