How to Do a Project-wide Search & Replace in Doom Emacs
This is a guide on how to do a project-wide text search and replace operation using Doom Emacs and all its preinstalled packages and keybindings.
Earlier this week I went looking for “how to search and replace in a whole project” in Emacs. I knew had Doom Emacs set up with evil turned on by default, and I knew to use SPC s p
to launch a project text search. What was next…?
Today, I’m documenting the process so that I won’t forget again. Hopefully it’ll help someone out there too.
TL;DR:
- for ivy module users
SPC s p foo C-c C-e :%s/foo/bar/g RET Z Z
- for vertico module users
SPC s p foo C-; E C-c C-p :%s/foo/bar/g RET Z Z
Entering those keys will replace “foo” with “bar” in your whole project. Magic!
Not sure if you’re using ivy or vertico? If you set up Doom Emacs after
, you’re probably using vertico. Otherwise, probably ivy. Check yourinit.el
file to see which is active1
The Long Explanation
- Make sure you’re in the right project and all your open buffers/files are saved. If you’re making a large change, maybe a
git commit
would give you a little extra insurance. SPC s p
to launch the project search. This is what you would normally use to search strings or regular expressions2 within your project. I’ve setivy-more-chars-alist
to not return any results until at least 3 characters have been entered3, otherwise counsel can be a little overzealous in returning results.- Type in a search string or regular expression,
foo
in this example, to get some results. This doesn’t have to be exactly what you want to replace; it just needs to be specific enough to get the right results into view. - This step depends on which completion engine you’ve set up for Doom.
- for ivy users1
C-c C-e
to transform the search results into a wgrep or writable grep buffer. This calls+ivy/woccur
, which is equivalent to pressingC-c C-o C-c C-p
(i.e.ivy-occur
for turning ivy results into a buffer andwgrep-change-to-wgrep-mode
to make it writable). for vertico users1
C-;
to runembark-act
. This Embark command is like a context menu for what you’re currently looking at.E
to runembark-export
, which exports what you’re looking at into a new buffer. Embark is smart enough to know that you’re looking at grep search results, so it will export to a new buffer ingrep-mode
C-c C-p
to runwgrep-change-to-wgrep-mode
to make the search results writable.
- for ivy users1
Use your preferred method of search and replace in a single file. Doom relies heavily on evil’s vim emulation, so the smoothest option for me was to type
:%s/foo/bar/g
to preview the changes and hittingRET
to apply.You can also edit the buffer manually using any technique you want.
M-%
orC-M-%
to do a regular Emacs query replace? Sure.C-x SPC
orC-v
to do a rectangle/visual block edit? Go for it.g z z
for evil-mc multiple cursors? No worries—just be careful of the readonly region of filenames on the left.At this stage nothing has been written to the files yet, so you can still undo easily. Go through all the lines in the search results to check if the changes are as expected.
You can also
SPC : wgrep
orM-x wgrep
to look at the other advanced wgrep actions.- Hit
Z Z
to write all the changes to their respective files, orZ Q
to abort the search and replace.
Footnotes:
This post was originally written back when ivy was still the default completion engine used by Doom Emacs. However, since this commit on , new users of Doom will be using the vertico completion stack. Existing users upgrading to the current version of Doom will keep using Ivy.
How can you tell if you’re using Ivy or vertico? Have a look at your init.el
file in your Doom config to check if ivy
or vertico
are present and uncommented. Whichever one is uncommented is the one you’re using.
Just a reminder that Emacs has its own idiosyncratic version of regular expressions. It’s weird, I know.
I have this in my config.el
to set the minimum characters to 3 before a search is fired. You can customize the threshold individually for each of the commands.
(after! ivy (setq ivy-more-chars-alist '((counsel-grep . 3) (counsel-rg . 3) (counsel-search . 3) (t . 3))))