From b4fb77fe8022931a3a8405d1aec2950c7debe84d Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Mon, 23 Jun 2025 19:42:23 +0200 Subject: [PATCH] elisp-mode.el: Support renaming symbol across project. --- lisp/progmodes/elisp-mode.el | 116 ++++++++++++++++++++++--------- lisp/progmodes/refactor-elisp.el | 28 ++++++-- 2 files changed, 106 insertions(+), 38 deletions(-) diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index 14b2524d570..7dbbfae2abe 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -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 diff --git a/lisp/progmodes/refactor-elisp.el b/lisp/progmodes/refactor-elisp.el index 382c0a7e47f..45556fe8b32 100644 --- a/lisp/progmodes/refactor-elisp.el +++ b/lisp/progmodes/refactor-elisp.el @@ -30,16 +30,17 @@ (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"))))) @@ -54,8 +55,17 @@ 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))) @@ -66,6 +76,14 @@ (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) -- 2.39.5