aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Gutov2026-03-31 19:30:33 +0300
committerDmitry Gutov2026-04-01 04:04:39 +0300
commitcda65ffa58e1280fabfdf3eb7340f429b2aedd4d (patch)
tree1994b6c5b1169ef28aafcc4aea5c465dc000de44
parentf898d94c7b117b77f66d9472f9103961b5f6b6af (diff)
downloademacs-cda65ffa58e1280fabfdf3eb7340f429b2aedd4d.tar.gz
emacs-cda65ffa58e1280fabfdf3eb7340f429b2aedd4d.zip
Add xref-edit-mode (bug#80616)
Based on the existing grep-edit-mode code. * lisp/progmodes/xref.el (xref-edit--prepare-buffer): New function. (xref-edit-mode-map, xref-edit-mode-hook): New variables. (xref-edit-mode, xref-change-to-xref-edit-mode) (xref-edit-save-changes): New functions. (xref--xref-buffer-mode-map): New binding ("e"). * doc/emacs/maintaining.texi (Identifier Search): Mention xref-change-to-xref-edit-mode. * etc/NEWS: Describe the addition.
-rw-r--r--doc/emacs/maintaining.texi10
-rw-r--r--etc/NEWS5
-rw-r--r--lisp/progmodes/xref.el87
3 files changed, 102 insertions, 0 deletions
diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi
index 4aee5e1045d..72f25c0bdf8 100644
--- a/doc/emacs/maintaining.texi
+++ b/doc/emacs/maintaining.texi
@@ -2926,6 +2926,16 @@ matches of that regexp in the names of the identifiers with
2926@code{xref-query-replace-in-results}, but is more convenient when you 2926@code{xref-query-replace-in-results}, but is more convenient when you
2927want to rename a single identifier specified by its name @var{from}. 2927want to rename a single identifier specified by its name @var{from}.
2928 2928
2929@findex xref-change-to-xref-edit-mode
2930@cindex Xref Edit mode
2931@cindex mode, Xref Edit
2932 Typing @kbd{e} in the @file{*xref*} buffer makes the buffer writable
2933and enters the Xref Edit mode. Similar to Occur Edit mode (@pxref{Other
2934Repeating Search}), you can edit the matching lines reported by
2935Xref backend and have those changes reflected in the buffer visiting the
2936originating file. Type @kbd{C-c C-c} to leave the Xref Edit mode and
2937return to the Xref mode.
2938
2929@findex tags-search 2939@findex tags-search
2930 @kbd{M-x tags-search} reads a regexp using the minibuffer, then 2940 @kbd{M-x tags-search} reads a regexp using the minibuffer, then
2931searches for matches in all the files in the selected tags table, one 2941searches for matches in all the files in the selected tags table, one
diff --git a/etc/NEWS b/etc/NEWS
index 2b4e81fee14..335778d248a 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -3202,6 +3202,11 @@ This minor mode binds 'xref-find-definitions-at-mouse' to
3202definition, following the convention from other editors. The global 3202definition, following the convention from other editors. The global
3203minor mode 'global-xref-mouse-mode' will enable this in all buffers. 3203minor mode 'global-xref-mouse-mode' will enable this in all buffers.
3204 3204
3205+++
3206*** New command 'xref-change-to-xref-edit-mode'.
3207It's bound to "e" and it switches an Xref buffer into an "editable" mode
3208like similar features in Occur and Grep buffers.
3209
3205** Revert 3210** Revert
3206 3211
3207+++ 3212+++
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index ebf8df9f795..b69a4c7fdde 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -73,6 +73,7 @@
73(require 'cl-lib) 73(require 'cl-lib)
74(require 'ring) 74(require 'ring)
75(require 'project) 75(require 'project)
76(require 'text-property-search)
76 77
77(eval-and-compile 78(eval-and-compile
78 (when (version< emacs-version "28.0.60") 79 (when (version< emacs-version "28.0.60")
@@ -1004,6 +1005,7 @@ point."
1004 (define-key map (kbd ".") #'xref-next-line) 1005 (define-key map (kbd ".") #'xref-next-line)
1005 (define-key map (kbd ",") #'xref-prev-line) 1006 (define-key map (kbd ",") #'xref-prev-line)
1006 (define-key map (kbd "M-,") #'xref-quit-and-pop-marker-stack) 1007 (define-key map (kbd "M-,") #'xref-quit-and-pop-marker-stack)
1008 (define-key map (kbd "e") #'xref-change-to-xref-edit-mode)
1007 map)) 1009 map))
1008 1010
1009(declare-function outline-search-text-property "outline" 1011(declare-function outline-search-text-property "outline"
@@ -1471,6 +1473,91 @@ between them by typing in the minibuffer with completion."
1471 'xref--show-defs-minibuffer #'xref-show-definitions-completing-read "28.1") 1473 'xref--show-defs-minibuffer #'xref-show-definitions-completing-read "28.1")
1472 1474
1473 1475
1476(defun xref-edit--prepare-buffer ()
1477 "Mark relevant regions read-only, and add relevant occur text-properties."
1478 (save-excursion
1479 (goto-char (point-min))
1480 (let ((inhibit-read-only t)
1481 match)
1482 (while (setq match (text-property-search-forward 'xref-group))
1483 (add-text-properties (prop-match-beginning match) (prop-match-end match)
1484 '(read-only t)))
1485 (goto-char (point-min))
1486 (while (setq match (text-property-search-forward 'xref-item))
1487 (let ((line-number-end (save-excursion
1488 (forward-line 0)
1489 (and (looking-at " *[0-9]+:")
1490 (match-end 0))))
1491 (m (xref-location-marker (xref-item-location (prop-match-value match )))))
1492 (when line-number-end
1493 (add-text-properties (prop-match-beginning match) line-number-end
1494 '(read-only t occur-prefix t)))
1495 (add-text-properties (prop-match-beginning match)
1496 (1+ (pos-eol))
1497 `(occur-target ((,m . ,m)))))))))
1498
1499(defvar xref-edit-mode-map
1500 (let ((map (make-sparse-keymap)))
1501 (define-key map (kbd "C-c C-c") #'xref-edit-save-changes)
1502 (define-key map (kbd "RET") #'xref-goto-xref)
1503 (define-key map (kbd "M-,") #'xref-quit-and-pop-marker-stack)
1504 (define-key map (kbd "C-o") #'xref-show-location-at-point)
1505 map)
1506 "Keymap for `xref-edit-mode'.")
1507
1508(defvar xref-edit-mode-hook nil
1509 "Hooks run when changing to Xref-Edit mode.")
1510
1511(defun xref-edit-mode ()
1512 "Major mode for editing *xref* buffers.
1513In this mode, changes to the *xref* buffer are applied to the
1514originating files.
1515\\<xref-edit-mode-map>
1516Type \\[xref-edit-save-changes] to exit Xref-Edit mode, return to Xref
1517mode.
1518
1519The only editable texts in an Xref-Edit buffer are the match results."
1520 (interactive)
1521 (error "This mode can be enabled only by `xref-change-to-xref-edit-mode'"))
1522(put 'xref-edit-mode 'mode-class 'special)
1523
1524(defun xref-change-to-xref-edit-mode ()
1525 "Switch to `xref-edit-mode' to edit *xref* buffer."
1526 (interactive)
1527 (unless (derived-mode-p 'xref--xref-buffer-mode)
1528 (error "Not an Xref buffer"))
1529 (use-local-map xref-edit-mode-map)
1530 (xref-edit--prepare-buffer)
1531 (setq buffer-read-only nil)
1532 (setq major-mode 'xref-edit-mode)
1533 (setq mode-name "Xref-Edit")
1534 (buffer-enable-undo)
1535 (set-buffer-modified-p nil)
1536 (setq buffer-undo-list nil)
1537 (add-hook 'after-change-functions #'occur-after-change-function nil t)
1538 (run-mode-hooks 'xref-edit-mode-hook)
1539 (message (substitute-command-keys
1540 "Editing: Type \\[xref-edit-save-changes] to return to Xref mode")))
1541
1542(defun xref-edit-save-changes ()
1543 "Switch back to Xref mode."
1544 (interactive)
1545 (unless (derived-mode-p 'xref-edit-mode)
1546 (error "Not a Xref-Edit buffer"))
1547 (remove-hook 'after-change-functions #'occur-after-change-function t)
1548 (use-local-map xref--xref-buffer-mode-map)
1549 (setq buffer-read-only t)
1550 (setq major-mode 'xref--xref-buffer-mode)
1551 (setq mode-name "XREF")
1552 (force-mode-line-update)
1553 (buffer-disable-undo)
1554 (setq buffer-undo-list t)
1555 (let ((inhibit-read-only t))
1556 (remove-text-properties (point-min) (point-max)
1557 '(occur-target nil occur-prefix nil)))
1558 (message "Switching to Xref mode"))
1559
1560
1474(defcustom xref-show-xrefs-function 'xref--show-xref-buffer 1561(defcustom xref-show-xrefs-function 'xref--show-xref-buffer
1475 "Function to display a list of search results. 1562 "Function to display a list of search results.
1476 1563