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 @@ -26,6 +26,7 @@
- [#161](https://github.com/clojure-emacs/clojure-ts-mode/pull/161): Fix converting collections with metadata: a quoted list with metadata after the quote (`'^:foo (1 2)`) failed to convert, and converting to a quoted list put the quote after the metadata.
- [#153](https://github.com/clojure-emacs/clojure-ts-mode/pull/153): Align threading macro arguments with the first argument instead of the previous one, which went wrong after a line with several forms or a trailing comment.
- [#154](https://github.com/clojure-emacs/clojure-ts-mode/pull/154): Fix semantic indentation of forms with metadata or a comment before an argument or the body, like `(doto ^Foo`, `(let ;; bindings` or `(comment ;; scratch`.
- [#155](https://github.com/clojure-emacs/clojure-ts-mode/pull/155): Fix indentation of a form on the line after its reader prefix (`'`, `` ` ``, `~`, `~@`, `@`, `#'`, `#=`), which no rule matched.

## 0.6.0 (2025-12-02)

Expand Down
35 changes: 34 additions & 1 deletion clojure-ts-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,31 @@ The possible values for this variable are
(const :tag "Simple fixed indentation rules." fixed))
:package-version '(clojure-ts-mode . "0.2.0"))

(defconst clojure-ts--reader-prefix-node-regexp
(rx bol (or "quoting_lit" "syn_quoting_lit" "unquoting_lit" "unquote_splicing_lit"
"derefing_lit" "var_quoting_lit" "evaling_lit")
eol)
"Node types of a form behind a reader prefix.
These are quote, syntax quote, unquote, unquote-splicing, deref, var
quote and read-eval.")

(defun clojure-ts--prefixed-form-indent (prefixed)
"Return (ANCHOR . OFFSET) for PREFIXED as if it started a line.

PREFIXED is a node like @form. When the form itself starts
on the line after the prefix, it is indented the way PREFIXED would be."
(treesit-simple-indent prefixed
(treesit-node-parent prefixed)
(treesit-node-start prefixed)))

(defun clojure-ts--anchor-prefixed-form (_node parent _bol)
"Return the anchor of PARENT, a prefixed form, as if it started a line."
(car (clojure-ts--prefixed-form-indent parent)))

(defun clojure-ts--offset-prefixed-form (_node parent _bol)
"Return the offset of PARENT, a prefixed form, as if it started a line."
(or (cdr (clojure-ts--prefixed-form-indent parent)) 0))

(defvar clojure-ts--fixed-indent-rules
;; This is in contrast to semantic
;; fixed-indent-rules come from https://tonsky.me/blog/clojurefmt/
Expand All @@ -1234,7 +1259,11 @@ The possible values for this variable are
((parent-is "^map_lit$") parent 1)
((parent-is "^list_lit$") parent 1)
((parent-is "^set_lit$") parent 2)
((parent-is "^ns_map_lit$") (nth-sibling 2) 1))))
((parent-is "^ns_map_lit$") (nth-sibling 2) 1)
;; A form on the line after its reader prefix, e.g. @ at the end of a line.
((parent-is ,clojure-ts--reader-prefix-node-regexp)
clojure-ts--anchor-prefixed-form
clojure-ts--offset-prefixed-form))))

(defvar clojure-ts--semantic-indent-rules-defaults
'(("alt!" . ((:block 0)))
Expand Down Expand Up @@ -1623,6 +1652,10 @@ used."
((parent-is "^read_cond_lit$") parent 3)
((parent-is "^tagged_or_ctor_lit$") parent 0)
((parent-is "^ns_map_lit$") (nth-sibling 2) 1)
;; A form on the line after its reader prefix, e.g. @ at the end of a line.
((parent-is ,clojure-ts--reader-prefix-node-regexp)
clojure-ts--anchor-prefixed-form
clojure-ts--offset-prefixed-form)
;; https://guide.clojure.style/#body-indentation
(clojure-ts--match-form-body clojure-ts--anchor-parent-opening-paren 2)
;; https://guide.clojure.style/#vertically-align-fn-args
Expand Down
39 changes: 39 additions & 0 deletions test/clojure-ts-mode-indentation-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,31 @@ DESCRIPTION is a string with the description of the spec."
x)]
(f 1))")

(it "should indent a form on the line after its reader prefix"
;; No rule used to match these lines, so they kept whatever indentation
;; they had. Start from no indentation at all.
(dolist (expected '("
(def table
['
[identity int? pos-int?]
[0 true false]])"
"
`(let [y# ~
x]
y#)"
"
(swap! state assoc :n @
(:counter state))"
"
{:get #'
handle-get}"
"
(def q '
(a b c))"))
(with-clojure-ts-buffer (replace-regexp-in-string "\n +" "\n" expected)
(indent-region (point-min) (point-max))
(expect (buffer-string) :to-equal expected))))

(it "should pick up dynamic indentation rules from clojure-ts-get-indent-function"
(with-clojure-ts-buffer "
(defmacro my-with-in-str
Expand Down Expand Up @@ -775,6 +800,20 @@ DESCRIPTION is a string with the description of the spec."
(java.util Date
UUID)))")

(it "should indent a form on the line after its reader prefix in fixed style"
(dolist (expected '("
(swap! state assoc :n @
(:counter state))"
"
['
[a b]]"))
(with-temp-buffer
(let ((clojure-ts-indent-style 'fixed))
(clojure-ts-mode)
(insert (replace-regexp-in-string "\n +" "\n" expected))
(indent-region (point-min) (point-max))
(expect (buffer-string) :to-equal expected)))))

(when-indenting-fixed-it "should indent namespaced maps correctly"
"
#:foo{:bar 1
Expand Down
Loading