Skip to content

Commit 89ab09e

Browse files
committed
Collect Scopes about CSS helper functions
1 parent 18b05ef commit 89ab09e

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

packages/tailwindcss-language-service/src/scopes/analyze.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { DocumentClassList, Span, State } from '../util/state'
33
import type {
44
ScopeContext,
55
ScopeAtRule,
6+
ScopeFn,
67
ScopeClassList,
78
ScopeClassName,
89
ScopeAtImport,
@@ -365,3 +366,52 @@ function* analyzeAtRule(
365366

366367
yield scope
367368
}
369+
370+
const HELPER_FN = /(?<name>config|theme|--theme|--spacing|--alpha)\s*\(/dg
371+
372+
/**
373+
* Find all class lists and classes within the given block of text
374+
*/
375+
function* analyzeHelperFns(boundary: LanguageBoundary, slice: string): Iterable<ScopeFn> {
376+
if (boundary.type !== 'css') return
377+
378+
// Look for helper functions in CSS
379+
for (let match of slice.matchAll(HELPER_FN)) {
380+
let groups = match.indices!.groups!
381+
382+
let source: ScopeFn['source'] = {
383+
scope: [groups.name[0], groups.name[1]],
384+
name: [groups.name[0], groups.name[1]],
385+
params: [groups.name[1], groups.name[1]],
386+
}
387+
388+
// Scan forward from each fn to find the end of the params
389+
let depth = 0
390+
for (let i = source.name[1]; i < slice.length; ++i) {
391+
if (depth === 0 && slice[i] === ';') {
392+
break
393+
} else if (slice[i] === '(') {
394+
depth += 1
395+
396+
if (depth === 1) {
397+
source.params = [i + 1, i + 1]
398+
}
399+
} else if (slice[i] === ')') {
400+
depth -= 1
401+
402+
if (depth === 0) {
403+
source.params[1] = i
404+
break
405+
}
406+
}
407+
}
408+
409+
source.scope[1] = source.params[1] + 1
410+
411+
yield {
412+
kind: 'css.fn',
413+
children: [],
414+
source,
415+
}
416+
}
417+
}

packages/tailwindcss-language-service/src/scopes/scope.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,50 @@ export interface ScopeThemePrefix {
332332
}
333333
}
334334

335+
/**
336+
* Represents a function in CSS
337+
*
338+
* Note: Only helper functions are marked with socpes currently
339+
*
340+
* ```
341+
* color: theme(--color-red-500);
342+
* ^^^^^^^^^^^^^^^^^^^^^^
343+
* ```
344+
*/
345+
export interface ScopeFn {
346+
kind: 'css.fn'
347+
children: AnyScope[]
348+
349+
source: {
350+
scope: Span
351+
352+
/**
353+
* Marks the function's name
354+
*
355+
* ```
356+
* color: theme(--color-red-500);
357+
* ^^^^^
358+
* color: --alpha(var(--color-red-500));
359+
* ^^^^^^^
360+
* ```
361+
*/
362+
name: Span
363+
364+
/**
365+
* Marks the function's parameters
366+
*
367+
* ```
368+
* Note: Only helper functions are marked with socpes
369+
* color: theme(--color-red-500);
370+
* ^^^^^^^^^^^^^^^
371+
* color: --alpha(var(--color-red-500));
372+
* ^^^^^^^^^^^^^^^^^^^^
373+
* ```
374+
*/
375+
params: Span
376+
}
377+
}
378+
335379
export type ScopeKind = keyof ScopeMap
336380
export type Scope<K extends ScopeKind> = ScopeMap[K]
337381
export type AnyScope = ScopeMap[ScopeKind]
@@ -348,4 +392,5 @@ type ScopeMap = {
348392
'theme.option.list': ScopeThemeOptionList
349393
'theme.option.name': ScopeThemeOptionName
350394
'theme.prefix': ScopeThemePrefix
395+
'css.fn': ScopeFn
351396
}

0 commit comments

Comments
 (0)