]> git.eshelyaron.com Git - emacs.git/commitdiff
Allow refactor backends to invalidate replacements
authorEshel Yaron <me@eshelyaron.com>
Wed, 10 Apr 2024 19:30:54 +0000 (21:30 +0200)
committerEshel Yaron <me@eshelyaron.com>
Wed, 10 Apr 2024 19:30:54 +0000 (21:30 +0200)
lisp/progmodes/refactor.el

index ac9e9128c759b34d8cccf4f955451f292c154ca3..889789b348369a7dc1f0a4e85363d0179462d8a3 100644 (file)
@@ -137,14 +137,22 @@ operations that BACKEND supports.")
 (cl-defgeneric refactor-backend-read-scoped-identifier (_backend)
   "Read an identifier and its scope for refactoring using BACKEND.
 
-Return a cons cell (IDENT . SCOPE), where IDENT is the identifier
-to act on and SCOPE is the scope of application.  The meaning of
-both IDENT and SCOPE are BACKEND-specific, but SCOPE is
-conventionally one of `expression', `defun', `file' or `project'."
+Return a cons cell (IDENT . SCOPE), where IDENT is the identifier to
+operate on and SCOPE is the scope of application.  The meaning of both
+IDENT and SCOPE are BACKEND-specific, but SCOPE is conventionally one of
+`expression', `defun', `file' or `project'."
   (when-let ((sym (symbol-at-point)))
     (cons (symbol-name sym) (if (project-current) 'project 'buffer))))
 
-(cl-defgeneric refactor-backend-read-replacement (_backend old scope)
+(cl-defgeneric refactor-backend-invalid-replacement (_backend _old _new _scope)
+  "Check if NEW is a valid replacement for OLD in SCOPE according to BACKEND.
+
+If it is invalid, for example if NEW is in conflict an identifier that
+is already in use, return a string to display as feedback to the user.
+Otherwise, if the replacement is valid, return nil."
+  nil)
+
+(cl-defgeneric refactor-backend-read-replacement (backend old scope)
   "Read a replacement for identifier OLD across SCOPE using BACKEND."
   (let ((case-fold-search nil))
     (save-excursion
@@ -154,9 +162,18 @@ conventionally one of `expression', `defun', `file' or `project'."
           (overlay-put ov 'refactor-rename-old t)
           (overlay-put ov 'face 'lazy-highlight)))))
   (unwind-protect
-      (read-string (format "Rename \"%s\" across %s to: "
-                           old (or scope "current scope"))
-                   nil nil old)
+      (let ((new nil)
+            (invalid nil))
+        (while (not new)
+          (setq new (read-string (format "%sRename \"%s\" across %s to: "
+                                         (or invalid "")
+                                         old (or scope "current scope"))
+                                 nil nil old))
+          (when-let ((inv (refactor-backend-invalid-replacement
+                           backend old new scope)))
+            (setq invalid (format "Invalid replacement \"%s\": %s\n" new inv)
+                  new nil)))
+        new)
     (remove-overlays (point-min) (point-max) 'refactor-rename-old t)))
 
 (cl-defgeneric refactor-backend-rename-edits (backend old new scope)