Folder Stats
Show file and folder counts for every directory in your workspace — directly in the Folder Stats view inside the Explorer sidebar.

Features
- Real directory tree — every subdirectory is a node; click to expand
- Per-directory stats — every node shows
"<N> dirs · <M> files" inline, plus a breakdown (Subdirectories, Files, All subdirectories, All files, Excluded)
- Files by Extension — workspace-level aggregate of file counts grouped by extension (e.g.
.ts, .json, .md)
- Default collapsed — workspace roots are collapsed on first view, so the panel stays compact until you click them
- Auto-refresh when files change (configurable debounce, default 300 ms)
- Multi-root workspaces — each workspace root gets its own top-level entry
- Quick search —
Folder Stats: Find Directory matches by name; reveals both Folder Stats and Explorer
- Expand All + VSCode's built-in Collapse All
- Compact numbers by default (
847, 12k, 1.2M); switch to exact (1,247,832) any time
- Configurable tree depth —
0 = infinite walk; N = walk N levels deep
- Active-editor sync (configurable, debounced 200 ms) — opening a file reveals its parent dir in both views
- Explorer right-click →
Folder Stats: Reveal in Folder Stats for one-click navigation
- Zero runtime dependencies — only Node built-ins and the
vscode API
Quick start
- Open the Explorer sidebar (
Cmd+Shift+E / Ctrl+Shift+E)
- Find the Folder Stats view
- Click ▶ next to a workspace folder to expand and see its stats
- Click any subdirectory's ▶ to drill in; click the directory name to toggle expand/collapse and reveal in Explorer
TreeView layout
> 📁 my-project [workspace root — collapsed by default]
▼ 📁 my-project [after expand]
Subdirectories: 2
Files: 2
📁 Files by Extension: 47 [workspace-only]
.ts 20
.ts 8
.json 5
.md 6
(no extension) 8
▼ 📁 src 4 dirs · 23 files [subdir — expanded]
Subdirectories: 4
Files: 23
▼ 📁 components 0 dirs · 5 files [deeper subdir]
...
🚫 Excluded: 3 [expand to per-pattern counts]
📁 test 2 dirs · 11 files
🚫 Excluded: 3
Clicking any directory node toggles expand/collapse and (on expand) reveals the same path in the Explorer sidebar.
Commands
Open with Cmd+Shift+P (or Ctrl+Shift+P):
| Command |
Title |
Description |
folderStats.refresh |
Folder Stats: Refresh |
Recompute all counts immediately |
folderStats.toggleAutoRefresh |
Folder Stats: Toggle Auto-Refresh |
Pause / resume automatic refresh on file changes |
folderStats.expandAll |
Folder Stats: Expand All |
Expand every directory node in the tree |
folderStats.search |
Folder Stats: Find Directory |
Search by name; reveals both Folder Stats and Explorer |
folderStats.syncToActiveFile |
Folder Stats: Sync to Active File |
Manually sync to the active editor's parent dir |
folderStats.revealInFolderStats |
Folder Stats: Reveal in Folder Stats |
Explorer right-click — reveal a folder in the Folder Stats view |
workbench.action.collapseAll |
(VSCode built-in) |
Collapse every directory node |
The first three commands are also available as buttons in the Folder Stats view title.
Keyboard shortcut (set your own)
We do not ship a default keybinding for the search / sync commands. macOS / Linux / Windows each have many overlapping system and app shortcuts, and any default we pick is wrong for some users (we tried — every pick conflicted for somebody).
30-second setup:
Cmd+K Cmd+S (Mac) / Ctrl+K Ctrl+S (Win/Linux) — open Keyboard Shortcuts
- Search
folderStats.search
- Double-click the row → press the keys you want →
Enter
- (Optional)
when clause: view == folderStatsView || editorTextFocus
- Repeat for
folderStats.syncToActiveFile if you want
Recommended picks that have worked for other users:
Cmd+Option+F — "F for find", often free
Cmd+Option+; — semicolon rarely conflicts
F8 — function keys are nearly impossible to intercept
Cmd+K F — VSCode chord style
Pick whatever your hands remember; we don't try to guess.
The first three commands are also available as buttons in the Folder Stats view title.
Explorer right-click
Right-click any folder in the Explorer → Folder Stats: Reveal in Folder Stats — the Folder Stats view scrolls to that directory and selects it.
Find Directory workflow
Cmd+Shift+P → "Folder Stats: Find Directory"
- Type a substring (e.g.
comp)
- QuickPick shows every match across all workspace roots:
- label = directory basename
- description = path relative to its workspace root (e.g.
src/components)
- detail =
"<N> dirs · <M> files · <absolute path>"
- Pick a match:
- All parent nodes expand in the Folder Stats view
- The match is selected and focused
- Explorer sidebar also reveals the same directory (linked navigation)
- A status bar message confirms:
📁 <full path> — <N> dirs · <M> files
Single match → reveal directly without showing the QuickPick.
Configuration
Open Settings (Cmd+, / Ctrl+,) and search folderStats.*, or edit settings.json:
| Setting |
Type |
Default |
Description |
folderStats.expandWorkspaceByDefault |
boolean |
false |
Expand each workspace root by default (default: collapsed) |
folderStats.recursiveSubdirs |
boolean |
false |
Count descendants subdirs (in addition to immediate) |
folderStats.recursiveFiles |
boolean |
false |
Count descendants files |
folderStats.maxDepth |
number |
0 (infinite) |
Limit tree depth. 0 = walk everything; N = walk N levels deep |
folderStats.excludePatterns |
string[] |
[".*", "node_modules", ".git", "out", "dist", "build", "target"] |
Names or single-segment globs skipped from counting |
folderStats.autoRefresh |
boolean |
true |
Recompute when files change |
folderStats.refreshDebounceMs |
number |
300 |
Debounce interval (ms) before recomputing after a file change |
folderStats.formatNumbers |
"compact" \| "exact" |
"compact" |
Number format |
folderStats.syncFromActiveEditor |
boolean |
true |
Reveal the parent dir of the active editor in Folder Stats + Explorer |
All settings use rich markdownDescription in the Settings UI — hover the setting name to see the full tooltip.
Example settings.json
{
// Auto-expand workspace roots (default is collapsed)
"folderStats.expandWorkspaceByDefault": true,
// Show descendant counts (slower on large monorepos)
"folderStats.recursiveSubdirs": true,
"folderStats.recursiveFiles": true,
// Limit tree depth if you have a huge repo
"folderStats.maxDepth": 3,
// Skip counting these
"folderStats.excludePatterns": [
".*",
"node_modules",
".git",
"out",
"dist",
"build",
"target",
"*.log",
],
// Show "1,247,832" instead of "1.2M"
"folderStats.formatNumbers": "exact",
// Disable auto-sync from active editor
"folderStats.syncFromActiveEditor": false,
}
A walk of an 8 400-entry synthetic tree (20 top-level dirs × 20 subdirs × 20 files) finishes in ~270 ms on a modern laptop.
Optimization stack:
- Patterns pre-compiled once per compute call (literal fast path; glob →
RegExp built up-front)
- Parallel
readdir at every level via Promise.all, bounded to 64 concurrent syscalls to avoid OS-level contention
- Single
readdir per directory — every needed count (entries, ext, exclude match) is captured in one pass
- Batched concurrency — subdirs are walked in chunks of 64 to bound memory
- Aggregate
fileByExtension once per level — no separate tree walk needed
Practical limits:
- A walk of a small project (~100 files) finishes in well under 100 ms
- A walk of a large project (~10 k files, no
node_modules) finishes in roughly 500 ms–2 s
node_modules (and any other configured exclude) is skipped, so the depth-bomb that often dominates other extensions does not affect this one
- Set
folderStats.maxDepth (e.g. 3) to cap walk depth for very large monorepos
Architecture
src/
├── extension.ts # activate / deactivate; commands + watcher + sync listeners
├── statsProvider.ts # TreeDataProvider; renders tree, owns per-folder cache
│ # + parent map for O(1) getParent() (required by reveal)
├── statsComputer.ts # parallel walkDirInternal; builds hierarchical DirectoryStats
│ # pre-compiled patterns + CONCURRENCY=64
├── watcher.ts # file-system watcher + debounce helper
├── formatter.ts # compact / exact number formatting
└── types.ts # DirectoryStats / ComputeOptions / StatsNode / ResolvedOptions
test/
├── formatter.test.ts
└── statsComputer.test.ts
Key design points:
computeTree runs walkDirInternal in parallel per level with concurrency cap of 64
- Excluded directories (
node_modules, etc.) are never read — saves the dominant cost on most projects
StatsProvider keeps a parentMap for O(1) getParent lookups, required by VSCode's reveal() API
- All public APIs are documented with JSDoc; module headers describe the file's purpose
Cross-view linked navigation
| Trigger |
Folder Stats |
Explorer |
| Open / Reload window |
(no focus steal) |
(no focus steal) |
| Switch active editor |
reveals parent dir |
reveals parent dir (debounced 200 ms, first event skipped) |
| Expand a directory in Folder Stats |
toggles expand |
reveals same path |
| Collapse a directory in Folder Stats |
toggles collapse |
(no focus steal — stays on current selection) |
| Right-click a folder in Explorer → "Reveal in Folder Stats" |
reveals + selects + focuses |
(source) |
| Pick a result in Find Directory |
reveals + selects |
reveals same path |
Note: VSCode does not expose Explorer selection events or a command to collapse a single Explorer folder, so the cross-view sync is best-effort.
Known limitations
- Cannot inject text into the built-in Explorer view. VSCode's public API does not allow third-party extensions to add descriptions to the workspace folder row in Explorer. Folder Stats provides a dedicated view for that reason.
- Symlinks are ignored — only real directories and files are counted; symlink loops cannot break the walk.
- No file-size accounting — only entry counts and per-extension grouping. File size is a separate dimension that could be added later.
Development
npm install # install dev deps
npm run check # tsc --noEmit for src and test
npm run test:unit # mocha unit tests (formatter / statsComputer)
npm run package # tsc + vsce package → .vsix
Tests use tsx to run TypeScript directly and create real temp directories with fs.mkdtemp — no mocks.
License
PolyForm Noncommercial License 1.0.0 — see LICENSE.
You may use, copy, modify, and redistribute this software for any non-commercial purpose. Commercial use is prohibited without prior written permission.
SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0