Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1082,3 +1082,72 @@ defineTest({
expect(Object.fromEntries(requests)).toEqual(map)
},
})

defineTest({
name: 'Completions show for variants that support default values *and* arbitrary values',
fs: {
'app.css': css`
@import 'tailwindcss';
@plugin "./plugin.js";
`,
'plugin.js': js`
export default function ({ matchVariant }) {
matchVariant('foo', (value) => ':is(' + value + ')', {
values: {
DEFAULT: 'default',
a: 'a',
}
})

matchVariant('bar', (value) => {
if (!value) return []
return ':is(' + value + ')'
}, {
values: {
a: 'a',
}
})
}
`,
},
prepare: async ({ root }) => ({ client: await createClient({ root }) }),
handle: async ({ client }) => {
let document = await client.open({
lang: 'html',
text: html`<div class=""></div>`,
})

// <div class=""></div>
// ^
let list = await document.completions({ line: 0, character: 12 })
let items = list?.items ?? []

// The default version of this variant is suggested
let item1 = items.find((item) => item.label.startsWith('foo:'))
expect(item1?.detail).toEqual(':is(default)')

// As are any values
// TODO: This test requires Tailwind CSS v4.1.13
// let item2 = items.find((item) => item.label.startsWith('foo-a:'))
// expect(item2?.detail).toEqual(':is(a)')
Comment on lines +1130 to +1132
Copy link
Member

@RobinMalfait RobinMalfait Aug 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming these tests run against multiple versions?

Can we check the version we are currently running, and enable these tests conditionally?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They do not… currently. At least not exactly. Each test can define which version it runs against by adding a package.json.

I left these in b/c I want to enable them once we tag a patch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I misunderstood. I had a brainfart and thought it was about v3 vs v4 😅

Yeah that makes sense to enable them once v4.1.13 is released 👍


// Ditto with arbitrary values
let item3 = items.find((item) => item.label.startsWith('foo-[]:'))
expect(item3).toBeDefined()
expect(item3?.detail).toEqual(undefined)

// This variant doesn't have a default so it's omitted
let item4 = items.find((item) => item.label.startsWith('bar:'))
expect(item4).not.toBeDefined()

// But does have a value so it is
// TODO: This test requires Tailwind CSS v4.1.13
// let item5 = items.find((item) => item.label.startsWith('bar-a:'))
// expect(item5?.detail).toEqual(':is(a)')

// And it supports arbitrary values so it should be as well
let item6 = items.find((item) => item.label.startsWith('bar-[]:'))
expect(item6).toBeDefined()
expect(item6?.detail).toEqual(undefined)
},
})
40 changes: 26 additions & 14 deletions packages/tailwindcss-language-service/src/completionProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,24 +188,36 @@ export function completionsFromClassList(
// },
}),
)
} else {
let resultingVariants = [...existingVariants, variant.name]
}

let selectors: string[] = []
let hasDefault = false
let resultingVariants = [...existingVariants, variant.name]

try {
selectors = variant.selectors()
} catch (err) {
// If the selectors function fails we don't want to crash the whole completion process
console.log('Error while trying to get selectors for variant')
console.log({
variant,
err,
})
let selectors: string[] = []

continue
}
try {
selectors = variant.selectors()
hasDefault = true
} catch (err) {
// If the selectors function fails we don't want to crash the whole completion process
console.log('Error while trying to get selectors for variant')
console.log({
variant,
err,
})

continue
}

// Commentary:
// The check for `selectors.length` was previously omitted because of the `force` variant that
// existed before the v4.0 release. It was used to place a utility after other base utilities
// and before any using variants. It was dropped before v4.0. An empty selector set is a good
// marker for "this outputs nothing" and as such should be omitted from suggestions.
//
// This does mean that the force variant won't be suggested if IntelliSense is used with
// earlier pre-release versions but this is fine.
if (hasDefault && selectors.length > 0) {
items.push(
variantItem({
label: `${variant.name}${sep}`,
Expand Down
1 change: 1 addition & 0 deletions packages/vscode-tailwindcss/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Prerelease

- Publish our fork of the CSS language server ([#1437](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1437))
- Suggest default variant values when they also support arbitrary values ([#1439](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1439))

## 0.14.26

Expand Down