From 4669ee4d853591657dca8201dbc6c3c9715c83bc Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Thu, 3 Apr 2025 14:50:58 -0400 Subject: [PATCH 1/3] Add test --- .../tests/completions/completions.test.js | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/packages/tailwindcss-language-server/tests/completions/completions.test.js b/packages/tailwindcss-language-server/tests/completions/completions.test.js index feb4999f..ce5c7831 100644 --- a/packages/tailwindcss-language-server/tests/completions/completions.test.js +++ b/packages/tailwindcss-language-server/tests/completions/completions.test.js @@ -740,6 +740,80 @@ defineTest({ }, }) +defineTest({ + name: 'v4: Completions show after a variant arbitrary value, using prefixes', + fs: { + 'app.css': css` + @import 'tailwindcss' prefix(tw); + `, + }, + prepare: async ({ root }) => ({ client: await createClient({ root }) }), + handle: async ({ client }) => { + let document = await client.open({ + lang: 'html', + text: '
', + }) + + //
+ // ^ + let completion = await document.completions({ line: 0, character: 26 }) + + expect(completion?.items.length).toBe(19236) + }, +}) + +defineTest({ + name: 'v4: Variant and utility suggestions show prefix when one has been typed', + fs: { + 'app.css': css` + @import 'tailwindcss' prefix(tw); + `, + }, + prepare: async ({ root }) => ({ client: await createClient({ root }) }), + handle: async ({ client }) => { + let document = await client.open({ + lang: 'html', + text: '
', + }) + + //
+ // ^ + let completion = await document.completions({ line: 0, character: 12 }) + + expect(completion?.items.length).toBe(19237) + + // Verify that variants and utilities are all prefixed + let prefixed = completion.items.filter((item) => !item.label.startsWith('tw:')) + expect(prefixed).toHaveLength(0) + }, +}) + +defineTest({ + name: 'v4: Variant and utility suggestions hide prefix when it has been typed', + fs: { + 'app.css': css` + @import 'tailwindcss' prefix(tw); + `, + }, + prepare: async ({ root }) => ({ client: await createClient({ root }) }), + handle: async ({ client }) => { + let document = await client.open({ + lang: 'html', + text: '
', + }) + + //
+ // ^ + let completion = await document.completions({ line: 0, character: 15 }) + + expect(completion?.items.length).toBe(19236) + + // Verify that no variants and utilities have prefixes + let prefixed = completion.items.filter((item) => item.label.startsWith('tw:')) + expect(prefixed).toHaveLength(0) + }, +}) + defineTest({ name: 'v4: Completions show inside class functions in JS/TS files', fs: { From b74160a068599f6c155b6361c5a3e7cc486cdb4d Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Tue, 1 Apr 2025 08:21:21 -0400 Subject: [PATCH 2/3] Fix variant completion validation when prefixes are used --- .../src/util/getVariantsFromClassName.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts b/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts index c30e729a..823b20f8 100644 --- a/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts +++ b/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts @@ -33,6 +33,12 @@ export function getVariantsFromClassName( // NOTE: This should never happen if (!state.designSystem) return false + let prefix = state.designSystem.theme.prefix ?? '' + + if (prefix !== '') { + className = `${prefix}:${className}` + } + // We don't use `compile()` so there's no overhead from PostCSS let compiled = state.designSystem.candidatesToCss([className]) From d650a8cfc511c3b57df1026aa4255e27877a34c3 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Tue, 1 Apr 2025 08:21:48 -0400 Subject: [PATCH 3/3] Improve DX around completions when prefixes are in use --- .../src/completionProvider.ts | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/tailwindcss-language-service/src/completionProvider.ts b/packages/tailwindcss-language-service/src/completionProvider.ts index 5c0c5552..843e9a8e 100644 --- a/packages/tailwindcss-language-service/src/completionProvider.ts +++ b/packages/tailwindcss-language-service/src/completionProvider.ts @@ -261,29 +261,25 @@ export function completionsFromClassList( // TODO: This is a bit of a hack if (prefix.length > 0) { - // No variants seen: suggest the prefix only + // No variants seen: + // - suggest the prefix as a variant + // - Modify the remaining items to include the prefix in the variant name if (existingVariants.length === 0) { - items = items.slice(0, 1) + items = items.map((item, idx) => { + if (idx === 0) return item - return withDefaults( - { - isIncomplete: false, - items, - }, - { - data: { - ...(state.completionItemData ?? {}), - ...(important ? { important } : {}), - variants: existingVariants, - }, - range: replacementRange, - }, - state.editor.capabilities.itemDefaults, - ) + item.label = `${prefix}:${item.label}` + + if (item.textEditText) { + item.textEditText = `${prefix}:${item.textEditText}` + } + + return item + }) } // The first variant is not the prefix: don't suggest anything - if (existingVariants[0] !== prefix) { + if (existingVariants.length > 0 && existingVariants[0] !== prefix) { return null } } @@ -304,6 +300,10 @@ export function completionsFromClassList( documentation = formatColor(color) } + if (prefix.length > 0 && existingVariants.length === 0) { + className = `${prefix}:${className}` + } + items.push({ label: className, kind,