]> git.eshelyaron.com Git - emacs.git/commitdiff
elisp-mode.el: Support renaming symbol across project.
authorEshel Yaron <me@eshelyaron.com>
Mon, 23 Jun 2025 17:42:23 +0000 (19:42 +0200)
committerEshel Yaron <me@eshelyaron.com>
Mon, 23 Jun 2025 17:42:46 +0000 (19:42 +0200)
lisp/progmodes/elisp-mode.el
lisp/progmodes/refactor-elisp.el

index 14b2524d5702604a1936f63873c98ae2d9180c2b..7dbbfae2abee123e077347e1bf8327d31518469f 100644 (file)
@@ -1223,7 +1223,6 @@ confidence."
 
 (cl-defmethod xref-backend-references ((_backend (eql 'elisp)) identifier)
   (let* ((pos (get-text-property 0 'pos identifier))
-         (enable-local-variables nil)
          (gc-cons-threshold (* 1024 1024 1024)))
     (or (elisp-make-xrefs (and pos (elisp-local-references pos)))
         (let (res
@@ -1252,26 +1251,73 @@ confidence."
                   (unless (eq beg (caar lla))
                     (push (cons beg len) lla))))
               (when lla
-                (with-work-buffer
-                  (insert-file-contents file)
-                  (pcase-dolist (`(,beg . ,len) lla)
-                    (goto-char beg)
-                    (push
-                     (let* ((begg (pos-bol))
-                            (endd (pos-eol))
-                            (line (buffer-substring begg endd))
-                            (cur (- beg begg)))
-                       (add-face-text-property
-                        cur (+ len cur) 'xref-match t line)
-                       (xref-make-match
-                        line
-                        (xref-make-file-pos-location
-                         file beg (line-number-at-pos beg))
-                        len))
-                     all))))
+                (if-let ((buf (get-file-buffer file)))
+                    (with-current-buffer buf
+                      (save-excursion
+                        (pcase-dolist (`(,beg . ,len) lla)
+                          (goto-char beg)
+                          (push
+                           (let* ((begg (pos-bol))
+                                  (endd (pos-eol))
+                                  (line (buffer-substring begg endd))
+                                  (cur (- beg begg)))
+                             (add-face-text-property
+                              cur (+ len cur) 'xref-match t line)
+                             (xref-make-match
+                              line
+                              (xref-make-file-pos-location
+                               file beg (line-number-at-pos beg))
+                              len))
+                           all))))
+                  (with-work-buffer
+                    (insert-file-contents file)
+                    (pcase-dolist (`(,beg . ,len) lla)
+                      (goto-char beg)
+                      (push
+                       (let* ((begg (pos-bol))
+                              (endd (pos-eol))
+                              (line (buffer-substring begg endd))
+                              (cur (- beg begg)))
+                         (add-face-text-property
+                          cur (+ len cur) 'xref-match t line)
+                         (xref-make-match
+                          line
+                          (xref-make-file-pos-location
+                           file beg (line-number-at-pos beg))
+                          len))
+                       all)))))
               (when all (setq res (nconc res (nreverse all))))))
           res))))
 
+(defun elisp-project-references (str typ)
+  (let (res
+        (types
+         (case (scope-get-symbol-type-property typ :namespace)
+           (face '(defface face))
+           (feature '(feature))
+           (widget-type '(widget-type))
+           (condition '(condition))
+           (icon '(deficon icon))
+           (coding '(defcoding coding))
+           (charset '(defcharset charset))
+           (variable '(defvar variable constant))
+           (symbol-type '(symbol-type symbol-type-definition))
+           (function '(defun function macro special-form major-mode)))))
+    (require 'project)
+    (dolist-with-progress-reporter
+        (file
+         (seq-filter
+          (lambda (file) (string= (file-name-extension file) "el"))
+          (project-files (project-current))))
+        "Scanning for references"
+      (let (hits)
+        (pcase-dolist (`(,type ,beg ,len . ,_) (gethash str (elisp-sym-name-index file)))
+          (when (or (null types) (memq type types))
+            (unless (eq beg (caar hits))
+              (push (cons beg len) hits))))
+        (when hits (push (cons file hits) res))))
+    (nreverse res)))
+
 (defun elisp-make-xref (beg len)
   (let* ((beg-end (save-excursion
                     (goto-char beg)
@@ -2840,20 +2886,24 @@ of TARGET."
 (defvar elisp-sym-name-index-cache (make-hash-table :test #'equal))
 
 (defun elisp-sym-name-index (file)
-  (let ((cached (gethash file elisp-sym-name-index-cache))
-        (modtime (file-attribute-modification-time (file-attributes file))))
-    (cdr
-     (if (time-less-p (or (car cached) 0) modtime)
-         (puthash file (cons modtime
-                             (with-work-buffer
-                               (setq lexical-binding t)
-                               (insert-file-contents file)
-                               (elisp-sym-name-index-1 file)))
-                  elisp-sym-name-index-cache)
-       cached))))
-
-(defun elisp-sym-name-index-1 (file)
-  (let ((all (make-hash-table :test #'equal)))
+  (if-let ((buf (get-file-buffer file))
+           ((buffer-modified-p buf)))
+      (with-current-buffer buf (elisp-sym-name-index-1 file))
+    (let ((cached (gethash file elisp-sym-name-index-cache))
+          (modtime (file-attribute-modification-time (file-attributes file))))
+      (cdr
+       (if (time-less-p (or (car cached) 0) modtime)
+           (puthash file (cons modtime
+                               (with-work-buffer
+                                 (setq lexical-binding t)
+                                 (insert-file-contents file)
+                                 (elisp-sym-name-index-1 file)))
+                    elisp-sym-name-index-cache)
+         cached)))))
+
+(defun elisp-sym-name-index-1 (source)
+  (let ((all (make-hash-table :test #'equal))
+        (scope-assume-func-p t))
     (save-excursion
       (goto-char (point-min))
       (condition-case e
@@ -2864,7 +2914,7 @@ of TARGET."
                 (list type beg len)
                 (gethash (buffer-substring beg (+ beg len)) all)))))
         (end-of-file all)
-        (error (message "Encountered error while scanning %s: %S" file e) all)))))
+        (error (message "Encountered error while scanning %s: %S" source e) all)))))
 
 (provide 'elisp-mode)
 ;;; elisp-mode.el ends here
index 382c0a7e47f1924acd5b96ca0122397f20a0a59f..45556fe8b32c78058a16219da1fbd8ef68fde51e 100644 (file)
 (defun elisp-refactor-backend () '(elisp rename extract))
 
 (cl-defmethod refactor-backend-read-scoped-identifier ((_backend (eql elisp)))
-  (let* ((pos (point)))
+  (let ((scope-assume-func-p t)
+        (pos (point)))
     (save-excursion
       (beginning-of-defun-raw)
       (catch 'var-def
-        (scope (lambda (_type beg len _id &optional def)
+        (scope (lambda (typ beg len _id &optional def)
                  (when (<= beg pos (+ beg len))
                    (throw 'var-def
                           (cons (propertize
                                  (buffer-substring-no-properties beg (+ beg len))
-                                 'pos beg)
+                                 'pos beg 'type typ)
                                 (unless def 'project))))))
         (user-error "No symbol to rename at point")))))
 
                 res))))
 
 (cl-defmethod refactor-backend-rename-edits
-  ((_backend (eql elisp)) _old _new (_scope (eql project)))
-  (error "Not implemented"))
+  ((_backend (eql elisp)) old new (_scope (eql project)))
+  (let ((type (get-text-property 0 'typ old)))
+    (let (res)
+      (pcase-dolist (`(,file . ,hits) (elisp-project-references old type))
+        (push (cons file
+                    (let (ser)
+                      (pcase-dolist (`(,beg . ,len) hits)
+                        (push (list beg (+ beg len) new) ser))
+                      ser))
+              res))
+      res)))
 
 (cl-defmethod refactor-backend-rename-highlight-regions
   ((_backend (eql elisp)) old (_scope (eql nil)))
       (alist-set beg res (+ beg len)))
     res))
 
+(cl-defmethod refactor-backend-rename-highlight-regions
+  ((_backend (eql elisp)) old (_scope (eql project)))
+  (let (res)
+    (pcase-dolist (`(,beg . ,len)
+                   (alist-get buffer-file-name (elisp-project-references old (get-text-property 0 'type old)) nil nil #'string=))
+      (alist-set beg res (+ beg len)))
+    res))
+
 (cl-defmethod refactor-backend-extract-validate-region
   ((_backend (eql elisp)) beg end)
   (unless (ignore-errors (read (buffer-substring beg end)) t)