iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Refactor

VS Code’s refactoring features lean on the language server to rename, extract, inline, and reorganize code safely — cross-file and across language boundaries. Knowing the shortcuts turns repetitive surgery into a single keystroke.

Rename, extract, source actions

EXAMPLE
// 1) Rename symbol — F2
//   • Cursor on identifier → F2 → type new name → Enter
//   • Updates every reference in the project, across files
//   • TS / Python / Rust / Go: respects scopes, doesn't touch unrelated names
//   • Works in JSX, .md headings (if symbol-aware), Tailwind class refs (with extension)

// 2) Quick Fix — Ctrl+. (Cmd+. on macOS)
//   • Opens the lightbulb menu on the current line
//   • Convert require to import, add missing import, fill type annotation,
//     remove unused variable, sort imports, generate getter/setter
//   • Quick Fix is the entry point to most refactorings

// 3) Extract function / variable / constant
//   • Select an expression → Ctrl+. → 'Extract to function/method'
//   • Pulls the selection out, infers parameters, picks a sensible scope
//
// Before:
function priceFor(order: Order) {
    const tax = order.subtotal * (order.region === 'AU' ? 0.10 : 0.05);
    return order.subtotal + tax;
}
//
// Select 'order.region === "AU" ? 0.10 : 0.05' → Extract to variable
function priceFor(order: Order) {
    const taxRate = order.region === 'AU' ? 0.10 : 0.05;
    return order.subtotal + order.subtotal * taxRate;
}

// 4) Inline variable / inline function
//   • Inverse of extract
//   • Useful when an extracted helper turns out to be too thin

// 5) Move to file
//   • Cursor on a top-level declaration → Ctrl+. → 'Move to a new file'
//   • Names the new file from the symbol
//   • Updates imports in every consumer

// 6) Convert async patterns
//   • Select a .then() chain → 'Convert to async function'
//   • Or 'Convert to Optional Chain' on x && x.y && x.y.z

// 7) Source actions on save (settings.json)
{
    'editor.codeActionsOnSave': {
        'source.organizeImports': 'explicit',
        'source.fixAll.eslint':   'explicit',
        'source.addMissingImports': 'explicit',
    },
    'editor.formatOnSave': true,
    'typescript.preferences.importModuleSpecifier': 'shortest',
}
//
// Every save: sorts imports, fixes auto-fixable lint, adds missing imports.

// 8) Multi-cursor refactoring (NOT a true refactor — but the practical workhorse)
//   • Ctrl+D — select next occurrence of the word under cursor
//   • Ctrl+Shift+L — select ALL occurrences in the file
//   • Alt+Click — drop additional cursors anywhere
//   • Ctrl+Alt+Down / Up — add cursor below / above
//
// Use Find & Replace with regex (Alt+R inside the search box) for project-wide edits.

// 9) Search-replace with capture groups
//   • Ctrl+Shift+H → enable regex (.* button)
//   • Find: `useState<(\\w+)>\\(` → Replace: `useState<$1 | null>(`
//   • Use 'files to include' / 'files to exclude' to scope the change

// 10) Symbol navigation — required for refactors that span files
//   • F12          — go to definition
//   • Shift+F12     — find all references
//   • Alt+F12       — peek definition (preview popover)
//   • Ctrl+T        — symbol search across the whole workspace
//   • Ctrl+Shift+O  — symbols in current file
//   • Ctrl+G        — go to line number

// 11) Refactoring via the language server
//   • TS: typescript-language-server
//   • Python: Pylance (proprietary) or Pyright + Python extension
//   • Rust: rust-analyzer — best refactor support outside JetBrains
//   • Go: gopls
//   • Java: Red Hat Java
//
//   The richer the language server, the better refactors are.
//   If F2 'rename' is missing, the language server probably isn't running.

// 12) Workspace settings for safer refactors
{
    'files.exclude': { '**/.next': true, '**/dist': true },          // hide generated
    'search.exclude': { '**/dist': true, '**/coverage': true },       // don't include in replacements
    'editor.suggest.preview': true,
    'editor.linkedEditing': true,                                     // rename matching JSX tag pair
}

// 13) Snippets — alternative to repetitive refactor work
//   • File → Preferences → Configure User Snippets
//   • Define `console.log('X →', X)` triggers, custom test scaffolds, etc.

// 14) Common bugs and pitfalls
//   • F2 renamed only the visible scope — language server not indexed full workspace
//   • Refactor crossed a project boundary you didn't intend — use 'Move to file' instead of cut/paste
//   • Auto-import picked a deep internal path — set 'importModuleSpecifier': 'shortest' or 'project-relative'
//   • organizeImports removed something you needed — annotate with /** @public */ or update tsconfig isolatedModules

Why it matters

Lean on F2, Ctrl+., and codeActionsOnSave — they preserve correctness across files in a way that hand-edits don’t. When a refactor option is missing, check that the language server is actually running for that file type; most of VS Code’s refactoring power lives there, not in the editor itself.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// F2          rename symbol everywhere
// Ctrl + .    quick fix / refactor menu (extract function/variable, sort imports, …)
Try it Yourself »

Discussion

Loading…