diff options
| author | Dmitry Gutov | 2016-01-18 22:11:46 +0300 |
|---|---|---|
| committer | Dmitry Gutov | 2016-01-18 22:14:17 +0300 |
| commit | 36b0729ce765c132e04586be0e2deca405b4c313 (patch) | |
| tree | 73bb61012ea69e263966e9b743b8544c8a4c71c5 | |
| parent | 5b62b980f9d89758211959486681dcb8d416ef84 (diff) | |
| download | emacs-36b0729ce765c132e04586be0e2deca405b4c313.tar.gz emacs-36b0729ce765c132e04586be0e2deca405b4c313.zip | |
Add xref-based replacements for Dired search commands
* lisp/dired-aux.el (dired-do-find-regexp)
(dired-do-find-regexp-and-replace): New commands.
http://lists.gnu.org/archive/html/emacs-devel/2016-01/msg00864.html
* lisp/dired.el (dired-mode-map): Change bindings for `A' and
`Q' to the new commands.
* lisp/progmodes/xref.el (xref-query-replace)
(xref-collect-matches): Add progress reporters.
(xref--find-ignores-arguments): Return nil for zero ignores.
(xref--show-xrefs): Add an optional argument.
(xref-collect-matches): Drop the assert. 'find' accepts a
regular file in place of directory argument, too.
| -rw-r--r-- | lisp/dired-aux.el | 35 | ||||
| -rw-r--r-- | lisp/dired.el | 16 | ||||
| -rw-r--r-- | lisp/progmodes/xref.el | 65 |
3 files changed, 91 insertions, 25 deletions
diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el index a8c40b2835c..831278cb762 100644 --- a/lisp/dired-aux.el +++ b/lisp/dired-aux.el | |||
| @@ -2713,6 +2713,41 @@ with the command \\[tags-loop-continue]." | |||
| 2713 | (tags-query-replace from to delimited | 2713 | (tags-query-replace from to delimited |
| 2714 | '(dired-get-marked-files nil nil 'dired-nondirectory-p))) | 2714 | '(dired-get-marked-files nil nil 'dired-nondirectory-p))) |
| 2715 | 2715 | ||
| 2716 | (declare-function xref--show-xrefs "xref") | ||
| 2717 | (declare-function xref-query-replace "xref") | ||
| 2718 | |||
| 2719 | ;;;###autoload | ||
| 2720 | (defun dired-do-find-regexp (regexp) | ||
| 2721 | "Find all matches for REGEXP in all marked files, recursively." | ||
| 2722 | (interactive "sSearch marked files (regexp): ") | ||
| 2723 | (require 'grep) | ||
| 2724 | (defvar grep-find-ignored-files) | ||
| 2725 | (let* ((files (dired-get-marked-files)) | ||
| 2726 | (ignores (nconc (mapcar | ||
| 2727 | (lambda (s) (concat s "/")) | ||
| 2728 | vc-directory-exclusion-list) | ||
| 2729 | grep-find-ignored-files)) | ||
| 2730 | (xrefs (cl-mapcan | ||
| 2731 | (lambda (file) | ||
| 2732 | (xref-collect-matches regexp "*" file | ||
| 2733 | (and (file-directory-p file) | ||
| 2734 | ignores))) | ||
| 2735 | files))) | ||
| 2736 | (unless xrefs | ||
| 2737 | (user-error "No matches for: %s" regexp)) | ||
| 2738 | (xref--show-xrefs xrefs nil t))) | ||
| 2739 | |||
| 2740 | ;;;###autoload | ||
| 2741 | (defun dired-do-find-regexp-and-replace (from to) | ||
| 2742 | "Replace matches of FROM with TO, in all marked files, recursively." | ||
| 2743 | (interactive | ||
| 2744 | (let ((common | ||
| 2745 | (query-replace-read-args | ||
| 2746 | "Query replace regexp in marked files" t t))) | ||
| 2747 | (list (nth 0 common) (nth 1 common)))) | ||
| 2748 | (with-current-buffer (dired-do-find-regexp from) | ||
| 2749 | (xref-query-replace from to))) | ||
| 2750 | |||
| 2716 | (defun dired-nondirectory-p (file) | 2751 | (defun dired-nondirectory-p (file) |
| 2717 | (not (file-directory-p file))) | 2752 | (not (file-directory-p file))) |
| 2718 | 2753 | ||
diff --git a/lisp/dired.el b/lisp/dired.el index d7bc318d17b..0ca14b30e2c 100644 --- a/lisp/dired.el +++ b/lisp/dired.el | |||
| @@ -1450,7 +1450,7 @@ Do so according to the former subdir alist OLD-SUBDIR-ALIST." | |||
| 1450 | (define-key map "." 'dired-clean-directory) | 1450 | (define-key map "." 'dired-clean-directory) |
| 1451 | (define-key map "~" 'dired-flag-backup-files) | 1451 | (define-key map "~" 'dired-flag-backup-files) |
| 1452 | ;; Upper case keys (except !) for operating on the marked files | 1452 | ;; Upper case keys (except !) for operating on the marked files |
| 1453 | (define-key map "A" 'dired-do-search) | 1453 | (define-key map "A" 'dired-do-find-regexp) |
| 1454 | (define-key map "C" 'dired-do-copy) | 1454 | (define-key map "C" 'dired-do-copy) |
| 1455 | (define-key map "B" 'dired-do-byte-compile) | 1455 | (define-key map "B" 'dired-do-byte-compile) |
| 1456 | (define-key map "D" 'dired-do-delete) | 1456 | (define-key map "D" 'dired-do-delete) |
| @@ -1460,7 +1460,7 @@ Do so according to the former subdir alist OLD-SUBDIR-ALIST." | |||
| 1460 | (define-key map "M" 'dired-do-chmod) | 1460 | (define-key map "M" 'dired-do-chmod) |
| 1461 | (define-key map "O" 'dired-do-chown) | 1461 | (define-key map "O" 'dired-do-chown) |
| 1462 | (define-key map "P" 'dired-do-print) | 1462 | (define-key map "P" 'dired-do-print) |
| 1463 | (define-key map "Q" 'dired-do-query-replace-regexp) | 1463 | (define-key map "Q" 'dired-do-find-regexp-and-replace) |
| 1464 | (define-key map "R" 'dired-do-rename) | 1464 | (define-key map "R" 'dired-do-rename) |
| 1465 | (define-key map "S" 'dired-do-symlink) | 1465 | (define-key map "S" 'dired-do-symlink) |
| 1466 | (define-key map "T" 'dired-do-touch) | 1466 | (define-key map "T" 'dired-do-touch) |
| @@ -3905,7 +3905,7 @@ Ask means pop up a menu for the user to select one of copy, move or link." | |||
| 3905 | 3905 | ||
| 3906 | ;;; Start of automatically extracted autoloads. | 3906 | ;;; Start of automatically extracted autoloads. |
| 3907 | 3907 | ||
| 3908 | ;;;### (autoloads nil "dired-aux" "dired-aux.el" "7b7e39be8bcaf5f35b2735c3f5635f40") | 3908 | ;;;### (autoloads nil "dired-aux" "dired-aux.el" "ab7321f16a90251f12f0031ddd6a710c") |
| 3909 | ;;; Generated autoloads from dired-aux.el | 3909 | ;;; Generated autoloads from dired-aux.el |
| 3910 | 3910 | ||
| 3911 | (autoload 'dired-diff "dired-aux" "\ | 3911 | (autoload 'dired-diff "dired-aux" "\ |
| @@ -4408,6 +4408,16 @@ with the command \\[tags-loop-continue]. | |||
| 4408 | 4408 | ||
| 4409 | \(fn FROM TO &optional DELIMITED)" t nil) | 4409 | \(fn FROM TO &optional DELIMITED)" t nil) |
| 4410 | 4410 | ||
| 4411 | (autoload 'dired-do-find-regexp "dired-aux" "\ | ||
| 4412 | Find all matches for REGEXP in all marked files, recursively. | ||
| 4413 | |||
| 4414 | \(fn REGEXP)" t nil) | ||
| 4415 | |||
| 4416 | (autoload 'dired-do-find-regexp-and-replace "dired-aux" "\ | ||
| 4417 | Replace matches of FROM with TO, in all marked files, recursively. | ||
| 4418 | |||
| 4419 | \(fn FROM TO)" t nil) | ||
| 4420 | |||
| 4411 | (autoload 'dired-show-file-type "dired-aux" "\ | 4421 | (autoload 'dired-show-file-type "dired-aux" "\ |
| 4412 | Print the type of FILE, according to the `file' command. | 4422 | Print the type of FILE, according to the `file' command. |
| 4413 | If you give a prefix to this command, and FILE is a symbolic | 4423 | If you give a prefix to this command, and FILE is a symbolic |
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el index 6220b4cdc92..2bccd857576 100644 --- a/lisp/progmodes/xref.el +++ b/lisp/progmodes/xref.el | |||
| @@ -511,11 +511,18 @@ references displayed in the current *xref* buffer." | |||
| 511 | (let ((fr (read-regexp "Xref query-replace (regexp)" ".*"))) | 511 | (let ((fr (read-regexp "Xref query-replace (regexp)" ".*"))) |
| 512 | (list fr | 512 | (list fr |
| 513 | (read-regexp (format "Xref query-replace (regexp) %s with: " fr))))) | 513 | (read-regexp (format "Xref query-replace (regexp) %s with: " fr))))) |
| 514 | (let (pairs item) | 514 | (let ((reporter (make-progress-reporter (format "Saving search results...") |
| 515 | 0 (line-number-at-pos (point-max)))) | ||
| 516 | (counter 0) | ||
| 517 | pairs item) | ||
| 515 | (unwind-protect | 518 | (unwind-protect |
| 516 | (progn | 519 | (progn |
| 517 | (save-excursion | 520 | (save-excursion |
| 518 | (goto-char (point-min)) | 521 | (goto-char (point-min)) |
| 522 | ;; TODO: This list should be computed on-demand instead. | ||
| 523 | ;; As long as the UI just iterates through matches one by | ||
| 524 | ;; one, there's no need to compute them all in advance. | ||
| 525 | ;; Then we can throw away the reporter. | ||
| 519 | (while (setq item (xref--search-property 'xref-item)) | 526 | (while (setq item (xref--search-property 'xref-item)) |
| 520 | (when (xref-match-length item) | 527 | (when (xref-match-length item) |
| 521 | (save-excursion | 528 | (save-excursion |
| @@ -535,9 +542,11 @@ references displayed in the current *xref* buffer." | |||
| 535 | (line-end-position)) | 542 | (line-end-position)) |
| 536 | (xref-item-summary item)) | 543 | (xref-item-summary item)) |
| 537 | (user-error "Search results out of date")) | 544 | (user-error "Search results out of date")) |
| 545 | (progress-reporter-update reporter (cl-incf counter)) | ||
| 538 | (push (cons beg end) pairs))))) | 546 | (push (cons beg end) pairs))))) |
| 539 | (setq pairs (nreverse pairs))) | 547 | (setq pairs (nreverse pairs))) |
| 540 | (unless pairs (user-error "No suitable matches here")) | 548 | (unless pairs (user-error "No suitable matches here")) |
| 549 | (progress-reporter-done reporter) | ||
| 541 | (xref--query-replace-1 from to pairs)) | 550 | (xref--query-replace-1 from to pairs)) |
| 542 | (dolist (pair pairs) | 551 | (dolist (pair pairs) |
| 543 | (move-marker (car pair) nil) | 552 | (move-marker (car pair) nil) |
| @@ -713,9 +722,9 @@ Return an alist of the form ((FILENAME . (XREF ...)) ...)." | |||
| 713 | 722 | ||
| 714 | (defvar xref--read-pattern-history nil) | 723 | (defvar xref--read-pattern-history nil) |
| 715 | 724 | ||
| 716 | (defun xref--show-xrefs (xrefs window) | 725 | (defun xref--show-xrefs (xrefs window &optional always-show-list) |
| 717 | (cond | 726 | (cond |
| 718 | ((not (cdr xrefs)) | 727 | ((and (not (cdr xrefs)) (not always-show-list)) |
| 719 | (xref-push-marker-stack) | 728 | (xref-push-marker-stack) |
| 720 | (xref--pop-to-location (car xrefs) window)) | 729 | (xref--pop-to-location (car xrefs) window)) |
| 721 | (t | 730 | (t |
| @@ -866,11 +875,12 @@ tools are used, and when." | |||
| 866 | (mapc #'kill-buffer | 875 | (mapc #'kill-buffer |
| 867 | (cl-set-difference (buffer-list) orig-buffers))))) | 876 | (cl-set-difference (buffer-list) orig-buffers))))) |
| 868 | 877 | ||
| 878 | ;;;###autoload | ||
| 869 | (defun xref-collect-matches (regexp files dir ignores) | 879 | (defun xref-collect-matches (regexp files dir ignores) |
| 870 | "Collect matches for REGEXP inside FILES in DIR. | 880 | "Collect matches for REGEXP inside FILES in DIR. |
| 871 | FILES is a string with glob patterns separated by spaces. | 881 | FILES is a string with glob patterns separated by spaces. |
| 872 | IGNORES is a list of glob patterns." | 882 | IGNORES is a list of glob patterns." |
| 873 | (cl-assert (directory-name-p dir)) | 883 | ;; DIR can also be a regular file for now; let's not advertise that. |
| 874 | (require 'semantic/fw) | 884 | (require 'semantic/fw) |
| 875 | (grep-compute-defaults) | 885 | (grep-compute-defaults) |
| 876 | (defvar grep-find-template) | 886 | (defvar grep-find-template) |
| @@ -885,6 +895,8 @@ IGNORES is a list of glob patterns." | |||
| 885 | (orig-buffers (buffer-list)) | 895 | (orig-buffers (buffer-list)) |
| 886 | (buf (get-buffer-create " *xref-grep*")) | 896 | (buf (get-buffer-create " *xref-grep*")) |
| 887 | (grep-re (caar grep-regexp-alist)) | 897 | (grep-re (caar grep-regexp-alist)) |
| 898 | (counter 0) | ||
| 899 | reporter | ||
| 888 | hits) | 900 | hits) |
| 889 | (with-current-buffer buf | 901 | (with-current-buffer buf |
| 890 | (erase-buffer) | 902 | (erase-buffer) |
| @@ -894,9 +906,17 @@ IGNORES is a list of glob patterns." | |||
| 894 | (push (cons (string-to-number (match-string 2)) | 906 | (push (cons (string-to-number (match-string 2)) |
| 895 | (match-string 1)) | 907 | (match-string 1)) |
| 896 | hits))) | 908 | hits))) |
| 909 | (setq reporter (make-progress-reporter | ||
| 910 | (format "Collecting search results...") | ||
| 911 | 0 (length hits))) | ||
| 897 | (unwind-protect | 912 | (unwind-protect |
| 898 | (cl-mapcan (lambda (hit) (xref--collect-matches hit regexp)) | 913 | (cl-mapcan (lambda (hit) |
| 914 | (prog1 | ||
| 915 | (progress-reporter-update reporter counter) | ||
| 916 | (cl-incf counter)) | ||
| 917 | (xref--collect-matches hit regexp)) | ||
| 899 | (nreverse hits)) | 918 | (nreverse hits)) |
| 919 | (progress-reporter-done reporter) | ||
| 900 | ;; TODO: Same as above. | 920 | ;; TODO: Same as above. |
| 901 | (mapc #'kill-buffer | 921 | (mapc #'kill-buffer |
| 902 | (cl-set-difference (buffer-list) orig-buffers))))) | 922 | (cl-set-difference (buffer-list) orig-buffers))))) |
| @@ -922,23 +942,24 @@ IGNORES is a list of glob patterns." | |||
| 922 | (defun xref--find-ignores-arguments (ignores dir) | 942 | (defun xref--find-ignores-arguments (ignores dir) |
| 923 | ;; `shell-quote-argument' quotes the tilde as well. | 943 | ;; `shell-quote-argument' quotes the tilde as well. |
| 924 | (cl-assert (not (string-match-p "\\`~" dir))) | 944 | (cl-assert (not (string-match-p "\\`~" dir))) |
| 925 | (concat | 945 | (when ignores |
| 926 | (shell-quote-argument "(") | 946 | (concat |
| 927 | " -path " | 947 | (shell-quote-argument "(") |
| 928 | (mapconcat | 948 | " -path " |
| 929 | (lambda (ignore) | 949 | (mapconcat |
| 930 | (when (string-match-p "/\\'" ignore) | 950 | (lambda (ignore) |
| 931 | (setq ignore (concat ignore "*"))) | 951 | (when (string-match-p "/\\'" ignore) |
| 932 | (if (string-match "\\`\\./" ignore) | 952 | (setq ignore (concat ignore "*"))) |
| 933 | (setq ignore (replace-match dir t t ignore)) | 953 | (if (string-match "\\`\\./" ignore) |
| 934 | (unless (string-prefix-p "*" ignore) | 954 | (setq ignore (replace-match dir t t ignore)) |
| 935 | (setq ignore (concat "*/" ignore)))) | 955 | (unless (string-prefix-p "*" ignore) |
| 936 | (shell-quote-argument ignore)) | 956 | (setq ignore (concat "*/" ignore)))) |
| 937 | ignores | 957 | (shell-quote-argument ignore)) |
| 938 | " -o -path ") | 958 | ignores |
| 939 | " " | 959 | " -o -path ") |
| 940 | (shell-quote-argument ")") | 960 | " " |
| 941 | " -prune -o ")) | 961 | (shell-quote-argument ")") |
| 962 | " -prune -o "))) | ||
| 942 | 963 | ||
| 943 | (defun xref--regexp-to-extended (str) | 964 | (defun xref--regexp-to-extended (str) |
| 944 | (replace-regexp-in-string | 965 | (replace-regexp-in-string |