Skip to content

Improve indentation test coverage and documentation#701

Merged
bbatsov merged 13 commits intomasterfrom
improve-indentation-tests
Mar 23, 2026
Merged

Improve indentation test coverage and documentation#701
bbatsov merged 13 commits intomasterfrom
improve-indentation-tests

Conversation

@bbatsov
Copy link
Copy Markdown
Member

@bbatsov bbatsov commented Mar 23, 2026

Comprehensive improvements to the indentation test suite and documentation for the backtracking indentation mechanism.

Test coverage goes from 355 to 415 specs, covering every built-in define-clojure-indent form and all configuration knobs.

New tests for built-in indent specs:

  • defmethod, multi-arity defn, nested letfn, try/catch/finally
  • as->, with-* fallback, def* fallback
  • condp, namespace-qualified special forms
  • fn, def, bound-fn
  • if, if-not, case, when, when-not, when-first, while, do, delay, future, comment
  • binding, loop, for, doseq, dotimes, when-let, if-let, when-some, if-some, doto, locking, fdef, this-as
  • testing, deftest, are, use-fixtures, async (clojure.test)
  • run, run*, fresh (core.logic)
  • go, go-loop, thread, alt!, alt!! (core.async)

New tests for configuration options:

  • clojure-indent-keyword-style — all three values with case A/B
  • clojure-use-backtracking-indent nil — simple specs work, complex specs degrade
  • clojure-max-backtracking — depth limiting
  • clojure-enable-indent-specs nil — uniform indentation, def*/with-* fallback
  • clojure-get-indent-function — custom spec lookup hook

Documentation improvements for backtracking indentation in the README (spec element table, annotated letfn/defrecord examples, correct built-in specs) and in docstrings (clojure-use-backtracking-indent, clojure-max-backtracking, clojure--find-indent-spec-backtracking, clojure-indent-function, put-clojure-indent).

bbatsov added 13 commits March 23, 2026 09:16
…and try/catch/finally

These forms all have indent specs but lacked dedicated tests verifying
their indentation output:

- defmethod with dispatch values (:defn style)
- Multi-arity defn with and without docstrings/metadata
- Nested letfn verifying the complex '(1 ((:defn)) nil) spec
  propagates correctly through multiple nesting levels
- try/catch/finally verifying specs 0/2/0
Test the as-> form (spec 2) and two implicit indentation behaviors
in clojure-indent-function:

- with-* forms get body-style indentation even without explicit
  indent specs (e.g., with-custom-thing)
- Unknown def* forms (defwhatever, ns/defwhatever) also get
  body-style indentation via the fallback regex match
Test condp (spec 2, two special args before body) and verify that
namespace-qualified versions of special forms (clojure.core/let,
clojure.core/when, clojure.core/defn) indent correctly via the
backtracking mechanism.
These :defn-style forms had indent specs but no dedicated tests
verifying body indentation. Includes fn with name, multi-arity fn,
and def with metadata.
Test if, if-not, case, when, when-not, when-first, while (spec 1),
and do, delay, future, comment (spec 0).
Test binding, loop, for, doseq, dotimes, when-let, if-let, when-some,
if-some, doto, locking, fdef, and this-as. All use spec 1 (one
special arg before body).
Test library-specific indent specs:
- clojure.test: testing (1), deftest (:defn), are (2),
  use-fixtures (:defn), async (1)
- core.logic: run (:defn), run* (:defn), fresh (:defn)
- core.async: go (0), go-loop (1), thread (0), alt! (0), alt!! (0)
The backtracking indent mechanism was poorly documented — the README
showed incorrect specs and didn't explain how positional indexing
works, and the docstrings were minimal.

README changes:
- Replace the vague "more sophisticated indent specifications"
  section with a detailed explanation of how backtracking works,
  including a table of spec elements and annotated examples for
  letfn and defrecord
- Update the built-in specs to match the actual code
- Rewrite the performance section with specifics about which forms
  break when backtracking is disabled

Docstring changes:
- clojure-use-backtracking-indent: explain what it does and what
  breaks when disabled
- clojure-max-backtracking: clarify relationship to backtracking
- clojure--find-indent-spec-backtracking: document the algorithm
  and positional indexing with an example
- clojure-indent-function: document list specs and the def*/with-*
  fallback behavior
- put-clojure-indent: document all allowed INDENT values with examples
Test all three values (always-align, always-indent, align-arguments)
with :require forms inside ns, covering both case A (arg on same line
as keyword) and case B (no args on same line).
Verify that when backtracking is disabled:
- Simple specs (integer, :defn) still work correctly
- Complex list-based specs (letfn) lose nested context
Verify that setting max-backtracking to 0 prevents context lookup
for nested forms, while the default depth of 3 allows it.
Verify that when indent specs are disabled:
- Forms like let and when get uniform (function-call) indentation
  instead of their special body-style indentation
- The def*/with-* fallback still applies regardless, since it's
  handled outside the spec lookup
Verify that the custom indent spec lookup hook works:
- A custom function can provide specs for unknown forms
- When the custom function returns nil, built-in specs still apply
@bbatsov bbatsov merged commit dd25ae7 into master Mar 23, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant