Skip to content
Draft
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
10 changes: 6 additions & 4 deletions packages/tailwindcss-language-service/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
"test": "vitest"
},
"dependencies": {
"@csstools/css-calc": "2.1.2",
"@csstools/css-parser-algorithms": "3.0.4",
"@csstools/css-tokenizer": "3.0.3",
"@csstools/media-query-list-parser": "2.0.4",
"@csstools/color-helpers": "^5.1.0",
"@csstools/css-calc": "^2.1.4",
"@csstools/css-parser-algorithms": "^3.0.5",
"@csstools/css-color-parser": "3.1.0",
"@csstools/css-tokenizer": "^3.0.4",
"@csstools/media-query-list-parser": "^4.0.3",
"@types/culori": "^2.1.0",
"@types/moo": "0.5.3",
"@types/semver": "7.3.10",
Expand Down
15 changes: 11 additions & 4 deletions packages/tailwindcss-language-service/src/util/color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as jit from './jit'
import * as culori from 'culori'
import namedColors from 'color-name'
import postcss from 'postcss'
import { replaceCssVarsWithFallbacks } from './rewriting'
import { createProcessor } from './rewriting'

const COLOR_PROPS = [
'accent-color',
Expand Down Expand Up @@ -64,9 +64,16 @@ function getColorsInString(state: State, str: string): (culori.Color | KeywordCo
return getKeywordColor(color) ?? tryParseColor(color)
}

str = replaceCssVarsWithFallbacks(state, str)
str = removeColorMixWherePossible(str)
str = resolveLightDark(str)
// @ts-ignore
state.processColors ??= createProcessor({
style: 'full-evaluation',
fontSize: null,
variables: new Map(),
state,
})

// @ts-ignore
str = state.processColors(str)

let possibleColors = str.matchAll(colorRegex)

Expand Down
20 changes: 20 additions & 0 deletions packages/tailwindcss-language-service/src/util/default-map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* A Map that can generate default values for keys that don't exist.
* Generated default values are added to the map to avoid recomputation.
*/
export class DefaultMap<T = string, V = any> extends Map<T, V> {
constructor(private factory: (key: T, self: DefaultMap<T, V>) => V) {
super()
}

get(key: T): V {
let value = super.get(key)

if (value === undefined) {
value = this.factory(key, this)
this.set(key, value)
}

return value
}
}
28 changes: 17 additions & 11 deletions packages/tailwindcss-language-service/src/util/jit.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import type { State } from './state'
import type { Container, Document, Root, Rule, Node, AtRule } from 'postcss'
import { addPixelEquivalentsToValue } from './pixelEquivalents'
import { addEquivalents } from './equivalents'
import { addThemeValues, inlineThemeValues } from './rewriting'
import { createProcessor } from './rewriting'

export function bigSign(bigIntValue: number | bigint): number {
// @ts-ignore
Expand Down Expand Up @@ -45,8 +43,14 @@ export async function stringifyRoot(state: State, root: Root, uri?: string): Pro

let css = clone.toString()

css = addThemeValues(css, state, settings.tailwindCSS)
css = addEquivalents(css, settings.tailwindCSS)
let process = createProcessor({
style: 'user-presentable',
fontSize: settings.tailwindCSS.showPixelEquivalents ? settings.tailwindCSS.rootFontSize : null,
variables: new Map(),
state,
})

css = process(css)

let identSize = state.v4 ? 2 : 4
let identPattern = state.v4 ? /^(?: )+/gm : /^(?: )+/gm
Expand All @@ -68,15 +72,17 @@ export function stringifyRules(state: State, rules: Rule[], tabSize: number = 2)
export async function stringifyDecls(state: State, rule: Rule, uri?: string): Promise<string> {
let settings = await state.editor.getConfiguration(uri)

let process = createProcessor({
style: 'full-evaluation',
fontSize: settings.tailwindCSS.showPixelEquivalents ? settings.tailwindCSS.rootFontSize : null,
variables: new Map(),
state,
})

let result = []

rule.walkDecls(({ prop, value }) => {
// In v4 we inline theme values into declarations (this is a no-op in v3)
value = inlineThemeValues(value, state).trim()

if (settings.tailwindCSS.showPixelEquivalents) {
value = addPixelEquivalentsToValue(value, settings.tailwindCSS.rootFontSize)
}
value = process(value).trim()

result.push(`${prop}: ${value};`)
})
Expand Down

This file was deleted.

37 changes: 0 additions & 37 deletions packages/tailwindcss-language-service/src/util/rewriting/calc.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { expect, test } from 'vitest'
import { colorFromString, colorMixFromString, equivalentColorFromString } from './color'

test('colorFromString', () => {
expect(colorFromString('red')).toEqual({ mode: 'rgb', r: 1, g: 0, b: 0 })
expect(colorFromString('rgb(255 0 0)')).toEqual({ mode: 'rgb', r: 1, g: 0, b: 0 })
expect(colorFromString('hsl(0 100% 50%)')).toEqual({ mode: 'hsl', h: 0, s: 1, l: 0.5 })
expect(colorFromString('#f00')).toEqual({ mode: 'rgb', r: 1, g: 0, b: 0 })
expect(colorFromString('#f003')).toEqual({ mode: 'rgb', r: 1, g: 0, b: 0, alpha: 0.2 })
expect(colorFromString('#ff0000')).toEqual({ mode: 'rgb', r: 1, g: 0, b: 0 })
expect(colorFromString('#ff000033')).toEqual({ mode: 'rgb', r: 1, g: 0, b: 0, alpha: 0.2 })

expect(colorFromString('color(srgb 1 0 0 )')).toEqual({ mode: 'rgb', r: 1, g: 0, b: 0 })
expect(colorFromString('color(srgb-linear 1 0 0 )')).toEqual({ mode: 'lrgb', r: 1, g: 0, b: 0 })
expect(colorFromString('color(display-p3 1 0 0 )')).toEqual({ mode: 'p3', r: 1, g: 0, b: 0 })
expect(colorFromString('color(a98-rgb 1 0 0 )')).toEqual({ mode: 'a98', r: 1, g: 0, b: 0 })
expect(colorFromString('color(prophoto-rgb 1 0 0 )')).toEqual({
mode: 'prophoto',
r: 1,
g: 0,
b: 0,
})
expect(colorFromString('color(rec2020 1 0 0 )')).toEqual({ mode: 'rec2020', r: 1, g: 0, b: 0 })

expect(colorFromString('color(xyz 1 0 0 )')).toEqual({ mode: 'xyz65', x: 1, y: 0, z: 0 })
expect(colorFromString('color(xyz-d65 1 0 0 )')).toEqual({ mode: 'xyz65', x: 1, y: 0, z: 0 })
expect(colorFromString('color(xyz-d50 1 0 0 )')).toEqual({ mode: 'xyz50', x: 1, y: 0, z: 0 })

expect(colorFromString('#ff000033cccc')).toEqual(null)

// none keywords work too
expect(colorFromString('rgb(255 none 0)')).toEqual({ mode: 'rgb', r: 1, b: 0 })
})

test('can compute color mix', () => {
expect(colorMixFromString('color-mix(in srgb, #f00 50%, transparent)')).toEqual({
mode: 'rgb',
r: 1,
g: 0,
b: 0,
alpha: 0.5,
})

expect(colorMixFromString('color-mix(in srgb, #ff33ff, #33ff3380)')).toEqual({
mode: 'rgb',
r: expect.closeTo(0.733),
g: expect.closeTo(0.467),
b: expect.closeTo(0.733),
alpha: 0.751,
})
})

test('equivalentColorFromString', () => {
expect(equivalentColorFromString('oklch(0.9876 0.0249 101.95)')).toEqual('#fefce9')
})
Loading