emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] master b6ac30a: Add new xref-query-replace command


From: Dmitry Gutov
Subject: [Emacs-diffs] master b6ac30a: Add new xref-query-replace command
Date: Tue, 21 Jul 2015 00:25:38 +0000

branch: master
commit b6ac30ab435596f1be6023ad22471bf570a11c4a
Author: Dmitry Gutov <address@hidden>
Commit: Dmitry Gutov <address@hidden>

    Add new xref-query-replace command
    
    * lisp/progmodes/xref.el (xref--match-buffer-bounds): New
    function, extracted from xref-pulse-momentarily.
    (xref-query-replace): New command.
    (xref--query-replace-1): New helper function.
    (xref--xref-buffer-mode-map): Add `r' binding.
---
 lisp/progmodes/xref.el |   78 ++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 72 insertions(+), 6 deletions(-)

diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index 1613692..5f681b0 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -342,18 +342,22 @@ elements is negated."
   (pcase-let ((`(,beg . ,end)
                (save-excursion
                  (or
-                  (let ((bounds (xref-match-bounds xref--current-item)))
-                    (when bounds
-                      (cons (progn (move-to-column (car bounds))
-                                   (point))
-                            (progn (move-to-column (cdr bounds))
-                                   (point)))))
+                  (xref--match-buffer-bounds xref--current-item)
                   (back-to-indentation)
                   (if (eolp)
                       (cons (line-beginning-position) (1+ (point)))
                     (cons (point) (line-end-position)))))))
     (pulse-momentary-highlight-region beg end 'next-error)))
 
+(defun xref--match-buffer-bounds (item)
+  (save-excursion
+    (let ((bounds (xref-match-bounds item)))
+      (when bounds
+        (cons (progn (move-to-column (car bounds))
+                     (point))
+              (progn (move-to-column (cdr bounds))
+                     (point)))))))
+
 ;; etags.el needs this
 (defun xref-clear-marker-stack ()
   "Discard all markers from the marker stack."
@@ -483,11 +487,72 @@ Used for temporary buffers.")
     (xref-quit)
     (xref--pop-to-location xref window)))
 
+(defun xref-query-replace (from to)
+  "Perform interactive replacement in all current matches."
+  (interactive
+   (list (read-regexp "Query replace regexp in matches" ".*")
+         (read-regexp "Replace with: ")))
+  (let (pairs item)
+    (unwind-protect
+        (progn
+          (save-excursion
+            (goto-char (point-min))
+            ;; TODO: Check that none of the matches are out of date;
+            ;; offer to re-scan otherwise.  Note that saving the last
+            ;; modification tick won't work, as long as not all of the
+            ;; buffers are kept open.
+            (while (setq item (xref--search-property 'xref-item))
+              (when (xref-match-bounds item)
+                (save-excursion
+                  (xref--goto-location (xref-item-location item))
+                  (let ((bounds (xref--match-buffer-bounds item))
+                        (beg (make-marker))
+                        (end (make-marker)))
+                    (move-marker beg (car bounds))
+                    (move-marker end (cdr bounds))
+                    (push (cons beg end) pairs)))))
+            (setq pairs (nreverse pairs)))
+          (unless pairs (user-error "No suitable matches here"))
+          (xref--query-replace-1 from to pairs))
+      (dolist (pair pairs)
+        (move-marker (car pair) nil)
+        (move-marker (cdr pair) nil)))))
+
+(defun xref--query-replace-1 (from to pairs)
+  (let* ((query-replace-lazy-highlight nil)
+         current-pair current-buf
+         ;; Counteract the "do the next match now" hack in
+         ;; `perform-replace'.  And still, it'll report that those
+         ;; matches were "filtered out" at the end.
+         (isearch-filter-predicate
+          (lambda (beg end)
+            (and current-pair
+                 (eq (current-buffer) current-buf)
+                 (>= beg (car current-pair))
+                 (<= end (cdr current-pair)))))
+         (replace-re-search-function
+          (lambda (from &optional _bound noerror)
+            (let (found)
+              (while (and (not found) pairs)
+                (setq current-pair (pop pairs)
+                      current-buf  (marker-buffer (car current-pair)))
+                (pop-to-buffer current-buf)
+                (goto-char (car current-pair))
+                (when (re-search-forward from (cdr current-pair) noerror)
+                  (setq found t)))
+              found))))
+    ;; FIXME: Despite this being a multi-buffer replacement, `N'
+    ;; doesn't work, because we're not using
+    ;; `multi-query-replace-map', and it would expect the below
+    ;; function to be called once per buffer.
+    (perform-replace from to t t nil)))
+
 (defvar xref--xref-buffer-mode-map
   (let ((map (make-sparse-keymap)))
     (define-key map [remap quit-window] #'xref-quit)
     (define-key map (kbd "n") #'xref-next-line)
     (define-key map (kbd "p") #'xref-prev-line)
+    (define-key map (kbd "r") #'xref-query-replace)
     (define-key map (kbd "RET") #'xref-goto-xref)
     (define-key map (kbd "C-o") #'xref-show-location-at-point)
     ;; suggested by Johan Claesson "to further reduce finger movement":
@@ -900,6 +965,7 @@ IGNORES is a list of glob patterns."
         (goto-char (point-min))
         (forward-line (1- line))
         (syntax-propertize (line-end-position))
+        ;; TODO: Handle multiple matches per line.
         (when (re-search-forward regexp (line-end-position) t)
           (goto-char (match-beginning 0))
           (let ((loc (xref-make-file-location file line



reply via email to

[Prev in Thread] Current Thread [Next in Thread]