diff options
| author | Dmitry Gutov | 2015-07-21 03:25:24 +0300 |
|---|---|---|
| committer | Dmitry Gutov | 2015-07-21 03:25:24 +0300 |
| commit | b6ac30ab435596f1be6023ad22471bf570a11c4a (patch) | |
| tree | d1cb92f4bea877afcb34d5c1374ef6af589747d1 | |
| parent | 4051fb202ee92edce95a3d0a9763d0d130f82770 (diff) | |
| download | emacs-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.el | 78 |
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 |