DWR.IO

Neovim / LazyVim Cheatsheet

Published on 08/05/2026

I've been a long-time Vim user, but only in the past couple years was I introduced to Neovim. I was really impressed by it overall, but I really ended up missing the comfort of all the settings and everything that I had in place with my standard Vim setup.

As a part of my more current workflow, I thought I'd give Neovim another shot and give it a little bit more time to get used to it. To help me out, I worked with Claude to put together a cheat sheet with a bunch of shortcuts and commands that I'd likely reach for on a day-to-day basis.

Note this is meant to serve as a reference for someone who already knows Vim. Core Vim motions, operators, text objects, registers, and Ex commands all work the same as they do in Vim. This document focuses on what LazyVim adds or changes on top of stock Vim, plus the Neovim-specific pieces that don't exist in Vim 8.

Leader key is <Space>. Local leader is \. See Notation for how key combinations are written throughout.

Version note: LazyVim moves fast. Two things have shifted recently and you may see either set depending on when your config was scaffolded:

  • Picker: older setups use telescope.nvim, newer ones use snacks.picker. The keymaps below are nearly identical either way.
  • File explorer: older setups use neo-tree, newer ones use snacks.explorer. Same <leader>e binding.
  • Surround: the mini.surround prefix changed from gz to gs. If gsa does nothing, try gza.

The authoritative answer for your install is always <leader>sk (search keymaps) — see Discoverability.


Table of Contents

  1. Notation: leaders and modifiers
  2. Discoverability
  3. LazyVim's changes to core Vim behavior
  4. Files, buffers, and pickers
  5. Search
  6. File explorer
  7. Windows, splits, and tabs
  8. LSP: navigation and code actions
  9. Diagnostics and Trouble
  10. Completion and snippets
  11. Git
  12. Terminal
  13. Motion: flash.nvim
  14. Surround, comments, and text objects
  15. Toggles
  16. Sessions and project management
  17. Plugin and tooling management
  18. Debugging and testing
  19. Neovim features Vim doesn't have
  20. Web dev specifics
  21. Config layout
  22. Troubleshooting
  23. Getting back up to speed

Notation: leaders and modifiers

Modifiers

NotationKey
<C-x>Ctrlnot Command
<S-x>Shift
<A-x> or <M-x>Alt / Meta (Option on macOS)
<D-x>Command / Super — GUI clients only
<leader>Leader key (Space)
<localleader>Local leader (\)

Other special keys you'll see: <CR> (Enter), <Esc>, <Tab>, <BS> (Backspace), <Space>, <Up/Down/Left/Right>, <F1><F12>, <C-_> (see below).

Combinations stack: <C-S-p> is Ctrl+Shift+P. A sequence without brackets is pressed in order — <leader>ff means Space, then f, then f.

<D- (Command) does not work in a terminal. Terminals cannot transmit the Command key, so it's only available in GUI clients like Neovide, VimR, and goneovim. Running inside a terminal multiplexer, treat Command as nonexistent — every Cmd+P / Cmd+Shift+F / Cmd+S reflex needs a new home (<leader><space>, <leader>/, <C-s>).

<S- with letters is redundant. Shift+letter is the uppercase letter, so <S-h> and H are the same mapping — LazyVim writes it verbosely for symmetry with <S-l>. Shift notation is only strictly necessary for non-letter keys: <S-Tab>, <S-CR>, <S-F5>, <S-Insert>.

Terminal modifier limits. Standard terminals collapse many Ctrl and Ctrl+Shift combinations into identical byte sequences, which is why almost no config binds <C-S-x>. If you want full modifier fidelity, use a terminal supporting the kitty keyboard protocol — Kitty, WezTerm, Ghostty, or recent Alacritty.

Two specific casualties worth knowing:

Leader vs. local leader

<leader> is for global mappings, active in every buffer. <localleader> is for buffer-local, filetype-specific mappings — the convention exists so a LaTeX or Org plugin can claim a dozen keys without colliding with your global namespace.

LazyVim core defines essentially zero <localleader> mappings. It sets the variable so plugins expecting it behave correctly. You'll only encounter it via plugins like vimtex, quarto, neorg, molten, or iron.nvim. For PHP/JS work you may never press it.

Note that stock Vim and Neovim default both leaders to \. LazyVim moves leader to Space and leaves localleader at the vanilla default — so if you never set mapleader in your .vimrc, your old Vim leader is now your local leader.

Checking your values:

:lua print(vim.inspect(vim.g.mapleader))
:lua print(vim.inspect(vim.g.maplocalleader))

Changing them — ordering is critical. Leader is resolved at mapping-definition time, not at press time. Set it after plugins load and existing mappings keep the old leader while new ones use the new value, producing a half-broken config that's genuinely hard to diagnose. In LazyVim the correct place is lua/config/options.lua, which loads before plugin setup:

-- lua/config/options.lua
vim.g.mapleader = " "
vim.g.maplocalleader = ","

("\\" in Lua is an escaped backslash — a single \ character.)

Discoverability (the most important section)

Do not memorize this document. Memorize these four things and let the editor teach you the rest.

KeyAction
<leader> then waitwhich-key popup shows every binding under that prefix. Works for any prefix: <leader>g, <leader>c, g, z, ], [
<leader>skSearch all keymaps in a fuzzy picker — the real source of truth
<leader>shSearch help tags
<leader>scSearch commands

Mnemonic scheme. LazyVim's prefixes are consistent, which is what makes them learnable:

PrefixDomain
<leader>ffind / file
<leader>ssearch
<leader>ccode (LSP, formatting, actions)
<leader>ggit
<leader>bbuffer
<leader>wwindow
<leader>xdiagnostics / trouble (examine)
<leader>uuI toggles
<leader>qquit / session
<leader>ddebug (requires DAP extra)
<leader>ttest (requires neotest extra)

Learn the prefix, then use which-key for the second key. That's the whole system.

LazyVim's changes to core Vim behavior

These are the ones that will feel "wrong" coming from stock Vim. Worth reading carefully.

KeyBehaviorDifference from Vim
j / kMove by display line when no count givenVim moves by logical line. With a count (5j) it reverts to logical lines
<Esc>Also clears search highlightVim leaves hlsearch on
<C-s>Save file (works in insert mode too)Not bound in Vim
<C-h/j/k/l>Move between windowsVim needs <C-w>h etc. (<C-w> prefix still works)
<C-Up/Down/Left/Right>Resize current windowNot bound in Vim
<A-j> / <A-k>Move current line (or visual selection) up/downNot bound in Vim
<S-h> / <S-l>Previous / next bufferVim: top/bottom of screen. Use H/L… which are now taken. Use gg/G or zt/zb mentally instead
n / NSearch next/prev, always centered and direction-consistentVim doesn't recenter
< / > in visualIndent and stay in visual modeVim drops out of visual mode
p in visualPaste without clobbering the registerVim replaces the unnamed register
gco / gcOAdd commented line below / above and enter insertNot in Vim
<C-/> or <C-_>Toggle terminalNot in Vim

Things that are unchanged and worth remembering: all operators (d, c, y, >, =, gu, gU, g~), all text objects (iw, ap, i", it, ab), all motions (f, t, %, {, }, (, ), [[, ]]), marks, macros (q, @), registers ("a), . repeat, Ctrl-o/Ctrl-i jumplist, g;/g, changelist.

Files, buffers, and pickers

Finding files

KeyAction
<leader><space>Find files in project root (the workhorse)
<leader>ffFind files in root dir
<leader>fFFind files in cwd (ignores root detection)
<leader>fgFind files tracked by git
<leader>frRecent files
<leader>fRRecent files (cwd)
<leader>fcFind config file (your Neovim config)
<leader>fnNew file

Root dir vs cwd: LazyVim auto-detects a "root" per buffer using LSP workspace, then .git, then falls back to cwd. In a monorepo or a multi-plugin WordPress project this matters a lot. Uppercase variants (<leader>fF, <leader>sW) generally mean "use cwd instead of root."

Buffers

KeyAction
<leader>,Switch buffer (picker)
<leader>fbBuffer picker
<S-h> / [bPrevious buffer
<S-l> / ]bNext buffer
<leader>bb or <leader>`Switch to other (last) buffer
<leader>bdDelete buffer, keep the window layout
<leader>bDDelete buffer and close the window
<leader>boDelete all other buffers
<leader>bpToggle pin buffer
<leader>bPDelete all non-pinned buffers
<leader>bl / <leader>brDelete buffers to the left / right

Inside a picker

KeyAction
<C-j> / <C-k> or <C-n> / <C-p>Next / previous item
<CR>Open
<C-v>Open in vertical split
<C-s>Open in horizontal split
<C-t>Open in new tab
<C-q>Send results to quickfix list
<Tab>Toggle multi-select
<C-/> (telescope) or ? (snacks)Show picker-specific keymaps
<Esc> twiceClose
KeyAction
<leader>/Grep the project (live grep) — use this constantly
<leader>sgGrep in root dir
<leader>sGGrep in cwd
<leader>swGrep the word under cursor (visual mode: grep the selection)
<leader>sWSame, in cwd
<leader>sbFuzzy search within current buffer
<leader>ssSearch symbols in current document (LSP)
<leader>sSSearch symbols across workspace (LSP)
<leader>srSearch and replace across project (grug-far / spectre)
<leader>sdDocument diagnostics
<leader>sDWorkspace diagnostics
<leader>stTODO / FIXME / HACK comments
<leader>sTTODO/FIX/FIXME only
<leader>shHelp pages
<leader>skKeymaps
<leader>scCommand history
<leader>sCCommands
<leader>s"Registers
<leader>smMarks
<leader>sjJumplist
<leader>sqQuickfix list
<leader>slLocation list
<leader>saAutocommands
<leader>sHHighlight groups (useful when writing a colorscheme override)
<leader>uCColorscheme picker with live preview
]t / [tNext / previous TODO comment

Grep syntax tip: the live grep uses ripgrep. You can pass rg flags inline in most setups by typing your pattern, then -- -g '*.php' to restrict to a glob. Check ? inside the picker for your version.

File explorer

<leader>e toggles the explorer at the project root. <leader>E opens at cwd. (In neo-tree setups, <leader>fe / <leader>fE.)

KeyAction (inside the tree)
<CR> / oOpen file or expand directory
aAdd file. End the name with / to create a directory. Supports foo/bar/baz.php to create nested paths
AAdd directory
dDelete
rRename
cCopy
mMove
y / x / pCopy / cut / paste
YCopy relative path to clipboard
HToggle hidden files
S / sOpen in horizontal / vertical split
tOpen in new tab
PToggle preview
RRefresh
.Set the selected directory as root
<BS>Navigate up one directory
?Show all explorer keymaps
qClose

Neo-tree also has source tabs: < and > cycle between filesystem, buffers, and git status views. The git status view is a fast way to see and stage changed files.

Windows, splits, and tabs

KeyAction
<leader>- or <leader>w-Split window below
<leader>| or <leader>w|Split window right
<leader>wdClose window
<leader>wmToggle maximize current window
<C-h/j/k/l>Navigate windows
<C-Up/Down/Left/Right>Resize window
<leader>uZZen mode (single, centered, distraction-free)
<leader>uzZoom mode

Tabs (Vim tabs = window layouts, not "tabs" in the VS Code sense — your buffers are the VS Code tabs):

KeyAction
<leader><tab><tab>New tab
<leader><tab>] / <leader><tab>[Next / previous tab
<leader><tab>dClose tab
<leader><tab>fFirst tab
<leader><tab>lLast tab

LSP: navigation and code actions

This is the part that replaces most of what you used VS Code for. <leader>cl shows LSP info for the current buffer.

KeyAction
gdGo to definition
gDGo to declaration
grGo to references (picker)
gIGo to implementation
gyGo to type definition
KHover documentation (press twice to enter the float and scroll)
gKSignature help
<C-k> (insert)Signature help
<leader>caCode action
<leader>cASource action (organize imports, etc.)
<leader>crRename symbol (project-wide)
<leader>cRRename file (and update imports, where supported)
<leader>cfFormat buffer (also runs on save by default)
<leader>cFFormat injected languages
<leader>ccRun codelens
<leader>cCRefresh codelens
<leader>ciLSP incoming calls
<leader>coLSP outgoing calls
<leader>csDocument symbols (outline)
<leader>cSToggle symbols outline sidebar (aerial/outline extra)
<C-o>Jump back after gd — this is the one people forget
<C-i>Jump forward

Formatting control: <leader>uf toggles autoformat globally, <leader>uF toggles it for the current buffer only. Very useful when you open a legacy file you don't want to reformat entirely.

Diagnostics and Trouble

KeyAction
]d / [dNext / previous diagnostic
]e / [eNext / previous error
]w / [wNext / previous warning
<leader>cdLine diagnostics (float)
<leader>xxToggle Trouble: document diagnostics
<leader>xXToggle Trouble: workspace diagnostics
<leader>xLLocation list in Trouble
<leader>xQQuickfix list in Trouble
<leader>xtTODO comments in Trouble
<leader>csSymbols in Trouble
<leader>udToggle diagnostics on/off
[q / ]qPrevious / next quickfix item (works everywhere)

Completion and snippets

LazyVim uses blink.cmp (newer) or nvim-cmp (older). Bindings are close to identical.

KeyAction
<C-Space>Trigger completion
<C-n> / <C-p> or <C-j> / <C-k>Next / previous item
<CR>Confirm
<Tab>Confirm / jump to next snippet placeholder
<S-Tab>Jump to previous snippet placeholder
<C-e>Abort completion
<C-b> / <C-f>Scroll the documentation window

Native Vim completion still works and is sometimes faster: <C-x><C-f> for file paths, <C-x><C-l> for whole lines, <C-n> for buffer words.

Git

KeyAction
<leader>ggLazyGit (root dir) — the main event
<leader>gGLazyGit (cwd)
<leader>gbGit blame line
<leader>gBOpen current line/selection in the browser (GitHub/GitLab)
<leader>gfGit history for current file
<leader>glGit log (root)
<leader>gLGit log (cwd)
<leader>gsGit status picker
<leader>gdGit diff (hunks)
<leader>gSGit stash picker

Gitsigns (in-buffer hunks)

KeyAction
]h / [hNext / previous hunk
]H / [HLast / first hunk
<leader>ghsStage hunk (works on a visual selection too)
<leader>ghrReset hunk
<leader>ghSStage entire buffer
<leader>ghuUndo stage hunk
<leader>ghRReset buffer
<leader>ghpPreview hunk inline
<leader>ghbBlame line (full)
<leader>ghdDiff this file
ihText object: inside hunk — use dih, vih, yih

Terminal

KeyAction
<C-/> or <C-_>Toggle floating terminal at root dir
<leader>ftTerminal at root dir
<leader>fTTerminal at cwd
<Esc><Esc>Leave terminal insert mode (back to normal mode)
<C-/>Hide the terminal from inside it
i / aRe-enter terminal insert mode

In a Herd session specifically: you may want to skip the built-in terminal entirely and let the multiplexer own your panes — one pane for Neovim, one for the shell, one for an agent. If you go that route, be aware <C-/>, <C-h/j/k/l>, and <S-h>/<S-l> are the bindings most likely to collide with a multiplexer prefix. Check for conflicts early; remapping the multiplexer prefix is usually easier than remapping LazyVim.

Motion: flash.nvim

This is the biggest genuinely-new motion capability versus stock Vim. It replaces f/t hunting and EasyMotion.

KeyAction
sFlash jump. Type 1–2 characters, labels appear on every match, press the label to jump
SFlash Treesitter. Labels appear on syntax nodes — jump to a function, a block, an argument
r (operator-pending)Remote flash — e.g. yr then jump somewhere and yank a text object there, cursor returns
R (operator-pending/visual)Treesitter search
<C-s> (in command mode)Toggle flash while typing a / search

s and S work as operator targets: ds<char><label> deletes to that point. Very fast once it's muscle memory.

Note: s in stock Vim is "substitute character" (= cl). LazyVim rebinds it. Use cl if you miss it.

Surround, comments, and text objects

mini.surround

Prefix is gs in current LazyVim, gz in older versions.

KeyAction
gsaAdd surround (visual mode, or gsaiw" to wrap a word in quotes)
gsdDelete surround — gsd" removes surrounding quotes
gsrReplace surround — gsr"' changes double to single quotes
gsf / gsFFind surround to the right / left
gshHighlight surround
gsnUpdate n lines for surround search

For HTML/JSX/Blade, gsat adds a tag; gsdt deletes the surrounding tag.

Comments

KeyAction
gccToggle comment on current line
gc + motionComment a motion — gcap comments a paragraph, gc3j three lines
gc (visual)Comment selection
gbcToggle block comment on line
gcoNew commented line below and enter insert
gcONew commented line above and enter insert
gcAAppend a comment at end of line and enter insert

Treesitter text objects

These make code manipulation far better than Vim's paragraph-based guessing.

ObjectMeaning
af / ifA function / inside a function
ac / icA class / inside a class
aa / iaAn argument/parameter / inside it
ai / iiAn indent block / inside it
ao / ioA loop / conditional block
a= / i=An assignment / inside it

Combine as usual: daf deletes a function, vic selects a class body, cia changes an argument.

KeyAction
]f / [fNext / previous function start
]c / [cNext / previous class start
]a / [aNext / previous argument
<C-Space> (normal mode)Incremental selection — expand selection by syntax node. Press repeatedly
<BS> (visual)Shrink incremental selection

mini.ai extras

a/i also work with:

Toggles

Everything under <leader>u toggles a UI or behavior setting. Press <leader>u and read the which-key menu.

KeyToggles
<leader>ufAuto-format (global)
<leader>uFAuto-format (buffer)
<leader>usSpelling
<leader>uwWord wrap
<leader>uLRelative line numbers
<leader>ulLine numbers
<leader>udDiagnostics
<leader>ucConceal level
<leader>uhInlay hints
<leader>ubBackground dark/light
<leader>uTTreesitter highlight
<leader>ugIndent guides
<leader>uDDim inactive
<leader>uAAnimations
<leader>uaToggle transparency / auto-pairs (varies)
<leader>uiInspect highlight group under cursor
<leader>uIInspect Treesitter tree
<leader>unDismiss all notifications
<leader>upToggle profiler

Sessions and project management

KeyAction
<leader>qqQuit all
<leader>qsRestore session for current directory
<leader>qlRestore last session
<leader>qdDon't save current session on exit
<leader>fpProjects picker (jump between projects)
<leader>qSSelect session

Sessions restore your open buffers and window layout per directory. Combined with the projects picker, this is your equivalent of VS Code workspaces.

Plugin and tooling management

Command / KeyAction
<leader>lOpen Lazy (plugin manager)
<leader>LLazyVim changelog
<leader>cmOpen Mason (LSP/formatter/linter installer)
:LazyExtrasBrowse and enable LazyVim extras — this is how you add language support
:Lazy updateUpdate plugins
:Lazy syncInstall/clean/update
:Lazy profileStartup time breakdown
:MasonSame as <leader>cm
:checkhealthDiagnose your whole setup
:LazyHealthHealth check for LazyVim specifically

Inside Lazy: I install, U update, X clean, S sync, L log, ? help, q quit. Inside Mason: i install, X uninstall, U update, / filter, g? help.

:LazyExtras is the single most useful command for a returning user. Rather than hand-configuring an LSP, enable lang.php, lang.typescript, lang.tailwind, lang.json, lang.yaml, etc., and LazyVim wires up the server, formatter, linter, and Treesitter parser for you.

Debugging and testing

Requires the dap.core and test.core extras (:LazyExtras).

DAP

KeyAction
<leader>dbToggle breakpoint
<leader>dBBreakpoint with condition
<leader>dcContinue / start
<leader>diStep into
<leader>doStep out
<leader>dOStep over
<leader>dtTerminate
<leader>duToggle DAP UI
<leader>drToggle REPL
<leader>deEval expression (works on a visual selection)

Neotest

KeyAction
<leader>ttRun tests in current file
<leader>tTRun all test files
<leader>trRun nearest test
<leader>tlRun last test
<leader>tsToggle test summary
<leader>toShow test output
<leader>tOToggle output panel
<leader>tSStop tests

Neovim features Vim doesn't have

Worth knowing these exist, since they're the reason the ecosystem works the way it does.

FeatureWhy it matters
Built-in LSPgd, gr, K, rename, code actions all come from the language server, not ctags. Configure with nvim-lspconfig + Mason
TreesitterReal syntax trees, not regex highlighting. Powers text objects, incremental selection, folding, and accurate indentation
Lua configinit.lua instead of .vimrc. Faster, and everything is a real API
vim.opt / vim.keymap.setLua equivalents of :set and :map
Floating windowsHover docs, LazyGit, terminals, pickers all live in floats
:terminalA real job-control terminal buffer
:checkhealthDiagnoses providers, clipboard, parsers, LSP
Virtual textInline diagnostics, git blame, inlay hints
Remote plugins / RPCHow agent integrations attach
vim.system / asyncNon-blocking jobs; why formatting on save doesn't freeze

Useful Ex commands: :LspInfo, :LspRestart, :ConformInfo (formatters), :TSInstallInfo, :messages, :Inspect, :InspectTree.

Web dev specifics

Relevant to PHP/WordPress and modern JS work:

Config layout

Your customizations go in ~/.config/nvim/lua/config/ and ~/.config/nvim/lua/plugins/. Never edit the LazyVim plugin itself.

~/.config/nvim/
├── init.lua -- bootstraps lazy.nvim, loads config
├── lua/
│ ├── config/
│ │ ├── autocmds.lua -- your autocommands (added to LazyVim's)
│ │ ├── keymaps.lua -- your keymaps (added to LazyVim's)
│ │ ├── lazy.lua -- lazy.nvim setup + which extras load
│ │ └── options.lua -- your options (added to LazyVim's)
│ └── plugins/
│ └── *.lua -- one file per plugin/override, auto-loaded
└── lazyvim.json -- tracks which extras you've enabled

Overriding a LazyVim keymap — you must delete it first if you want the key free:

-- lua/config/keymaps.lua
vim.keymap.del("n", "<S-h>")
vim.keymap.set("n", "<S-h>", "H", { desc = "Top of screen" })

Overriding a plugin's options — create a file in lua/plugins/:

-- lua/plugins/gitsigns.lua
return {
"lewis6991/gitsigns.nvim",
opts = {
current_line_blame = true,
},
}

Disabling a plugin:

return { "folke/flash.nvim", enabled = false }

Troubleshooting

SymptomCheck
A keymap does nothing<leader>sk and search for it — it may have moved between versions
gd doesn't work:LspInfo — is a server attached? :Mason — is it installed?
Formatting not running:ConformInfo — is a formatter configured for this filetype?
Highlighting is wrong:TSInstallInfo, then :TSInstall <lang>
Slow startup:Lazy profile
Something broke after an update:LazyL for the log; lazy.nvim supports lockfile restore
Clipboard doesn't reach the system:checkhealth → look at the clipboard provider section
Key conflicts inside a multiplexerTest <C-h>, <C-/>, <S-h>, <S-l>, <C-s> first — these are the usual suspects

Getting back up to speed

A realistic ramp for someone who is already very comfortable with Vim:

Week 1 — prefixes only. Learn <leader><space> (find file), <leader>/ (grep), <leader>, (buffers), <leader>e (explorer), <leader>gg (lazygit). Use which-key for everything else. Don't try to memorize; just pause after <leader> and read.

Week 2 — LSP. Force yourself to use gd, <C-o>, gr, K, <leader>ca, <leader>cr, ]d/[d. These are the ones that make Neovim competitive with VS Code, and they're only five or six keys.

Week 3 — Treesitter text objects and flash. daf, cia, vic, then s for jumping. This is where you exceed what VS Code can do.

Week 4 — customize. Now that you know what annoys you, edit lua/config/keymaps.lua. Not before.

Supporting resources: :Tutor inside Neovim for the basics refresher, :help lazyvim for the plugin's own docs, and <leader>sk whenever you're stuck. The LazyVim site (lazyvim.org) has a keymaps page generated from the same source as this document's contents, so it's a good cross-check against your specific version.

One habit that pays off disproportionately: when you catch yourself doing something inefficiently, stop and run <leader>sk with a guess at the word. LazyVim's descriptions are written in plain English, so searching "rename" or "stage" or "symbol" usually finds it in one try.

---

Category: Dev Tools

Tags: vim, neovim, cheatsheets

← Back to all notes