I have already discussed how one can
leverage ugrep and consult-xref to make a completing-read variant of
project-find-regexp.
However, today I would like to go a bit further and show how easily Consult can
be extended to include a command that behaves like consult-grep,
consult-git-grep, and consult-ripgrep, but with ugrep as its engine. True,
something along these lines has been
around for a while. And yet I have
no need for an external package when Consult has everything in place in order to
devise a custom command.
By inspecting the code behind consult-git-grep and consult-ripgrep the
pattern is pretty clear:
- a
defcustomwith the command (e.g.,git grep) and its arguments - a builder function to create the actual command to be invoked (e.g.,
consult--git-grep-make-builder) - a wrapper around
consult--grep(e.g.,consult-git-grep)
Therefore, the solution is simple:
(defvar mu-consult-ugrep-args (string-join '("ugrep"
"--color=never"
"--exclude-dir=.git/"
"--hidden"
"--ignore-binary"
"--ignore-case"
"--line-buffered"
"--line-number"
"--null"
"--recursive")
" "))
(defun mu-consult-ugrep-make-builder (paths)
"Create ugrep command line builder given PATHS."
(let ((cmd (consult--build-args mu-consult-ugrep-args)))
(lambda (input)
(pcase-let* ((`(,arg . ,opts) (consult--command-split input))
(`(,re . ,hl)
(funcall consult--regexp-compiler arg 'extended t)))
(when re
(cons (append cmd
(cdr (mapcan (lambda (x) (list "--and" "-e" x)) re))
opts paths)
hl))))))
(defun mu-consult-ugrep (&optional dir initial)
"Search with `ugrep' for files in DIR with INITIAL input."
(interactive "P")
(require 'consult)
(consult--grep "Ugrep" #'mu-consult-ugrep-make-builder dir initial))
Since I am always using the same ugrep options, my builder function is a bit
simpler than consult--git-grep-make-builder or
consult--ripgrep-make-builder. Other than that, it’s only a matter of
understanding the right options for ugrep.
Building on top of Consult is great. Much like consult-git-grep and
consult-ripgrep, the integration with Embark is around the corner. And now
that the power of the minibuffer is available during my searches I have no use
for mu-recursive-grep anymore.