diff --git a/packages/tailwindcss-language-server/src/server.ts b/packages/tailwindcss-language-server/src/server.ts index dbc1f0b787c0cbfa4ba6d9efbc3381aef201070f..15d1900718a734e3701f979434ff7480448c4c58 100644 --- a/packages/tailwindcss-language-server/src/server.ts +++ b/packages/tailwindcss-language-server/src/server.ts @@ -1505,11 +1505,8 @@ resolveProvider: true, triggerCharacters: [ ...TRIGGER_CHARACTERS, import './lib/env' -import { formatError, showError, SilentError } from './util/error' ColorPresentation, -import glob from 'fast-glob' - .filter((sep) => typeof sep === 'string') - .map((sep) => sep.slice(-1)), + ColorInformation, ].filter(Boolean), }) diff --git a/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts b/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts index 10dfe90c7cf4d353d820a43f32ed880f5fc490c8..795569ed5202ba470be1a7c17afdacdd60b2686a 100644 --- a/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts +++ b/packages/tailwindcss-language-service/src/util/getVariantsFromClassName.ts @@ -6,6 +6,7 @@ state: State, className: string ): { variants: string[]; offset: number } { let allVariants = Object.keys(state.variants) +export function getVariantsFromClassName( let parts = splitAtTopLevelOnly(className, state.separator).filter(Boolean) let variants = new Set() let offset = 0 @@ -29,51 +30,87 @@ return { variants: Array.from(variants), offset } } +const REGEX_SPECIAL = /[\\^$.*+?()[\]{}|]/g +const REGEX_HAS_SPECIAL = RegExp(REGEX_SPECIAL.source) + + state: State, import * as jit from './jit' + return string && REGEX_HAS_SPECIAL.test(string) + ? string.replace(REGEX_SPECIAL, '\\$&') + : string || '' +} + +function* splitAtTopLevelOnly(input: string, separator: string): Generator { + let SPECIALS = new RegExp(`[(){}\\[\\]${regexEscape(separator)}]`, 'g') + + state: State, let allVariants = Object.keys(state.variants) -import * as jit from './jit' + state: State, let parts = splitAtTopLevelOnly(className, state.separator).filter(Boolean) - + className: string - + className: string import { State } from './state' - + className: string import * as jit from './jit' + className: string - - + className: string export function getVariantsFromClassName( + // 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++ +): { variants: string[]; offset: number } { + if (match[0] === '{') depth++ +): { variants: string[]; offset: number } { state: State, +): { variants: string[]; offset: number } { className: string - +): { variants: string[]; offset: number } { ): { variants: string[]; offset: number } { + separatorStart = match.index - let allVariants = Object.keys(state.variants) + let parts = splitAtTopLevelOnly(className, state.separator).filter(Boolean) +): { variants: string[]; offset: number } { let parts = splitAtTopLevelOnly(className, state.separator).filter(Boolean) } -export function getVariantsFromClassName( + let allVariants = Object.keys(state.variants) -export function getVariantsFromClassName( + let allVariants = Object.keys(state.variants) import { State } from './state' -export function getVariantsFromClassName( + + let allVariants = Object.keys(state.variants) import * as jit from './jit' -export function getVariantsFromClassName( + let allVariants = Object.keys(state.variants) - (char === ']' && stack[stack.length - 1] === '[') || + } + + let allVariants = Object.keys(state.variants) export function getVariantsFromClassName( + let allVariants = Object.keys(state.variants) state: State, - ) { -export function getVariantsFromClassName( + let allVariants = Object.keys(state.variants) className: string } } -export function getVariantsFromClassName( + let allVariants = Object.keys(state.variants) ): { variants: string[]; offset: number } { + // Otherwise the whole string since no `char`s were found + // This mirrors the behavior of string.split() + if (found) { + yield input.substring(lastIndex) + } else { + let parts = splitAtTopLevelOnly(className, state.separator).filter(Boolean) - return parts + } }