Last update: 15 February 2020
I recently switched to Doom Emacs from Spacemacs. The Doom documentation is currently quite sparse, so I've posted here my personal notes on using Doom along with a description of my workflow (something I find vital but missing from most documentation). Doom might be for you if
I'm not going into detail on how Emacs works or basic Vim keybindings here---the focus is on the things I found different and useful.
You definitely want to use the
develop branch, not
master! This means:
git clone https://github.com/hlissner/doom-emacs ~/.emacs.d cd ~/.emacs.d git checkout develop
bin/doom quickstart to get everything setup. Run
bin/doom help to see what other commands are available.
I find it useful to manage my work with projects and workspaces. I get two benefits from this: it's faster to find the things I'm looking for as I only look within the current project, and it's faster to get to work as I can save and restore window and buffer configuration. In Doom this means using projectile and persp-mode.
The basic workflow is:
Decide what I'm working on. Is this a distinct project, or is it something that spans multiple projects (such as managing my todo lists) or doesn't need to live in a project (such as doing a quick calculation in a scratch buffer)? If it's a distinct project it gets it's own workspace. If not I'll do it in the default workspace (
Switch to the appropriate workspace with
SPC TAB . If the appropriate workspace is not loaded load it with
SPC TAB l. Otherwise create a new workspace with
SPC TAB n. If I'm creating a new workspace I will often give it a more informative name, using
SPC TAB r and then save with
SPC TAB s.
If I'm switching to an existing workspace Emacs will be returned to the state it was in when I last worked on the project and I can get straight to work. If not I'll select a project for this workspace using
SPC p p and then get to work.
Three very useful commands are:
SPC SPCto switch to a file within the current project, with fuzzy completion; and
SPC ,to switch to a buffer within the current project, again with fuzzy completion.
SPC frto load a recently viewed file.
Things I do all the time:
SPC : for
SPC gg for Magit, which is the only sane way to use Git.
Things I occasionally do:
SPC . to find a file. Using
SPC SPC is faster, so try to use
SPC . only when you want to switch to something outside the current project.
SPC b B to switch to a buffer outside the current project.
If you know there is text somewhere, but you don't know where, there are two basic ways to find it:
SPC sp to search all files in the current project. (Press
SPC s and wait for a popup for other options.) When you have a buffer of matches you can jump to the one your want by pressing return or press
C-c C-e to edit all the files at once. You can then do whatever edits you want and press
C-c C-c to commit, or
C-c C-k to abandon your changes. This is super useful for both simple and complex edits across multiple files.
/ to search within the current buffer. Use
N to go to next and
previous matches. (
? to search backwards.)
If you can see text on the screen and want to move to it there are a number of ways to quickly get there.
s and type two characters to jump forward to the nearest match, and
S to jump backwards. Type
, to cycle to matches earlier in the buffer and
; to cycle through matches later in the buffer. By default these functions only jump to a match on the current line, but if you cycle with
; they'll jump around the whole buffer. This is evil-snipe, which has an amazing cover graphic / logo.
gs SPC, start typing the word you're looking at. You'll either jump directly there or be given a quick key to press to disambiguate the phrase you've typed. If you add
(setq avy-all-windows t) to your
config.el this will work across all visible windows. I love this method for quickly jumping around the screen. This uses Avy.
gs and wait for the popup for a million other search methods. I don't really use them as the two options above do everything I need, but maybe I'm missing out.
If the things I want to jump to is fairly close to where the cursor is I'll use evil-snipe (
S). If it's far away or in another window I'll use Avy (
gs SPC). Both can be used to create text objects. Imagine we have the text
Just some example text
with the cursor at the
J and we want to select
Just some example. We could use
v3e but we have to count the number of words (and remember the difference between
w). Instead we could type
s uses evil-snipe, and
le is the two characters we're sniping. We could also use
vgsSPCle to use Avy (and we'd then have to move the cursor one character right).
There is a bit of complexity using evil-snipe. It's bound to
s for visual selection, but other commands (e.g. yank or
s to evil-surround. In these cases evil-snipe is bound to
Z to snipe backwards). If we don't want include the letters we're sniping in the text object we can use
X for backwards sniping).
It can be useful to restrict a buffer to a selection of text, called narrowing in Emacs. This is particularly useful with multiple cursors (see below). To narrow to the current selection use
SPC rn and use
SPC rw to perform the reverse, called widening. Narrowing to the current function, with
SPC rf, is also useful.
Multiple cursors allow you to edit multiple things at a time. Doom provides two implementations of multiple cursors, evil-multiedit and evil-mc. I find evil-multiedit easier to use, but it is less powerful.
There are two ways to start editing with evil-multiedit:
select one example of the things you want to edit and press
R to select all the rest in the current buffer (perhaps narrowing first); or
move the cursor over one example of the word you want to edit and then repeatedly press
M-D to select next or previous examples respectively.
Having selected some regions you can move between them using
C-n (next) and
C-p (previous). Pressing
RET on a region will remove it from the selection, so by using these commands you can choose arbitrary regions to include.
Once you have selected the regions, edits made to one region will be reflected in all the others. The majority of evil commands work with evil-multiedit.
Finally use the usual cancel key (
C-[) to exit out of evil-multiedit.
The other multiple cursor implementation, evil-mc, is a bit more flexible but harder to use. There are more options for creating multiple cursors with evil-mc:
gzmto create cursors at all matches for the word at point (possibly narrowing the buffer first);
gzdto create a cursor at point and move to the next match (and
gzDto create and move to the previous match);
gzjto create a cursor at point and move to the next line (and
gzkto create and move to the previous line); or
gzzto create a cursor at point.
Cursors will mirror the commands you enter at the "real" cursor. You can temporarily turn off multiple cursors with
gzt. This is automatically done if you create a cursor at point with
gzz. Start the cursors again with the same key combo,
Most commands work with evil-mc but a few commands I commonly use do not:
DEL(backspace) in insert mode will not be repeated across all cursors in some modes; and
ysiwdoesn't work across all cursors in all the modes I tried (though
ciw, for example, does).
If the cursors get out of sync undoing a few commands usually sorts things out.
When you've finished with multiple cursors press
gzu to remove them all.
u is undo, but this uses the
undo-tree system which is a bit more
complicated that you might be used to.
M-_ is redo
C-x u shows the undo tree.
SPC cd to go to the definition of the symbol at point.
SPC cD to list references to the symbol at point.
K to lookup documentation for the symbol at point. Uses Dash docsets if you
have them installed and the major mode is correctly configured.
SPC p c or
M-b to compile (build)
[e for next and previous flycheck error, respectively
SPC c x to list current errors (from flycheck)
gccto comment or uncomment the current line or selection;
SPC oAfor the Org agenda;
SPC xfor a temporary buffer for random notes;
M--to decrease, and
M-+to reset it.
I learned most of what I know about Doom by reading through the default keybindings and looking up commands I didn't recognise. It's easy to experiment in a scratch buffer to figure out what a command does.
Learning Doom has been a gradual process for me. I didn't adopt all the above at once! In fact many things I only figured out when I came to write this document.