@@ -239,28 +239,75 @@ prefixed with some ns (or ns alias):
239239(put-clojure-indent 'my-ns/do 1)
240240```
241241
242- The bodies of certain more complicated macros and special forms
243- (e.g. ` letfn ` , ` deftype ` , ` extend-protocol ` , etc) are indented using
244- a contextual backtracking indentation method, require more sophisticated
245- indent specifications. Here are a few examples:
242+ ##### Backtracking (contextual) indentation
243+
244+ Certain macros and special forms (e.g. ` letfn ` , ` deftype ` ,
245+ ` extend-protocol ` ) contain * nested* sub-forms that each need their
246+ own indentation style. For these, ` clojure-mode ` uses ** backtracking
247+ indentation** : when indenting a line, it walks up the sexp tree to
248+ find a parent form with an indent spec, then uses the current
249+ position within that spec to decide how to indent.
250+
251+ A backtracking indent spec is a ** quoted list** where each element
252+ controls indentation at the corresponding argument position
253+ (0-indexed). The allowed elements are:
254+
255+ | Element | Meaning |
256+ | ---------| ---------|
257+ | An integer N | First N args are "special" (indented further); rest are body |
258+ | ` :defn ` | Indent like a function/macro body |
259+ | ` :form ` | Indent like a regular form |
260+ | ` nil ` | Use default indentation rules |
261+ | A list ` (SPEC) ` | This position holds a ** list of forms** , each indented according to SPEC |
262+
263+ For example, ` letfn ` uses ` '(1 ((:defn)) nil) ` :
264+
265+ ``` clojure
266+ (letfn [(twice [x] ; ; pos 0 → spec 1 (1 special arg = the binding vector)
267+ (* x 2 )) ; ; inside binding → spec ((:defn)) applies:
268+ (thrice [x] ; ; each binding is a list of :defn-style forms
269+ (* x 3 ))] ; ; so function bodies get :defn indentation
270+ (+ (twice 5 ) ; ; pos 1+ → spec nil (default → body indentation)
271+ (thrice 5 )))
272+ ```
273+
274+ And ` defrecord ` uses ` '(2 nil nil (:defn)) ` :
275+
276+ ``` clojure
277+ (defrecord MyRecord ; ; pos 0 → spec 2 (2 special args: name + fields)
278+ [field1 field2] ; ; pos 1 → spec nil (within special args zone)
279+ SomeProtocol ; ; pos 2 → spec nil
280+ (some-method [this] ; ; pos 3+ → spec (:defn) — each method gets :defn-style
281+ (do-stuff this)))
282+ ```
283+
284+ Here are the built-in backtracking specs:
246285
247286``` el
248287(define-clojure-indent
249- (implement '(1 (1)))
250- (letfn '(1 ((:defn)) nil))
251- (proxy '(2 nil nil (1)))
252- (reify '(:defn (1)))
253- (deftype '(2 nil nil (1)))
254- (defrecord '(2 nil nil (1)))
255- (specify '(1 (1)))
256- (specify '(1 (1))))
288+ (letfn '(1 ((:defn)) nil))
289+ (deftype '(2 nil nil (:defn)))
290+ (defrecord '(2 nil nil (:defn)))
291+ (defprotocol '(1 (:defn)))
292+ (definterface '(1 (:defn)))
293+ (reify '(:defn (1)))
294+ (proxy '(2 nil nil (:defn)))
295+ (extend-protocol '(1 :defn))
296+ (extend-type '(1 :defn))
297+ (specify '(1 :defn))
298+ (specify! '(1 :defn)))
257299```
258300
259301These follow the same rules as the ` :style/indent ` metadata specified by [ cider-nrepl] [ ] .
260- For instructions on how to write these specifications, see
302+ For more details on writing indent specifications, see
261303[ this document] ( https://docs.cider.mx/cider/indent_spec.html ) .
262304The only difference is that you're allowed to use lists instead of vectors.
263305
306+ Backtracking is controlled by ` clojure-use-backtracking-indent `
307+ (default ` t ` ) and limited to ` clojure-max-backtracking ` levels
308+ (default 3). Disabling backtracking will break indentation for
309+ all forms with list-based specs.
310+
264311The indentation of [ special arguments] ( https://docs.cider.mx/cider/indent_spec.html#special-arguments ) is controlled by
265312` clojure-special-arg-indent-factor ` , which by default indents special arguments
266313a further ` lisp-body-indent ` when compared to ordinary arguments.
@@ -724,13 +771,16 @@ See [this ticket](https://github.com/clojure-emacs/clojure-mode/issues/270) for
724771
725772### Indentation Performance
726773
727- ` clojure-mode ` 's indentation engine is a bit slow. You can speed things up
728- significantly by disabling ` clojure-use-backtracking-indent ` , but this will
729- break the indentation of complex forms like ` deftype ` , ` defprotocol ` , ` reify ` ,
730- ` letfn ` , etc.
774+ ` clojure-mode ` 's indentation engine is a bit slow due to the
775+ [ backtracking indentation] ( #backtracking-contextual-indentation ) logic
776+ that walks up the sexp tree for context. You can speed things up
777+ significantly by setting ` clojure-use-backtracking-indent ` to ` nil ` ,
778+ but this will break the indentation of forms with list-based specs
779+ (` deftype ` , ` defrecord ` , ` defprotocol ` , ` definterface ` , ` reify ` ,
780+ ` proxy ` , ` letfn ` , ` extend-type ` , ` extend-protocol ` , ` specify ` ,
781+ ` specify! ` ). Simple integer and ` :defn ` specs will continue to work.
731782
732- We should look into ways to optimize the performance of the backtracking
733- indentation logic. See [ this ticket] ( https://github.com/clojure-emacs/clojure-mode/issues/606 ) for more
783+ See [ this ticket] ( https://github.com/clojure-emacs/clojure-mode/issues/606 ) for more
734784details.
735785
736786### Font-locking Implementation
0 commit comments