[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/clojure-ts-mode 7497ae9c41: [#61] Fix some issues with ind
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/clojure-ts-mode 7497ae9c41: [#61] Fix some issues with indentation for items with metadata |
Date: |
Thu, 3 Apr 2025 12:59:51 -0400 (EDT) |
branch: elpa/clojure-ts-mode
commit 7497ae9c41a7ad264baf9b08f6590726f75f227c
Author: Roman Rudakov <rrudakov@fastmail.com>
Commit: Bozhidar Batsov <bozhidar@batsov.dev>
[#61] Fix some issues with indentation for items with metadata
- Collection elements (except of lists) are properly indented.
- Body of special forms when the entire form has metadata is properly
indented.
- Additionally fix syntax highlighting of special forms when the entire
form has metadata.
---
CHANGELOG.md | 2 ++
README.md | 5 +++
clojure-ts-mode.el | 48 +++++++++++++++++++++++++--
test/clojure-ts-mode-font-lock-test.el | 7 +++-
test/clojure-ts-mode-indentation-test.el | 27 ++++++++++++++-
test/samples/indentation.clj | 56 ++++++++++++++++++++++++++++++++
6 files changed, 140 insertions(+), 5 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a4cf9685fd..8d7953fd4f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,8 @@
line.
- Improve semantic indentation rules to be more consistent with cljfmt.
- Introduce `clojure-ts-semantic-indent-rules` customization option.
+- [#61](https://github.com/clojure-emacs/clojure-ts-mode/issues/61): Fix issue
with indentation of collection items with metadata.
+- Proper syntax highlighting for expressions with metadata.
## 0.2.3 (2025-03-04)
diff --git a/README.md b/README.md
index 4fb47b2b32..b1aa3cf114 100644
--- a/README.md
+++ b/README.md
@@ -291,6 +291,11 @@ and `clojure-mode` (this is very helpful when dealing with
`derived-mode-p` chec
- Navigation by sexp/lists might work differently on Emacs versions lower
than 31. Starting with version 31, Emacs uses TreeSitter 'things' settings,
if
available, to rebind some commands.
+- The indentation of list elements with metadata is inconsistent with other
+ collections. This inconsistency stems from the grammar's interpretation of
+ nearly every definition or function call as a list. Therefore, modifying the
+ indentation for list elements would adversely affect the indentation of
+ numerous other forms.
## Frequently Asked Questions
diff --git a/clojure-ts-mode.el b/clojure-ts-mode.el
index 19fa6052d4..d0fa310680 100644
--- a/clojure-ts-mode.el
+++ b/clojure-ts-mode.el
@@ -404,9 +404,9 @@ with the markdown_inline grammar."
;; `clojure.core'.
:feature 'builtin
:language 'clojure
- `(((list_lit meta: _ :? :anchor (sym_lit !namespace name: (sym_name)
@font-lock-keyword-face))
+ `(((list_lit meta: _ :* :anchor (sym_lit !namespace name: (sym_name)
@font-lock-keyword-face))
(:match ,clojure-ts--builtin-symbol-regexp @font-lock-keyword-face))
- ((list_lit meta: _ :? :anchor
+ ((list_lit meta: _ :* :anchor
(sym_lit namespace: ((sym_ns) @ns
(:equal "clojure.core" @ns))
name: (sym_name) @font-lock-keyword-face))
@@ -608,6 +608,12 @@ This does not include the NODE's namespace."
(let ((first-child (treesit-node-child node 0 t)))
(treesit-node-child node (if (clojure-ts--metadata-node-p first-child) (1+
n) n) t)))
+(defun clojure-ts--node-with-metadata-parent (node)
+ "Return parent for NODE only if NODE has metadata, otherwise returns nil."
+ (when-let* ((prev-sibling (treesit-node-prev-sibling node))
+ ((clojure-ts--metadata-node-p prev-sibling)))
+ (treesit-node-parent (treesit-node-parent node))))
+
(defun clojure-ts--symbol-matches-p (symbol-regexp node)
"Return non-nil if NODE is a symbol that matches SYMBOL-REGEXP."
(and (clojure-ts--symbol-node-p node)
@@ -977,6 +983,30 @@ forms like deftype, defrecord, reify, proxy, etc."
(and prev-sibling
(clojure-ts--metadata-node-p prev-sibling))))
+(defun clojure-ts--anchor-parent-skip-metadata (_node parent _bol)
+ "Anchor function that returns position of PARENT start for NODE.
+
+If PARENT has optional metadata we skip it and return starting position
+of the first child's opening paren.
+
+NOTE: This anchor is used to fix indentation issue for forms with type
+hints."
+ (let ((first-child (treesit-node-child parent 0 t)))
+ (if (clojure-ts--metadata-node-p first-child)
+ ;; We don't need named node here
+ (treesit-node-start (treesit-node-child parent 1))
+ (treesit-node-start parent))))
+
+(defun clojure-ts--match-collection-item-with-metadata (node-type)
+ "Returns a matcher for a collection item with metadata by NODE-TYPE.
+
+The returned matcher accepts NODE, PARENT and BOL and returns true only
+if NODE has metadata and its parent has type NODE-TYPE."
+ (lambda (node _parent _bol)
+ (string-equal node-type
+ (treesit-node-type
+ (clojure-ts--node-with-metadata-parent node)))))
+
(defun clojure-ts--semantic-indent-rules ()
"Return a list of indentation rules for `treesit-simple-indent-rules'."
`((clojure
@@ -984,11 +1014,23 @@ forms like deftype, defrecord, reify, proxy, etc."
(clojure-ts--match-docstring parent 0)
;; https://guide.clojure.style/#body-indentation
(clojure-ts--match-method-body parent 2)
- (clojure-ts--match-form-body parent 2)
+ (clojure-ts--match-form-body clojure-ts--anchor-parent-skip-metadata 2)
;; https://guide.clojure.style/#threading-macros-alignment
(clojure-ts--match-threading-macro-arg prev-sibling 0)
;; https://guide.clojure.style/#vertically-align-fn-args
(clojure-ts--match-function-call-arg (nth-sibling 2 nil) 0)
+ ;; Collections items with metadata.
+ ;;
+ ;; This should be before `clojure-ts--match-with-metadata', otherwise they
+ ;; will never be matched.
+ (,(clojure-ts--match-collection-item-with-metadata "vec_lit")
grand-parent 1)
+ (,(clojure-ts--match-collection-item-with-metadata "map_lit")
grand-parent 1)
+ (,(clojure-ts--match-collection-item-with-metadata "set_lit")
grand-parent 2)
+ ;;
+ ;; If we enable this rule for lists, it will break many things.
+ ;; (,(clojure-ts--match-collection-item-with-metadata "list_lit")
grand-parent 1)
+ ;;
+ ;; All other forms with metadata.
(clojure-ts--match-with-metadata parent 0)
;; Literal Sequences
((parent-is "list_lit") parent 1) ;;
https://guide.clojure.style/#one-space-indent
diff --git a/test/clojure-ts-mode-font-lock-test.el
b/test/clojure-ts-mode-font-lock-test.el
index c3cafd53f4..b6ea46cdce 100644
--- a/test/clojure-ts-mode-font-lock-test.el
+++ b/test/clojure-ts-mode-font-lock-test.el
@@ -162,4 +162,9 @@ DESCRIPTION is the description of the spec."
(when-fontifying-it "non-built-ins-with-same-name"
("(h/for query {})"
(2 2 font-lock-type-face)
- (4 6 nil))))
+ (4 6 nil)))
+
+ (when-fontifying-it "special-forms-with-metadata"
+ ("^long (if true 1 2)"
+ (2 5 font-lock-type-face)
+ (8 9 font-lock-keyword-face))))
diff --git a/test/clojure-ts-mode-indentation-test.el
b/test/clojure-ts-mode-indentation-test.el
index 23a432be8a..8375f802b6 100644
--- a/test/clojure-ts-mode-indentation-test.el
+++ b/test/clojure-ts-mode-indentation-test.el
@@ -239,4 +239,29 @@ DESCRIPTION is a string with the description of the spec."
(= x y)
2 3
4 5
- 6 6)"))))
+ 6 6)")))
+
+(it "should indent collections elements with metadata correctly"
+ "
+(def x
+ [a b [c ^:foo
+ d
+ e]])"
+
+ "
+#{x
+ y ^:foo
+ z}"
+
+ "
+{:hello ^:foo
+ \"world\"
+ :foo
+ \"bar\"}")
+
+(it "should indent body of special forms correctly considering metadata"
+ "
+(let [result ^long
+ (if true
+ 1
+ 2)])"))
diff --git a/test/samples/indentation.clj b/test/samples/indentation.clj
index 78a7aa6023..79d7809015 100644
--- a/test/samples/indentation.clj
+++ b/test/samples/indentation.clj
@@ -210,3 +210,59 @@
(close
[this]
(is properly indented)))
+
+(def x
+ [a b [c ^:foo
+ d
+ e]])
+
+#{x
+ y ^:foo
+ z}
+
+{:hello ^:foo
+ "world"
+ :foo
+ "bar"}
+
+;; NOTE: List elements with metadata are not indented correctly.
+'(one
+ two ^:foo
+ three)
+
+^{:nextjournal.clerk/visibility {:code :hide}}
+(defn actual
+ [args])
+
+(def ^:private hello
+ "World")
+
+;; A few examples from clojure core.
+
+;; NOTE: This one is not indented correctly, I'm keeping it here as a reminder
+;; to fix it later.
+(defonce ^:dynamic
+ ^{:private true
+ :doc "A ref to a sorted set of symbols representing loaded libs"}
+ *loaded-libs* (ref (sorted-set)))
+
+(defn index-of
+ "Return index of value (string or char) in s, optionally searching
+ forward from from-index. Return nil if value not found."
+ {:added "1.8"}
+ ([^CharSequence s value]
+ (let [result ^long
+ (if (instance? Character value)
+ (.indexOf (.toString s) ^int (.charValue ^Character value))
+ (.indexOf (.toString s) ^String value))]
+ (if (= result -1)
+ nil
+ result)))
+ ([^CharSequence s value ^long from-index]
+ (let [result ^long
+ (if (instance? Character value)
+ (.indexOf (.toString s) ^int (.charValue ^Character value)
(unchecked-int from-index))
+ (.indexOf (.toString s) ^String value (unchecked-int from-index)))]
+ (if (= result -1)
+ nil
+ result))))
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [nongnu] elpa/clojure-ts-mode 7497ae9c41: [#61] Fix some issues with indentation for items with metadata,
ELPA Syncer <=