Skip to content

Commit 1e2d55c

Browse files
Suggest default variant values when they also support arbitrary values (#1439)
Given the following plugin: ```js matchVariant('foo', (value) => ':is(' + value + ')', { values: { DEFAULT: 'default', a: 'a', } }) ``` We wouldn't suggest `foo:` as a variant even though it works. This is because we were suggesting the arbitrary `foo-[]:` *or* the default variant instead of both if a variant supported arbitrary values in v4. This PR fixes the above and now both will be suggested.
1 parent 5ce025c commit 1e2d55c

File tree

3 files changed

+96
-14
lines changed

3 files changed

+96
-14
lines changed

packages/tailwindcss-language-server/tests/completions/completions.test.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,3 +1082,72 @@ defineTest({
10821082
expect(Object.fromEntries(requests)).toEqual(map)
10831083
},
10841084
})
1085+
1086+
defineTest({
1087+
name: 'Completions show for variants that support default values *and* arbitrary values',
1088+
fs: {
1089+
'app.css': css`
1090+
@import 'tailwindcss';
1091+
@plugin "./plugin.js";
1092+
`,
1093+
'plugin.js': js`
1094+
export default function ({ matchVariant }) {
1095+
matchVariant('foo', (value) => ':is(' + value + ')', {
1096+
values: {
1097+
DEFAULT: 'default',
1098+
a: 'a',
1099+
}
1100+
})
1101+
1102+
matchVariant('bar', (value) => {
1103+
if (!value) return []
1104+
return ':is(' + value + ')'
1105+
}, {
1106+
values: {
1107+
a: 'a',
1108+
}
1109+
})
1110+
}
1111+
`,
1112+
},
1113+
prepare: async ({ root }) => ({ client: await createClient({ root }) }),
1114+
handle: async ({ client }) => {
1115+
let document = await client.open({
1116+
lang: 'html',
1117+
text: html`<div class=""></div>`,
1118+
})
1119+
1120+
// <div class=""></div>
1121+
// ^
1122+
let list = await document.completions({ line: 0, character: 12 })
1123+
let items = list?.items ?? []
1124+
1125+
// The default version of this variant is suggested
1126+
let item1 = items.find((item) => item.label.startsWith('foo:'))
1127+
expect(item1?.detail).toEqual(':is(default)')
1128+
1129+
// As are any values
1130+
// TODO: This test requires Tailwind CSS v4.1.13
1131+
// let item2 = items.find((item) => item.label.startsWith('foo-a:'))
1132+
// expect(item2?.detail).toEqual(':is(a)')
1133+
1134+
// Ditto with arbitrary values
1135+
let item3 = items.find((item) => item.label.startsWith('foo-[]:'))
1136+
expect(item3).toBeDefined()
1137+
expect(item3?.detail).toEqual(undefined)
1138+
1139+
// This variant doesn't have a default so it's omitted
1140+
let item4 = items.find((item) => item.label.startsWith('bar:'))
1141+
expect(item4).not.toBeDefined()
1142+
1143+
// But does have a value so it is
1144+
// TODO: This test requires Tailwind CSS v4.1.13
1145+
// let item5 = items.find((item) => item.label.startsWith('bar-a:'))
1146+
// expect(item5?.detail).toEqual(':is(a)')
1147+
1148+
// And it supports arbitrary values so it should be as well
1149+
let item6 = items.find((item) => item.label.startsWith('bar-[]:'))
1150+
expect(item6).toBeDefined()
1151+
expect(item6?.detail).toEqual(undefined)
1152+
},
1153+
})

packages/tailwindcss-language-service/src/completionProvider.ts

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -188,24 +188,36 @@ export function completionsFromClassList(
188188
// },
189189
}),
190190
)
191-
} else {
192-
let resultingVariants = [...existingVariants, variant.name]
191+
}
193192

194-
let selectors: string[] = []
193+
let hasDefault = false
194+
let resultingVariants = [...existingVariants, variant.name]
195195

196-
try {
197-
selectors = variant.selectors()
198-
} catch (err) {
199-
// If the selectors function fails we don't want to crash the whole completion process
200-
console.log('Error while trying to get selectors for variant')
201-
console.log({
202-
variant,
203-
err,
204-
})
196+
let selectors: string[] = []
205197

206-
continue
207-
}
198+
try {
199+
selectors = variant.selectors()
200+
hasDefault = true
201+
} catch (err) {
202+
// If the selectors function fails we don't want to crash the whole completion process
203+
console.log('Error while trying to get selectors for variant')
204+
console.log({
205+
variant,
206+
err,
207+
})
208+
209+
continue
210+
}
208211

212+
// Commentary:
213+
// The check for `selectors.length` was previously omitted because of the `force` variant that
214+
// existed before the v4.0 release. It was used to place a utility after other base utilities
215+
// and before any using variants. It was dropped before v4.0. An empty selector set is a good
216+
// marker for "this outputs nothing" and as such should be omitted from suggestions.
217+
//
218+
// This does mean that the force variant won't be suggested if IntelliSense is used with
219+
// earlier pre-release versions but this is fine.
220+
if (hasDefault && selectors.length > 0) {
209221
items.push(
210222
variantItem({
211223
label: `${variant.name}${sep}`,

packages/vscode-tailwindcss/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Prerelease
44

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

78
## 0.14.26
89

0 commit comments

Comments
 (0)