Remove duplicate `variant` + `value` pairs from completions (#874)
* Refactor * Support using multiple fixtures in a single test file * Add test * Remove duplicate `variant` + `value` pairs from completions * Update changelogmaster
parent
a13708b995
commit
2caebd1d48
|
@ -3,13 +3,11 @@ import * as cp from 'node:child_process'
|
||||||
import * as rpc from 'vscode-jsonrpc'
|
import * as rpc from 'vscode-jsonrpc'
|
||||||
import { beforeAll } from 'vitest'
|
import { beforeAll } from 'vitest'
|
||||||
|
|
||||||
let settings = {}
|
|
||||||
let initPromise
|
|
||||||
let childProcess
|
|
||||||
let docSettings = new Map()
|
|
||||||
|
|
||||||
async function init(fixture) {
|
async function init(fixture) {
|
||||||
childProcess = cp.fork('./bin/tailwindcss-language-server', { silent: true })
|
let settings = {}
|
||||||
|
let docSettings = new Map()
|
||||||
|
|
||||||
|
let childProcess = cp.fork('./bin/tailwindcss-language-server', { silent: true })
|
||||||
|
|
||||||
const capabilities = {
|
const capabilities = {
|
||||||
textDocument: {
|
textDocument: {
|
||||||
|
@ -116,7 +114,7 @@ async function init(fixture) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
initPromise = new Promise((resolve) => {
|
let initPromise = new Promise((resolve) => {
|
||||||
connection.onRequest(new rpc.RequestType('client/registerCapability'), ({ registrations }) => {
|
connection.onRequest(new rpc.RequestType('client/registerCapability'), ({ registrations }) => {
|
||||||
if (registrations.findIndex((r) => r.method === 'textDocument/completion') > -1) {
|
if (registrations.findIndex((r) => r.method === 'textDocument/completion') > -1) {
|
||||||
resolve()
|
resolve()
|
||||||
|
@ -177,33 +175,18 @@ async function init(fixture) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function withFixture(fixture, callback) {
|
export function withFixture(fixture, callback) {
|
||||||
let c
|
let c = {}
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
c = await init(fixture)
|
// Using the connection object as the prototype lets us access the connection
|
||||||
|
// without defining getters for all the methods and also lets us add helpers
|
||||||
|
// to the connection object without having to resort to using a Proxy
|
||||||
|
Object.setPrototypeOf(c, await init(fixture))
|
||||||
|
|
||||||
return () => c.connection.end()
|
return () => c.connection.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
callback({
|
callback(c)
|
||||||
get connection() {
|
|
||||||
return c.connection
|
|
||||||
},
|
|
||||||
get sendRequest() {
|
|
||||||
return c.sendRequest
|
|
||||||
},
|
|
||||||
get onNotification() {
|
|
||||||
return c.onNotification
|
|
||||||
},
|
|
||||||
get openDocument() {
|
|
||||||
return c.openDocument
|
|
||||||
},
|
|
||||||
get updateSettings() {
|
|
||||||
return c.updateSettings
|
|
||||||
},
|
|
||||||
get updateFile() {
|
|
||||||
return c.updateFile
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// let counter = 0
|
// let counter = 0
|
||||||
|
|
|
@ -119,3 +119,35 @@ withFixture('basic', (c) => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
withFixture('overrides-variants', (c) => {
|
||||||
|
async function completion({
|
||||||
|
lang,
|
||||||
|
text,
|
||||||
|
position,
|
||||||
|
context = {
|
||||||
|
triggerKind: 1,
|
||||||
|
},
|
||||||
|
settings,
|
||||||
|
}) {
|
||||||
|
let textDocument = await c.openDocument({ text, lang, settings })
|
||||||
|
|
||||||
|
return c.sendRequest('textDocument/completion', {
|
||||||
|
textDocument,
|
||||||
|
position,
|
||||||
|
context,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
test.concurrent(
|
||||||
|
'duplicate variant + value pairs do not produce multiple completions',
|
||||||
|
async () => {
|
||||||
|
let result = await completion({
|
||||||
|
text: '<div class="custom-hover"></div>',
|
||||||
|
position: { line: 0, character: 23 },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.items.filter((item) => item.label.endsWith('custom-hover:')).length).toBe(1)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
function ({ addVariant, matchVariant }) {
|
||||||
|
matchVariant('custom', (value) => `.custom:${value} &`, { values: { hover: 'hover' } })
|
||||||
|
addVariant('custom-hover', `.custom:hover &:hover`)
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
|
@ -138,6 +138,7 @@ export function completionsFromClassList(
|
||||||
}
|
}
|
||||||
|
|
||||||
let items: CompletionItem[] = []
|
let items: CompletionItem[] = []
|
||||||
|
let seenVariants = new Set<string>()
|
||||||
|
|
||||||
if (!important) {
|
if (!important) {
|
||||||
let variantOrder = 0
|
let variantOrder = 0
|
||||||
|
@ -163,9 +164,16 @@ export function completionsFromClassList(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
items.push(
|
for (let variant of state.variants) {
|
||||||
...state.variants.flatMap((variant) => {
|
if (existingVariants.includes(variant.name)) {
|
||||||
let items: CompletionItem[] = []
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seenVariants.has(variant.name)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
seenVariants.add(variant.name)
|
||||||
|
|
||||||
if (variant.isArbitrary) {
|
if (variant.isArbitrary) {
|
||||||
items.push(
|
items.push(
|
||||||
|
@ -180,7 +188,7 @@ export function completionsFromClassList(
|
||||||
// },
|
// },
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
} else if (!existingVariants.includes(variant.name)) {
|
} else {
|
||||||
let shouldSortVariants = !semver.gte(state.version, '2.99.0')
|
let shouldSortVariants = !semver.gte(state.version, '2.99.0')
|
||||||
let resultingVariants = [...existingVariants, variant.name]
|
let resultingVariants = [...existingVariants, variant.name]
|
||||||
|
|
||||||
|
@ -204,8 +212,7 @@ export function completionsFromClassList(
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
newText:
|
newText:
|
||||||
resultingVariants.slice(0, resultingVariants.length - 1).join(sep) +
|
resultingVariants.slice(0, resultingVariants.length - 1).join(sep) + sep,
|
||||||
sep,
|
|
||||||
range: {
|
range: {
|
||||||
start: {
|
start: {
|
||||||
...classListRange.start,
|
...classListRange.start,
|
||||||
|
@ -223,11 +230,18 @@ export function completionsFromClassList(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (variant.values.length) {
|
for (let value of variant.values ?? []) {
|
||||||
|
if (existingVariants.includes(`${variant.name}-${value}`)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seenVariants.has(`${variant.name}-${value}`)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
seenVariants.add(`${variant.name}-${value}`)
|
||||||
|
|
||||||
items.push(
|
items.push(
|
||||||
...variant.values
|
|
||||||
.filter((value) => !existingVariants.includes(`${variant.name}-${value}`))
|
|
||||||
.map((value) =>
|
|
||||||
variantItem({
|
variantItem({
|
||||||
label:
|
label:
|
||||||
value === 'DEFAULT'
|
value === 'DEFAULT'
|
||||||
|
@ -236,12 +250,8 @@ export function completionsFromClassList(
|
||||||
detail: variant.selectors({ value }).join(', '),
|
detail: variant.selectors({ value }).join(', '),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return items
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.classList) {
|
if (state.classList) {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
## 0.11.x (Pre-Release)
|
## 0.11.x (Pre-Release)
|
||||||
|
|
||||||
- Add support for Glimmer (#867)
|
- Add support for Glimmer (#867)
|
||||||
|
- Ignore duplicate variant + value pairs (#874)
|
||||||
|
|
||||||
## 0.10.1
|
## 0.10.1
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue