emacs-elpa-diffs
[Top][All Lists]
Advanced

[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))))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]