From 2f5e2f2380a8d82448ec6a2728a5d2b3f7270e13 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 14 Apr 2025 13:54:34 -0400 Subject: [PATCH 1/2] Add failing test --- packages/tailwindcss/src/intellisense.test.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/tailwindcss/src/intellisense.test.ts b/packages/tailwindcss/src/intellisense.test.ts index 24e418be3fa9..fba60e8824e6 100644 --- a/packages/tailwindcss/src/intellisense.test.ts +++ b/packages/tailwindcss/src/intellisense.test.ts @@ -598,3 +598,25 @@ test('Theme keys with underscores are suggested with underscores', async () => { expect(entries).not.toContainEqual(['p-2_5', { modifiers: [] }]) expect(entries).not.toContainEqual(['p-logo.margin', { modifiers: [] }]) }) + +test('Custom @utility and existing utility with names matching theme keys dont give duplicate results', async () => { + let input = css` + @theme reference { + --leading-sm: 0.25rem; + --text-header: 1.5rem; + } + + @utility text-header { + text-transform: uppercase; + } + ` + + let design = await __unstable__loadDesignSystem(input) + + let classList = design.getClassList() + let classMap = new Map(classList) + let matches = classList.filter(([className]) => className === 'text-header') + + expect(matches).toHaveLength(1) + expect(classMap.get('text-header')?.modifiers).toEqual(['sm']) +}) From 7bd0709fc1e2cd6c478a18e29f9ff1c7cb1a6546 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 14 Apr 2025 14:04:11 -0400 Subject: [PATCH 2/2] Ensure class suggestions are unique --- packages/tailwindcss/src/intellisense.ts | 39 ++++++++++++------------ 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/packages/tailwindcss/src/intellisense.ts b/packages/tailwindcss/src/intellisense.ts index ca9c1a386099..27db3a540bc3 100644 --- a/packages/tailwindcss/src/intellisense.ts +++ b/packages/tailwindcss/src/intellisense.ts @@ -20,16 +20,18 @@ export type ClassEntry = [string, ClassMetadata] const IS_FRACTION = /^\d+\/\d+$/ export function getClassList(design: DesignSystem): ClassEntry[] { - let list: ClassItem[] = [] + let items = new DefaultMap((utility) => ({ + name: utility, + utility, + fraction: false, + modifiers: [], + })) // Static utilities only work as-is for (let utility of design.utilities.keys('static')) { - list.push({ - name: utility, - utility, - fraction: false, - modifiers: [], - }) + let item = items.get(utility) + item.fraction = false + item.modifiers = [] } // Functional utilities have their own list of completions @@ -42,28 +44,25 @@ export function getClassList(design: DesignSystem): ClassEntry[] { let name = value === null ? utility : `${utility}-${value}` - list.push({ - name, - utility, - fraction, - modifiers: group.modifiers, - }) + let item = items.get(name) + item.utility = utility + item.fraction ||= fraction + item.modifiers.push(...group.modifiers) if (group.supportsNegative) { - list.push({ - name: `-${name}`, - utility: `-${utility}`, - fraction, - modifiers: group.modifiers, - }) + let item = items.get(`-${name}`) + item.utility = `-${utility}` + item.fraction ||= fraction + item.modifiers.push(...group.modifiers) } } } } - if (list.length === 0) return [] + if (items.size === 0) return [] // Sort utilities by their class name + let list = Array.from(items.values()) list.sort((a, b) => compare(a.name, b.name)) let entries = sortFractionsLast(list)