Skip to content

Commit 805f31e

Browse files
committed
Fix some issues with short anonymous functions
1 parent 09f7da6 commit 805f31e

5 files changed

+85
-11
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
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.
88
- [#82](https://github.com/clojure-emacs/clojure-ts-mode/issues/82): Introduce `clojure-ts-outline-variant`.
9+
- [#86](https://github.com/clojure-emacs/clojure-ts-mode/pull/86): Better handling of function literals:
10+
- Syntax highlighting of built-in keywords.
11+
- Consistent indentation with regular forms.
12+
- Support for automatic aligning forms.
913

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

clojure-ts-mode.el

+47-11
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,13 @@ literals with regex grammar."
514514
(:equal "clojure.core" @ns))
515515
name: (sym_name) @font-lock-keyword-face))
516516
(:match ,clojure-ts--builtin-symbol-regexp @font-lock-keyword-face))
517+
((anon_fn_lit meta: _ :* :anchor (sym_lit !namespace name: (sym_name) @font-lock-keyword-face))
518+
(:match ,clojure-ts--builtin-symbol-regexp @font-lock-keyword-face))
519+
((anon_fn_lit meta: _ :* :anchor
520+
(sym_lit namespace: ((sym_ns) @ns
521+
(:equal "clojure.core" @ns))
522+
name: (sym_name) @font-lock-keyword-face))
523+
(:match ,clojure-ts--builtin-symbol-regexp @font-lock-keyword-face))
517524
((sym_name) @font-lock-builtin-face
518525
(:match ,clojure-ts--builtin-dynamic-var-regexp @font-lock-builtin-face)))
519526

@@ -726,6 +733,14 @@ literals with regex grammar."
726733
"Return non-nil if NODE is a Clojure list."
727734
(string-equal "list_lit" (treesit-node-type node)))
728735

736+
(defun clojure-ts--anon-fn-node-p (node)
737+
"Return non-nil if NODE is a Clojure function literal."
738+
(string-equal "anon_fn_lit" (treesit-node-type node)))
739+
740+
(defun clojure-ts--opening-paren-node-p (node)
741+
"Return non-nil if NODE is an opening paren."
742+
(string-equal "(" (treesit-node-text node)))
743+
729744
(defun clojure-ts--symbol-node-p (node)
730745
"Return non-nil if NODE is a Clojure symbol."
731746
(string-equal "sym_lit" (treesit-node-type node)))
@@ -1249,7 +1264,8 @@ PARENT not should be a list. If first symbol in the expression has an
12491264
indentation rule in `clojure-ts--semantic-indent-rules-defaults' or
12501265
`clojure-ts-semantic-indent-rules' check if NODE should be indented
12511266
according to the rule. If NODE is nil, use next node after BOL."
1252-
(and (clojure-ts--list-node-p parent)
1267+
(and (or (clojure-ts--list-node-p parent)
1268+
(clojure-ts--anon-fn-node-p parent))
12531269
(let* ((first-child (clojure-ts--node-child-skip-metadata parent 0)))
12541270
(when-let* ((rule (clojure-ts--find-semantic-rule node parent 0)))
12551271
(and (not (clojure-ts--match-with-metadata node))
@@ -1265,7 +1281,8 @@ according to the rule. If NODE is nil, use next node after BOL."
12651281

12661282
(defun clojure-ts--match-function-call-arg (node parent _bol)
12671283
"Match NODE if PARENT is a list expressing a function or macro call."
1268-
(and (clojure-ts--list-node-p parent)
1284+
(and (or (clojure-ts--list-node-p parent)
1285+
(clojure-ts--anon-fn-node-p parent))
12691286
;; Can the following two clauses be replaced by checking indexes?
12701287
;; Does the second child exist, and is it not equal to the current node?
12711288
(treesit-node-child parent 1 t)
@@ -1284,7 +1301,8 @@ according to the rule. If NODE is nil, use next node after BOL."
12841301
"Match NODE if it is an argument to a PARENT threading macro."
12851302
;; We want threading macros to indent 2 only if the ->> is on it's own line.
12861303
;; If not, then align function arg.
1287-
(and (clojure-ts--list-node-p parent)
1304+
(and (or (clojure-ts--list-node-p parent)
1305+
(clojure-ts--anon-fn-node-p parent))
12881306
(let ((first-child (treesit-node-child parent 0 t)))
12891307
(clojure-ts--symbol-matches-p
12901308
clojure-ts--threading-macro
@@ -1335,19 +1353,17 @@ according to the rule. If NODE is nil, use next node after BOL."
13351353
(and prev-sibling
13361354
(clojure-ts--metadata-node-p prev-sibling))))
13371355

1338-
(defun clojure-ts--anchor-parent-skip-metadata (_node parent _bol)
1356+
(defun clojure-ts--anchor-parent-opening-paren (_node parent _bol)
13391357
"Return position of PARENT start for NODE.
13401358
13411359
If PARENT has optional metadata we skip it and return starting position
13421360
of the first child's opening paren.
13431361
13441362
NOTE: This serves as an anchor function to resolve an indentation issue
13451363
for forms with type hints."
1346-
(let ((first-child (treesit-node-child parent 0 t)))
1347-
(if (clojure-ts--metadata-node-p first-child)
1348-
;; We don't need named node here
1349-
(treesit-node-start (treesit-node-child parent 1))
1350-
(treesit-node-start parent))))
1364+
(thread-first parent
1365+
(treesit-search-subtree #'clojure-ts--opening-paren-node-p nil t 1)
1366+
(treesit-node-start)))
13511367

13521368
(defun clojure-ts--match-collection-item-with-metadata (node-type)
13531369
"Return a matcher for a collection item with metadata by NODE-TYPE.
@@ -1359,6 +1375,18 @@ if NODE has metadata and its parent has type NODE-TYPE."
13591375
(treesit-node-type
13601376
(clojure-ts--node-with-metadata-parent node)))))
13611377

1378+
(defun clojure-ts--anchor-nth-sibling (n &optional named)
1379+
"Return the start of the Nth child of PARENT.
1380+
1381+
NAMED non-nil means count only named nodes.
1382+
1383+
NOTE: This is a replacement for built-in `nth-sibling' anchor preset,
1384+
which doesn't work properly for named nodes (see the bug
1385+
https://debbugs.gnu.org/cgi/bugreport.cgi?bug=78065)"
1386+
(lambda (_n parent &rest _)
1387+
(treesit-node-start
1388+
(treesit-node-child parent n named))))
1389+
13621390
(defun clojure-ts--semantic-indent-rules ()
13631391
"Return a list of indentation rules for `treesit-simple-indent-rules'."
13641392
`((clojure
@@ -1385,11 +1413,11 @@ if NODE has metadata and its parent has type NODE-TYPE."
13851413
((parent-is "read_cond_lit") parent 3)
13861414
((parent-is "tagged_or_ctor_lit") parent 0)
13871415
;; https://guide.clojure.style/#body-indentation
1388-
(clojure-ts--match-form-body clojure-ts--anchor-parent-skip-metadata 2)
1416+
(clojure-ts--match-form-body clojure-ts--anchor-parent-opening-paren 2)
13891417
;; https://guide.clojure.style/#threading-macros-alignment
13901418
(clojure-ts--match-threading-macro-arg prev-sibling 0)
13911419
;; https://guide.clojure.style/#vertically-align-fn-args
1392-
(clojure-ts--match-function-call-arg (nth-sibling 2 nil) 0)
1420+
(clojure-ts--match-function-call-arg ,(clojure-ts--anchor-nth-sibling 1 t) 0)
13931421
;; https://guide.clojure.style/#one-space-indent
13941422
((parent-is "list_lit") parent 1))))
13951423

@@ -1561,6 +1589,14 @@ have changed."
15611589
((list_lit
15621590
((sym_lit) @sym
15631591
(:match ,(clojure-ts-symbol-regexp clojure-ts-align-cond-forms) @sym)))
1592+
@cond)
1593+
((anon_fn_lit
1594+
((sym_lit) @sym
1595+
(:match ,(clojure-ts-symbol-regexp clojure-ts-align-binding-forms) @sym))
1596+
(vec_lit) @bindings-vec))
1597+
((anon_fn_lit
1598+
((sym_lit) @sym
1599+
(:match ,(clojure-ts-symbol-regexp clojure-ts-align-cond-forms) @sym)))
15641600
@cond))
15651601
(when clojure-ts-align-reader-conditionals
15661602
'(((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 "function literals"
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 function literals"
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 function literals"
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+
;; Function literals
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)