From 78e3721c2331ba54bbfa2dbb3065bfa019fa5b58 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sat, 5 Sep 2026 20:04:16 -0700 Subject: [PATCH] perf(palette): reuse allowed quality arrays during matching (#18966) --- .../match-field-allocation.test.ts | 90 +++++++++++++++++++ .../src/lib/palette-match/match-field.ts | 26 +++--- 2 files changed, 103 insertions(+), 13 deletions(-) create mode 100644 src/renderer/src/lib/palette-match/match-field-allocation.test.ts diff --git a/src/renderer/src/lib/palette-match/match-field-allocation.test.ts b/src/renderer/src/lib/palette-match/match-field-allocation.test.ts new file mode 100644 index 00000000000..5b0021d2e71 --- /dev/null +++ b/src/renderer/src/lib/palette-match/match-field-allocation.test.ts @@ -0,0 +1,90 @@ +import { describe, expect, it, vi } from 'vitest' +import { + indexPaletteField, + type PaletteIdentifierKind, + type PaletteFieldProfile +} from './indexed-field' +import { matchPaletteField } from './match-field' +import { createPaletteQueryToken } from './palette-query' + +describe('palette field quality allocation', () => { + it.each(['scan', 's', '123', 'scna', 'zzz'])( + 'does not allocate a Set per field for %s', + (query) => { + const profiles: PaletteFieldProfile[] = [ + 'structured-label', + 'identifier', + 'path', + 'prose', + 'exact-alias' + ] + const fields = Array.from({ length: 1_000 }, (_, i) => + indexPaletteField({ + id: String(i), + profile: profiles[i % profiles.length], + text: 'scan daily 1234 workspace', + ...(i % 2 === 0 ? { identifier: { kind: 'number' as const } } : {}) + })! + ) + const token = createPaletteQueryToken(query, 0) + let allocations = 0 + const NativeSet = globalThis.Set + class CountedSet extends NativeSet { + constructor(values?: Iterable | null) { + super(values) + allocations++ + } + } + vi.stubGlobal('Set', CountedSet) + try { + for (const field of fields) { + matchPaletteField(field, token) + } + } finally { + vi.unstubAllGlobals() + } + expect(allocations).toBe(0) + } + ) +}) + +describe('palette quality restrictions remain local to each match', () => { + it.each(['number', 'version', 'date', 'port', 'sha', 'key'])( + 'preserves prefix permissions for %s', + (kind) => { + const field = indexPaletteField({ + id: 'id', + profile: 'identifier', + text: '12345', + identifier: { kind } + })! + const prefix = createPaletteQueryToken('123', 0) + const exact = createPaletteQueryToken('12345', 0) + const expected = ['port', 'sha', 'key'].includes(kind) + ? { quality: 'field-prefix', ranges: [{ start: 0, end: 3 }] } + : null + expect(matchPaletteField(field, prefix)).toEqual(expected) + expect(matchPaletteField(field, exact)).toEqual({ + quality: 'field-exact', + ranges: [{ start: 0, end: 5 }] + }) + expect(matchPaletteField(field, prefix)).toEqual(expected) + } + ) + + it.each(['structured-label', 'identifier', 'path', 'prose', 'exact-alias'])( + 'preserves typo restrictions for %s without mutating the profile', + (profile) => { + const field = indexPaletteField({ id: 'id', profile, text: 'scan' })! + expect(matchPaletteField(field, createPaletteQueryToken('s', 0))).toEqual({ + quality: 'field-prefix', + ranges: [{ start: 0, end: 1 }] + }) + expect(matchPaletteField(field, createPaletteQueryToken('scam', 0))).toEqual( + ['structured-label', 'prose'].includes(profile) + ? { quality: 'typo', ranges: [{ start: 0, end: 4 }] } + : null + ) + } + ) +}) diff --git a/src/renderer/src/lib/palette-match/match-field.ts b/src/renderer/src/lib/palette-match/match-field.ts index f9015ec49a5..1744219524c 100644 --- a/src/renderer/src/lib/palette-match/match-field.ts +++ b/src/renderer/src/lib/palette-match/match-field.ts @@ -32,7 +32,7 @@ const SIGILS = new Set(['#', '!']) function allowedQualities( field: PaletteIndexedField, token: PaletteQueryToken -): ReadonlySet { +): readonly PaletteMatchQuality[] { let qualities = paletteProfileAllowedQualities(field.profile) if (field.identifier && !identifierKindAllowsPrefix(field.identifier.kind)) { qualities = qualities.filter((quality) => !PREFIX_QUALITIES.has(quality)) @@ -43,7 +43,7 @@ function allowedQualities( if (token.isIdentifierLike) { qualities = qualities.filter((quality) => quality !== 'typo') } - return new Set(qualities) + return qualities } /** `#123` must not reach a GitLab MR, and `!123` must not reach a GitHub PR. */ @@ -83,15 +83,15 @@ function toRanges(field: PaletteIndexedField, start: number, end: number): reado function matchLiteral( field: PaletteIndexedField, token: PaletteQueryToken, - qualities: ReadonlySet + qualities: readonly PaletteMatchQuality[] ): PaletteFieldMatch | null { const normalized = field.text.normalized const text = token.text - if (qualities.has('field-exact') && normalized === text) { + if (qualities.includes('field-exact') && normalized === text) { return { quality: 'field-exact', ranges: toRanges(field, 0, normalized.length) } } - if (qualities.has('word-exact')) { + if (qualities.includes('word-exact')) { const word = field.words.find((entry) => entry.text === text) if (word) { return { quality: 'word-exact', ranges: toRanges(field, word.start, word.end) } @@ -101,10 +101,10 @@ function matchLiteral( return { quality: 'word-exact', ranges: toRanges(field, atom.start, atom.end) } } } - if (qualities.has('field-prefix') && normalized.startsWith(text)) { + if (qualities.includes('field-prefix') && normalized.startsWith(text)) { return { quality: 'field-prefix', ranges: toRanges(field, 0, text.length) } } - if (qualities.has('word-prefix')) { + if (qualities.includes('word-prefix')) { const word = field.words.find((entry) => entry.text.startsWith(text)) const atom = field.atoms.find((entry) => normalized.startsWith(text, entry.start)) const start = word && atom ? Math.min(word.start, atom.start) : (word?.start ?? atom?.start) @@ -117,13 +117,13 @@ function matchLiteral( if (literalIndex === -1) { return null } - if (qualities.has('boundary-substring') && isWordStart(field, literalIndex)) { + if (qualities.includes('boundary-substring') && isWordStart(field, literalIndex)) { return { quality: 'boundary-substring', ranges: toRanges(field, literalIndex, literalIndex + text.length) } } - if (qualities.has('literal-substring')) { + if (qualities.includes('literal-substring')) { return { quality: 'literal-substring', ranges: toRanges(field, literalIndex, literalIndex + text.length) @@ -135,9 +135,9 @@ function matchLiteral( function matchCompact( field: PaletteIndexedField, token: PaletteQueryToken, - qualities: ReadonlySet + qualities: readonly PaletteMatchQuality[] ): PaletteFieldMatch | null { - if (!qualities.has('compact') || token.compact.length < MIN_COMPACT_LENGTH) { + if (!qualities.includes('compact') || token.compact.length < MIN_COMPACT_LENGTH) { return null } for (const atom of field.atoms) { @@ -152,9 +152,9 @@ function matchCompact( function matchTypo( field: PaletteIndexedField, token: PaletteQueryToken, - qualities: ReadonlySet + qualities: readonly PaletteMatchQuality[] ): PaletteFieldMatch | null { - if (!qualities.has('typo') || !token.isLetterOnly || !isPaletteTypoCandidate(token.text)) { + if (!qualities.includes('typo') || !token.isLetterOnly || !isPaletteTypoCandidate(token.text)) { return null } for (const word of field.words) {