selectFromList Architecture
A webview-based fuzzy-search selector that replaces VS Code's native QuickPick for richer interaction (multi-select, range select, alternate actions).
Key Files
| File |
Role |
src/selectFromList.js |
Extension-side provider — creates/reuses webview, posts items, resolves promises |
src/selectFromList/main.js |
Webview-side — List class (virtual scrolling, selection), FuzzySearch class (TextMate-style ranking) |
src/selectFromList/index.html |
HTML shell with inline CSS, search input, CSP nonces |
src/selectFromList.json |
Manifest fragment (contributes views, commands, configuration) |
Data Flow
Command invokes showSelectFromList(items, options)
→ chooseItems() on provider
→ createWebviewPanel() (reuses if alive) or sidebar path
→ reveal() panel (so it's visible before init)
→ await _isReady (resolves when webview posts "ready")
→ postMessage({ type: "init", items, requestId })
→ webview receives init
→ pre-lowercases labels (_lowerLabel, _lowerDescription)
→ computeVisible() → render() (virtual scrolling)
→ focus search input
→ user types → 30ms debounce → computeVisible() → render()
→ user submits (Enter / double-click)
→ postMessage({ type: "submit", indexes, requestId })
→ provider resolves promise with selected items
FuzzySearch (TextMate port)
rank(filter, candidate, candidateLower, out) — entry point, filter is already lowercase
calculateRank(lhs, rhs, rhsLower, out) — matrix-based scoring from TextMate's ranker.cc
rankFile(filter, filename, directory, filenameLower, directoryLower) — tries filename first, falls back to full path
isSubset(needle, haystackLower) — fast pre-check before full ranking
- Pre-allocated typed arrays (
_matrix, _first, _last, _capitals) avoid GC pressure
Performance Characteristics
- Virtual scrolling — only ~60 DOM nodes regardless of list size (20-row overscan)
- Incremental filtering — typing more chars narrows from previous matches, not full list
- Pre-lowercased labels —
toLowerCase() called once at init, not per-char in hot loop
- Panel reuse —
retainContextWhenHidden: true, panel is reused across opens
Constraints
- The
FuzzySearch.calculateRank algorithm is a TextMate port — modify with extreme care
requestId prevents stale responses from previous invocations
close() disposes panel (panel path) or hides sidebar — different teardown per render mode
- The
_isReady promise pattern: created fresh when panel/sidebar is new, stays resolved when reusing
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: refactoring-select-from-list3description: Domain knowledge for the selectFromList webview component in vscode-textmate. Use when modifying, debugging, or extending the fuzzy-search selector UI — covers architecture, data flow, key files, and known constraints. Use when this capability is needed.4---56# selectFromList Architecture78A webview-based fuzzy-search selector that replaces VS Code's native QuickPick for richer interaction (multi-select, range select, alternate actions).910## Key Files1112| File | Role |13| ------------------------------- | -------------------------------------------------------------------------------------------------------- |14| `src/selectFromList.js` | Extension-side provider — creates/reuses webview, posts items, resolves promises |15| `src/selectFromList/main.js` | Webview-side — `List` class (virtual scrolling, selection), `FuzzySearch` class (TextMate-style ranking) |16| `src/selectFromList/index.html` | HTML shell with inline CSS, search input, CSP nonces |17| `src/selectFromList.json` | Manifest fragment (contributes views, commands, configuration) |1819## Data Flow2021```22Command invokes showSelectFromList(items, options)23 → chooseItems() on provider24 → createWebviewPanel() (reuses if alive) or sidebar path25 → reveal() panel (so it's visible before init)26 → await _isReady (resolves when webview posts "ready")27 → postMessage({ type: "init", items, requestId })28 → webview receives init29 → pre-lowercases labels (_lowerLabel, _lowerDescription)30 → computeVisible() → render() (virtual scrolling)31 → focus search input32 → user types → 30ms debounce → computeVisible() → render()33 → user submits (Enter / double-click)34 → postMessage({ type: "submit", indexes, requestId })35 → provider resolves promise with selected items36```3738## FuzzySearch (TextMate port)3940- `rank(filter, candidate, candidateLower, out)` — entry point, filter is already lowercase41- `calculateRank(lhs, rhs, rhsLower, out)` — matrix-based scoring from TextMate's `ranker.cc`42- `rankFile(filter, filename, directory, filenameLower, directoryLower)` — tries filename first, falls back to full path43- `isSubset(needle, haystackLower)` — fast pre-check before full ranking44- Pre-allocated typed arrays (`_matrix`, `_first`, `_last`, `_capitals`) avoid GC pressure4546## Performance Characteristics4748- **Virtual scrolling** — only ~60 DOM nodes regardless of list size (20-row overscan)49- **Incremental filtering** — typing more chars narrows from previous matches, not full list50- **Pre-lowercased labels** — `toLowerCase()` called once at init, not per-char in hot loop51- **Panel reuse** — `retainContextWhenHidden: true`, panel is reused across opens5253## Constraints5455- The `FuzzySearch.calculateRank` algorithm is a TextMate port — modify with extreme care56- `requestId` prevents stale responses from previous invocations57- `close()` disposes panel (panel path) or hides sidebar — different teardown per render mode58- The `_isReady` promise pattern: created fresh when panel/sidebar is new, stays resolved when reusing5960---61> Converted and distributed by [TomeVault](https://tomevault.io/claim/elia) — claim your Tome and manage your conversions.62<!-- tomevault:4.0:skill_md:2026-04-11 -->