diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el index 3e3a37f6da..a8283d0d4a 100644 --- a/lisp/progmodes/xref.el +++ b/lisp/progmodes/xref.el @@ -1246,12 +1246,45 @@ xref-matches-in-directory (declare-function tramp-tramp-file-p "tramp") (declare-function tramp-file-local-name "tramp") +;; '-s' because 'git ls-files' can output broken symlinks. +(defconst xref-grep-search-template + "xargs -0 grep -snHE -e " + "Use Grep to search a list of files piped from stdin.") + +;; See https://github.com/BurntSushi/ripgrep/issues/152 on +;; the subject of non-deterministic output. +(defconst xref-ripgrep-search-template + "xargs -0 rg -nH --no-messages -g '!*/' -e | sort -t: -k1 -k2n" + "Use ripgrep to search a list of files piped from stdin. + +The arguments are chosen carefully so that the output format is +compatible with Grep. As well as its '-s' argument. + +Note: by default, ripgrep's output order is non-deterministic +because it does the search in parallel. You can use the template +without the '| sort ...' part if GNU sort is not available on +your system and/or stable ordering is not important to you.") + +(defcustom xref-search-command-template xref-grep-search-template + "Command template to search a list of files piped from stdin. + +Allowed fields: + + for extra arguments such as -i and --color + for the regexp itself (in Extended format)" + :type `(choice + (const :tag "Use Grep" ,xref-grep-search-template) + (const :tag "Use ripgrep" ,xref-ripgrep-search-template) + (string :tag "User defined"))) + ;;;###autoload (defun xref-matches-in-files (regexp files) "Find all matches for REGEXP in FILES. Return a list of xref values. FILES must be a list of absolute file names." (cl-assert (consp files)) + (require 'grep) + (defvar grep-highlight-matches) (pcase-let* ((output (get-buffer-create " *project grep output*")) (`(,grep-re ,file-group ,line-group . ,_) (car grep-regexp-alist)) @@ -1261,13 +1294,12 @@ xref-matches-in-files ;; first file is remote, they all are, and on the same host. (dir (file-name-directory (car files))) (remote-id (file-remote-p dir)) - ;; 'git ls-files' can output broken symlinks. - (command (format "xargs -0 grep %s -snHE -e %s" - (if (and case-fold-search - (isearch-no-upper-case-p regexp t)) - "-i" - "") - (shell-quote-argument (xref--regexp-to-extended regexp))))) + ;; The 'auto' default would be fine too, but ripgrep can't handle + ;; the options we pass in that case. + (grep-highlight-matches) + (command (grep-expand-template xref-search-command-template + (xref--regexp-to-extended regexp) + regexp))) (when remote-id (require 'tramp) (setq files (mapcar