summaryrefslogtreecommitdiff
path: root/doc/indent_spec.md
blob: 9f68845f108f4f7471fd0c6515c9270a60ea1f4b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
## 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.

```clojure
(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.

```clojure
(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).

```clojure
(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 [[:defn]] :form]`. This means

- `letfn` has one special argument (the bindings list).
- The first arg has an indent spec of `[[:defn]]`, which means all forms
  _inside_ the first arg have an indent spec of `[:defn]`.
- The second argument, and all other arguments, are regular forms.

```clojure
(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:

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

Here's another way one could do it:

```clojure
(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.