Skip to content

Commit

Permalink
Fix IntelliSense when separator is -- (#628)
Browse files Browse the repository at this point in the history
* Fix IntelliSense when separator is `--`

* Tidy up
  • Loading branch information
bradlc committed Oct 13, 2022
1 parent d073bb9 commit c9acd0d
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 56 deletions.
5 changes: 4 additions & 1 deletion packages/tailwindcss-language-server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1504,7 +1504,10 @@ class TW {
resolveProvider: true,
triggerCharacters: [
...TRIGGER_CHARACTERS,
...projects.map((project) => project.state.separator).filter(Boolean),
...projects
.map((project) => project.state.separator)
.filter((sep) => typeof sep === 'string')
.map((sep) => sep.slice(-1)),
].filter(Boolean),
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function getVariantsFromClassName(
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

Expand All @@ -29,66 +29,34 @@ export function getVariantsFromClassName(
return { variants: Array.from(variants), offset }
}

const REGEX_SPECIAL = /[\\^$.*+?()[\]{}|]/g
const REGEX_HAS_SPECIAL = RegExp(REGEX_SPECIAL.source)
// 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

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
for (let idx = 0; idx < input.length; idx++) {
let char = input[idx]

// 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
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
}

0 comments on commit c9acd0d

Please sign in to comment.