Add support for arbitrary variants (#557)
* Support arbitrary variants * Bump typescript and types versionsmaster
parent
5516e3321c
commit
76cbaa4948
File diff suppressed because it is too large
Load Diff
|
@ -38,7 +38,7 @@
|
|||
"@tailwindcss/typography": "0.5.0",
|
||||
"@types/debounce": "1.2.0",
|
||||
"@types/node": "14.14.34",
|
||||
"@types/vscode": "1.60.0",
|
||||
"@types/vscode": "1.67.0",
|
||||
"builtin-modules": "3.2.0",
|
||||
"chokidar": "3.5.1",
|
||||
"color-name": "1.1.4",
|
||||
|
@ -65,7 +65,7 @@
|
|||
"stack-trace": "0.0.10",
|
||||
"tailwindcss": "3.0.11",
|
||||
"terser": "4.6.12",
|
||||
"typescript": "4.2.4",
|
||||
"typescript": "4.6.4",
|
||||
"vscode-css-languageservice": "5.4.1",
|
||||
"vscode-languageserver": "7.0.0",
|
||||
"vscode-languageserver-textdocument": "1.0.1",
|
||||
|
|
|
@ -38,6 +38,6 @@
|
|||
"prettier": "2.3.0",
|
||||
"tsdx": "0.14.1",
|
||||
"tslib": "2.2.0",
|
||||
"typescript": "4.2.4"
|
||||
"typescript": "4.6.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -364,10 +364,10 @@ async function provideClassAttributeCompletions(
|
|||
try {
|
||||
let tokens = Array.from(lexer)
|
||||
let last = tokens[tokens.length - 1]
|
||||
if (last.type.startsWith('start') || last.type === 'classlist') {
|
||||
if (last.type.startsWith('start') || last.type === 'classlist' || last.type.startsWith('arb')) {
|
||||
let classList = ''
|
||||
for (let i = tokens.length - 1; i >= 0; i--) {
|
||||
if (tokens[i].type === 'classlist') {
|
||||
if (tokens[i].type === 'classlist' || tokens[i].type.startsWith('arb')) {
|
||||
classList = tokens[i].value + classList
|
||||
} else {
|
||||
break
|
||||
|
|
|
@ -207,7 +207,7 @@ export async function findClassListsInHtmlRange(
|
|||
|
||||
try {
|
||||
for (let token of lexer) {
|
||||
if (token.type === 'classlist') {
|
||||
if (token.type === 'classlist' || token.type.startsWith('arb')) {
|
||||
if (currentClassList) {
|
||||
currentClassList.value += token.value
|
||||
} else {
|
||||
|
|
|
@ -1,28 +1,94 @@
|
|||
import { State } from './state'
|
||||
import * as jit from './jit'
|
||||
|
||||
export function getVariantsFromClassName(
|
||||
state: State,
|
||||
className: string
|
||||
): { variants: string[]; offset: number } {
|
||||
let str = className
|
||||
let allVariants = Object.keys(state.variants)
|
||||
let allVariantsByLength = allVariants.sort((a, b) => b.length - a.length)
|
||||
let parts = Array.from(splitAtTopLevelOnly(className, state.separator)).filter(Boolean)
|
||||
let variants = new Set<string>()
|
||||
let offset = 0
|
||||
|
||||
while (str) {
|
||||
let found = false
|
||||
for (let variant of allVariantsByLength) {
|
||||
if (str.startsWith(variant + state.separator)) {
|
||||
variants.add(variant)
|
||||
str = str.substr(variant.length + state.separator.length)
|
||||
offset += variant.length + state.separator.length
|
||||
found = true
|
||||
break
|
||||
}
|
||||
for (let part of parts) {
|
||||
if (
|
||||
allVariants.includes(part) ||
|
||||
(state.jit &&
|
||||
part.startsWith('[') &&
|
||||
part.endsWith(']') &&
|
||||
jit.generateRules(state, [`${part}${state.separator}[color:red]`]).rules.length > 0)
|
||||
) {
|
||||
variants.add(part)
|
||||
offset += part.length + state.separator.length
|
||||
continue
|
||||
}
|
||||
if (!found) str = ''
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
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')
|
||||
|
||||
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--
|
||||
|
||||
if (matchesSeparator && depth === 0) {
|
||||
if (separatorStart === 0) {
|
||||
separatorStart = match.index
|
||||
}
|
||||
|
||||
separatorIndex++
|
||||
}
|
||||
|
||||
if (matchesFullSeparator && depth === 0) {
|
||||
found = true
|
||||
|
||||
yield input.substring(lastIndex, separatorStart)
|
||||
lastIndex = separatorStart + separatorLength
|
||||
}
|
||||
|
||||
if (separatorIndex === separatorLength) {
|
||||
separatorIndex = 0
|
||||
separatorStart = 0
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { lazy } from './lazy'
|
|||
|
||||
const classAttributeStates: () => { [x: string]: moo.Rules } = () => ({
|
||||
doubleClassList: {
|
||||
arb: { match: new RegExp('(?<!\\\\)\\['), push: 'arbitrary' },
|
||||
lbrace: { match: new RegExp('(?<!\\\\)\\{'), push: 'interpBrace' },
|
||||
rbrace: { match: new RegExp('(?<!\\\\)\\}'), pop: 1 },
|
||||
end: { match: new RegExp('(?<!\\\\)"'), pop: 1 },
|
||||
|
@ -40,6 +41,11 @@ const classAttributeStates: () => { [x: string]: moo.Rules } = () => ({
|
|||
double: { match: new RegExp('(?<!\\\\)"'), pop: 1 },
|
||||
text: { match: new RegExp('[\\s\\S]'), lineBreaks: true },
|
||||
},
|
||||
arbitrary: {
|
||||
arb: { match: new RegExp('(?<!\\\\)\\]'), pop: 1 },
|
||||
space: { match: /\s/, pop: 1, lineBreaks: true },
|
||||
arb2: { match: new RegExp('[\\s\\S]'), lineBreaks: true },
|
||||
},
|
||||
})
|
||||
|
||||
const simpleClassAttributeStates: { [x: string]: moo.Rules } = {
|
||||
|
|
|
@ -321,7 +321,7 @@
|
|||
"check": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/vscode": "1.60.0",
|
||||
"@types/vscode": "1.67.0",
|
||||
"color-name": "1.1.4",
|
||||
"concurrently": "7.0.0",
|
||||
"rimraf": "3.0.2",
|
||||
|
|
Loading…
Reference in New Issue