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