Universal Ctags: SymbolIndex::ctagsBinary() prefers ctags-universal over
the alternatives-managed ctags (Exuberant 5.9 has no Kotlin/TS parser).
Kotlin/TypeScript now indexed — symbol count on mobydick: 17k -> 397k.
At 397k, per-keystroke ranking was 0.3-0.7s. Three structural fixes:
1. FuzzyRanker: replace std::set with vector+sort+unique; add
withRanges=false fast path that skips highlight computation during
the bulk scan (ranges materialised only for displayed rows).
2. PaletteModel: cap visible results to top-1000 (partial_sort),
score-only bulk, ranges for the top-K only. Nobody scrolls 400k.
3. Min query length (2): 1-char queries match everything and produce a
useless, expensive full scan; below the threshold the palette shows
the capped full list unranked. First real ranking at 2 chars; from
there incremental narrowing makes every keystroke sub-2ms.
Measured (mobydick 397k symbols, RelWithDebInfo, type-forward):
s: 337ms -> ~0ms | se: 507ms -> 373ms (1x first scan) |
ser: 586ms -> 1.9ms | server: 198ms -> 0.8ms.
3 new tests (incrementalMatchesFullScan, visibleResultsAreCapped,
shortQueryDoesNotRank). docs/PERF.md updated.