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 takesn
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!!"))
(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.