Skip to content

Commit a4aeab8

Browse files
committed
wip
1 parent d6ec2a4 commit a4aeab8

File tree

7 files changed

+104
-97
lines changed

7 files changed

+104
-97
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,9 @@ withFixture('v4/basic', (c) => {
567567
withFixture('v4/basic', (c) => {
568568
let completion = buildCompletion(c)
569569

570-
test('Completions have customizable pixel equivalents (1rem == 10px)', async ({ expect }) => {
570+
test.only('Completions have customizable pixel equivalents (1rem == 10px)', async ({
571+
expect,
572+
}) => {
571573
await c.updateSettings({
572574
tailwindCSS: {
573575
rootFontSize: 10,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { expect, test } from 'vitest'
2+
import { State, TailwindCssSettings } from './state'
3+
import { DesignSystem } from './v4'
4+
import { addThemeValues } from './add-theme-values'
5+
6+
test('Inlicing calc expressions using the design system', () => {
7+
let map = new Map<string, string>([['--spacing', '0.25rem']])
8+
9+
let state: State = {
10+
enabled: true,
11+
designSystem: {
12+
resolveThemeValue: (name) => map.get(name) ?? null,
13+
} as DesignSystem,
14+
}
15+
16+
let settings: TailwindCssSettings = {
17+
rootFontSize: 10,
18+
} as any
19+
20+
expect(addThemeValues('calc(var(--spacing) * 4)', state, settings)).toBe('1rem')
21+
})

packages/tailwindcss-language-service/src/util/add-theme-values.ts

Lines changed: 32 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,50 @@
11
import type { State, TailwindCssSettings } from './state'
22

3-
import type { Plugin } from 'postcss'
4-
import { inlineCalc } from './css-calc'
5-
import { getEquivalentColor } from './colorEquivalents'
3+
import { evaluateExpression, replaceCssCalc } from './css-calc'
4+
import { replaceCssVars } from './css-vars'
65
import { addPixelEquivalentsToValue } from './pixelEquivalents'
7-
import { Comment } from './comments'
8-
9-
export function wip(state: State, comments: Comment[], settings: TailwindCssSettings): Plugin {
10-
return {
11-
postcssPlugin: 'plugin',
12-
Declaration(decl) {
13-
let value = inlineCalc(state, decl.value)
14-
if (value === decl.value) return
15-
16-
let comment = ''
17-
18-
let color = getEquivalentColor(value)
19-
if (color !== value) {
20-
comment = `${value} = ${color}`
21-
} else {
22-
let pixels = addPixelEquivalentsToValue(value, settings.rootFontSize, false)
23-
if (pixels !== value) {
24-
comment = `${value} = ${pixels}`
25-
}
26-
}
27-
28-
comments.push({
29-
index: decl.source.end.offset,
30-
value: result,
31-
})
32-
},
33-
}
34-
}
356

36-
export function addThemeValues(state: State, settings: TailwindCssSettings) {
37-
// Add fallbacks to variables with their theme values
7+
export function addThemeValues(css: string, state: State, settings: TailwindCssSettings) {
8+
// TODO: Add fallbacks to variables with their theme values
389
// Ideally these would just be commentss like
3910
// `var(--foo) /* 3rem = 48px */` or
4011
// `calc(var(--spacing) * 5) /* 1.25rem = 20px */`
12+
13+
css = replaceCssCalc(css, (expr) => {
14+
let inlined = replaceCssVars(expr, (name) => {
15+
if (!name.startsWith('--')) return null
16+
17+
let value = state.designSystem.resolveThemeValue?.(name) ?? null
18+
if (value !== null) return value
19+
20+
return null
21+
})
22+
23+
let evaluated = evaluateExpression(inlined)
24+
25+
// No changes were made so we can just return the original expression
26+
if (expr === evaluated) return expr
27+
28+
let equiv = addPixelEquivalentsToValue(evaluated, settings.rootFontSize, false)
29+
if (equiv !== evaluated) {
30+
return `calc(${expr}) /* ${evaluated} = ${equiv} */`
31+
}
32+
33+
return `calc(${expr}) /* ${evaluated} */`
34+
})
35+
4136
css = replaceCssVars(css, (name) => {
4237
if (!name.startsWith('--')) return null
4338

4439
let value = state.designSystem.resolveThemeValue?.(name) ?? null
4540
if (value === null) return null
4641

47-
let comment = ''
48-
49-
let color = getEquivalentColor(value)
50-
if (color !== value) {
51-
comment = ` /* ${value} = ${color} */`
52-
} else {
53-
let pixels = addPixelEquivalentsToValue(value, settings.rootFontSize, false)
54-
if (pixels !== value) {
55-
comment = ` /* ${value} = ${pixels} */`
56-
}
42+
let equiv = addPixelEquivalentsToValue(value, settings.rootFontSize, false)
43+
if (equiv !== value) {
44+
return `var(${name}) /* ${value} = ${equiv} */`
5745
}
5846

59-
return `var(${name})${comment}`
47+
return `var(${name}) /* ${value} */`
6048
})
6149

6250
return css

packages/tailwindcss-language-service/src/util/css-calc.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,7 @@ test('Inlicing calc expressions using the design system', () => {
2323

2424
expect(inlineCalc(state, 'calc(var(--spacing) * 4)')).toBe('1rem')
2525
expect(inlineCalc(state, 'calc(var(--spacing) / 4)')).toBe('0.0625rem')
26+
expect(inlineCalc(state, 'calc(var(--spacing) * 1)')).toBe('0.25rem')
27+
expect(inlineCalc(state, 'calc(var(--spacing) * -1)')).toBe('-0.25rem')
28+
expect(inlineCalc(state, 'calc(1.25 / 0.875)')).toBe('1.4286')
2629
})

packages/tailwindcss-language-service/src/util/css-calc.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,16 @@ function parseLength(length: string): [number, string] | null {
7070
return [numberPart, match[2]]
7171
}
7272

73+
function round(n: number, precision: number): number {
74+
return Math.round(n * Math.pow(10, precision)) / Math.pow(10, precision)
75+
}
76+
7377
export function evaluateExpression(str: string): string | null {
7478
// We're only interested simple calc expressions of the form
7579
// A + B, A - B, A * B, A / B
7680

77-
let parts = str.split(/\s*([+*/-])\s*/)
81+
let parts = str.split(/\s+([+*/-])\s+/)
82+
7883
if (parts.length === 1) return null
7984
if (parts.length !== 3) return null
8085

@@ -98,13 +103,13 @@ export function evaluateExpression(str: string): string | null {
98103

99104
switch (parts[1]) {
100105
case '+':
101-
return (a[0] + b[0]).toString() + a[1]
106+
return round(a[0] + b[0], 4).toString() + a[1]
102107
case '*':
103-
return (a[0] * b[0]).toString() + a[1]
108+
return round(a[0] * b[0], 4).toString() + a[1]
104109
case '-':
105-
return (a[0] - b[0]).toString() + a[1]
110+
return round(a[0] - b[0], 4).toString() + a[1]
106111
case '/':
107-
return (a[0] / b[0]).toString() + a[1]
112+
return round(a[0] / b[0], 4).toString() + a[1]
108113
}
109114

110115
return null
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* Any item in the CSS string which may be analyzed and optionally replaced
3+
*/
4+
export type CssNode = CalcExpression | CssVariable
5+
6+
/**
7+
* A calc(…) expression in a CSS value
8+
*/
9+
export interface CalcExpression {
10+
kind: 'calc-expression'
11+
offset: OffsetRange
12+
parts: string[]
13+
}
14+
15+
/**
16+
* A var(…) expression which may have an optional fallback value
17+
*/
18+
export interface CssVariable {
19+
kind: 'css-variable'
20+
offset: OffsetRange
21+
name: string
22+
fallback: string | null
23+
}
24+
25+
export interface OffsetRange {
26+
/** The zero-based offset where this node starts */
27+
start: number
28+
29+
/** The zero-based offset where this node ends */
30+
end: number
31+
}

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

Lines changed: 4 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import type { State, TailwindCssSettings } from './state'
22
import type { Container, Document, Root, Rule, Node, AtRule } from 'postcss'
33
import { addPixelEquivalentsToValue } from './pixelEquivalents'
44
import { addEquivalents } from './equivalents'
5-
import { replaceCssVars } from './css-vars'
5+
import { replaceCssVars, replaceCssVarsWithFallbacks } from './css-vars'
66
import { addColorEquivalentToValue, getEquivalentColor } from './colorEquivalents'
7-
import { inlineCalc, replaceCssCalc } from './css-calc'
7+
import { evaluateExpression, inlineCalc, replaceCssCalc } from './css-calc'
88
import { applyComments, Comment } from './comments'
9+
import { addThemeValues } from './add-theme-values'
910

1011
export function bigSign(bigIntValue) {
1112
// @ts-ignore
@@ -38,49 +39,6 @@ export function generateRules(
3839
}
3940
}
4041

41-
export function addThemeValues(root: Root, state: State, settings: TailwindCssSettings) {
42-
if (!state.designSystem) return root
43-
44-
let comments: Comment[] = []
45-
46-
root.walkDecls((decl) => {
47-
let result = inlineCalc(state, decl.value)
48-
if (result === decl.value) return
49-
50-
comments.push({
51-
index: decl.source.end.offset,
52-
value: result,
53-
})
54-
})
55-
56-
// Add fallbacks to variables with their theme values
57-
// Ideally these would just be commentss like
58-
// `var(--foo) /* 3rem = 48px */` or
59-
// `calc(var(--spacing) * 5) /* 1.25rem = 20px */`
60-
css = replaceCssVars(css, (name) => {
61-
if (!name.startsWith('--')) return null
62-
63-
let value = state.designSystem.resolveThemeValue?.(name) ?? null
64-
if (value === null) return null
65-
66-
let comment = ''
67-
68-
let color = getEquivalentColor(value)
69-
if (color !== value) {
70-
comment = ` /* ${value} = ${color} */`
71-
} else {
72-
let pixels = addPixelEquivalentsToValue(value, settings.rootFontSize, false)
73-
if (pixels !== value) {
74-
comment = ` /* ${value} = ${pixels} */`
75-
}
76-
}
77-
78-
return `var(${name})${comment}`
79-
})
80-
81-
return css
82-
}
83-
8442
export async function stringifyRoot(state: State, root: Root, uri?: string): Promise<string> {
8543
let settings = await state.editor.getConfiguration(uri)
8644
let clone = root.clone()
@@ -89,10 +47,9 @@ export async function stringifyRoot(state: State, root: Root, uri?: string): Pro
8947
node.remove()
9048
})
9149

92-
addThemeValues(clone, state, settings.tailwindCSS)
93-
9450
let css = clone.toString()
9551

52+
css = addThemeValues(css, state, settings.tailwindCSS)
9653
css = addEquivalents(css, settings.tailwindCSS)
9754

9855
let identSize = state.v4 ? 2 : 4

0 commit comments

Comments
 (0)