branch: elpa/clojure-mode
commit 9c1993f3ea0650620c8e329cb7f15742dde040e6
Merge: 7d38bafd26 8d8a1a7739
Author: Bozhidar Batsov <[email protected]>
Commit: GitHub <[email protected]>

    Merge pull request #693 from clojure-emacs/fix-add-arity-metadata
    
    Fix clojure-add-arity severing arglist metadata
---
 CHANGELOG.md                                 |  1 +
 clojure-mode.el                              | 43 ++++++++++++++++++++++++----
 test/clojure-mode-refactor-add-arity-test.el | 42 +++++++++++++++++++++++++--
 3 files changed, 79 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index daf08b3b07..aade064273 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@
 ### Bugs fixed
 
 * [#402](https://github.com/clojure-emacs/clojure-mode/issues/402): Font-lock 
protocol method docstrings with `font-lock-doc-face`.
+* [#649](https://github.com/clojure-emacs/clojure-mode/issues/649): Fix 
`clojure-add-arity` severing arglist metadata (`^String`, `^:keyword`, 
`^{...}`) when converting single-arity to multi-arity.
 * Fix typos in `clojure-mode-extra-font-locking`: `halt-when?` -> `halt-when`, 
`simple-indent?` -> `simple-ident?`.
 * Fix `doc` and `find-doc` misplaced under `clojure.core` instead of 
`clojure.repl` in extra font-locking.
 
diff --git a/clojure-mode.el b/clojure-mode.el
index 7ff2a16d3f..64137bc1e9 100644
--- a/clojure-mode.el
+++ b/clojure-mode.el
@@ -3225,6 +3225,22 @@ Assumes cursor is at beginning of function."
   (insert "[")
   (save-excursion (insert "])\n(" (match-string 0))))
 
+(defun clojure--find-arglist-metadata-start (bracket-pos)
+  "Return the start of metadata annotations before BRACKET-POS.
+If no metadata is found, return BRACKET-POS.
+Handles ^Type, ^:keyword, ^{:key val}, and chains like ^:private ^String.
+Relies on ^ having prefix syntax (\\=') in the Clojure syntax table,
+which makes `backward-sexp' from `[' treat `^String [' as two sexps."
+  (save-excursion
+    (goto-char bracket-pos)
+    (let ((result bracket-pos))
+      (ignore-errors
+        (while (progn
+                 (backward-sexp)
+                 (eq (char-after) ?^))
+          (setq result (point))))
+      result)))
+
 (defun clojure--add-arity-internal ()
   "Add an arity to a function.
 
@@ -3242,14 +3258,31 @@ Assumes cursor is at beginning of function."
       (save-excursion (insert "])\n(")))
      ((looking-back "\\[" 1)  ;; single-arity fn
       (let* ((same-line (= beg-line (line-number-at-pos)))
-             (new-arity-text (concat (when same-line "\n") "([")))
+             (bracket-pos (1- (point)))
+             (meta-start (clojure--find-arglist-metadata-start bracket-pos)))
         (save-excursion
           (goto-char end)
           (insert ")"))
-
-        (re-search-backward " +\\[")
-        (replace-match new-arity-text)
-        (save-excursion (insert "])\n([")))))))
+        (if (< meta-start bracket-pos)
+            ;; Has metadata before arglist — move it inside the arity
+            ;; wrapper so it stays associated with the original arglist.
+            ;; E.g. (defn foo ^String [x] ...) becomes:
+            ;;   (defn foo ([]) (^String [x] ...))
+            (let ((meta-text (replace-regexp-in-string
+                              "[ \t\n\r]+" " "
+                              (string-trim
+                               (buffer-substring meta-start bracket-pos)))))
+              (goto-char meta-start)
+              (skip-chars-backward " \t\n")
+              (delete-region (point) (1+ bracket-pos))
+              (insert "\n([")
+              (save-excursion
+                (insert "])\n(" meta-text " [")))
+          ;; No metadata — original behavior
+          (let ((new-arity-text (concat (when same-line "\n") "([")))
+            (re-search-backward " +\\[")
+            (replace-match new-arity-text)
+            (save-excursion (insert "])\n([")))))))))
 
 ;;;###autoload
 (defun clojure-add-arity ()
diff --git a/test/clojure-mode-refactor-add-arity-test.el 
b/test/clojure-mode-refactor-add-arity-test.el
index 5f1c5fb9f4..5982482695 100644
--- a/test/clojure-mode-refactor-add-arity-test.el
+++ b/test/clojure-mode-refactor-add-arity-test.el
@@ -72,9 +72,47 @@
   body)"
 
     "(defn foo
-  ^{:bla \"meta\"}
   ([|])
-  ([arg]
+  (^{:bla \"meta\"} [arg]
+   body))"
+
+    (clojure-add-arity))
+
+  (when-refactoring-with-point-it "should handle a single-arity defn with 
^Type metadata"
+    "(defn string
+  ^String
+  |[x]
+  (str x))"
+
+    "(defn string
+  ([|])
+  (^String [x]
+   (str x)))"
+
+    (clojure-add-arity))
+
+  (when-refactoring-with-point-it "should handle a single-arity defn with 
^:keyword metadata"
+    "(defn fo|o
+  ^:private
+  [arg]
+  body)"
+
+    "(defn foo
+  ([|])
+  (^:private [arg]
+   body))"
+
+    (clojure-add-arity))
+
+  (when-refactoring-with-point-it "should handle a single-arity defn with 
multiple metadata"
+    "(defn fo|o
+  ^:private ^String
+  [arg]
+  body)"
+
+    "(defn foo
+  ([|])
+  (^:private ^String [arg]
    body))"
 
     (clojure-add-arity))

Reply via email to