Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
- [#169](https://github.com/clojure-emacs/clojure-ts-mode/pull/169): Add `clojure-ts-remove-unused-keys` (`C-c C-r k`), which removes the names the top-level form at point destructures from maps but never uses, along with their `:or` defaults.
- [#164](https://github.com/clojure-emacs/clojure-ts-mode/pull/164): Support `setopt-local` for `clojure-ts-indent-style`, `clojure-ts-extra-def-forms` and `clojure-ts-comment-macro-font-lock-body`, which Emacs 31 refused to set locally and Emacs 32 warned about in `.dir-locals.el`.
- [#168](https://github.com/clojure-emacs/clojure-ts-mode/pull/168): Indent docstrings like `clojure-mode`, moving the lines indented less than `clojure-ts-docstring-fill-prefix-width` to that column and keeping deeper ones, instead of leaving them alone with `clojure-ts-use-markdown-inline` and flattening them without it.
- [#174](https://github.com/clojure-emacs/clojure-ts-mode/pull/174): Speed up indentation of forms with many children, like a big `comment` form, which slowed down with the cube of their length.

## 0.6.0 (2025-12-02)

Expand Down
67 changes: 42 additions & 25 deletions clojure-ts-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -933,7 +933,7 @@ literals with regex grammar."

(defun clojure-ts--opening-paren-node-p (node)
"Return non-nil if NODE is an opening paren."
(string-equal "(" (treesit-node-text node)))
(string-equal "(" (treesit-node-type node)))

(defun clojure-ts--symbol-node-p (node)
"Return non-nil if NODE is a Clojure symbol."
Expand Down Expand Up @@ -981,9 +981,21 @@ If there is no namespace, returns nil."

(defun clojure-ts--counted-child-p (node count-discarded)
"Return non-nil if NODE is a value child of its parent.
If COUNT-DISCARDED is non-nil, a form discarded with #_ counts too."
(or (equal "value" (treesit-node-field-name node))
(and count-discarded (equal "dis_expr" (treesit-node-type node)))))
The parent has to be a collection, like a list, a map or a reader
conditional. If COUNT-DISCARDED is non-nil, a form discarded with #_
counts too."
;; Getting the field walks the siblings before NODE, which adds up in a
;; form with many children, so it's told from the type where it can be.
(pcase (treesit-node-type node)
("dis_expr" count-discarded)
((or "comment" "meta_lit" "old_meta_lit" "ERROR") nil)
;; The prefix of a namespaced map comes right after its #, or after an
;; error in broken code, and only then is the field worth getting.
((or "kwd_lit" "auto_res_mark")
(or (not (member (treesit-node-type (treesit-node-prev-sibling node))
'("#" "ERROR")))
(equal "value" (treesit-node-field-name node))))
(_ (treesit-node-check node 'named))))

(defun clojure-ts--value-children (node)
"Return the value children of NODE.
Expand Down Expand Up @@ -1426,17 +1438,18 @@ changes."
clojure-ts-semantic-indent-rules))))
(cdr clojure-ts--semantic-indent-rules-cache))

(defun clojure-ts--value-position (node &optional count-discarded)
(defun clojure-ts--value-position (node &optional count-discarded limit)
"Return how many value children of NODE's parent come before NODE.

Only children in the value field count, so metadata, comments and the
parens don't: in (when ^boolean x y), y is at position 2, after the
symbol (0) and x (1). For a metadata node this is the position of the
value it belongs to. If COUNT-DISCARDED is non-nil, forms discarded
with #_ count too."
with #_ count too. If LIMIT is non-nil, stop counting once past it: the
result then only tells whether the position is greater than LIMIT."
(let ((pos 0)
(sibling (treesit-node-prev-sibling node)))
(while sibling
(while (and sibling (or (null limit) (<= pos limit)))
(when (clojure-ts--counted-child-p sibling count-discarded)
(setq pos (1+ pos)))
(setq sibling (treesit-node-prev-sibling sibling)))
Expand All @@ -1456,7 +1469,7 @@ whose body doesn't start on a line of its own is indented like a call
instead. Forms discarded with #_ count as values here, like in
`clojure-mode'."
(when-let* ((node (or node (treesit-node-first-child-for-pos parent bol)))
((> (clojure-ts--value-position node t) block)))
((> (clojure-ts--value-position node t block) block)))
(let ((body (clojure-ts--node-child-skip-metadata parent (1+ block) t)))
(or (null body)
(save-excursion
Expand Down Expand Up @@ -1588,23 +1601,27 @@ syntax tree and recursively attempts to find a rule, incrementally
increasing the CURRENT-DEPTH. If a rule is not found upon reaching the
root of the syntax tree, it returns nil. A rule is considered a match
only if the CURRENT-DEPTH matches the rule's required depth."
(let* ((idx (1- (clojure-ts--value-position node))))
(if-let* ((rule-set (clojure-ts--find-semantic-rules-for-node parent))
((not (equal rule-set :not-found))))
(seq-find (lambda (rule)
(pcase-let ((`(,rule-type ,rule-depth ,rule-idx) rule))
(if (equal rule-type :block)
(zerop current-depth)
(and (equal rule-depth current-depth)
(or (null rule-idx)
(equal rule-idx idx))))))
rule-set)
;; Let's go no more than 3 levels up to avoid performance degradation.
(when-let* (((< current-depth 3))
(new-parent (treesit-node-parent parent)))
(clojure-ts--find-semantic-rule parent
new-parent
(1+ current-depth))))))
(if-let* ((rule-set (clojure-ts--find-semantic-rules-for-node parent))
((not (equal rule-set :not-found))))
(seq-find (lambda (rule)
(pcase-let ((`(,rule-type ,rule-depth ,rule-idx) rule))
(if (equal rule-type :block)
(zerop current-depth)
(and (equal rule-depth current-depth)
(or (null rule-idx)
;; Only counted when a rule needs it, and only
;; as far as needed, as it takes longer the more
;; forms come before NODE.
(equal rule-idx
(1- (clojure-ts--value-position
node nil (1+ rule-idx)))))))))
rule-set)
;; Let's go no more than 3 levels up to avoid performance degradation.
(when-let* (((< current-depth 3))
(new-parent (treesit-node-parent parent)))
(clojure-ts--find-semantic-rule parent
new-parent
(1+ current-depth)))))

(defun clojure-ts--match-form-body (node parent bol)
"Match if NODE has to be indented as a form body.
Expand Down
9 changes: 8 additions & 1 deletion test/clojure-ts-mode-indentation-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -1475,4 +1475,11 @@ c)
(when-aligning-it "should support namespaced maps"
"#:hello {:world true
:foo \"bar\"
:some-very-long \"value\"}"))
:some-very-long \"value\"}")

(when-aligning-it "should support auto-resolved namespaced maps"
"#::{:world true
:foo \"bar\"}"

"#::alias{:a 1
:bbb 2}"))
Loading