diff --git a/CHANGELOG.md b/CHANGELOG.md index bba27d96..2ab24b70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,14 @@ ## master (unreleased) +### New features + +* [#556](https://github.com/clojure-emacs/clojure-mode/issues/556): `clojure-rename-ns-alias` picks up existing aliases for minibuffer completion. + ### Bugs fixed +* [#556](https://github.com/clojure-emacs/clojure-mode/issues/556): Fix renaming of ns aliases containing regex characters. +* [#555](https://github.com/clojure-emacs/clojure-mode/issues/555): Fix ns detection for ns forms with complex metadata. * [#550](https://github.com/clojure-emacs/clojure-mode/issues/550): Fix `outline-regexp` so `outline-insert-heading` behaves correctly. * [#551](https://github.com/clojure-emacs/clojure-mode/issues/551): Indent `clojure-align` region before aligning. * [#520](https://github.com/clojure-emacs/clojure-mode/issues/508): Fix allow `clojure-align-cond-forms` to recognize qualified forms. diff --git a/clojure-mode.el b/clojure-mode.el index 3d7e0be0..c9a419f4 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -2683,19 +2683,35 @@ lists up." (insert sexp) (clojure--replace-sexps-with-bindings-and-indent))) +(defun clojure-collect-ns-aliases (ns-form) + "Collect all namespace aliases in NS-FORM." + (with-temp-buffer + (delay-mode-hooks + (clojure-mode) + (insert ns-form) + (goto-char (point-min)) + (let ((end (point-max)) + (rgx (rx ":as" (+ space) + (group-n 1 (+ (not (in " ,]\n")))))) + (res ())) + (while (re-search-forward rgx end 'noerror) + (unless (or (clojure--in-string-p) (clojure--in-comment-p)) + (push (match-string-no-properties 1) res))) + res)))) + (defun clojure--rename-ns-alias-internal (current-alias new-alias) "Rename a namespace alias CURRENT-ALIAS to NEW-ALIAS." (clojure--find-ns-in-direction 'backward) - (let ((rgx (concat ":as +" current-alias)) + (let ((rgx (concat ":as +" (regexp-quote current-alias) "\\_>")) (bound (save-excursion (forward-list 1) (point)))) (when (search-forward-regexp rgx bound t) (replace-match (concat ":as " new-alias)) (save-excursion - (while (re-search-forward (concat current-alias "/") nil t) + (while (re-search-forward (concat (regexp-quote current-alias) "/") nil t) (when (not (nth 3 (syntax-ppss))) (replace-match (concat new-alias "/"))))) (save-excursion - (while (re-search-forward (concat "#::" current-alias "{") nil t) + (while (re-search-forward (concat "#::" (regexp-quote current-alias) "{") nil t) (replace-match (concat "#::" new-alias "{")))) (message "Successfully renamed alias '%s' to '%s'" current-alias new-alias)))) @@ -2746,15 +2762,17 @@ With a numeric prefix argument the let is introduced N lists up." (defun clojure-rename-ns-alias () "Rename a namespace alias." (interactive) - (let ((current-alias (read-from-minibuffer "Current alias: "))) - (save-excursion - (clojure--find-ns-in-direction 'backward) - (let ((rgx (concat ":as +" current-alias)) - (bound (save-excursion (forward-list 1) (point)))) - (if (save-excursion (search-forward-regexp rgx bound t)) - (let ((new-alias (read-from-minibuffer "New alias: "))) - (clojure--rename-ns-alias-internal current-alias new-alias)) - (message "Cannot find namespace alias: '%s'" current-alias)))))) + (save-excursion + (clojure--find-ns-in-direction 'backward) + (let* ((current-alias (completing-read "Current alias: " + (clojure-collect-ns-aliases + (thing-at-point 'list)))) + (rgx (concat ":as +" (regexp-quote current-alias) "\\_>")) + (bound (save-excursion (forward-list 1) (point)))) + (if (save-excursion (search-forward-regexp rgx bound t)) + (let ((new-alias (read-from-minibuffer "New alias: "))) + (clojure--rename-ns-alias-internal current-alias new-alias)) + (message "Cannot find namespace alias: '%s'" current-alias))))) (defun clojure--add-arity-defprotocol-internal () "Add an arity to a signature inside a defprotocol. diff --git a/test/clojure-mode-refactor-rename-ns-alias-test.el b/test/clojure-mode-refactor-rename-ns-alias-test.el index aba75b32..d5d99cb9 100644 --- a/test/clojure-mode-refactor-rename-ns-alias-test.el +++ b/test/clojure-mode-refactor-rename-ns-alias-test.el @@ -43,6 +43,23 @@ (+ (foo/a 1) (b 2))" (clojure--rename-ns-alias-internal "lib" "foo")) + (when-refactoring-it "should handle multiple aliases with common prefixes" + + "(ns foo + (:require [clojure.string :as string] + [clojure.spec.alpha :as s] + [clojure.java.shell :as shell])) + +(s/def ::abc string/blank?) +" + "(ns foo + (:require [clojure.string :as string] + [clojure.spec.alpha :as spec] + [clojure.java.shell :as shell])) + +(spec/def ::abc string/blank?) +" + (clojure--rename-ns-alias-internal "s" "spec")) (when-refactoring-it "should handle ns declarations with missing as" "(ns cljr.core @@ -95,8 +112,34 @@ ;; TODO refactor using new-lib/foo (+ (new-lib/a 1) (b 2))" - (clojure--rename-ns-alias-internal "lib" "new-lib"))) + (clojure--rename-ns-alias-internal "lib" "new-lib")) + + (when-refactoring-it "should escape regex characters" + "(ns test.ns + (:require [my.math.subtraction :as math.-] + [my.math.multiplication :as math.*])) + +(math.*/operator 1 (math.-/subtract 2 3))" + "(ns test.ns + (:require [my.math.subtraction :as math.-] + [my.math.multiplication :as m*])) + +(m*/operator 1 (math.-/subtract 2 3))" + (clojure--rename-ns-alias-internal "math.*" "m*")) + + (it "should offer completions" + (expect + (clojure-collect-ns-aliases + "(ns test.ns + (:require [my.math.subtraction :as math.-] + [my.math.multiplication :as math.*] + [clojure.spec.alpha :as s] + ;; [clojure.spec.alpha2 :as s2] + [symbols :as abc123.-$#.%*+!@])) + +(math.*/operator 1 (math.-/subtract 2 3))") + :to-equal '("abc123.-$#.%*+!@" "s" "math.*" "math.-")))) - (provide 'clojure-mode-refactor-rename-ns-alias-test) +(provide 'clojure-mode-refactor-rename-ns-alias-test) ;;; clojure-mode-refactor-rename-ns-alias-test.el ends here