Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
43 changes: 38 additions & 5 deletions clojure-mode.el
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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 ()
Expand Down
42 changes: 40 additions & 2 deletions test/clojure-mode-refactor-add-arity-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down