Why I Finally Sat Down and Learned This Properly
I was pair programming with a senior dev on my team — she was cleaning up a React component while I watched. In about 30 seconds she had renamed a variable across the file, moved a block of code three places down, deleted four lines of boilerplate, and formatted the whole thing. No mouse. No triple-clicking to highlight a line, no copy-paste dance, no right-click context menus. I asked her to slow down and show me what she was doing. She laughed and said “I don’t even know anymore, my hands just do it.” That was the moment I realized I had been using VS Code like Notepad with syntax highlighting.
The gap between knowing a shortcut exists and actually having it in muscle memory is enormous. I had bookmarked probably three different cheat sheets over the years. I could tell you that Ctrl+P opens quick file search. But under any real pressure — debugging a production issue, jumping between files fast, doing a big refactor — I’d revert straight back to the mouse. The bookmarked cheat sheet doesn’t help when you’re in flow and your hands are already moving. Muscle memory only builds through deliberate, slightly uncomfortable repetition where you force yourself to stop, use the shortcut, and accept that it’s slower at first.
So I did something that felt dumb but worked: I banned my own mouse for one full afternoon per week for a month. Every time I reached for it, I stopped and figured out the keyboard equivalent. It sucked. My productivity tanked temporarily. By week three, things I’d been avoiding — like multi-cursor editing and quick symbol navigation — started feeling automatic. The investment compounded faster than I expected.
Here’s what this guide actually covers: the shortcuts I use every single day without thinking, the ones I ignored for years because I didn’t understand when they were useful (looking at you, Ctrl+K Ctrl+0 for fold all), and the keybinding customizations I made that finally stuck because they matched how my brain works rather than whatever VS Code shipped as default. I’m not going to list 80 shortcuts and call it a cheat sheet. I’m covering the ones that changed how I work, with context for when you’d actually reach for them.
Every shortcut in this guide is listed for both macOS and Windows/Linux side by side. I got tired of reading articles that pick one platform and then say “substitute Cmd for Ctrl” — that works for 60% of shortcuts and silently breaks on the rest. On macOS, some shortcuts use Option where Windows uses Alt, some use Cmd where you’d expect Ctrl, and a handful are completely different mappings with no obvious pattern. I’ve verified each one in VS Code 1.89 on both platforms.
The Shortcuts That Changed How I Navigate Code
The one that genuinely changed my workflow first was Cmd+T (Ctrl+T on Windows/Linux). I used to grep through the codebase constantly — grep -r "functionName" ./src — wait for results, then open the file. Cmd+T does the same thing in under a second, shows you the file and line, and you jump straight there. It searches symbols across the entire workspace: functions, classes, interfaces, variables. I’d estimate I’ve cut my terminal-switching by 40% just from this one shortcut.
Cmd+P (Ctrl+P) is the one everyone knows but half the people I’ve worked with still use the sidebar file explorer out of habit. Stop. The sidebar is fine for orientation when you’re new to a project, but once you know the codebase, it’s three clicks to do what Cmd+P does in two keystrokes. You can also type : after the filename to jump to a specific line — so userService.ts:142 gets you exactly where you need to be in one shot.
VS Code Shortcuts I Actually Use Every Day (and a Few That Took Me Too Long to Discover)
; Cmd+P tricks most people miss:
; "filename:linenumber" → opens file at exact line
; "@symbolname" → switch to symbol search (same as Cmd+Shift+O)
; "#symbolname" → workspace symbol search (same as Cmd+T)
; "edt " → browse open editors only
Cmd+Shift+O (Ctrl+Shift+O) is embarrassingly underused. In a 600-line service file with 15 methods, scrolling to find the right function is slow and error-prone. This shortcut gives you a filterable list of every symbol in the current file — type the first three letters and you’re there. The thing that caught me off guard: you can type : after the trigger to group symbols by category (methods, properties, constructors). So it becomes a real outline view, not just a jump list.
Ctrl+G — just go to line. I used to click in the line number gutter or scroll with my mouse to reach a specific line from a stack trace. That’s embarrassing to admit now. When an error says app.js:287, I hit Ctrl+G, type 287, done. It’s also available as : inside Cmd+P, but the dedicated shortcut is faster when you already have the file open.
Cmd+Shift+\ (Ctrl+Shift+\) is the bracket jump. Drop your cursor on any opening or closing bracket, hit this, and you land on its match. This is the one I reach for in deeply nested callback hell or complex JSX — instead of manually counting which } closes which block, one keystroke gets you there. Combine it with Shift to select everything between the brackets and you’ve got a quick way to cut an entire block.
Alt+Left / Alt+Right (Windows/Linux) or Ctrl+- / Ctrl+Shift+- (Mac) is cursor position history — exactly like the browser back/forward buttons, but for where your cursor has been in the editor. You jump into a function definition with F12, look at it, then Alt+Left snaps you back to where you were. Before I internalized this, I’d manually find my way back by scanning the file. Now my navigation pattern is: jump forward aggressively to explore, then navigate back cleanly. The history goes surprisingly deep — you can bounce through 10+ positions across different files.
Multi-Cursor and Selection Tricks That Actually Save Time
The thing that separates developers who know about multi-cursor from developers who use it daily is muscle memory on the edge cases. Cmd+D / Ctrl+D is the one everyone sees in conference talk demos, but most people use it a few times and bail. The real power comes when you chain it: hit it once to select the first occurrence, keep hitting it to skip through matches one by one, and if you overshoot, Cmd+U / Ctrl+U undoes the last cursor addition without killing your whole selection. That undo-cursor behavior isn’t documented prominently anywhere — I found it by accident after six months of frustration.
Cmd+Shift+L / Ctrl+Shift+L is the move when you already know you want every occurrence. Say you’re renaming a CSS class used twelve times in a Tailwind template, or swapping every var to const in a legacy file. Don’t sit there hammering Cmd+D twelve times. Select the word once, hit Ctrl+Shift+L, and you instantly have twelve cursors. From there, type your replacement and every instance updates simultaneously. The gotcha: it matches all occurrences in the file, not just the visible ones — scroll up first to confirm you actually want a global replace, not just a local one.
Alt+Click is the escape hatch for when occurrences aren’t identical strings. Say you need to add a semicolon to lines 12, 47, and 83 — three completely unrelated spots. Just Alt+Click each target location to drop independent cursors wherever you want. Combine this with Cmd+Alt+Down / Ctrl+Alt+Down for the column editing case — when you have a block of sequential lines and want to append or prepend the same thing to each. That arrow shortcut adds a cursor one line below the current one, so holding it for half a second drops you into a vertical cursor column.
// Before: irregular variable names, can't use Ctrl+D cleanly
const userName = 'Alice'
const user_id = 42
const UserEmail = '[email protected]'
// Alt+Click on each value, then type replacement in one pass
// Cursor 1 → 'Alice', Cursor 2 → 42, Cursor 3 → '[email protected]'
Box selection with Shift+Alt+drag (mouse drag while holding those keys) is the one I genuinely forgot existed for two years. It lets you drag a literal rectangle selection across your code — columns 5 through 20, rows 10 through 25, for example. This is invaluable when working with CSV data, log output, or alignment-heavy configs where you want to extract or replace a specific column of values. Every other approach to this problem is slower. The limitation: it doesn’t play nicely with proportional fonts or when lines vary wildly in length, so it works best on fixed-width data.
Cmd+L / Ctrl+L looks boring but I use it constantly for line-level operations. Hit it once to select the current line. Hit it again to extend the selection to the next line. This is faster than any combination of Home, Shift+End, and arrow keys, especially when you want to grab 3-4 consecutive lines and cut/paste them. Stack it with multi-cursor: position cursors on three different lines, then hit Ctrl+L to select all three full lines at once. From there a single cut removes all three blocks regardless of their content or indentation level.
Editing Shortcuts I Use More Than I Expected
The shortcut that changed how I work more than anything else is Alt+Up / Alt+Down. I used to cut a line, move the cursor, then paste — which is three operations and pollutes the clipboard. Now I just hold Alt and tap an arrow key. I use this constantly for reordering imports: sorting them by length, grouping React imports before utility imports, moving a hook call above a variable declaration. The muscle memory built up faster than I expected because the feedback is immediate and satisfying.
Shift+Alt+Up / Shift+Alt+Down is the duplicate-line shortcut I didn’t know I needed. My old workflow was Cmd+C on an empty selection (which copies the whole line in VS Code), then Cmd+V on the next line — clunky. The duplicate shortcut does it in one move and leaves my clipboard alone. I use it most when writing similar CSS properties or creating near-identical object entries that I’ll modify right after. The catch: on some Linux setups, Shift+Alt+Down conflicts with desktop window manager shortcuts. You’ll know immediately because your whole terminal window starts moving instead of your code.
# Before: cut-paste workflow pollutes clipboard
# Cmd+X → move cursor → Cmd+V
# After: just use these
Alt+Up # move line up
Alt+Down # move line down
Shift+Alt+Up # duplicate line above
Shift+Alt+Down # duplicate line below
Cmd+Shift+K (Ctrl+Shift+K on Windows/Linux) deletes an entire line without touching your clipboard. This sounds minor until you’ve spent time debugging why a paste produced unexpected output — turns out you deleted a line mid-session with Cmd+X and forgot. The delete-line shortcut is pure destruction: gone, no clipboard side effects. I also use Cmd+Enter constantly. Instead of pressing End to jump to the end of a line before hitting Enter, Cmd+Enter inserts a new line below from wherever your cursor is sitting. Your cursor could be in the middle of a word and it still works. The inverse, Cmd+Shift+Enter, inserts a line above — genuinely useful when you’re inside a function body and realize you need a variable declaration two lines up.
Cmd+/ is the toggle-line-comment shortcut, and the thing most people miss is that it works perfectly with multi-cursor selections. Select three lines with Shift+Down, hit Cmd+/, all three get commented out simultaneously. Hit it again, they uncomment. It respects the language too — // for JavaScript, # for Python, -- for SQL. I’ve used this to comment out an entire block of config while debugging, then uncomment it cleanly without any diff noise from spacing changes.
Shift+Alt+F runs the document formatter — whatever you’ve configured as the default, whether that’s Prettier, ESLint, or the built-in formatter. I run this manually before every commit as a final sanity check, even though I also have format-on-save enabled. The reason: format-on-save runs on the active file, but I sometimes have unsaved scratch edits I’m not ready to commit. Running Shift+Alt+F consciously before staging means I’m formatting intentionally. One gotcha: if you haven’t set a default formatter for the file type and multiple formatters are installed, VS Code prompts you to pick one. Resolve this permanently in your workspace settings:
// .vscode/settings.json
{
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
},
"editor.formatOnSave": true
}
The pattern across all of these shortcuts is that they eliminate clipboard side effects and reduce cursor repositioning. Every time you move the cursor to the end of a line before pressing Enter, or copy something just to paste it one line down, you’re adding latency and potential for mistakes. These shortcuts remove that overhead entirely — which is why they end up being the ones you use without thinking after a week of deliberate practice.
The Terminal and Panel Shortcuts That Declutter Your Workspace
The shortcut most developers sleep on is Cmd+B (macOS) / Ctrl+B (Windows/Linux) to toggle the sidebar. No extension needed, no zen mode required — just hammer that and you’ve instantly reclaimed ~250px of horizontal space. I use it constantly when I’m reading code that requires scrolling right. The muscle memory takes maybe two days to build, and then you’ll wonder why you ever left the sidebar open permanently.
The terminal toggle situation is worth getting opinionated about. The default Ctrl+` requires two hands on most keyboards, which breaks flow. I remapped mine in keybindings.json to Cmd+J on macOS — one thumb, no awkward reach. The catch: Cmd+J is already bound to toggling the bottom panel by default, so you need to decide whether you want both bindings or collapse them into one. Here’s what my config looks like:
// keybindings.json — accessible via Cmd+Shift+P > "Open Keyboard Shortcuts (JSON)"
[
{
"key": "cmd+j",
"command": "workbench.action.terminal.toggleTerminal",
"when": "!terminalFocus"
},
{
"key": "cmd+j",
"command": "workbench.action.togglePanel",
"when": "terminalFocus"
// pressing Cmd+J again while inside the terminal hides the whole panel
}
]
Ctrl+Tab is underrated for the specific scenario where you have 4-6 files open and you’re bouncing between two of them. It cycles through your recently-used editors in recency order — not left-to-right tab order, which is the thing that surprises most people. So if you just switched from routes.ts to controller.ts, one Ctrl+Tab jumps you straight back. It’s faster than Cmd+P for this case because you don’t have to type anything.
Pairing Ctrl+Tab with Cmd+W / Ctrl+W is how I do quick tab cleanup. I’ll realize I’ve got 10 tabs open, cycle through with Ctrl+Tab, and close anything I’m done with using Cmd+W without my hands leaving the keyboard. The whole operation takes maybe 15 seconds. The gotcha here: if you close a tab with unsaved changes, VS Code prompts you — which is fine — but if you’ve got files.autoSave set to afterDelay, it silently saves and closes, which is occasionally alarming the first time it happens.
Split editor via Cmd+\ / Ctrl+\ is the one I reach for during code review. Open the file you’re editing, split, then use Cmd+P in the right pane to open the reference file. The thing that isn’t obvious: VS Code remembers split layout between sessions if you have workbench.editor.restoreViewState enabled. What I find more useful than a permanent split is treating it as a temporary second view — reference something, close the split with Cmd+W, back to single pane. That workflow keeps the workspace clean without requiring discipline about closing splits manually every time.
Cmd+B/Ctrl+B— toggle sidebar; faster focus mode than any extensionCtrl+`(or remap toCmd+J) — terminal toggle; fix the default binding immediatelyCtrl+Tab— cycles by recency, not tab position — this surprises peopleCmd+W/Ctrl+W— close tab; combine withCtrl+Tabfor fast cleanupCmd+\/Ctrl+\— split editor; use it temporarily, not permanently
Search and Replace Shortcuts Worth Memorizing
Most developers use Cmd+F to find something, read the first match, then close the panel. The move that actually changes how you work is pressing Alt+Enter after your search term — it selects every match in the file simultaneously. From there you’re in multi-cursor mode and you can bulk rename, wrap in quotes, or delete all at once. I use this probably a dozen times a day and still run into developers who’ve never seen it.
Cmd+H (or Ctrl+H on Windows/Linux) opens find-and-replace in the current file. The thing that catches people off guard here is the regex toggle — that little .* button in the panel. Once you flip that on, you can do capture group replacements like $1, $2 in the replace field. So a pattern like (\w+)_(\w+) can become $2_$1 and you’ve just swapped two parts of every variable name in the file without writing a script.
Cmd+Shift+F is workspace-wide search, and most people ignore the two small input fields that appear below the search box: “files to include” and “files to exclude”. These accept glob patterns. I’ll routinely do something like this to scope a search to just the API layer:
Search: fetchUser
Files to include: src/api/**/*.ts
Files to exclude: **/*.test.ts, **/node_modules
Without those filters you end up wading through test fixtures and generated files. The exclude field alone cuts noise by 80% on any real codebase.
Cmd+Shift+H is the one I treat with respect. Workspace-wide replace is genuinely dangerous if you’re moving fast — there’s no undo that spans multiple files reliably across a git-dirty working tree. My rule: before running a workspace replace, I do a git stash or at minimum make sure the diff is clean. That said, renaming a deprecated prop across 40 files in 10 seconds is a legitimate superpower. The “preserve case” checkbox in the replace panel is underused too — it’ll keep UserName, userName, and USERNAME cased correctly in the output.
Keeping your hands on the keyboard during an active search is where F3 (next match) and Shift+F3 (previous match) matter. The search panel stays open, your cursor moves through matches, and you can hit Escape when you’ve found what you need. The alternative — grabbing the mouse to click through results — sounds minor but it breaks your mental context every time. Combine F3 with Cmd+D for adding individual matches to a multi-cursor selection and you’ve got granular control: skip the matches you want to leave alone, select the ones you want to change.
Code Intelligence Shortcuts (Where VS Code Earns Its Keep)
The shortcut that changed how I read unfamiliar codebases was F12. One keystroke, and you’re at the definition — not just in the current file, but across the entire project. With TypeScript or any LSP-enabled language (Rust via rust-analyzer, Go via gopls, Python via Pylance), it follows the type system, not just text. That means it jumps to the actual interface or class declaration, not some string that happens to match. I’ve pair-programmed with people who still Cmd+Click for this. Same result, extra finger gymnastics.
The one that most devs sleep on is Alt+F12 — peek definition. Instead of navigating away, it opens an inline popup anchored to your current line. I use this constantly when reading through middleware chains or utility functions where I need a quick sanity check on what a helper does without losing my place. The popup is fully interactive: you can scroll through the definition, edit it, even peek again from within it. Close it with Escape and you’re exactly where you were.
// You're here, reading this call:
const result = await parseRequestBody(req, schema);
// Alt+F12 opens this inline, right below that line:
// async function parseRequestBody(req: Request, schema: ZodSchema) {
// const raw = await req.json();
// return schema.parseAsync(raw); // <- you see the Zod call, close, move on
// }
Shift+F12 is find-all-references, and it beats grep for one specific reason: it understands scope. grep -r "handleClick" will match comments, strings, and unrelated variables with similar names. Shift+F12 shows you every place the symbol is actually used — imports, call sites, type annotations. The results panel shows file path, line number, and surrounding context. When I'm about to delete or refactor a function, this is the first thing I run. If the count is 0, I delete without hesitation.
F2 is rename symbol and it's one of those shortcuts where the value compounds with project size. Rename a prop in a React component and it cascades through every file that imports and uses it — not a find-replace that matches strings, but a semantic rename that understands references. The thing that caught me off guard the first time: it renames across .ts, .tsx, .test.ts, even updates string-based references in some frameworks if your LSP supports it. After the rename, VS Code shows you a diff preview before committing. Always check that diff — occasionally it catches a match you didn't expect.
Cmd+. (Ctrl+. on Windows/Linux) is the quick fix trigger — the same menu as clicking the lightbulb icon, but without breaking your flow. I use it constantly for: adding missing imports that TypeScript flags, wrapping code in try/catch, implementing interface methods, and pulling variables into a destructuring pattern. Pair this with Ctrl+Space for manually triggering IntelliSense. Autocomplete sometimes doesn't fire after you've been editing mid-expression, or after pasting code. Ctrl+Space forces the suggestion list open regardless.
Make Shift+Alt+O part of your pre-commit ritual if you work in TypeScript or JavaScript. It removes unused imports, deduplicates, and groups them according to your tsconfig or ESLint rules. The gotcha: it only works reliably if TypeScript can resolve all your imports. If you've got path aliases set up in tsconfig.json but your editor isn't picking them up, it'll sometimes remove imports it thinks are unused but aren't. Verify your paths config is loading correctly before trusting this blindly on a big cleanup.
// tsconfig.json — make sure this is in place for Shift+Alt+O to resolve aliases
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@utils/*": ["src/utils/*"],
"@components/*": ["src/components/*"]
}
}
}
How to Actually Customize Keybindings Without Breaking Everything
The thing that trips people up first: the keybindings UI (Cmd+K Cmd+S on macOS, Ctrl+K Ctrl+S on Windows/Linux) looks like it's the whole story, but it's not. The UI is fine for discovery — you can search by name, see what's already bound, and click to reassign. But the moment you want conditional shortcuts (fire this only in the terminal, not in the editor), you need keybindings.json. Click the file icon in the top-right of the keybindings panel to open it directly.
Here's a real remap I made because Ctrl+` on a MacBook with an external keyboard was awkward — the backtick sits in a different position than I expected and I kept missing it. I remapped terminal toggle to Cmd+\ instead:
[
{
"key": "cmd+\\",
"command": "workbench.action.terminal.toggleTerminal",
// Only fires when focus is NOT inside a text editor
// without this, cmd+\ would also conflict with column selection
"when": "!editorFocus"
},
{
// Disable the original so it doesn't ghost-fire
"key": "ctrl+`",
"command": "-workbench.action.terminal.toggleTerminal"
}
]
The when clause is the part nobody reads about until something breaks. VS Code exposes a huge list of context keys — editorFocus, terminalFocus, inDebugMode, resourceExtname == .md' — and you can combine them with &&, ||, and !. Without a when clause, your shortcut fires everywhere, including inside extension panels and dialogs where it'll either do nothing or override something else you care about. Run Developer: Inspect Context Keys from the command palette to see exactly what context keys are active for wherever your cursor is sitting right now.
Before binding anything, check for conflicts. In the keybindings UI, type the key combination into the search bar — not the command name, the actual key sequence like ctrl+shift+k. VS Code will list every command bound to that key, including extension-added ones. I've been burned more than once by binding something to Cmd+Shift+P variants only to find a Vim extension had already claimed it. The search result shows you the when clause for each existing binding, so you can tell if there's a real conflict or if the contexts are mutually exclusive.
One thing that confuses people early on: settings.json and keybindings.json are completely separate files in different locations. Your settings live at ~/Library/Application Support/Code/User/settings.json on macOS, and keybindings at ~/Library/Application Support/Code/User/keybindings.json. You can open settings JSON with workbench.action.openSettingsJson from the command palette — but that opens settings, not keybindings. To open keybindings JSON directly, use workbench.action.openGlobalKeybindingsFile. I have both commands memorized because I kept opening the wrong file for months.
Back up your keybindings with Settings Sync — it's built into VS Code (no extension needed since VS Code 1.48), and it ties to your GitHub or Microsoft account. Enable it under Settings Sync: Turn On from the command palette, then check that "Keybindings" is ticked in the sync categories. One gotcha: platform-specific keybindings. If you work on both macOS and Linux, VS Code will by default sync the same keybindings.json to both machines, which means your cmd+\ bindings do nothing on Linux. Toggle "Sync keybindings per platform" in the Settings Sync config to maintain separate files per OS — this setting is off by default and almost nobody knows it exists until they switch machines and find half their shortcuts dead.
The Shortcuts I Ignored Too Long (And Now Can't Live Without)
I spent probably two years opening the Command Palette and typing "theme" every time I wanted to switch color schemes. The actual shortcut — Cmd+K Cmd+T on Mac, Ctrl+K Ctrl+T on Windows/Linux — was sitting right there, and I never bothered to learn it. That's the pattern with this whole list: these aren't obscure shortcuts buried in a plugin. They ship with VS Code, they work instantly, and I avoided them because I already had a "good enough" workflow.
Zen Mode (Cmd+K Z / Ctrl+K Z) sounds like a gimmick until you try writing a 2,000-word technical spec inside your editor. It kills the sidebar, the activity bar, the status bar — everything. Just you and the file. Press Escape twice to get back out. I now write all my documentation and PR descriptions directly in a markdown file with Zen Mode on. The difference in focus is real, not placebo. If you're someone who keeps flipping to Notion or Confluence to write docs, try this first.
Folding all code in an unfamiliar file is one of those moves that feels obvious in hindsight. Cmd+K Cmd+0 / Ctrl+K Ctrl+0 collapses every block — classes, functions, loops, all of it — down to a skeleton. You get the structure of a 1,200-line file in about 30 lines. Then you unfold only what you need with Cmd+K Cmd+J / Ctrl+K Ctrl+J to expand everything, or just click the arrows next to what interests you. This is the first thing I do when I get dropped into legacy code I've never seen before.
# These two are a pair — learn them together
Ctrl+K Ctrl+0 → Fold all regions
Ctrl+K Ctrl+J → Unfold all regions
# If you only want specific depth levels:
Ctrl+K Ctrl+1 → Fold to level 1 (top-level only)
Ctrl+K Ctrl+2 → Fold to level 2
# ...up to Ctrl+K Ctrl+9
The markdown preview one genuinely embarrassed me when I found it. Ctrl+Shift+V opens a rendered preview of your markdown file. That's it. I opened Settings, Googled "VS Code markdown preview shortcut", and even installed an extension — all before accidentally hitting this combo. The side-by-side version is Cmd+K V / Ctrl+K V, which splits the editor so you see raw markdown on the left and the rendered output on the right simultaneously. That's the one you actually want for editing README files.
The language mode switcher (Cmd+K M / Ctrl+K M) solves a specific but frequent annoyance: files with wrong or missing extensions. Dockerfiles have no extension. .env files don't always get recognized. Shell scripts sometimes open as plain text. Instead of digging through the bottom status bar to click the language indicator, hit Ctrl+K M, type the language you want, and you get proper syntax highlighting and IntelliSense immediately. This also matters when you paste a JSON blob into an untitled scratch file — set the language mode to JSON and the formatter will work correctly on it.
A Printable-Style Reference Table
The shortcuts below are verified against VS Code 1.89+. If something doesn't match what you see, open the full keybindings editor with Cmd+K Cmd+S (macOS) or Ctrl+K Ctrl+S (Windows/Linux) — that's your ground truth, not any cheat sheet including this one. A few bindings shifted between 1.85 and 1.89, especially around multi-cursor and terminal focus, so the version check matters.
Navigation
Action macOS Windows / Linux
──────────────────────────────────────────────────────────────────────
Go to File Cmd+P Ctrl+P
Go to Line Ctrl+G Ctrl+G
Go to Symbol (file) Cmd+Shift+O Ctrl+Shift+O
Go to Symbol (workspace) Cmd+T Ctrl+T
Go to Definition F12 F12
Peek Definition Alt+F12 Alt+F12
Go Back / Forward Ctrl+- / Ctrl+Shift+- Alt+Left / Alt+Right
Switch Editor Tab Cmd+Opt+Left/Right Ctrl+PageUp / PageDown
Focus Explorer Cmd+Shift+E Ctrl+Shift+E
Toggle Sidebar Cmd+B Ctrl+B
Jump to Matching Bracket Cmd+Shift+\ Ctrl+Shift+\
The one that trips up new users: Ctrl+G is the same on both platforms. macOS doesn't swap it to Cmd+G. I've seen people remap it assuming it was wrong. It isn't — VS Code intentionally keeps it as Ctrl on Mac for that one shortcut.
Editing
Action macOS Windows / Linux
──────────────────────────────────────────────────────────────────────
Multi-cursor (click) Opt+Click Alt+Click
Add cursor above/below Cmd+Opt+Up/Down Ctrl+Alt+Up/Down
Select all occurrences Cmd+Shift+L Ctrl+Shift+L
Add next occurrence Cmd+D Ctrl+D
Move line up/down Opt+Up / Opt+Down Alt+Up / Alt+Down
Copy line up/down Shift+Opt+Up/Down Shift+Alt+Up/Down
Delete line Cmd+Shift+K Ctrl+Shift+K
Indent / Outdent line Cmd+] / Cmd+[ Ctrl+] / Ctrl+[
Toggle line comment Cmd+/ Ctrl+/
Toggle block comment Shift+Opt+A Shift+Alt+A
Format document Shift+Opt+F Shift+Alt+F
Format selection Cmd+K Cmd+F Ctrl+K Ctrl+F
Trigger rename symbol F2 F2
Undo / Redo Cmd+Z / Cmd+Shift+Z Ctrl+Z / Ctrl+Y
Search
Action macOS Windows / Linux
──────────────────────────────────────────────────────────────────────
Find in file Cmd+F Ctrl+F
Replace in file Cmd+Opt+F Ctrl+H
Find in workspace Cmd+Shift+F Ctrl+Shift+F
Replace in workspace Cmd+Shift+H Ctrl+Shift+H
Find next / previous Cmd+G / Cmd+Shift+G F3 / Shift+F3
Toggle case sensitive Cmd+Opt+C (in find) Alt+C (in find)
Toggle regex Cmd+Opt+R (in find) Alt+R (in find)
Toggle whole word Cmd+Opt+W (in find) Alt+W (in find)
The in-file find modifiers (Cmd+Opt+C/R/W on Mac) only fire when the find widget has focus. I lost a good 20 minutes once wondering why they weren't working — my cursor was still in the editor. Click inside the search box first, or use Cmd+F to open it fresh.
Code Intelligence
Action macOS Windows / Linux
──────────────────────────────────────────────────────────────────────
Trigger suggestion Ctrl+Space Ctrl+Space
Trigger parameter hints Cmd+Shift+Space Ctrl+Shift+Space
Show hover Cmd+K Cmd+I Ctrl+K Ctrl+I
Go to References Shift+F12 Shift+F12
Find all references Opt+Shift+F12 Alt+Shift+F12
Rename symbol F2 F2
Quick Fix Cmd+. Ctrl+.
Run Code Action Cmd+Shift+. Ctrl+Shift+.
Open Problems panel Cmd+Shift+M Ctrl+Shift+M
Next / Prev error F8 / Shift+F8 F8 / Shift+F8
Peek Call Hierarchy Ctrl+Shift+H Ctrl+Shift+H
Cmd+. (Quick Fix) and Cmd+Shift+. (Code Action) feel redundant until you realize Quick Fix is context-aware — it fires when there's a squiggle or suggestion — while the Code Action shortcut opens the full refactor menu regardless. If you're doing extract-to-function or convert-to-arrow-function refactors regularly, muscle memory on Cmd+Shift+. saves more time than any other shortcut on this list.
How I Built These Into Muscle Memory (Without Flashcards)
The mistake I made early on was downloading a cheat sheet, staring at it for 20 minutes, and then using exactly zero new shortcuts the next day. The problem isn't memory — it's that you're trying to replace a motor habit, not memorize a fact. Your fingers already know where to reach for the mouse. Overwriting that takes repetition under pressure, not passive reading.
The approach that actually worked for me: one shortcut per week, used obsessively until it's automatic, then move on. Pick something you do constantly — for me it was Ctrl+P (quick file open) week one, then Ctrl+Shift+L (select all occurrences) week two. The rule is strict: every time you'd normally reach for the mouse or use a menu for that action, you stop and use the shortcut instead, even if it's slower at first. By Thursday it's faster. By Friday you've stopped thinking about it. That's muscle memory. Seven shortcuts across seven weeks beats a 50-shortcut cheat sheet every single time.
VSCodeCanDoThat.com is genuinely useful here because it shows shortcuts in context — animated demos of what actually happens in the editor, not just a key binding in a table. When you see Alt+Click drop multiple cursors across a file in real time, your brain stores it differently than reading "multi-cursor: Alt+Click". I've sent that site to junior devs instead of explaining things verbally because the visual context does the teaching faster than I can.
The uncomfortable one: turn off your mouse for navigation for a full week. Not for design work, not for browser tabs — just inside VS Code. Force yourself to use Ctrl+Shift+E for the explorer, Ctrl+` for the terminal, Ctrl+B to toggle the sidebar. The first two days feel broken. Day three you start problem-solving instead of fumbling. By day five you're moving around the editor faster than you did with the mouse, and you didn't need a single flashcard to get there. The friction is the point — it forces your hands to learn the path.
On vscodevim: I've used it, I've uninstalled it, I've reinstalled it. Honest take — if you already think in Vim motions, it's excellent and you'll never want it off. If you're learning Vim and VS Code simultaneously, you're fighting two learning curves at once and you'll probably quit both. The extension itself is solid; the vim.normalModeKeyBindingsNonRecursive config in your settings.json lets you keep VS Code shortcuts intact for things like the integrated terminal:
{
"vim.normalModeKeyBindingsNonRecursive": [
{
"before": [""],
"after": ["", "zz"] // centers screen after half-page jump
}
],
"vim.handleKeys": {
"": false, // lets VS Code's quick open through instead of Vim taking it
"": false
}
}
That config alone saves hours of frustration. But if you've never touched Vim and you're just trying to move faster in VS Code, skip vscodevim for now and stack the native shortcuts first. You can always add it later. And if you're thinking about how editor shortcuts fit into a broader productivity setup across your tools and stack, the guide on Essential SaaS Tools for Small Business in 2026 at techdigestor.com covers a lot of ground on tools that actually slot together instead of fighting each other.