Emacs and ripgrep
I've been using ripgrep for a number of years now. Before it came on the scene I
remember using the early versions of ack and ag, but it appears
ripgrep won in
the end and deservedly so. I'm guessing BurntSushi's relentless focus on making
it fast is why it eventually became the most popular.
ripgrep is so exceptionally fast, I use it multiple times a day to find
references in code, track down dead code and general searching in large code
However, I've noticed that I usually call it from the terminal and as an avid
Emacs user that cannot stand. Emacs has excellent integration with Grep, but it
ripgrep by default. It does however support
git grep through
its Version Control integration, which is what I've been using this far.
Due to the popularity of
ripgrep there are multiple packages that provide
integration with Emacs, such as deadgrep, rg.el and counsel-ag. However, since
my needs for
rigrep integration are basic I wanted to see if I could just
implement it myself.
Before switching to
ripgrep I used to call
git grep by using the
package in Emacs. When invoked the function prompts for the pattern and then
git grep in the root directory of the Git repository of the current file.
This is basically how I generally want Grep integration in Emacs to work: I want to grep interactively from any file and have it default to recursively searching the repository root.
This was easy enough to implement with
git grep as it's just a thin wrapper
(defun mpolden/vc-git-grep () "Run git grep interactively in the current VC tree." (interactive) (vc-git-grep (grep-read-regexp) "" (vc-root-dir)))
A few searches for "emacs ripgrep" led me to this useful blog post: Blazing-fast
jump-to-grep in Emacs using ripgrep - apparently you can customize the command
grep.el by setting the
While the examples in the blog post were fine I didn't copy them outright. I use Emacs on multiple machines and therefore I try to ensure that any customizations I add to my emacs.d continue to work even if preferred programs are not installed.
I therefore decided to my custom grep code should support both
grep, preferring them in that order and falling back gracefully if
ripgrep isn't installed.
After a few iterations I ended up with the following:
(defvar mpolden/rg-template "rg -nH --no-heading <C> -e <R> -- <F>" "The grep template to use when rg (ripgrep) is installed.") (defvar mpolden/git-grep-template "git --no-pager grep -n <C> -e <R> -- <F>" "The grep template to use when git is installed. This is only used when running grep in a Git repository.") (defun mpolden/grep () "Recursively grep in `default-directory' or current Git repository. This tries to use either rg or git grep if available, and otherwise falls back to regular grep." (interactive) (let* ((git-root-dir (locate-dominating-file default-directory ".git")) (dir (or git-root-dir default-directory)) (use-rg (executable-find "rg")) (use-git (and git-root-dir (executable-find "git"))) (grep-template (cond (use-rg mpolden/rg-template) (use-git mpolden/git-grep-template)))) (grep-apply-setting 'grep-template grep-template) ;; never use null device as all programs support -H (grep-apply-setting 'grep-use-null-device nil) (if (or use-rg use-git) (lgrep (grep-read-regexp) "" dir) (rgrep (grep-read-regexp) "*" dir))))
First, we detect if the file we're visiting is inside a Git repository. If it is we recursively grep starting at the root of the repository, if not we recursively grep in the directory of the file we're visiting.
Second, we prefer using
ripgrep if it's in
PATH and otherwise we fall back
git grep if we're in a Git repository and
git is installed. Finally we
fall back to regular
grep if neither of the programs are available.
Finally we call either
rgrep depending on if the program we
selected searches recursively by default. Since we now detect the Git repository
directly and only use functions from
grep we no longer need
vc-git at all.
That's it. Now I can use
ripgrep from within Emacs with just a few lines of
(My complete Grep integration is available here.)