From 8affea4d18eb606fa3dfbaa7fc3ea9b20b79fc54 Mon Sep 17 00:00:00 2001 From: yuhan0 Date: Tue, 20 Apr 2021 19:30:54 +0800 Subject: [PATCH 1/5] Rename ns aliases in region --- clojure-mode.el | 115 ++++++++++++++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 44 deletions(-) diff --git a/clojure-mode.el b/clojure-mode.el index ee8c56ba..eae2f43a 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -2670,38 +2670,6 @@ 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 +" (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 (regexp-quote current-alias) "/") nil t) - (when (not (nth 3 (syntax-ppss))) - (replace-match (concat new-alias "/"))))) - (save-excursion - (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)))) - ;;;###autoload (defun clojure-let-backward-slurp-sexp (&optional n) "Slurp the s-expression before the let form into the let form. @@ -2745,21 +2713,80 @@ With a numeric prefix argument the let is introduced N lists up." (interactive) (clojure--move-to-let-internal (read-from-minibuffer "Name of bound symbol: "))) + +;;; Renaming ns aliases + +(defun clojure--alias-usage-regexp (alias) + "Regexp for matching usages of ALIAS in qualified symbols, keywords and maps. +When nil, match all namespace usages. +The first match-group is the alias." + (let ((alias (if alias (regexp-quote alias) clojure--sym-regexp))) + (concat "#::\\(?1:" alias "\\)[ ,\r\n\t]*{" + "\\|" + "\\(?1:" alias "\\)/"))) + +(defun clojure--rename-ns-alias-usages (current-alias new-alias beg end) + "Rename all usages of CURRENT-ALIAS in region BEG to END with NEW-ALIAS." + (let ((rgx (clojure--alias-usage-regexp current-alias))) + (save-excursion + (goto-char end) + (setq end (point-marker)) + (goto-char beg) + (while (re-search-forward rgx end 'noerror) + (when (not (clojure--in-string-p)) ;; replace in comments, but not strings + (goto-char (match-beginning 1)) + (delete-region (point) (match-end 1)) + (insert new-alias)))))) + +(defun clojure--collect-ns-aliases (beg end ns-form-p) + "Collect all aliases between BEG and END. +When NS-FORM-P is non-nil, treat the region as a ns form +and pick up aliases from [... :as alias] forms, +otherwise pick up alias usages from keywords / symbols." + (let ((res ())) + (save-excursion + (let ((rgx (if ns-form-p + (rx ":as" (+ space) + (group-n 1 (+ (not (in " ,]\n"))))) + (clojure--alias-usage-regexp nil)))) + (goto-char beg) + (while (re-search-forward rgx end 'noerror) + (unless (or (clojure--in-string-p) (clojure--in-comment-p)) + (cl-pushnew (match-string-no-properties 1) res + :test #'equal))) + (reverse res))))) + +(defun clojure--rename-ns-alias-internal (current-alias new-alias) + "Rename a namespace alias CURRENT-ALIAS to NEW-ALIAS. +Assume point is at the start of ns form." + (clojure--find-ns-in-direction 'backward) + (let ((rgx (concat ":as +" (regexp-quote current-alias) "\\_>")) + (bound (save-excursion (forward-list 1) (point-marker)))) + (when (search-forward-regexp rgx bound t) + (replace-match (concat ":as " new-alias)) + (clojure--rename-ns-alias-usages current-alias new-alias bound (point-max))))) + ;;;###autoload (defun clojure-rename-ns-alias () - "Rename a namespace alias." + "Rename a namespace alias. +If a region is active, only pick up and rename aliases within the region." (interactive) - (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))))) + (if (use-region-p) + (let* ((beg (region-beginning)) + (end (region-end)) + (current-alias (completing-read "Current alias: " + (clojure--collect-ns-aliases + beg end nil))) + (new-alias (read-from-minibuffer (format "Replace %s with: " current-alias)))) + (clojure--rename-ns-alias-usages current-alias new-alias beg end)) + (save-excursion + (clojure--find-ns-in-direction 'backward) + (let* ((bounds (bounds-of-thing-at-point 'list)) + (current-alias (completing-read "Current alias: " + (clojure--collect-ns-aliases + (car bounds) (cdr bounds) t))) + (new-alias (read-from-minibuffer (format "Replace %s with: " current-alias)))) + (clojure--rename-ns-alias-internal current-alias new-alias))))) (defun clojure--add-arity-defprotocol-internal () "Add an arity to a signature inside a defprotocol. From b3a1e23f3c44f73aa77231839b272c928b43359e Mon Sep 17 00:00:00 2001 From: yuhan0 Date: Thu, 22 Apr 2021 05:11:20 +0800 Subject: [PATCH 2/5] Update tests --- ...jure-mode-refactor-rename-ns-alias-test.el | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/test/clojure-mode-refactor-rename-ns-alias-test.el b/test/clojure-mode-refactor-rename-ns-alias-test.el index d5d99cb9..37fe90c6 100644 --- a/test/clojure-mode-refactor-rename-ns-alias-test.el +++ b/test/clojure-mode-refactor-rename-ns-alias-test.el @@ -127,18 +127,48 @@ (m*/operator 1 (math.-/subtract 2 3))" (clojure--rename-ns-alias-internal "math.*" "m*")) - (it "should offer completions" + (when-refactoring-it "should replace aliases in region" + "(str/join []) + +(s/with-gen #(string/includes? % \"gen/nope\") + #(gen/fmap (fn [[s1 s2]] (str s1 \"hello\" s2)) + (gen/tuple (gen/string-alphanumeric) (gen/string-alphanumeric)))) + +(gen/different-library)" + "(string/join []) + +(s/with-gen #(string/includes? % \"gen/nope\") + #(s.gen/fmap (fn [[s1 s2]] (str s1 \"hello\" s2)) + (s.gen/tuple (s.gen/string-alphanumeric) (s.gen/string-alphanumeric)))) + +(gen/different-library)" + + (clojure--rename-ns-alias-usages "str" "string" (point-min) 13) + (clojure--rename-ns-alias-usages "gen" "s.gen" (point-min) (- (point-max) 23))) + + (it "should offer completions for ns forms" (expect - (clojure-collect-ns-aliases - "(ns test.ns + (with-clojure-buffer + "(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.-")))) +(math.*/operator 1 (math.-/subtract 2 3))" + (clojure--collect-ns-aliases (point-min) (point-max) 'ns-form)) + :to-equal '("math.-" "math.*" "s" "abc123.-$#.%*+!@"))) + + (it "should offer completions for usages in region" + (expect + (with-clojure-buffer + "(s/with-gen #(string/includes? % \"hello\") + #(gen/fmap (fn [[s1 s2]] (str s1 \"hello\" s2)) + (gen/tuple (gen/string-alphanumeric) (gen/string-alphanumeric))))" + (clojure--collect-ns-aliases (point-min) (point-max) nil)) + :to-equal '("s" "string" "gen")))) + (provide 'clojure-mode-refactor-rename-ns-alias-test) From 9bb3b678a1982ade678d8cca9896c12a88d92008 Mon Sep 17 00:00:00 2001 From: yuhan0 Date: Sun, 2 May 2021 14:24:50 +0800 Subject: [PATCH 3/5] fix regexp --- clojure-mode.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clojure-mode.el b/clojure-mode.el index eae2f43a..c6b6af18 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -2723,7 +2723,7 @@ The first match-group is the alias." (let ((alias (if alias (regexp-quote alias) clojure--sym-regexp))) (concat "#::\\(?1:" alias "\\)[ ,\r\n\t]*{" "\\|" - "\\(?1:" alias "\\)/"))) + "\\_<\\(?1:" alias "\\)/"))) (defun clojure--rename-ns-alias-usages (current-alias new-alias beg end) "Rename all usages of CURRENT-ALIAS in region BEG to END with NEW-ALIAS." From c72a2bffe7e8f2c38f92a529518632288a4c413b Mon Sep 17 00:00:00 2001 From: yuhan0 Date: Sun, 2 May 2021 14:31:20 +0800 Subject: [PATCH 4/5] Rename multiple aliases in loop --- clojure-mode.el | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/clojure-mode.el b/clojure-mode.el index c6b6af18..ba17bf6b 100644 --- a/clojure-mode.el +++ b/clojure-mode.el @@ -2728,7 +2728,7 @@ The first match-group is the alias." (defun clojure--rename-ns-alias-usages (current-alias new-alias beg end) "Rename all usages of CURRENT-ALIAS in region BEG to END with NEW-ALIAS." (let ((rgx (clojure--alias-usage-regexp current-alias))) - (save-excursion + (save-mark-and-excursion (goto-char end) (setq end (point-marker)) (goto-char beg) @@ -2772,13 +2772,17 @@ Assume point is at the start of ns form." If a region is active, only pick up and rename aliases within the region." (interactive) (if (use-region-p) - (let* ((beg (region-beginning)) - (end (region-end)) - (current-alias (completing-read "Current alias: " - (clojure--collect-ns-aliases - beg end nil))) - (new-alias (read-from-minibuffer (format "Replace %s with: " current-alias)))) - (clojure--rename-ns-alias-usages current-alias new-alias beg end)) + (let ((beg (region-beginning)) + (end (copy-marker (region-end))) + current-alias new-alias) + ;; while loop for renaming multiple aliases in the region. + ;; C-g or leave blank to break out of the loop + (while (not (string-empty-p + (setq current-alias + (completing-read "Current alias: " + (clojure--collect-ns-aliases beg end nil))))) + (setq new-alias (read-from-minibuffer (format "Replace %s with: " current-alias))) + (clojure--rename-ns-alias-usages current-alias new-alias beg end))) (save-excursion (clojure--find-ns-in-direction 'backward) (let* ((bounds (bounds-of-thing-at-point 'list)) From 71eb80b105698952817d78934001a8a5fd79ac5b Mon Sep 17 00:00:00 2001 From: yuhan0 Date: Thu, 22 Apr 2021 06:40:04 +0800 Subject: [PATCH 5/5] update readme and changelog --- CHANGELOG.md | 1 + README.md | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8debde75..1458ceee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### New features +* [#590](https://github.com/clojure-emacs/clojure-mode/pull/590): Extend `clojure-rename-ns-alias` to work on selected regions. * [#567](https://github.com/clojure-emacs/clojure-mode/issues/567): Add new commands `clojure-toggle-ignore`, `clojure-toggle-ignore-surrounding-form`, and `clojure-toggle-defun` for inserting/deleting #_ ignore forms. * [#582](https://github.com/clojure-emacs/clojure-mode/pull/582): Add `clojure-special-arg-indent-factor` to control special argument indentation. diff --git a/README.md b/README.md index c0fb55e6..d573082c 100644 --- a/README.md +++ b/README.md @@ -405,10 +405,14 @@ form. If called with a prefix argument slurp the previous n forms. ### Rename ns alias -`clojure-rename-ns-alias`: Rename an alias inside a namespace declaration. +`clojure-rename-ns-alias`: Rename an alias inside a namespace declaration, +and all of its usages in the buffer +If there is an active selected region, only rename usages of aliases within the region, +without affecting the namespace declaration. + ### Add arity to a function `clojure-add-arity`: Add a new arity to an existing single-arity or multi-arity function.