Skip to content

Commit 17f3d2a

Browse files
Merge branch 'main' into fix/selector-recalculation
2 parents 2060a3f + d907701 commit 17f3d2a

File tree

9 files changed

+365
-67
lines changed

9 files changed

+365
-67
lines changed

packages/tailwindcss-language-server/src/tw.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { URI } from 'vscode-uri'
4141
import normalizePath from 'normalize-path'
4242
import * as path from 'node:path'
4343
import * as fs from 'node:fs/promises'
44+
import * as fsSync from 'node:fs'
4445
import type * as chokidar from 'chokidar'
4546
import picomatch from 'picomatch'
4647
import * as parcel from './watcher/index.js'
@@ -188,7 +189,8 @@ export class TW {
188189
let base = baseUri.fsPath
189190

190191
try {
191-
await fs.access(base, fs.constants.F_OK | fs.constants.R_OK)
192+
// TODO: Change this to fs.constants after the node version bump
193+
await fs.access(base, fsSync.constants.F_OK | fsSync.constants.R_OK)
192194
} catch (err) {
193195
console.error(
194196
`Unable to access the workspace folder [${base}]. This may happen if the directory does not exist or the current user does not have the necessary permissions to access it.`,

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

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -313,8 +313,8 @@ withFixture('v4/basic', (c) => {
313313
let result = await completion({ lang, text, position, settings })
314314
let textEdit = expect.objectContaining({ range: { start: position, end: position } })
315315

316-
expect(result.items.length).toBe(19283)
317-
expect(result.items.filter((item) => item.label.endsWith(':')).length).toBe(346)
316+
expect(result.items.length).not.toBe(0)
317+
expect(result.items.filter((item) => item.label.endsWith(':')).length).not.toBe(0)
318318
expect(result).toEqual({
319319
isIncomplete: false,
320320
items: expect.arrayContaining([
@@ -692,7 +692,7 @@ defineTest({
692692
// ^
693693
let completion = await document.completions({ line: 0, character: 23 })
694694

695-
expect(completion?.items.length).toBe(19236)
695+
expect(completion?.items.length).not.toBe(0)
696696
},
697697
})
698698

@@ -714,7 +714,7 @@ defineTest({
714714
// ^
715715
let completion = await document.completions({ line: 0, character: 22 })
716716

717-
expect(completion?.items.length).toBe(19236)
717+
expect(completion?.items.length).not.toBe(0)
718718
},
719719
})
720720

@@ -736,7 +736,7 @@ defineTest({
736736
// ^
737737
let completion = await document.completions({ line: 0, character: 31 })
738738

739-
expect(completion?.items.length).toBe(19236)
739+
expect(completion?.items.length).not.toBe(0)
740740
},
741741
})
742742

@@ -758,7 +758,7 @@ defineTest({
758758
// ^
759759
let completion = await document.completions({ line: 0, character: 26 })
760760

761-
expect(completion?.items.length).toBe(19236)
761+
expect(completion?.items.length).not.toBe(0)
762762
},
763763
})
764764

@@ -780,7 +780,7 @@ defineTest({
780780
// ^
781781
let completion = await document.completions({ line: 0, character: 12 })
782782

783-
expect(completion?.items.length).toBe(19237)
783+
expect(completion?.items.length).not.toBe(0)
784784

785785
// Verify that variants and utilities are all prefixed
786786
let prefixed = completion.items.filter((item) => !item.label.startsWith('tw:'))
@@ -806,7 +806,7 @@ defineTest({
806806
// ^
807807
let completion = await document.completions({ line: 0, character: 15 })
808808

809-
expect(completion?.items.length).toBe(19236)
809+
expect(completion?.items.length).not.toBe(0)
810810

811811
// Verify that no variants and utilities have prefixes
812812
let prefixed = completion.items.filter((item) => item.label.startsWith('tw:'))
@@ -839,7 +839,7 @@ defineTest({
839839
// ^
840840
let completion = await document.completions({ line: 0, character: 20 })
841841

842-
expect(completion?.items.length).toBe(19236)
842+
expect(completion?.items.length).not.toBe(0)
843843
},
844844
})
845845

@@ -870,7 +870,7 @@ defineTest({
870870
// ^
871871
let completion = await document.completions({ line: 1, character: 22 })
872872

873-
expect(completion?.items.length).toBe(19236)
873+
expect(completion?.items.length).not.toBe(0)
874874
},
875875
})
876876

@@ -960,24 +960,24 @@ defineTest({
960960
// ^
961961
let completionA = await document.completions({ line: 0, character: 13 })
962962

963-
expect(completionA?.items.length).toBe(19236)
963+
expect(completionA?.items.length).not.toBe(0)
964964

965965
// return <Test className={cn("")} />;
966966
// ^
967967
let completionB = await document.completions({ line: 3, character: 30 })
968968

969-
expect(completionB?.items.length).toBe(19236)
969+
expect(completionB?.items.length).not.toBe(0)
970970

971971
// return <Test className={cn("")} />;
972972
// ^
973973
let completionC = await document.completions({ line: 7, character: 30 })
974974

975-
expect(completionC?.items.length).toBe(19236)
975+
expect(completionC?.items.length).not.toBe(0)
976976

977977
// let y = cva("");
978978
// ^
979979
let completionD = await document.completions({ line: 10, character: 13 })
980980

981-
expect(completionD?.items.length).toBe(19236)
981+
expect(completionD?.items.length).not.toBe(0)
982982
},
983983
})

packages/tailwindcss-language-server/tests/env/v4.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ defineTest({
4949
},
5050
})
5151

52-
expect(completion?.items.length).toBe(19235)
52+
expect(completion?.items.length).not.toBe(0)
5353
},
5454
})
5555

@@ -233,7 +233,7 @@ defineTest({
233233
},
234234
})
235235

236-
expect(completion?.items.length).toBe(19235)
236+
expect(completion?.items.length).not.toBe(0)
237237
},
238238
})
239239

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

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -202,9 +202,7 @@ export function completionsFromClassList(
202202
variant,
203203
err,
204204
})
205-
}
206205

207-
if (selectors.length === 0) {
208206
continue
209207
}
210208

@@ -2282,24 +2280,37 @@ export async function resolveCompletionItem(
22822280
if (state.v4) {
22832281
if (item.kind === 9) return item
22842282
if (item.detail && item.documentation) return item
2283+
2284+
let base = state.designSystem.compile([className])[0]
22852285
let root = state.designSystem.compile([[...variants, className].join(state.separator)])[0]
2286+
22862287
let rules = root.nodes.filter((node) => node.type === 'rule')
22872288
if (rules.length === 0) return item
22882289

22892290
if (!item.detail) {
22902291
if (rules.length === 1) {
22912292
let decls: postcss.Declaration[] = []
22922293

2293-
root.walkDecls((node) => {
2294+
// Remove any `@property` rules
2295+
base = base.clone()
2296+
base.walkAtRules((rule) => {
2297+
// Ignore declarations inside `@property` rules
2298+
if (rule.name === 'property') {
2299+
rule.remove()
2300+
}
2301+
2302+
// Ignore declarations @supports (-moz-orient: inline)
2303+
// this is a hack used for `@property` fallbacks in Firefox
2304+
if (rule.name === 'supports' && rule.params === '(-moz-orient: inline)') {
2305+
rule.remove()
2306+
}
2307+
})
2308+
2309+
base.walkDecls((node) => {
22942310
decls.push(node)
22952311
})
22962312

2297-
item.detail = await jit.stringifyDecls(
2298-
state,
2299-
postcss.rule({
2300-
nodes: decls,
2301-
}),
2302-
)
2313+
item.detail = await jit.stringifyDecls(state, postcss.rule({ nodes: decls }))
23032314
} else {
23042315
item.detail = `${rules.length} rules`
23052316
}

packages/tailwindcss-language-service/src/util/color.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ function getKeywordColor(value: unknown): KeywordColor | null {
5050

5151
// https://github.com/khalilgharbaoui/coloregex
5252
const colorRegex = new RegExp(
53-
`(?:^|\\s|\\(|,)(#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgba?|hsla?|(?:ok)?(?:lab|lch))\\(\\s*(-?[\\d.]+%?(\\s*[,/]\\s*|\\s+)+){2,3}\\s*([\\d.]+%?|var\\([^)]+\\))?\\)|transparent|currentColor|${Object.keys(
53+
`(?:^|\\s|\\(|,)(#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgba?|hsla?|(?:ok)?(?:lab|lch))\\(\\s*(-?[\\d.]+(%|deg|rad|grad|turn)?(\\s*[,/]\\s*|\\s+)+){2,3}\\s*([\\d.]+%?|var\\([^)]+\\))?\\)|transparent|currentColor|${Object.keys(
5454
namedColors,
5555
).join('|')})(?:$|\\s|\\)|,)`,
5656
'gi',
@@ -61,7 +61,7 @@ function getColorsInString(state: State, str: string): (culori.Color | KeywordCo
6161

6262
function toColor(match: RegExpMatchArray) {
6363
let color = match[1].replace(/var\([^)]+\)/, '1')
64-
return getKeywordColor(color) ?? culori.parse(color)
64+
return getKeywordColor(color) ?? tryParseColor(color)
6565
}
6666

6767
str = replaceCssVarsWithFallbacks(state, str)
@@ -275,8 +275,8 @@ export function getColorFromValue(value: unknown): culori.Color | KeywordColor |
275275
) {
276276
return null
277277
}
278-
const color = culori.parse(trimmedValue)
279-
return color ?? null
278+
279+
return tryParseColor(trimmedValue)
280280
}
281281

282282
let toRgb = culori.converter('rgb')
@@ -296,11 +296,21 @@ export function formatColor(color: culori.Color): string {
296296

297297
const COLOR_MIX_REGEX = /color-mix\(in [^,]+,\s*(.*?)\s*(\d+|\.\d+|\d+\.\d+)%,\s*transparent\)/g
298298

299+
function tryParseColor(color: string) {
300+
try {
301+
return culori.parse(color) ?? null
302+
} catch (err) {
303+
console.error('Error parsing color', color)
304+
console.error(err)
305+
return null
306+
}
307+
}
308+
299309
function removeColorMixWherePossible(str: string) {
300310
return str.replace(COLOR_MIX_REGEX, (match, color, percentage) => {
301311
if (color.startsWith('var(')) return match
302312

303-
let parsed = culori.parse(color)
313+
let parsed = tryParseColor(color)
304314
if (!parsed) return match
305315

306316
let alpha = Number(percentage) / 100

0 commit comments

Comments
 (0)