Specifying Indent Rules for Emacs Lisp

Eric J.M. Ritz has an excellent post on indenting Elisp code. Say what? Isn’t that what Emacs is for? Yes but there’s a corner case when you’re writing macros.

Here’s a macro that I use to make my Elisp code work on a region if one’s defined or on the whole buffer if not.

(defmacro with-region-or-buffer (args &rest body)
  "Execute BODY with BEG and END bound to the beginning and end of the
current region if one exists or the current buffer if not."
  `(let ((,(car args) (if (use-region-p) (region-beginning) (point-min)))
         (,(cadr args) (if (use-region-p) (region-end) (point-max))))
     ,@body))

As it stands, a call to that macro would get formatted as

(with-region-or-buffer (start end)
                       (blah start end)
                       (more blahs start end))

but that’s not what we want. We’d like it to get formatted as

(with-region-or-buffer (start end)
  (blah start end)
  (more blahs start end))

The easy way to get Emacs to indent it properly is with the declare declaration as shown in the slightly revised version of with-region-or-buffer shown below.

(defmacro with-region-or-buffer (args &rest body)
  "Execute BODY with BEG and END bound to the beginning and end of the
current region if one exists or the current buffer if not."
  (declare (indent 1))
  `(let ((,(car args) (if (use-region-p) (region-beginning) (point-min)))
         (,(cadr args) (if (use-region-p) (region-end) (point-max))))
     ,@body))

It’s really hard to find the documentation for this (at least I always find it hard) so I’m glad to have Ritz’s post for bookmarking purposes. The post has a link to the documentation for declare so you can see what other arguments to the indent declaration are possible.

Update: what’s → what

This entry was posted in Programming and tagged , . Bookmark the permalink.

6 Responses to Specifying Indent Rules for Emacs Lisp

  1. Phil says:

    It’s pretty easy to find the documentation for this — the top-level “Macros” node in the elisp manual has an “Indenting Macros” menu item.

    It’s not indexed as well as it could be, admittedly.

    • jcs jcs says:

      Yup, but I always search for “declare” or “indent” and don’t find anything. Once you get it firmly in your mind that it’s a macro thing it’s easier to find.

      • Phil says:

        It isn’t just a macro thing, actually (I only learned this fairly recently). If you look up ‘declare’ in the index (if you actually remember that’s what you’re looking for, you’re pretty well sorted), it tells you you can use it with defun and defsubst as well.

      • Eric JM Ritz says:

        Emacs also kind of supports declare from Common Lisp which is how I came across the Elisp version of declare in the first place. I have a hard copy of “Common Lisp the Language, 2nd Edition” which, for whatever reason, I will just flip through at random while waiting for things. The chapter about declare finally made me wonder, “Huh, does Elisp have anything similar?”

        Thanks for the link and mention. I was wondering today why traffic to my blog suddenly spiked, heh.

  2. Fuco says:

    Now if you want to really get your mind blown, read about the `debug` declarations! That will allow you to edebug your macros. I didn’t use macros for a looong time because the inability to debug them was too much to get over (imagine 30 lines of elisp in a wrapper like your `with-region-or-buffer` and no debug … yuck)

    Luckily, you can just add `(declare (debug symbolp symbolp body))` and edebug will now magically debug your code :P

    Read (info “(elisp) Specification List”) for more info on specifications.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>