Skip to content

Commit 3eb2019

Browse files
committed
[#124] Correctly indent keyword-initial lists in 'fixed' style
When using the 'fixed' indentation style, lists where the first element is a keyword (e.g., `(:foo ...)`) should have their children indented by a single space, per the style description.
1 parent 08b1c3b commit 3eb2019

File tree

3 files changed

+83
-50
lines changed

3 files changed

+83
-50
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
- [#118](https://github.com/clojure-emacs/clojure-ts-mode/pull/118): Add some ns manipulation functions from `clojure-mode`.
1313
- Fix a bug in `clojure-ts-add-arity` when body has more than one expression.
1414
- [#120](https://github.com/clojure-emacs/clojure-ts-mode/issues/120): Fix a bug when symbols with metadata were not listed in imenu.
15+
- [#124](https://github.com/clojure-emacs/clojure-ts-mode/issues/124): Correctly indent lists that start with a keyword when using the
16+
`fixed` indentation style.
1517

1618
## 0.5.1 (2025-06-17)
1719

clojure-ts-mode.el

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,9 +1003,9 @@ If there is no namespace, returns nil."
10031003
(defun clojure-ts--node-child-skip-metadata (node n)
10041004
"Return the Nth child of NODE like `treesit-node-child', sans metadata.
10051005
Skip the optional metadata node at pos 0 if present."
1006-
(let ((value-nodes (thread-last (treesit-node-children node t)
1007-
(seq-filter (lambda (child)
1008-
(string= (treesit-node-field-name child) "value"))))))
1006+
(let ((value-nodes (seq-filter (lambda (child)
1007+
(string= (treesit-node-field-name child) "value"))
1008+
(treesit-node-children node t))))
10091009
(seq-elt value-nodes n)))
10101010

10111011
(defun clojure-ts--first-value-child (node)
@@ -1213,8 +1213,7 @@ The possible values for this variable are
12131213
(and (clojure-ts--list-node-p parent)
12141214
;; Should we also check for keyword first child, as in (:k map) calls?
12151215
(let ((first-child (treesit-node-child parent 0 t)))
1216-
(or (clojure-ts--symbol-node-p first-child)
1217-
(clojure-ts--keyword-node-p first-child)))))
1216+
(clojure-ts--symbol-node-p first-child))))
12181217
parent 2)
12191218
((parent-is "vec_lit") parent 1)
12201219
((parent-is "map_lit") parent 1)

test/clojure-ts-mode-indentation-test.el

Lines changed: 77 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
(require 'clojure-ts-mode)
2525
(require 'cl-lib)
2626
(require 'buttercup)
27+
(require 'test-helper "test/test-helper")
2728
(require 's nil t) ;Don't burp if it's missing during compilation.
2829

2930

@@ -74,6 +75,25 @@ DESCRIPTION is a string with the description of the spec."
7475
(expect (buffer-string) :to-equal ,(concat "\n" form))))
7576
forms))))
7677

78+
(defmacro when-indenting-fixed-it (description &rest forms)
79+
"Return a buttercup spec.
80+
81+
Check that all FORMS correspond to properly indented sexps.
82+
83+
DESCRIPTION is a string with the description of the spec.
84+
85+
Sets `clojure-ts-indent-style' to `fixed'."
86+
(declare (indent 1))
87+
`(it ,description
88+
(progn
89+
,@(mapcar (lambda (form)
90+
`(with-temp-buffer
91+
(let ((clojure-ts-indent-style 'fixed))
92+
(clojure-ts-mode)
93+
(insert "\n" ,form);,(replace-regexp-in-string "\n +" "\n " form))
94+
(indent-region (point-min) (point-max))
95+
(expect (buffer-string) :to-equal ,(concat "\n" form)))))
96+
forms))))
7797

7898
(defmacro when-aligning-it (description &rest forms)
7999
"Return a buttercup spec.
@@ -179,32 +199,32 @@ DESCRIPTION is a string with the description of the spec."
179199
[_foo]
180200
(+ 1 1))")
181201

182-
(when-indenting-it "should support function calls via vars"
183-
"
202+
(when-indenting-it "should support function calls via vars"
203+
"
184204
(#'foo 5
185205
6)")
186206

187-
(when-indenting-it "should support function literals"
188-
"
207+
(when-indenting-it "should support function literals"
208+
"
189209
#(or true
190210
false
191211
%)")
192212

193-
(when-indenting-it "should support block-0 expressions"
194-
"
213+
(when-indenting-it "should support block-0 expressions"
214+
"
195215
(do (aligned)
196216
(vertically))"
197217

198-
"
218+
"
199219
(do
200220
(indented)
201221
(with-2-spaces))"
202222

203-
"
223+
"
204224
(future
205225
(body is indented))"
206226

207-
"
227+
"
208228
(try
209229
(something)
210230
;; A bit of block 2 rule
@@ -214,108 +234,112 @@ DESCRIPTION is a string with the description of the spec."
214234
e-info
215235
\"Second argument is aligned vertically with the first one.\"))")
216236

217-
(when-indenting-it "should support block-1 expressions"
218-
"
237+
(when-indenting-it "should support block-1 expressions"
238+
"
219239
(case x
220240
2 (print 2)
221241
3 (print 3)
222242
(print \"Default\"))"
223243

224-
"
244+
"
225245
(cond-> {}
226246
:always (assoc :hello \"World\")
227247
false (do nothing))"
228248

229-
"
249+
"
230250
(with-precision 32
231251
(/ (bigdec 20) (bigdec 30)))"
232252

233-
"
253+
"
234254
(testing \"Something should work\"
235255
(is (something-working?)))")
236256

237-
(when-indenting-it "should support block-2 expressions"
238-
"
257+
(when-indenting-it "should support block-2 expressions"
258+
"
239259
(are [x y]
240260
(= x y)
241261
2 3
242262
4 5
243263
6 6)"
244264

245-
"
265+
"
246266
(as-> {} $
247267
(assoc $ :hello \"World\"))"
248268

249-
"
269+
"
250270
(as-> {}
251271
my-map
252272
(assoc my-map :hello \"World\"))"
253273

254-
"
274+
"
255275
(defrecord MyThingR []
256276
IProto
257277
(foo [this x] x))")
258278

259-
(when-indenting-it "should support inner-0 expressions"
260-
"
279+
(when-indenting-it "should support inner-0 expressions"
280+
"
261281
(fn named-lambda [x]
262282
(+ x x))"
263283

264-
"
284+
"
265285
(defmethod hello :world
266286
[arg1 arg2]
267287
(+ arg1 arg2))"
268288

269-
"
289+
"
270290
(reify
271291
AutoCloseable
272292
(close
273293
[this]
274294
(is properly indented)))")
275295

276-
(it "should prioritize custom semantic indentation rules"
277-
(with-clojure-ts-buffer "
296+
(it "should prioritize custom semantic indentation rules"
297+
(with-clojure-ts-buffer "
278298
(are [x y]
279299
(= x y)
280300
2 3
281301
4 5
282302
6 6)"
283-
(setopt clojure-ts-semantic-indent-rules '(("are" . ((:block 1)))))
284-
(indent-region (point-min) (point-max))
285-
(expect (buffer-string) :to-equal "
303+
(setopt clojure-ts-semantic-indent-rules '(("are" . ((:block 1)))))
304+
(indent-region (point-min) (point-max))
305+
(prog1
306+
(expect (buffer-string) :to-equal "
286307
(are [x y]
287308
(= x y)
288309
2 3
289310
4 5
290-
6 6)")))
311+
6 6)")
312+
;; `setopt' cannot set variable locally so we need to restore it's
313+
;; original value.
314+
(setopt clojure-ts-semantic-indent-rules nil))))
291315

292-
(it "should indent collections elements with metadata correctly"
293-
"
316+
(it "should indent collections elements with metadata correctly"
317+
"
294318
(def x
295319
[a b [c ^:foo
296320
d
297321
e]])"
298322

299-
"
323+
"
300324
#{x
301325
y ^:foo
302326
z}"
303327

304-
"
328+
"
305329
{:hello ^:foo
306330
\"world\"
307331
:foo
308332
\"bar\"}")
309333

310-
(it "should indent body of special forms correctly considering metadata"
311-
"
334+
(it "should indent body of special forms correctly considering metadata"
335+
"
312336
(let [result ^long
313337
(if true
314338
1
315339
2)])")
316340

317-
(it "should pick up dynamic indentation rules from clojure-ts-get-indent-function"
318-
(with-clojure-ts-buffer "
341+
(it "should pick up dynamic indentation rules from clojure-ts-get-indent-function"
342+
(with-clojure-ts-buffer "
319343
(defmacro my-with-in-str
320344
\"[DOCSTRING]\"
321345
{:style/indent 1}
@@ -324,9 +348,9 @@ DESCRIPTION is a string with the description of the spec."
324348
325349
(my-with-in-str \"34\"
326350
(prompt \"How old are you?\"))"
327-
(setq-local clojure-ts-get-indent-function #'cider--get-symbol-indent-mock)
328-
(indent-region (point-min) (point-max))
329-
(expect (buffer-string) :to-equal "
351+
(setq-local clojure-ts-get-indent-function #'cider--get-symbol-indent-mock)
352+
(indent-region (point-min) (point-max))
353+
(expect (buffer-string) :to-equal "
330354
(defmacro my-with-in-str
331355
\"[DOCSTRING]\"
332356
{:style/indent 1}
@@ -336,7 +360,7 @@ DESCRIPTION is a string with the description of the spec."
336360
(my-with-in-str \"34\"
337361
(prompt \"How old are you?\"))"))
338362

339-
(with-clojure-ts-buffer "
363+
(with-clojure-ts-buffer "
340364
(defmacro my-letfn
341365
\"[DOCSTRING]\"
342366
{:style/indent [1 [[:defn]] :form]}
@@ -349,9 +373,9 @@ DESCRIPTION is a string with the description of the spec."
349373
(* (twice y) 3))]
350374
(println \"Twice 15 =\" (twice 15))
351375
(println \"Six times 15 =\" (six-times 15)))"
352-
(setq-local clojure-ts-get-indent-function #'cider--get-symbol-indent-mock)
353-
(indent-region (point-min) (point-max))
354-
(expect (buffer-string) :to-equal "
376+
(setq-local clojure-ts-get-indent-function #'cider--get-symbol-indent-mock)
377+
(indent-region (point-min) (point-max))
378+
(expect (buffer-string) :to-equal "
355379
(defmacro my-letfn
356380
\"[DOCSTRING]\"
357381
{:style/indent [1 [[:defn]] :form]}
@@ -363,7 +387,15 @@ DESCRIPTION is a string with the description of the spec."
363387
(six-times [y]
364388
(* (twice y) 3))]
365389
(println \"Twice 15 =\" (twice 15))
366-
(println \"Six times 15 =\" (six-times 15)))"))))
390+
(println \"Six times 15 =\" (six-times 15)))")))
391+
392+
(when-indenting-fixed-it "should indent children of a list with 2 spaces if first node is a symbol"
393+
"(ns indentation
394+
(:require
395+
[clojure.string :as str])
396+
(:import
397+
(java.util Date
398+
UUID)))"))
367399

368400
(describe "clojure-ts-align"
369401
(it "should handle improperly indented content"

0 commit comments

Comments
 (0)