Doom Emacs common default keybindings
A categorized cheat sheet of commonly used Doom Emacs keybindings, written as a companion to my Neovim-to-Doom migration post. All sequences use the default SPC leader in evil mode unless noted otherwise.
Search
Doom splits what Telescope used to do as one tool across a few focused commands, mostly under the SPC s ("search") prefix, backed by consult and vertico abd SPC f ("find").
Think of search for like a semi-manual search (i want to look inside dir called x for x). and as find as i don't know where x is and i want to find it.
| Keybinding | Action |
|---|---|
SPC s s | Search (fuzzy) inside the current buffer |
SPC s S | Search current buffer, pre-filled with symbol at point |
SPC s b | Search across all open buffers |
SPC s p | Search (grep) across the whole project |
SPC s P | Search across a different (non-current) project |
SPC * or SPC s * | Search project for the symbol/word at point |
SPC s d | Search a specific directory |
SPC s i | Jump to a symbol (function, variable) in the current buffer (imenu) |
SPC s I | Jump to a symbol across the whole project |
SPC s r | Resume the last search session |
SPC / | Shorthand alias for project-wide search |
SPC SPC | Fuzzy find a file by name in the current project |
SPC . | Fuzzy find a file relative to the current directory |
A few notes on regex vs. literal search: project search (SPC s p) is backed by ripgrep, which treats your query as a regex-capable pattern by default — no special mode-switch needed the way Telescope sometimes required. Inside the search minibuffer, you can keep typing to narrow results live, and C-n / C-p (or j / k if you're in evil-collection's minibuffer bindings) move between matches before you hit RET to jump.
Search and replace
Because evil mode emulates real vim, the classic :s substitute command works exactly like it did in Neovim — no new syntax to learn here, just a different set of tools for the project-wide case.
Single line (cursor's current line only):
| Command | Action |
|---|---|
:s/old/new/ | Replace the first match on the current line |
:s/old/new/g | Replace all matches on the current line |
/old then cgn | Jump to the next match of old and change it; repeat with . to step through remaining matches one at a time, reviewing each |
Whole buffer:
| Command | Action |
|---|---|
:%s/old/new/g | Replace every match in the buffer |
:%s/old/new/gc | Replace every match in the buffer, prompting y/n/a/q/l to confirm each one |
Visual-select lines, then : | Auto-fills :'<,'>s/old/new/g, scoping the replace to just the selected region |
Evil's :s uses vim-flavored regex syntax (\(...\) for groups, \d, \w, etc.), not native Emacs regex — so muscle memory from Neovim's :%s transfers directly, which was one of the smoother parts of this whole switch.
Project-wide, where Doom departs from a single Telescope-style command and gives you two real options:
- Quick and blunt —
M-x projectile-replacefor a literal string replace across the whole project, orM-x projectile-replace-regexpfor a regex-based one. Projectile walks you through matches file by file with a confirmation prompt. No leader-key binding for this by default, so it's worth binding one if you use it often. - Visual and reviewable — run
SPC s pto ripgrep the project, export the results into a real, editable buffer (viaembark-export, typically onC-c C-efrom the search results — exact key can vary by Doom version, check withSPC h kif it doesn't fire), then flip that results buffer intowgrep-mode(C-c C-p). At that point it behaves like any other buffer: run:%s/old/new/gacross it, eyeball the diff-style view, thenC-c C-eto write the changes back out to every affected file, orC-c C-kto bail out without touching anything.
I lean on option 2 for anything risky — being able to see every change across every file before committing to it is a step up from a blind project-wide replace.
Windows
Window management borrows from vim's C-w prefix but also has a full SPC w leader-based equivalent, which is what I default to.
| Keybinding | Action |
|---|---|
SPC w v | Split window vertically (side by side) |
SPC w s | Split window horizontally (stacked) |
SPC w c or SPC w d | Close the current window |
SPC w o | Close all other windows (keep only the current one) |
SPC w m | Toggle maximize on the current window (zoom in/out) |
SPC w w | Cycle focus to the next window |
SPC w h/j/k/l | Move focus left / down / up / right |
SPC w H/J/K/L | Move the window itself (swap position) left / down / up / right |
SPC w = | Balance all windows to equal size |
SPC w r | Rotate windows forward |
SPC w R | Rotate windows backward |
SPC w u | Undo the last window layout change |
SPC w U | Redo a window layout change |
SPC 1–SPC 9 | Jump directly to window number N |
The vim-native C-w versions (C-w v, C-w s, C-w c, C-w =, C-w o) still work if that's what your fingers already know — Doom doesn't take those away.
Git
Doom's git interface is Magit, which is one of the bigger quality-of-life upgrades coming from a terminal-git-plus-fugitive workflow. Rather than a thin keybinding wrapper, Magit gives you a full interactive status buffer: you stage and unstage individual hunks or even individual lines, write commit messages in a dedicated buffer with syntax highlighting, and see diffs inline without leaving the window. It's discoverable — pressing ? almost anywhere in Magit shows you every available action in that context, which made it easy to pick up without memorizing everything up front.
The general workflow I use daily: open the status buffer, stage the hunks I want with s, commit with c c, and push with P p. Everything happens without touching a separate terminal.
| Keybinding | Action |
|---|---|
SPC g g | Open Magit status (the main git dashboard) |
SPC g b | Show git blame for the current line/region |
SPC g B | Open the current line/file on the remote (GitHub, etc.) in your browser |
SPC g L | Open the Magit log for the current buffer/project |
SPC g f | Find/open a file as it exists in a git revision |
SPC g t | Open git-timemachine to step through a file's history |
SPC g C | Clone a repository |
SPC g F | Fetch from remote |
Inside the Magit status buffer itself (not leader-key bindings, just single keys):
| Key | Action |
|---|---|
s | Stage the hunk/file at point |
u | Unstage the hunk/file at point |
c c | Start a commit |
P p | Push to the current branch's upstream |
F p | Pull from upstream |
b b | Checkout a branch |
RET | Visit the thing at point (file, commit, hunk) |
g r | Refresh the status buffer |
q | Quit back out of Magit |
| 'd w' | diff compare: working tree to HEAD |
| 'd r' | diff compare: range compare |
Notes
- To get the diff of only the current file in magit use
M-x magit-diff-buffer-file
Terminal
| Keybinding | Action |
|---|---|
SPC o t | Toggle a popup terminal (vterm) at the project root |
SPC o T | Open a dedicated (non-popup) terminal buffer |
vterm behaves a bit differently than a typical Emacs buffer: while you're in insert state, most keystrokes pass straight through to the underlying shell, since it's a real terminal emulation rather than a text buffer. Drop into evil's normal state (ESC) to scroll back, visually select, or yank terminal output the way you would in a normal buffer.
Buffers & Files
| Keybinding | Action |
|---|---|
SPC b b | Switch to another open buffer |
Ctl 6 | Switch to the latest buffer |
SPC b d | Kill (close) the current buffer |
SPC b n | Go to the next buffer |
SPC b p | Go to the previous buffer |
] b | Next buffer (Doom's "bracket" navigation convention) |
[ b | Previous buffer |
SPC f f | Find/open a file |
SPC f s | Save the current file |
SPC f r | Open a recently-opened file |
SPC f y | Copy the current file's path to the clipboard |
Doom's bracket convention ([ x / ] x) shows up in a few other places too — ] e / [ e for next/previous error, ] t / [ t for tabs, and so on. Once you notice the pattern it applies more broadly than it first appears.
Code Navigation & LSP
| Keybinding | Action |
|---|---|
SPC c d | Jump to definition |
SPC c D | Jump to references |
g d | Jump to definiition |
g D | Jump to references |
SPC c r | Rename symbol |
SPC c a | Show code actions |
SPC c f | Format the buffer/region |
K | Show documentation/hover info for symbol at point |
SPC h k | Describe a keybinding (what does this key actually do?) |
Workspaces
| Keybinding | Action |
|---|---|
SPC TAB TAB | open workspaces bar |
SPC TAB 1 | open workspaces number 1 |
Optional but commonly used plugins
Doom ships with a lot enabled out of the box (projectile, which-key, company, doom-modeline), but these are worth knowing about since they come up often in the Doom ecosystem and are easy to enable via module flags in ~/.doom.d/init.el or as extra packages in ~/.doom.d/packages.el:
- treemacs — a file-tree sidebar, similar to nvim-tree or neo-tree. Toggle with
SPC o p. - org-roam — networked note-taking/zettelkasten built on top of org-mode, popular for personal knowledge management.
- magit-todos — surfaces
TODO/FIXMEcomments from your codebase inside Magit's status buffer. - corfu — a lighter-weight alternative to the default
companycompletion frontend, if you want something closer to a minimal popup. - flycheck (or flymake on newer configs) — inline linting/diagnostics, usually pulled in automatically by language modules.
- apheleia or format-all — auto-formatting on save, handy if you're used to
conform.nvimor similar. - centaur-tabs — adds a visible tab bar across the top of the frame, if you miss bufferline-style tabs from Neovim.
After enabling any module or package, run doom sync and restart Emacs for it to take effect — that deliberate step is exactly the thing I appreciated after fighting silent breakage in Neovim.

