Skip to content

Commit 6ffbd9f

Browse files
committed
Fix some issues with short anonymous functions
1 parent e960a90 commit 6ffbd9f

5 files changed

+85
-11
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
- [#16](https://github.com/clojure-emacs/clojure-ts-mode/issues/16): Introduce `clojure-ts-align`.
66
- [#11](https://github.com/clojure-emacs/clojure-ts-mode/issues/11): Enable regex syntax highlighting.
77
- [#16](https://github.com/clojure-emacs/clojure-ts-mode/issues/16): Add support for automatic aligning forms.
8+
- Better handling of short anonymous functions
9+
- Syntax highlighting of built-in keywords.
10+
- Consistent indentation with regular forms.
11+
- Support for automatic aligning forms.
812

913
## 0.3.0 (2025-04-15)
1014

clojure-ts-mode.el

+47-11
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,13 @@ literals with regex grammar."
503503
(:equal "clojure.core" @ns))
504504
name: (sym_name) @font-lock-keyword-face))
505505
(:match ,clojure-ts--builtin-symbol-regexp @font-lock-keyword-face))
506+
((anon_fn_lit meta: _ :* :anchor (sym_lit !namespace name: (sym_name) @font-lock-keyword-face))
507+
(:match ,clojure-ts--builtin-symbol-regexp @font-lock-keyword-face))
508+
((anon_fn_lit meta: _ :* :anchor
509+
(sym_lit namespace: ((sym_ns) @ns
510+
(:equal "clojure.core" @ns))
511+
name: (sym_name) @font-lock-keyword-face))
512+
(:match ,clojure-ts--builtin-symbol-regexp @font-lock-keyword-face))
506513
((sym_name) @font-lock-builtin-face
507514
(:match ,clojure-ts--builtin-dynamic-var-regexp @font-lock-builtin-face)))
508515

@@ -715,6 +722,14 @@ literals with regex grammar."
715722
"Return non-nil if NODE is a Clojure list."
716723
(string-equal "list_lit" (treesit-node-type node)))
717724

725+
(defun clojure-ts--anon-fn-node-p (node)
726+
"Return non-nil if NODE is a Clojure anonymous lambda."
727+
(string-equal "anon_fn_lit" (treesit-node-type node)))
728+
729+
(defun clojure-ts--opening-paren-node-p (node)
730+
"Return non-nil if NODE is an opening paren."
731+
(string-equal "(" (treesit-node-text node)))
732+
718733
(defun clojure-ts--symbol-node-p (node)
719734
"Return non-nil if NODE is a Clojure symbol."
720735
(string-equal "sym_lit" (treesit-node-type node)))
@@ -1224,7 +1239,8 @@ PARENT not should be a list. If first symbol in the expression has an
12241239
indentation rule in `clojure-ts--semantic-indent-rules-defaults' or
12251240
`clojure-ts-semantic-indent-rules' check if NODE should be indented
12261241
according to the rule. If NODE is nil, use next node after BOL."
1227-
(and (clojure-ts--list-node-p parent)
1242+
(and (or (clojure-ts--list-node-p parent)
1243+
(clojure-ts--anon-fn-node-p parent))
12281244
(let* ((first-child (clojure-ts--node-child-skip-metadata parent 0)))
12291245
(when-let* ((rule (clojure-ts--find-semantic-rule node parent 0)))
12301246
(and (not (clojure-ts--match-with-metadata node))
@@ -1240,7 +1256,8 @@ according to the rule. If NODE is nil, use next node after BOL."
12401256

12411257
(defun clojure-ts--match-function-call-arg (node parent _bol)
12421258
"Match NODE if PARENT is a list expressing a function or macro call."
1243-
(and (clojure-ts--list-node-p parent)
1259+
(and (or (clojure-ts--list-node-p parent)
1260+
(clojure-ts--anon-fn-node-p parent))
12441261
;; Can the following two clauses be replaced by checking indexes?
12451262
;; Does the second child exist, and is it not equal to the current node?
12461263
(treesit-node-child parent 1 t)
@@ -1259,7 +1276,8 @@ according to the rule. If NODE is nil, use next node after BOL."
12591276
"Match NODE if it is an argument to a PARENT threading macro."
12601277
;; We want threading macros to indent 2 only if the ->> is on it's own line.
12611278
;; If not, then align function arg.
1262-
(and (clojure-ts--list-node-p parent)
1279+
(and (or (clojure-ts--list-node-p parent)
1280+
(clojure-ts--anon-fn-node-p parent))
12631281
(let ((first-child (treesit-node-child parent 0 t)))
12641282
(clojure-ts--symbol-matches-p
12651283
clojure-ts--threading-macro
@@ -1310,19 +1328,17 @@ according to the rule. If NODE is nil, use next node after BOL."
13101328
(and prev-sibling
13111329
(clojure-ts--metadata-node-p prev-sibling))))
13121330

1313-
(defun clojure-ts--anchor-parent-skip-metadata (_node parent _bol)
1331+
(defun clojure-ts--anchor-parent-opening-paren (_node parent _bol)
13141332
"Return position of PARENT start for NODE.
13151333
13161334
If PARENT has optional metadata we skip it and return starting position
13171335
of the first child's opening paren.
13181336
13191337
NOTE: This serves as an anchor function to resolve an indentation issue
13201338
for forms with type hints."
1321-
(let ((first-child (treesit-node-child parent 0 t)))
1322-
(if (clojure-ts--metadata-node-p first-child)
1323-
;; We don't need named node here
1324-
(treesit-node-start (treesit-node-child parent 1))
1325-
(treesit-node-start parent))))
1339+
(thread-first parent
1340+
(treesit-search-subtree #'clojure-ts--opening-paren-node-p nil t 1)
1341+
(treesit-node-start)))
13261342

13271343
(defun clojure-ts--match-collection-item-with-metadata (node-type)
13281344
"Return a matcher for a collection item with metadata by NODE-TYPE.
@@ -1334,6 +1350,18 @@ if NODE has metadata and its parent has type NODE-TYPE."
13341350
(treesit-node-type
13351351
(clojure-ts--node-with-metadata-parent node)))))
13361352

1353+
(defun clojure-ts--anchor-nth-sibling (n &optional named)
1354+
"Return the start of the Nth child of PARENT.
1355+
1356+
NAMED non-nil means count only named nodes.
1357+
1358+
NOTE: This is a replacement for built-in `nth-sibling' anchor preset,
1359+
which doesn't work properly for named nodes (see the bug
1360+
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=78065)"
1361+
(lambda (_n parent &rest _)
1362+
(treesit-node-start
1363+
(treesit-node-child parent n named))))
1364+
13371365
(defun clojure-ts--semantic-indent-rules ()
13381366
"Return a list of indentation rules for `treesit-simple-indent-rules'."
13391367
`((clojure
@@ -1360,11 +1388,11 @@ if NODE has metadata and its parent has type NODE-TYPE."
13601388
((parent-is "read_cond_lit") parent 3)
13611389
((parent-is "tagged_or_ctor_lit") parent 0)
13621390
;; https://guide.clojure.style/#body-indentation
1363-
(clojure-ts--match-form-body clojure-ts--anchor-parent-skip-metadata 2)
1391+
(clojure-ts--match-form-body clojure-ts--anchor-parent-opening-paren 2)
13641392
;; https://guide.clojure.style/#threading-macros-alignment
13651393
(clojure-ts--match-threading-macro-arg prev-sibling 0)
13661394
;; https://guide.clojure.style/#vertically-align-fn-args
1367-
(clojure-ts--match-function-call-arg (nth-sibling 2 nil) 0)
1395+
(clojure-ts--match-function-call-arg ,(clojure-ts--anchor-nth-sibling 1 t) 0)
13681396
;; https://guide.clojure.style/#one-space-indent
13691397
((parent-is "list_lit") parent 1))))
13701398

@@ -1536,6 +1564,14 @@ have changed."
15361564
((list_lit
15371565
((sym_lit) @sym
15381566
(:match ,(clojure-ts-symbol-regexp clojure-ts-align-cond-forms) @sym)))
1567+
@cond)
1568+
((anon_fn_lit
1569+
((sym_lit) @sym
1570+
(:match ,(clojure-ts-symbol-regexp clojure-ts-align-binding-forms) @sym))
1571+
(vec_lit) @bindings-vec))
1572+
((anon_fn_lit
1573+
((sym_lit) @sym
1574+
(:match ,(clojure-ts-symbol-regexp clojure-ts-align-cond-forms) @sym)))
15391575
@cond))
15401576
(when clojure-ts-align-reader-conditionals
15411577
'(((read_cond_lit) @read-cond)

test/clojure-ts-mode-font-lock-test.el

+4
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ DESCRIPTION is the description of the spec."
169169
(2 5 font-lock-type-face)
170170
(8 9 font-lock-keyword-face)))
171171

172+
(when-fontifying-it "anonymous functions"
173+
("#(or one two)"
174+
(3 4 font-lock-keyword-face)))
175+
172176
(when-fontifying-it "should highlight function name in all known forms"
173177
("(letfn [(add [x y]
174178
(+ x y))

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

+17
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,12 @@ DESCRIPTION is a string with the description of the spec."
184184
(#'foo 5
185185
6)")
186186

187+
(when-indenting-it "should support short lambdas"
188+
"
189+
#(or true
190+
false
191+
%)")
192+
187193
(when-indenting-it "should support block-0 expressions"
188194
"
189195
(do (aligned)
@@ -462,6 +468,17 @@ b |20])"
462468
(let [a b
463469
c d])")
464470

471+
(when-aligning-it "should handle short lambdas"
472+
"
473+
#(let [hello 1
474+
foo \"hone\"]
475+
(pringln hello))"
476+
477+
"
478+
^{:some :metadata} #(let [foo %
479+
bar-zzz %]
480+
foo)")
481+
465482
(when-aligning-it "should handle a blank line"
466483
"
467484
(let [this-is-a-form b

test/samples/test.clj

+13
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,19 @@
4141

4242
0 0i)
4343

44+
;; Anonymous lambdas
45+
46+
^{:some "metadata"} #(let [foo %
47+
bar-zzz %]
48+
foo)
49+
50+
#(or one
51+
two)
52+
53+
#(let [hello 1
54+
foo "hone"]
55+
(pringln hello))
56+
4457
;; examples of valid namespace definitions
4558
(comment
4659
(ns .validns)

0 commit comments

Comments
 (0)