Dev Tools

Troubleshooting Sluggish VS Code: Extensions, Indexing and File Watching

Input lag and stalled saves are rarely editor bugs. A reproducible order: bisect with an empty window to isolate extensions, clean up extension hosts, control index scope with files.exclude and search.exclude, and tune watching on large repositories.

By LaoHand Team·9 min read·Updated 2026-09-30

Establish a Baseline Before Blaming Anything

The precondition for any diagnosis is turning a vague feeling into data. Run Developer: Show Running Extensions to list each extension activation time, Developer: Toggle Performance for typing latency and render times, and note which helper process owns the CPU.

Separating renderer from extension host matters. High typing latency with idle extension hosts points at the renderer or a language server such as tsserver; sustained high CPU in Code Helper (Plugin) almost always names the culprit. Remember the role split visible in Task Manager: Code Helper (Renderer), Code Helper (Plugin), and Code Helper (Watcher) — the last one owns file watching exclusively.

A mandatory step is capturing a process and memory snapshot with code --status, then reproducing in an empty window: code --disable-extensions .. If the project is still slow with every extension disabled, the cause lies in editor settings, file count or the workspace itself.

# 空窗口打开,禁用全部扩展——最直接的归因手段
code --disable-extensions .

# 只跑单个扩展,用于二分定位
code --disable-extensions --locate-extension ms-python.python

# 查看当前会话进程与内存快照
code --status

# 界面内操作
# 命令面板 -> Developer: Show Running Extensions
# 命令面板 -> Developer: Toggle Performance
# 命令面板 -> Developer: Open Extension Host Logs

Bisecting Extensions

With few extensions (under 20) disabling them one by one is fastest. With many, bisect: move all extensions to a temp directory, restore half, and repeat until one extension remains. Bisection turns N trials into log2(N) — 40 extensions need only 6 rounds.

An even better daily routine uses three commands. Sort Developer: Show Running Extensions by activation time and suspect the slowest; run Developer: Restart Extension Host and see whether the lag disappears; then check Developer: Open Extension Host Logs for repeated errors or loops.

Once identified there are three responses: upgrade (many perf issues are fixed upstream), pin the version via a lockfile so the whole team matches, or recommend disabling through "unwantedExtensions" in extensions.json. Note that a recommendation is not enforcement — users can still install it.

// .vscode/extensions.json —— 团队统一扩展集合与禁用建议
{
  "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"],
  "unwantedExtensions": [
    {
      "identifier": {
        "id": "some.heavy-extension"
      },
      "recommendation": "该扩展在大型仓库中显著拖慢启动,请在团队内统一禁用"
    }
  ]
}

// 二分定位脚本骨架(Bash)
// 1) 备份当前扩展目录
// 2) 循环:恢复一半扩展 -> code --disable-extensions . 复现 -> 记录卡顿程度

Tuning Indexing and File Watching on Large Repositories

In repositories with hundreds of thousands of files, the file watcher saturates inodes and CPU. The three most effective Linux-side moves: point files.watcherExclude at generated directories, prefer native recursive watching (files.watcherUsePolling: false — never enable polling by default), and on container or network filesystems use a gVisor-style FS to reduce inode events.

TypeScript projects need dedicated tuning: raise typescript.tsserver.maxTsServerMemory (e.g. 4096), set typescript.disableAutomaticTypeAcquisition: true to stop auto-fetching @types packages that trigger a second indexing pass, and consider typescript.tsserver.useSyntaxServer for the lighter syntax-only server.

Two underused editor switches matter too: editor.codeLens: false and editor.minimap.enabled: false measurably reduce rendering cost on large files. Keep files.hotExit at its default so closing a window does not serialize unsaved state and fake a stall. After changing settings run Developer: Reload Window to rebuild the index, then compare Developer: Toggle Performance readings.

// 大仓库 settings.json
{
  "files.watcherExclude": {
    "**/.git/objects/**": true,
    "**/node_modules/**": true,
    "**/dist/**": true,
    "**/build/**": true,
    "**/.next/**": true
  },
  "files.watcherUsePolling": false,
  "typescript.tsserver.maxTsServerMemory": 4096,
  "typescript.disableAutomaticTypeAcquisition": true,
  "editor.codeLens": false,
  "editor.minimap.enabled": false,
  "files.hotExit": "onExitOnly",
  "search.followSymlinks": false
}

Official References

Each command links to its official documentation below, so you can verify the latest usage and read deeper.