Indentation Specification

Overview

An indent spec can be used to specify intricate indentation rules for the more complex macros (or functions). It is provided as a value in the var metadata, under the :style/indent key.

(defmacro with-in-str
  "[DOCSTRING]"
  {:style/indent 1}
  [s & body]
  ...cut for brevity...)

It can take one of 3 forms:

  • Absent, meaning "indent like a regular function call".

  • An integer or a keyword x, which is shorthand for the list [x].

  • A list, meaning that this function/macro takes a number of special arguments, and then all other arguments are non-special.

    • The first element describes how the arguments are indented relative to the sexp. It can be:

      • An integer n, which indicates this function/macro takes n special arguments (see below for the meaning of this).

      • The keyword :form, meaning "every arg indents like a function form".

      • The keyword :defn, which means "every arg not on the first line is non-special".

    • Each following element is an indent spec on its own, and it details the internal structure of the argument on the same position as this element. So, when that argument is a form, this element specifies how to indent that form internally (if it’s not a form the spec is irrelevant).

    • If the function/macro has more arguments than the list has elements, the last element of the list applies to all remaining arguments.


Examples

Here we go into several examples using some well-known macros and forms from clojure.core. Obviously these are already known by clojure-mode, so you don’t need to specify them. They are just examples to guide you when writing indent specs for your own macros, or for macros from third party libs.

One very simple example is the do form. All of its arguments get the same indentation, and none of them are special. So its indent spec is simply [0], or 0 for short.

(do
  (something)
  (quick))

(do (whatever)
    (you)
    (want))

Sticking to simplicity, the when-let* macro has one special argument (the binding vector) and there’s no out-of-the-ordinary internal structure involved. So the indent spec is just 1 (which is shorthand for [1]).


Let’s see something more sophisticated. If the defrecord indent spec used by clojure-mode is [2 :form :form [1]]. This is saying:

  • defrecord has 2 special arguments (the name and the arglist).

  • The first two arguments have no special internal structure.

  • All remaining arguments have an internal indent spec of [1] (which means only the arglist is indented specially and the rest is the body).

(defrecord Thing [a]
  FileNameMap
  (getContentTypeFor [_ file-name]
    (str a "-" file-name))
  Object
  (toString [_]
    "My very own thing!!"))

For something even more complicated: letfn is [1 :form]. This means

  • letfn has one special argument (the bindings list).

  • The first arg has an indent spec of , which means all forms inside the first arg have an indent spec of [:defn].

  • The second argument, and all other arguments, are regular forms.

(letfn [(twice [x]
          (* x 2))
        (six-times [y]
          (* (twice y) 3))]
  (six-times 15))

Special Arguments

Many macros have a number of "special" arguments, followed by an arbitrary number of "non-special" arguments (sometimes called the body). The "non-special" arguments have a small indentation (usually 2 spaces). The special arguments are usually on the same line as the macro name, but, when necessary, they are placed on a separate line with additional indentation.

For instance, defrecord has two special arguments, and here’s how it might be indented:

(defrecord TheNameOfTheRecord
    [a pretty long argument list]
  SomeType
  (assoc [_ x]
    (.assoc pretty x 10)))

Here’s another way one could do it:

(defrecord TheNameOfTheRecord
           [a pretty long argument list]
  SomeType
  (assoc [_ x]
    (.assoc pretty x 10)))

The point of the indent spec is not to specify how many spaces to use.

The point is just to say "a defrecord has 2 special arguments", and then let the editor and the user come to an agreement on how many spaces they like to use for special and non-special arguments.

Internal indentation

The issue goes a bit deeper. Note the last argument in that defrecord. A regular function form would be internally indented as:

(assoc [_ x]
       (.assoc pretty x 10))

But this is not a regular function call, it’s a definition. So we want to specify that this form internally has 1 special argument (the arglist vector), so that it will be indented like this:

(assoc [_ x]
  (.assoc pretty x 10))

The indent spec does this as well. It lets you specify that, for each argument beyond the 2nd, if it is a form, it should be internally indented as having 1 special argument.