Skip to content

Commit 8d8a1a7

Browse files
committed
[Fix #649] Preserve arglist metadata when adding arity
clojure-add-arity was leaving metadata annotations (^String, ^:keyword, ^{...}) stranded between the defn name and arity forms. Now metadata stays attached to its arglist inside the arity wrapper.
1 parent 7d38baf commit 8d8a1a7

3 files changed

Lines changed: 79 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
### Bugs fixed
1919

2020
* [#402](https://github.com/clojure-emacs/clojure-mode/issues/402): Font-lock protocol method docstrings with `font-lock-doc-face`.
21+
* [#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.
2122
* Fix typos in `clojure-mode-extra-font-locking`: `halt-when?` -> `halt-when`, `simple-indent?` -> `simple-ident?`.
2223
* Fix `doc` and `find-doc` misplaced under `clojure.core` instead of `clojure.repl` in extra font-locking.
2324

clojure-mode.el

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3225,6 +3225,22 @@ Assumes cursor is at beginning of function."
32253225
(insert "[")
32263226
(save-excursion (insert "])\n(" (match-string 0))))
32273227

3228+
(defun clojure--find-arglist-metadata-start (bracket-pos)
3229+
"Return the start of metadata annotations before BRACKET-POS.
3230+
If no metadata is found, return BRACKET-POS.
3231+
Handles ^Type, ^:keyword, ^{:key val}, and chains like ^:private ^String.
3232+
Relies on ^ having prefix syntax (\\=') in the Clojure syntax table,
3233+
which makes `backward-sexp' from `[' treat `^String [' as two sexps."
3234+
(save-excursion
3235+
(goto-char bracket-pos)
3236+
(let ((result bracket-pos))
3237+
(ignore-errors
3238+
(while (progn
3239+
(backward-sexp)
3240+
(eq (char-after) ?^))
3241+
(setq result (point))))
3242+
result)))
3243+
32283244
(defun clojure--add-arity-internal ()
32293245
"Add an arity to a function.
32303246
@@ -3242,14 +3258,31 @@ Assumes cursor is at beginning of function."
32423258
(save-excursion (insert "])\n(")))
32433259
((looking-back "\\[" 1) ;; single-arity fn
32443260
(let* ((same-line (= beg-line (line-number-at-pos)))
3245-
(new-arity-text (concat (when same-line "\n") "([")))
3261+
(bracket-pos (1- (point)))
3262+
(meta-start (clojure--find-arglist-metadata-start bracket-pos)))
32463263
(save-excursion
32473264
(goto-char end)
32483265
(insert ")"))
3249-
3250-
(re-search-backward " +\\[")
3251-
(replace-match new-arity-text)
3252-
(save-excursion (insert "])\n([")))))))
3266+
(if (< meta-start bracket-pos)
3267+
;; Has metadata before arglist — move it inside the arity
3268+
;; wrapper so it stays associated with the original arglist.
3269+
;; E.g. (defn foo ^String [x] ...) becomes:
3270+
;; (defn foo ([]) (^String [x] ...))
3271+
(let ((meta-text (replace-regexp-in-string
3272+
"[ \t\n\r]+" " "
3273+
(string-trim
3274+
(buffer-substring meta-start bracket-pos)))))
3275+
(goto-char meta-start)
3276+
(skip-chars-backward " \t\n")
3277+
(delete-region (point) (1+ bracket-pos))
3278+
(insert "\n([")
3279+
(save-excursion
3280+
(insert "])\n(" meta-text " [")))
3281+
;; No metadata — original behavior
3282+
(let ((new-arity-text (concat (when same-line "\n") "([")))
3283+
(re-search-backward " +\\[")
3284+
(replace-match new-arity-text)
3285+
(save-excursion (insert "])\n([")))))))))
32533286

32543287
;;;###autoload
32553288
(defun clojure-add-arity ()

test/clojure-mode-refactor-add-arity-test.el

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,47 @@
7272
body)"
7373

7474
"(defn foo
75-
^{:bla \"meta\"}
7675
([|])
77-
([arg]
76+
(^{:bla \"meta\"} [arg]
77+
body))"
78+
79+
(clojure-add-arity))
80+
81+
(when-refactoring-with-point-it "should handle a single-arity defn with ^Type metadata"
82+
"(defn string
83+
^String
84+
|[x]
85+
(str x))"
86+
87+
"(defn string
88+
([|])
89+
(^String [x]
90+
(str x)))"
91+
92+
(clojure-add-arity))
93+
94+
(when-refactoring-with-point-it "should handle a single-arity defn with ^:keyword metadata"
95+
"(defn fo|o
96+
^:private
97+
[arg]
98+
body)"
99+
100+
"(defn foo
101+
([|])
102+
(^:private [arg]
103+
body))"
104+
105+
(clojure-add-arity))
106+
107+
(when-refactoring-with-point-it "should handle a single-arity defn with multiple metadata"
108+
"(defn fo|o
109+
^:private ^String
110+
[arg]
111+
body)"
112+
113+
"(defn foo
114+
([|])
115+
(^:private ^String [arg]
78116
body))"
79117

80118
(clojure-add-arity))

0 commit comments

Comments
 (0)