1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
diff --git a/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts b/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts
index 795569ed5202ba470be1a7c17afdacdd60b2686a..10dfe90c7cf4d353d820a43f32ed880f5fc490c8 100644
--- a/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts
+++ b/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts
@@ -6,7 +6,7 @@ state: State,
className: string
): { variants: string[]; offset: number } {
let allVariants = Object.keys(state.variants)
- let parts = Array.from(splitAtTopLevelOnly(className, state.separator)).filter(Boolean)
+ let parts = splitAtTopLevelOnly(className, state.separator).filter(Boolean)
let variants = new Set<string>()
let offset = 0
@@ -29,66 +29,34 @@
return { variants: Array.from(variants), offset }
}
-const REGEX_SPECIAL = /[\\^$.*+?()[\]{}|]/g
-const REGEX_HAS_SPECIAL = RegExp(REGEX_SPECIAL.source)
-
-function regexEscape(string: string): string {
- return string && REGEX_HAS_SPECIAL.test(string)
- ? string.replace(REGEX_SPECIAL, '\\$&')
- : string || ''
-}
-
-function* splitAtTopLevelOnly(input: string, separator: string): Generator<string> {
- let SPECIALS = new RegExp(`[(){}\\[\\]${regexEscape(separator)}]`, 'g')
+// https://github.com/tailwindlabs/tailwindcss/blob/a8a2e2a7191fbd4bee044523aecbade5823a8664/src/util/splitAtTopLevelOnly.js
+function splitAtTopLevelOnly(input: string, separator: string): string[] {
+ let stack: string[] = []
+ let parts: string[] = []
+ let lastPos = 0
- let depth = 0
- let lastIndex = 0
- let found = false
- let separatorIndex = 0
- let separatorStart = 0
- let separatorLength = separator.length
-
- // Find all paren-like things & character
- // And only split on commas if they're top-level
- for (let match of input.matchAll(SPECIALS)) {
- let matchesSeparator = match[0] === separator[separatorIndex]
- let atEndOfSeparator = separatorIndex === separatorLength - 1
- let matchesFullSeparator = matchesSeparator && atEndOfSeparator
-
- if (match[0] === '(') depth++
- if (match[0] === ')') depth--
- if (match[0] === '[') depth++
- if (match[0] === ']') depth--
- if (match[0] === '{') depth++
- if (match[0] === '}') depth--
+ for (let idx = 0; idx < input.length; idx++) {
+ let char = input[idx]
- if (matchesSeparator && depth === 0) {
- if (separatorStart === 0) {
- separatorStart = match.index
+ if (stack.length === 0 && char === separator[0]) {
+ if (separator.length === 1 || input.slice(idx, idx + separator.length) === separator) {
+ parts.push(input.slice(lastPos, idx))
+ lastPos = idx + separator.length
}
-
- separatorIndex++
}
- if (matchesFullSeparator && depth === 0) {
- found = true
-
- yield input.substring(lastIndex, separatorStart)
- lastIndex = separatorStart + separatorLength
- }
-
- if (separatorIndex === separatorLength) {
- separatorIndex = 0
- separatorStart = 0
+ if (char === '(' || char === '[' || char === '{') {
+ stack.push(char)
+ } else if (
+ (char === ')' && stack[stack.length - 1] === '(') ||
+ (char === ']' && stack[stack.length - 1] === '[') ||
+ (char === '}' && stack[stack.length - 1] === '{')
+ ) {
+ stack.pop()
}
}
- // Provide the last segment of the string if available
- // Otherwise the whole string since no `char`s were found
- // This mirrors the behavior of string.split()
- if (found) {
- yield input.substring(lastIndex)
- } else {
- yield input
- }
+ parts.push(input.slice(lastPos))
+
+ return parts
}
|