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...)
There are two supported formats for indent specs: the modern tuple format (preferred) and the legacy positional format (deprecated, will be removed in clojure-mode 6).
Modern format (preferred)
The modern format uses explicit rule tuples and is shared across clojure-mode,
clojure-ts-mode, and cljfmt. The
format is identical in all three tools, so indent specs are portable across
editors and formatters.
Key concepts
Before diving into the rules, here are the terms used throughout:
- Body-style indentation
-
Arguments are indented by 2 spaces relative to the enclosing form. This is the standard indentation for macro bodies:
(when true (foo) ;; body — indented 2 spaces (bar)) - Special arguments
-
Arguments that precede the body. When placed on their own line, they get additional indentation to visually distinguish them from the body:
(defrecord TheNameOfTheRecord [a pretty long argument list] ;; special arg — extra indentation SomeType ;; body — standard 2-space indentation (method [this] ...)) - Depth
-
The nesting level within the form, counting from 0. Depth 0 is the form’s direct arguments, depth 1 is the arguments inside those arguments, depth 2 is one level deeper, and so on.
- Position
-
The 0-indexed argument position within the enclosing form (excluding the form name itself). For example, in
(let [x 1] body), the binding vector[x 1]is at position 0 andbodyis at position 1.
Rule types
Each rule is a vector of the form [:block N], [:inner D], or [:inner D I]:
| Rule | Meaning |
|---|---|
|
The first N arguments are "special" (indented further when on their own line); remaining arguments get body-style indentation (2 spaces). |
|
At nesting depth D inside the form, all sub-forms get body-style indentation.
|
|
Like |
Rules are combined in a vector. For example:
-
1or[[:block 1]]— one special arg, then body (e.g.,when,let) -
:defnor[[:inner 0]]— all args are body (e.g.,defn,fn) -
[[:block 2] [:inner 1]]— two special args, nested sub-forms get body indent (e.g.,defrecord,deftype) -
[[:block 1] [:inner 2 0]]— one special arg, depth-2 nesting at position 0 only (e.g.,letfn)
Simple specs (an integer or :defn) are shorthand for the corresponding
single-rule vector.
Legacy format (deprecated)
The legacy format uses positional lists where each element controls indentation at the corresponding argument position. It takes one of these 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 takesnspecial 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.
-
| The legacy positional format will be removed in clojure-mode 6. New code should use the modern tuple format. |
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.
Simple specs
The do form has no special arguments — all args get body indentation.
Its indent spec is simply 0 (shorthand for [[:block 0]]).
(do
(something) ;; body (2-space indent)
(quick))
The when-let macro has one special argument (the binding vector).
Its indent spec is 1 (shorthand for [[:block 1]]).
(when-let [x (foo)] ;; position 0 — special arg
(bar x)) ;; position 1+ — body
The defn macro uses :defn (shorthand for [[:inner 0]]), meaning all
arguments get body-style indentation regardless of position.
(defn my-fn
[x] ;; depth 0 — body-indented
(inc x)) ;; depth 0 — body-indented
Multi-rule specs
defrecord uses [[:block 2] [:inner 1]]. This means:
-
[:block 2]— 2 special arguments (the name and the fields vector). -
[:inner 1]— at depth 1 (inside the protocol method forms), use body-style indentation. This is what makes method bodies indent correctly.
(defrecord Thing [a] ;; depth 0, pos 0-1: special args ([:block 2])
FileNameMap ;; depth 0, pos 2+: body
(getContentTypeFor [_ file-name] ;; depth 1: body-indented ([:inner 1])
(str a "-" file-name))
Object
(toString [_]
"My very own thing!!"))
letfn uses [[:block 1] [:inner 2 0]]. This is the most complex built-in
spec:
-
[:block 1]— 1 special argument (the bindings vector). -
[:inner 2 0]— at depth 2, only at position 0, use body-style indentation. Why position 0? Because inside the bindings vector (depth 1), each binding is a list like(twice [x] body). Position 0 in each binding is the function name+arglist, and the body follows. The position restriction ensures that only the function definitions inside the bindings get body-style indentation.
(letfn [(twice [x] ;; depth 0, pos 0: special arg ([:block 1])
(* x 2)) ;; depth 2, pos 0: body-indented ([:inner 2 0])
(six-times [y]
(* (twice y) 3))]
(six-times 15)) ;; depth 0, pos 1+: body
Choosing the right rule
As a rule of thumb:
-
Use
[:block N]for macros with N special arguments before a body — this is the most common case. Examples:when(1),let(1),condp(2),catch(2). -
Use
[:inner 0]for def-like forms where all arguments are body. Examples:defn,fn,deftest,defmethod. -
Use
[:inner 1]when the form contains sub-forms whose bodies need body-style indentation (like protocol method definitions). Examples:defprotocol,deftype,defrecord. -
Combine rules when a form has both special args and nested structure. Examples:
defrecord=[:block 2]+[:inner 1].
Legacy format equivalents
For reference, the same specs in the legacy positional format:
| Form | Modern | Legacy |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 body-style indentation (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.
This is controlled by the [:block N] rule. For instance, defrecord has
[:block 2] (two special arguments), and here’s how it might be indented:
(defrecord TheNameOfTheRecord
[a pretty long argument list] ;; special arg — extra indent
SomeType ;; body — 2 spaces
(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" (via
[:block 2]), 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 (Depth)
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 method definition. So we want the method body to get body-style indentation:
(assoc [_ x] (.assoc pretty x 10))
This is what the [:inner 1] rule does — it says "at depth 1 (inside the
arguments of this form), use body-style indentation." Combined with
[:block 2], the full spec [[:block 2] [:inner 1]] gives defrecord both
special argument handling and correct method body indentation.
Namespace-Qualified Symbols
In clojure-mode, you can specify different indentation for the same symbol
depending on its namespace. This is useful when a library redefines a form
with different semantics:
;; Default indentation for `do`
(put-clojure-indent 'do '((:block 0)))
;; Custom indentation for `my-ns/do`
(put-clojure-indent 'my-ns/do '((:block 1)))
When a namespace-qualified symbol has no explicit spec, clojure-mode
automatically falls back to the unqualified symbol’s spec. For example,
clojure.core/let uses the same spec as let.
Indentation inference
It’s worth noting that starting from cider-nrepl 0.32, indentation can be inferred for you, so you wouldn’t have to specify it.
For that to happen, it’s most recommended that you write idiomatic Clojure macros:
-
If your macro is analog to a clojure.core one, name it identically
-
e.g. name your macro
defprotocol, notmy-defprotocol-
(this is intentful usage of Clojure’s namespace system)
-
-
-
If your macro is analog to a clojure.core one, mirror all its arglists
-
The exact names that you choose for your args do not matter
-
It’s the structure of the arglists that have to match.
-
It doesn’t matter if you express a given arg as a name, or as a destructured map/vector.
-
-
Name 'body' args like using clojure.core customs
-
good:
[opts body] -
bad:
[opts etc] -
good:
[& body] -
bad:
[& etc] -
Other commonly accepted names include
forms,clauses, etc.
-
You certainly don’t have to follow these suggestions - it’s only for your convenience, as the indentation produced by CIDER will be better.
Other tools may eventually also use these very same inference rules.