[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/consult f32168ac20 2/4: Refactoring: consult-grep multi
From: |
ELPA Syncer |
Subject: |
[elpa] externals/consult f32168ac20 2/4: Refactoring: consult-grep multi directory support |
Date: |
Sat, 25 Feb 2023 17:57:30 -0500 (EST) |
branch: externals/consult
commit f32168ac20dc46b29e0023f2aee447662f764f0b
Author: Daniel Mendler <mail@daniel-mendler.de>
Commit: Daniel Mendler <mail@daniel-mendler.de>
Refactoring: consult-grep multi directory support
---
CHANGELOG.org | 17 ++--
README.org | 6 +-
consult.el | 293 ++++++++++++++++++++++++----------------------------------
3 files changed, 133 insertions(+), 183 deletions(-)
diff --git a/CHANGELOG.org b/CHANGELOG.org
index 1da0886dcd..3e6a219ff9 100644
--- a/CHANGELOG.org
+++ b/CHANGELOG.org
@@ -4,16 +4,13 @@
* Development
-Multi-path handling contributed by Robert Weiner (@rswgnu):
-- Add support for grep and find over multiple directories and files.
-- Change =consult--directory-prompt=. With a prefix arg sent as =dir= to one
- of the consult search commands:
- if it is a single C-u, prompt for a single directory to recursively search;
- with any other prefix arg, prompt for a space-separated string of positive
- number of directories or files.
-- Rename =consult--git-grep-builder= to =consult--git-grep-make-builder=
- and return lambda so can pass in =search-path-list=.
-
+- BREAKING: Remove the "." argument from =consult-grep-args= and
+ =consult-ripgrep-args=, since directories or files to search are appended by
the
+ command line builder. Take this change into account, when you use a
customized
+ version of those variables.
+- =consult-grep=: Add support for grep and find over multiple files or
directory.
+ If the prefix argument DIR is a single C-u, prompt for a directories or files
+ to search recursively via =completing-read-multiple=.
- =consult-buffer= and =consult-isearch-history=: Align annotations dynamically
depending on candidate width, instead of computing the alignment beforehand.
- Add the full path as =help-echo= property to abbreviated directory paths and
diff --git a/README.org b/README.org
index c820b2ade3..eb575a03c1 100644
--- a/README.org
+++ b/README.org
@@ -256,9 +256,8 @@ their descriptions.
completion style like orderless. =consult-grep= supports preview. If the
=consult-project-function= returns non-nil, =consult-grep= searches the
current project directory. Otherwise the =default-directory= is searched. If
- =consult-grep= is invoked with prefix argument =C-u M-s g=, you can specify
the
- directory manually. With any other prefix argument, =C-u C-u M-s g=, you can
- enter any positive number of space-separated directories or files to search
over.
+ =consult-grep= is invoked with prefix argument =C-u M-s g=, you can specify
one or
+ multiple files or directories manually.
- =consult-find=, =consult-locate=: Find file by matching the path against a
regexp.
Like for =consult-grep=, either the project root or the current directory is
the
root directory for the search. The input string is treated similarly to
@@ -1198,6 +1197,7 @@ Code contributions:
- [[https://github.com/aagon][Aymeric Agon-Rambosson]]
- [[https://github.com/geolessel][Geoffrey Lessel]]
- [[https://github.com/piotrkwiecinski][Piotr Kwiecinski]]
+- [[https://github.com/rswgnu][Robert Weiner]]
Advice and useful discussions:
- [[https://github.com/clemera/][Clemens Radermacher]]
diff --git a/consult.el b/consult.el
index db13ef551c..5cf56a6f93 100644
--- a/consult.el
+++ b/consult.el
@@ -247,7 +247,8 @@ See `consult--multi' for a description of the source data
structure."
(defcustom consult-grep-args
'("grep" (consult--grep-exclude-args)
- "--null --line-buffered --color=never --ignore-case --line-number -I -r .")
+ "--null --line-buffered --color=never --ignore-case\
+ --with-filename --line-number -I -r")
"Command line arguments for grep, see `consult-grep'.
The dynamically computed arguments are appended.
Can be either a string, or a list of strings or expressions."
@@ -263,7 +264,7 @@ Can be either a string, or a list of strings or
expressions."
(defcustom consult-ripgrep-args
"rg --null --line-buffered --color=never --max-columns=1000 --path-separator
/\
- --smart-case --no-heading --line-number --search-zip ."
+ --smart-case --no-heading --with-filename --line-number --search-zip"
"Command line arguments for ripgrep, see `consult-ripgrep'.
The dynamically computed arguments are appended.
Can be either a string, or a list of strings or expressions."
@@ -581,26 +582,6 @@ Turn ARG into a list, and for each element either:
(ensure-list (eval x 'lexical))))
(ensure-list arg)))
-(defun consult--build-args-with-paths (arg search-path-list)
- "Return ARG and SEARCH-PATH-LIST joined as a flat list of split strings.
-
-Turn ARG into a list, and for each element either:
-- split it if it a string.
-- eval it if it is an expression.
-
-If SEARCH-PATH-LIST is non-nil and not equal to (\".\"),
-splice its paths in at the end of the arg list."
- (let ((args (consult--build-args arg)))
- (if (and search-path-list
- (not (equal search-path-list '("."))))
- (if (member "." args)
- ;; Replace occurrences of "." with `search-path-list' in `args'
- (flatten-list
- (mapcar (lambda (elt) (if (equal elt ".") search-path-list elt))
- args))
- (nconc args search-path-list))
- args)))
-
(defun consult--command-split (str)
"Return command argument and options list given input STR."
(save-match-data
@@ -733,81 +714,60 @@ The line beginning/ending BEG/END is bound in BODY."
(kill-local-variable ',(cdr x))))
local)))))))
-(defun consult--abbreviate-directory (dir)
- "Return abbreviated directory DIR for use in `completing-read' prompt."
+(defun consult--abbreviate-file (file)
+ "Return abbreviated file name of FILE for use in `completing-read' prompt."
(save-match-data
- (let ((adir (abbreviate-file-name dir)))
- (if (string-match "/\\([^/]+\\)/\\([^/]+\\)/\\'" adir)
- (propertize (format "…/%s/%s/" (match-string 1 adir) (match-string 2
adir))
- 'help-echo adir)
- adir))))
+ (let ((afile (abbreviate-file-name file)))
+ (if (string-match "/\\([^/]+\\)/\\([^/]+/?\\)\\'" afile)
+ (propertize (format "…/%s/%s" (match-string 1 afile) (match-string 2
afile))
+ 'help-echo afile)
+ afile))))
(defun consult--directory-prompt (prompt dir)
- "Return a list of latest PROMPT, expanded DIR and a list of paths to search.
-
-PROMPT is the prompt prefix. The directory
-is appended to the prompt prefix. For projects
-only the project name is shown. The `default-directory'
-is not shown. Other directories are abbreviated and
-only the last two path components are shown.
-
-If DIR is null or equal to `default-directory', local
-`search-path-list' is set to nil. Otherwise, when DIR
-is a string, it is split into space-separated path
-patterns and a flattened list of the results is saved
-in `search-path-list'.
-
-The last element of the return value is either the
-expanded value of DIR if it is a single directory, the
-current project root directory if any from a call to
-`consult-project-function' or else `default-directory'."
- (let* ((abbrev-dir ".")
- (possible-prefix-arg dir)
+ "Return prompt, paths and default directory.
+
+PROMPT is the prompt prefix. The directory is appended to the
+prompt prefix. For projects only the project name is shown. The
+`default-directory' is not shown. Other directories are
+abbreviated and only the last two path components are shown.
+
+If DIR is a string, it is returned as default directory. If DIR
+is a list of strings, the list is returned as search paths. If
+DIR is the value (4) corresponding to two key prefix argument
+key presses, the user is asked for the directories or files to search.
+Otherwise the `consult-project-function' is tried to retrieve the
+default directory. If no project is found the
+`default-directory' is returned as is."
+ (let* ((paths nil)
(dir
- (cond
- ((or (stringp dir) (and (listp dir) (stringp (car dir))))
- dir)
- (dir
- ;; Preserve this-command across `read-directory-name' call,
- ;; such that `consult-customize' continues to work.
- (let ((this-command this-command))
- (pcase dir
- ;; Single C-u prefix arg, prompt for a single directory
- ('(4) (read-directory-name "Directory: " nil nil t))
- ;; Any other prefix arg, prompt for any number of existing
- ;; directories or files
- (_ (let ((crm-separator " "))
- (completing-read-multiple "Search Locations (default =
.): "
- #'completion-file-name-table
nil t nil nil abbrev-dir))))))
- (t (or (consult--project-root) default-directory))))
- (edir (file-name-as-directory
- (expand-file-name (if (and (stringp dir)
- (file-directory-p dir))
- dir
- abbrev-dir))))
- ;; Bind default-directory in order to find the project
- (pdir (let ((default-directory edir)) (consult--project-root)))
- (search-path-list
- (if (equal dir default-directory)
- (list abbrev-dir)
- (when (stringp dir)
- ;; Convert space-separated paths to a list
- (split-string dir)))))
+ (pcase dir
+ ((pred stringp) dir)
+ ('nil (or (consult--project-root) default-directory))
+ (_
+ (pcase (if (stringp (car-safe dir))
+ dir
+ ;; Preserve this-command across
`completing-read-multiple' call,
+ ;; such that `consult-customize' continues to work.
+ (let ((this-command this-command))
+ (completing-read-multiple "Directories or files: "
+
#'completion-file-name-table
+ nil t nil
'file-name-history)))
+ ((and `(,p) (guard (file-directory-p p))) p)
+ (ps (setq paths ps)
+ default-directory)))))
+ (edir (file-name-as-directory (expand-file-name dir)))
+ (pdir (let ((default-directory edir))
+ ;; Bind default-directory in order to find the project
+ (consult--project-root))))
(list
- (format "%s %s: " prompt
- (if (and possible-prefix-arg (not (stringp possible-prefix-arg))
- (not (equal possible-prefix-arg '(4))))
- (if (listp dir)
- (mapcar #'consult--abbreviate-directory dir)
- (format "(%s)" (consult--abbreviate-directory dir)))
- (format "(%s)"
- (if (equal edir pdir)
- (concat "Project " (consult--project-name pdir))
- (consult--abbreviate-directory
- (if (and (stringp dir) (file-directory-p dir))
- dir
- abbrev-dir))))))
- search-path-list
+ (format "%s (%s): " prompt
+ (pcase paths
+ (`(,p) (consult--abbreviate-file p))
+ (`(,p . ,_)
+ (format "%d paths, %s, …" (length paths)
(consult--abbreviate-file p)))
+ ((guard (equal edir pdir)) (concat "Project "
(consult--project-name pdir)))
+ (_ (consult--abbreviate-file edir))))
+ (or paths '("."))
edir)))
(defun consult--default-project-function (may-prompt)
@@ -2197,7 +2157,6 @@ PROPS are optional properties passed to `make-process'."
(when args
(funcall async 'indicator 'running)
(consult--async-log "consult--async-process started %S\n" args)
- ;; `args' is a list of the command-line args run by consult for
this command
(setq count 0
proc-buf (generate-new-buffer " *consult-async-stderr*")
proc (apply #'make-process
@@ -4365,7 +4324,7 @@ QUERY is passed to `consult--buffer-query'."
(cond
((and ndir (eq dir 'project))
(format ", Project %s" (consult--project-name ndir)))
- (ndir (concat ", " (consult--abbreviate-directory ndir)))
+ (ndir (concat ", " (consult--abbreviate-file ndir)))
(t "")))
buffers)))
@@ -4738,21 +4697,15 @@ Take the variables `grep-find-ignored-directories' and
(bound-and-true-p grep-find-ignored-directories))))
(defun consult--grep (prompt make-builder dir initial)
- "Run grep in DIR.
-
-MAKE-BUILDER is the function that returns the command
-line builder function. PROMPT is the prompt string.
-INITIAL is inital input."
- ;; Must call `consult--directory-prompt' to set local `search-path-list'
- ;; prior to calling the `make-builder' function where the path list is used.
- (let* ((prompt-paths-dir (consult--directory-prompt prompt dir))
- (prompt (nth 0 prompt-paths-dir))
- (search-path-list (nth 1 prompt-paths-dir))
- default-directory
- builder)
- (setq dir (nth 2 prompt-paths-dir)
- default-directory dir
- builder (funcall make-builder search-path-list))
+ "Run asynchronous grep.
+
+MAKE-BUILDER is the function that returns the command line
+builder function. DIR is a directory or a list of file or
+directories. PROMPT is the prompt string. INITIAL is inital
+input."
+ (pcase-let* ((`(,prompt ,paths ,dir) (consult--directory-prompt prompt dir))
+ (default-directory dir)
+ (builder (funcall make-builder paths)))
(consult--read
(consult--async-command builder
(consult--grep-format builder)
@@ -4775,16 +4728,16 @@ INITIAL is inital input."
(eq 0 (apply #'call-process-region (point-min) (point-max)
(car cmd) nil nil nil `(,@(cdr cmd) "^(?=.*b)(?=.*a)")))))
-(defun consult--grep-make-builder (search-path-list)
- "Build grep command line and grep across SEARCH-PATH-LIST."
- (let* ((cmd (consult--build-args-with-paths consult-grep-args
search-path-list))
+(defun consult--grep-make-builder (paths)
+ "Build grep command line and grep across PATHS."
+ (let* ((cmd (consult--build-args consult-grep-args))
(type (if (consult--grep-lookahead-p (car cmd) "-P") 'pcre
'extended)))
(lambda (input)
(pcase-let* ((`(,arg . ,opts) (consult--command-split input))
(flags (append cmd opts))
(ignore-case (or (member "-i" flags) (member
"--ignore-case" flags))))
(if (or (member "-F" flags) (member "--fixed-strings" flags))
- (cons (append cmd (list "-e" arg) opts)
+ (cons (append cmd (list "-e" arg) opts paths)
(apply-partially #'consult--highlight-regexps
(list (regexp-quote arg)) ignore-case))
(pcase-let ((`(,re . ,hl) (funcall consult--regexp-compiler arg type
ignore-case)))
@@ -4792,33 +4745,41 @@ INITIAL is inital input."
(cons (append cmd
(list (if (eq type 'pcre) "-P" "-E") ;; perl or
extended
"-e" (consult--join-regexps re type))
- opts)
+ opts paths)
hl))))))))
;;;###autoload
(defun consult-grep (&optional dir initial)
"Search with `grep' for files in DIR where the content matches a regexp.
-The initial input is given by the INITIAL argument.
-
-The input string is split, the first part of the string (grep input) is
-passed to the asynchronous grep process and the second part of the string is
-passed to the completion-style filtering.
-
-The input string is split at a punctuation character, which is given as the
-first character of the input string. The format is similar to Perl-style
-regular expressions, e.g., /regexp/. Furthermore command line options can be
-passed to grep, specified behind --. The overall prompt input has the form
+The initial input is given by the INITIAL argument. DIR can be
+nil, a directory string or a list of file/directory paths. If
+`consult-grep' is called interactively with a prefix argument,
+the user can specify the directory to search in. By default the
+project directory is used if `consult-project-function' is
+defined and returns non-nil. Otherwise the `default-directory'
+is searched.
+
+The input string is split, the first part of the string (grep
+input) is passed to the asynchronous grep process and the second
+part of the string is passed to the completion-style filtering.
+
+The input string is split at a punctuation character, which is
+given as the first character of the input string. The format is
+similar to Perl-style regular expressions, e.g., /regexp/.
+Furthermore command line options can be passed to grep, specified
+behind --. The overall prompt input has the form
`#async-input -- grep-opts#filter-string'.
-Note that the grep input string is transformed from Emacs regular expressions
-to Posix regular expressions. Always enter Emacs regular expressions at the
-prompt. `consult-grep' behaves like builtin Emacs search commands, e.g.,
-Isearch, which take Emacs regular expressions. Furthermore the asynchronous
-input split into words, each word must match separately and in any order. See
-`consult--regexp-compiler' for the inner workings. In order to disable
-transformations of the grep input, adjust `consult--regexp-compiler'
-accordingly.
+Note that the grep input string is transformed from Emacs regular
+expressions to Posix regular expressions. Always enter Emacs
+regular expressions at the prompt. `consult-grep' behaves like
+builtin Emacs search commands, e.g., Isearch, which take Emacs
+regular expressions. Furthermore the asynchronous input split
+into words, each word must match separately and in any order.
+See `consult--regexp-compiler' for the inner workings. In order
+to disable transformations of the grep input, adjust
+`consult--regexp-compiler' accordingly.
Here we give a few example inputs:
@@ -4828,45 +4789,42 @@ Here we give a few example inputs:
#word -- -C3 : Search for word, include 3 lines as context
#first#second : Search for first, quick filter for second.
-The symbol at point is added to the future history. If `consult-grep'
-is called interactively with a prefix argument, the user can specify
-the directory to search in. By default the project directory is used
-if `consult-project-function' is defined and returns non-nil.
-Otherwise the `default-directory' is searched."
+The symbol at point is added to the future history."
(interactive "P")
(consult--grep "Grep" #'consult--grep-make-builder dir initial))
;;;;; Command: consult-git-grep
-(defun consult--git-grep-make-builder (search-path-list)
- "Create grep command line builder given SEARCH-PATH-LIST."
- (let ((cmd (consult--build-args-with-paths consult-git-grep-args
search-path-list)))
+(defun consult--git-grep-make-builder (paths)
+ "Create grep command line builder given PATHS."
+ (let ((cmd (consult--build-args consult-git-grep-args)))
(lambda (input)
(pcase-let* ((`(,arg . ,opts) (consult--command-split input))
(flags (append cmd opts))
(ignore-case (or (member "-i" flags) (member
"--ignore-case" flags))))
(if (or (member "-F" flags) (member "--fixed-strings" flags))
- (cons (append cmd (list "-e" arg) opts)
+ (cons (append cmd (list "-e" arg) opts paths)
(apply-partially #'consult--highlight-regexps
(list (regexp-quote arg)) ignore-case))
(pcase-let ((`(,re . ,hl) (funcall consult--regexp-compiler arg
'extended ignore-case)))
(when re
- (cons (append cmd (cdr (mapcan (lambda (x) (list "--and" "-e"
x)) re)) opts)
+ (cons (append cmd
+ (cdr (mapcan (lambda (x) (list "--and" "-e" x))
re))
+ opts paths)
hl))))))))
;;;###autoload
(defun consult-git-grep (&optional dir initial)
- "Search with `git grep' for files in DIR where the content matches a regexp.
-The initial input is given by the INITIAL argument. See `consult-grep'
-for more details."
+ "Search with `git grep' for files in DIR with INITIAL input.
+See `consult-grep' for details."
(interactive "P")
(consult--grep "Git-grep" #'consult--git-grep-make-builder dir initial))
;;;;; Command: consult-ripgrep
-(defun consult--ripgrep-make-builder (search-path-list)
- "Create ripgrep command line builder given SEARCH-PATH-LIST."
- (let* ((cmd (consult--build-args-with-paths consult-ripgrep-args
search-path-list))
+(defun consult--ripgrep-make-builder (paths)
+ "Create ripgrep command line builder given PATHS."
+ (let* ((cmd (consult--build-args consult-ripgrep-args))
(type (if (consult--grep-lookahead-p (car cmd) "-P") 'pcre
'extended)))
(lambda (input)
(pcase-let* ((`(,arg . ,opts) (consult--command-split input))
@@ -4877,21 +4835,20 @@ for more details."
(not (string-match-p "[[:upper:]]" arg)))
(or (member "-i" flags) (member
"--ignore-case" flags)))))
(if (or (member "-F" flags) (member "--fixed-strings" flags))
- (cons (append cmd (list "-e" arg) opts)
+ (cons (append cmd (list "-e" arg) opts paths)
(apply-partially #'consult--highlight-regexps
(list (regexp-quote arg)) ignore-case))
(pcase-let ((`(,re . ,hl) (funcall consult--regexp-compiler arg type
ignore-case)))
(when re
(cons (append cmd (and (eq type 'pcre) '("-P"))
(list "-e" (consult--join-regexps re type))
- opts)
+ opts paths)
hl))))))))
;;;###autoload
(defun consult-ripgrep (&optional dir initial)
- "Search with `rg' for files in DIR where the content matches a regexp.
-The initial input is given by the INITIAL argument. See `consult-grep'
-for more details."
+ "Search with `rg' for files in DIR with INITIAL input.
+See `consult-grep' for details."
(interactive "P")
(consult--grep "Ripgrep" #'consult--ripgrep-make-builder dir initial))
@@ -4919,9 +4876,11 @@ INITIAL is inital input."
:category 'file
:history '(:input consult--find-history)))
-(defun consult--find-make-builder (search-path-list)
- "Build find command line, finding across SEARCH-PATH-LIST."
- (let* ((cmd (consult--build-args-with-paths consult-find-args
search-path-list))
+(defun consult--find-make-builder (paths)
+ "Build find command line, finding across PATHS."
+ (let* ((cmd (mapcan (lambda (x)
+ (if (equal x ".") paths (list x)))
+ (consult--build-args consult-find-args)))
(type (if (eq 0 (call-process-shell-command
(concat (car cmd) " -regextype emacs -version")))
'emacs 'basic)))
@@ -4946,24 +4905,18 @@ INITIAL is inital input."
;;;###autoload
(defun consult-find (&optional dir initial)
"Search for files in DIR matching input regexp given INITIAL input.
-
-The find process is started asynchronously, similar to `consult-grep'.
-See `consult-grep' for more details regarding the asynchronous search."
+See `consult-grep' for details regarding the asynchronous search
+and the arguments."
(interactive "P")
- ;; Must call `consult--directory-prompt' to set local `search-path-list'
- ;; prior to calling `consult--find-make-builder' where the path list is
- ;; used.
- (let* ((prompt-paths-dir (consult--directory-prompt "Find" dir))
- (prompt (nth 0 prompt-paths-dir))
- (search-path-list (nth 1 prompt-paths-dir))
- (default-directory (nth 2 prompt-paths-dir)))
- (find-file (consult--find prompt
- (consult--find-make-builder search-path-list)
initial))))
+ (pcase-let* ((`(,prompt ,paths ,dir) (consult--directory-prompt "Find" dir))
+ (default-directory dir)
+ (builder (consult--find-make-builder paths)))
+ (find-file (consult--find prompt builder initial))))
;;;;; Command: consult-locate
(defun consult--locate-builder (input)
- "Build command line given config and INPUT."
+ "Build command line from INPUT."
(pcase-let ((`(,arg . ,opts) (consult--command-split input)))
(unless (string-blank-p arg)
(cons (append (consult--build-args consult-locate-args)
@@ -4985,7 +4938,7 @@ details regarding the asynchronous search."
;;;;; Command: consult-man
(defun consult--man-builder (input)
- "Build command line given config and INPUT."
+ "Build command line from INPUT."
(pcase-let* ((`(,arg . ,opts) (consult--command-split input))
(`(,re . ,hl) (funcall consult--regexp-compiler arg 'basic t)))
(when re