aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Gutov2015-07-21 03:25:24 +0300
committerDmitry Gutov2015-07-21 03:25:24 +0300
commitb6ac30ab435596f1be6023ad22471bf570a11c4a (patch)
treed1cb92f4bea877afcb34d5c1374ef6af589747d1
parent4051fb202ee92edce95a3d0a9763d0d130f82770 (diff)
downloademacs-b6ac30ab435596f1be6023ad22471bf570a11c4a.tar.gz
emacs-b6ac30ab435596f1be6023ad22471bf570a11c4a.zip
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.
-rw-r--r--lisp/progmodes/xref.el78
1 files changed, 72 insertions, 6 deletions
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index 16136927193..5f681b0f2ce 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -342,18 +342,22 @@ elements is negated."
342 (pcase-let ((`(,beg . ,end) 342 (pcase-let ((`(,beg . ,end)
343 (save-excursion 343 (save-excursion
344 (or 344 (or
345 (let ((bounds (xref-match-bounds xref--current-item))) 345 (xref--match-buffer-bounds xref--current-item)
346 (when bounds
347 (cons (progn (move-to-column (car bounds))
348 (point))
349 (progn (move-to-column (cdr bounds))
350 (point)))))
351 (back-to-indentation) 346 (back-to-indentation)
352 (if (eolp) 347 (if (eolp)
353 (cons (line-beginning-position) (1+ (point))) 348 (cons (line-beginning-position) (1+ (point)))
354 (cons (point) (line-end-position))))))) 349 (cons (point) (line-end-position)))))))
355 (pulse-momentary-highlight-region beg end 'next-error))) 350 (pulse-momentary-highlight-region beg end 'next-error)))
356 351
352(defun xref--match-buffer-bounds (item)
353 (save-excursion
354 (let ((bounds (xref-match-bounds item)))
355 (when bounds
356 (cons (progn (move-to-column (car bounds))
357 (point))
358 (progn (move-to-column (cdr bounds))
359 (point)))))))
360
357;; etags.el needs this 361;; etags.el needs this
358(defun xref-clear-marker-stack () 362(defun xref-clear-marker-stack ()
359 "Discard all markers from the marker stack." 363 "Discard all markers from the marker stack."
@@ -483,11 +487,72 @@ Used for temporary buffers.")
483 (xref-quit) 487 (xref-quit)
484 (xref--pop-to-location xref window))) 488 (xref--pop-to-location xref window)))
485 489
490(defun xref-query-replace (from to)
491 "Perform interactive replacement in all current matches."
492 (interactive
493 (list (read-regexp "Query replace regexp in matches" ".*")
494 (read-regexp "Replace with: ")))
495 (let (pairs item)
496 (unwind-protect
497 (progn
498 (save-excursion
499 (goto-char (point-min))
500 ;; TODO: Check that none of the matches are out of date;
501 ;; offer to re-scan otherwise. Note that saving the last
502 ;; modification tick won't work, as long as not all of the
503 ;; buffers are kept open.
504 (while (setq item (xref--search-property 'xref-item))
505 (when (xref-match-bounds item)
506 (save-excursion
507 (xref--goto-location (xref-item-location item))
508 (let ((bounds (xref--match-buffer-bounds item))
509 (beg (make-marker))
510 (end (make-marker)))
511 (move-marker beg (car bounds))
512 (move-marker end (cdr bounds))
513 (push (cons beg end) pairs)))))
514 (setq pairs (nreverse pairs)))
515 (unless pairs (user-error "No suitable matches here"))
516 (xref--query-replace-1 from to pairs))
517 (dolist (pair pairs)
518 (move-marker (car pair) nil)
519 (move-marker (cdr pair) nil)))))
520
521(defun xref--query-replace-1 (from to pairs)
522 (let* ((query-replace-lazy-highlight nil)
523 current-pair current-buf
524 ;; Counteract the "do the next match now" hack in
525 ;; `perform-replace'. And still, it'll report that those
526 ;; matches were "filtered out" at the end.
527 (isearch-filter-predicate
528 (lambda (beg end)
529 (and current-pair
530 (eq (current-buffer) current-buf)
531 (>= beg (car current-pair))
532 (<= end (cdr current-pair)))))
533 (replace-re-search-function
534 (lambda (from &optional _bound noerror)
535 (let (found)
536 (while (and (not found) pairs)
537 (setq current-pair (pop pairs)
538 current-buf (marker-buffer (car current-pair)))
539 (pop-to-buffer current-buf)
540 (goto-char (car current-pair))
541 (when (re-search-forward from (cdr current-pair) noerror)
542 (setq found t)))
543 found))))
544 ;; FIXME: Despite this being a multi-buffer replacement, `N'
545 ;; doesn't work, because we're not using
546 ;; `multi-query-replace-map', and it would expect the below
547 ;; function to be called once per buffer.
548 (perform-replace from to t t nil)))
549
486(defvar xref--xref-buffer-mode-map 550(defvar xref--xref-buffer-mode-map
487 (let ((map (make-sparse-keymap))) 551 (let ((map (make-sparse-keymap)))
488 (define-key map [remap quit-window] #'xref-quit) 552 (define-key map [remap quit-window] #'xref-quit)
489 (define-key map (kbd "n") #'xref-next-line) 553 (define-key map (kbd "n") #'xref-next-line)
490 (define-key map (kbd "p") #'xref-prev-line) 554 (define-key map (kbd "p") #'xref-prev-line)
555 (define-key map (kbd "r") #'xref-query-replace)
491 (define-key map (kbd "RET") #'xref-goto-xref) 556 (define-key map (kbd "RET") #'xref-goto-xref)
492 (define-key map (kbd "C-o") #'xref-show-location-at-point) 557 (define-key map (kbd "C-o") #'xref-show-location-at-point)
493 ;; suggested by Johan Claesson "to further reduce finger movement": 558 ;; suggested by Johan Claesson "to further reduce finger movement":
@@ -900,6 +965,7 @@ IGNORES is a list of glob patterns."
900 (goto-char (point-min)) 965 (goto-char (point-min))
901 (forward-line (1- line)) 966 (forward-line (1- line))
902 (syntax-propertize (line-end-position)) 967 (syntax-propertize (line-end-position))
968 ;; TODO: Handle multiple matches per line.
903 (when (re-search-forward regexp (line-end-position) t) 969 (when (re-search-forward regexp (line-end-position) t)
904 (goto-char (match-beginning 0)) 970 (goto-char (match-beginning 0))
905 (let ((loc (xref-make-file-location file line 971 (let ((loc (xref-make-file-location file line