diff --git a/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts index 4459d249bed274dee0a18fe58c3db84f9574acf2..e73aab6ccf35957193efb9b05ab017f26202dad9 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/getCssConflictDiagnostics.ts @@ -20,7 +20,9 @@ let diagnostics: CssConflictDiagnostic[] = [] const classLists = await findClassListsInDocument(state, document) classLists.forEach((classList) => { - const classNames = getClassNamesInClassList(classList) + const classNames = Array.isArray(classList) + ? classList.flatMap(getClassNamesInClassList) + : getClassNamesInClassList(classList) classNames.forEach((className, index) => { if (state.jit) { diff --git a/packages/tailwindcss-language-service/src/diagnostics/getRecommendedVariantOrderDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getRecommendedVariantOrderDiagnostics.ts index 236195719ef87e1f693a7ccb1e2f23ad0ccd5652..51e8e82121e37d54f42458b702341285e167b3fd 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/getRecommendedVariantOrderDiagnostics.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/getRecommendedVariantOrderDiagnostics.ts @@ -22,7 +22,7 @@ let diagnostics: RecommendedVariantOrderDiagnostic[] = [] const classLists = await findClassListsInDocument(state, document) - classLists.forEach((classList) => { + classLists.flat().forEach((classList) => { const classNames = getClassNamesInClassList(classList) classNames.forEach((className) => { let { rules } = jit.generateRules(state, [className.className]) diff --git a/packages/tailwindcss-language-service/src/documentColorProvider.ts b/packages/tailwindcss-language-service/src/documentColorProvider.ts index 081d1c0c8086a553d36727711fb0aae744d38ca9..f40b93a64449d0f47a95584c7e7e791a894c1e19 100644 --- a/packages/tailwindcss-language-service/src/documentColorProvider.ts +++ b/packages/tailwindcss-language-service/src/documentColorProvider.ts @@ -20,7 +20,7 @@ let settings = await state.editor.getConfiguration(document.uri) if (settings.tailwindCSS.colorDecorators === false) return colors let classLists = await findClassListsInDocument(state, document) - classLists.forEach((classList) => { + classLists.flat().forEach((classList) => { let classNames = getClassNamesInClassList(classList) classNames.forEach((className) => { let color = getColor(state, className.className) diff --git a/packages/tailwindcss-language-service/src/util/find.ts b/packages/tailwindcss-language-service/src/util/find.ts index d9a23c461e9c490c4d9708b6768822329c04c25d..d0ad26681d864cb45c0d893a556e2bd88cc44497 100644 --- a/packages/tailwindcss-language-service/src/util/find.ts +++ b/packages/tailwindcss-language-service/src/util/find.ts @@ -77,7 +77,15 @@ mode?: 'html' | 'css', includeCustom: boolean = true ): Promise { const classLists = await findClassListsInRange(state, doc, range, mode, includeCustom) - return flatten(classLists.map(getClassNamesInClassList)) + return flatten( + classLists.flatMap((classList) => { + if (Array.isArray(classList)) { + return classList.map(getClassNamesInClassList) + } else { + return [getClassNamesInClassList(classList)] + } + }) + ) } export async function findClassNamesInDocument( @@ -85,7 +93,15 @@ state: State, doc: TextDocument ): Promise { const classLists = await findClassListsInDocument(state, doc) - return flatten(classLists.map(getClassNamesInClassList)) + return flatten( + classLists.flatMap((classList) => { + if (Array.isArray(classList)) { + return classList.map(getClassNamesInClassList) + } else { + return [getClassNamesInClassList(classList)] + } + }) + ) } export function findClassListsInCssRange(doc: TextDocument, range?: Range): DocumentClassList[] { @@ -182,7 +198,7 @@ export async function findClassListsInHtmlRange( state: State, doc: TextDocument, range?: Range -): Promise { +): Promise> { const text = doc.getText(range) const matches = matchClassAttributes( @@ -190,7 +206,7 @@ text, (await state.editor.getConfiguration(doc.uri)).tailwindCSS.classAttributes ) - const result: DocumentClassList[] = [] + const result: Array = [] matches.forEach((match) => { const subtext = text.substr(match.index + match[0].length - 1) @@ -201,9 +217,11 @@ ? getComputedClassAttributeLexer() : getClassAttributeLexer() lexer.reset(subtext) - let classLists: { value: string; offset: number }[] = [] - let token: moo.Token + let classLists: Array<{ value: string; offset: number } | { value: string; offset: number }[]> = + [] + let rootClassList: { value: string; offset: number }[] = [] let currentClassList: { value: string; offset: number } + let depth = 0 try { for (let token of lexer) { @@ -218,56 +236,53 @@ } } } else { if (currentClassList) { - classLists.push({ - value: currentClassList.value, - offset: currentClassList.offset, - }) + if (depth === 0) { + rootClassList.push({ + value: currentClassList.value, + offset: currentClassList.offset, + }) + } else { + classLists.push({ + value: currentClassList.value, + offset: currentClassList.offset, + }) + } } currentClassList = undefined } + if (token.type === 'lbrace') { + depth += 1 + } else if (token.type === 'rbrace') { + depth -= 1 + } } } catch (_) {} if (currentClassList) { - classLists.push({ - value: currentClassList.value, - offset: currentClassList.offset, - }) + if (depth === 0) { + rootClassList.push({ + value: currentClassList.value, + offset: currentClassList.offset, + }) + } else { + classLists.push({ + value: currentClassList.value, + offset: currentClassList.offset, + }) + } } + classLists.push(rootClassList) + result.push( ...classLists - .map(({ value, offset }) => { - if (value.trim() === '') { - return null - } - - const before = value.match(/^\s*/) - const beforeOffset = before === null ? 0 : before[0].length - const after = value.match(/\s*$/) - const afterOffset = after === null ? 0 : -after[0].length - - const start = indexToPosition( - text, - match.index + match[0].length - 1 + offset + beforeOffset - ) - const end = indexToPosition( - text, - match.index + match[0].length - 1 + offset + value.length + afterOffset - ) - - return { - classList: value.substr(beforeOffset, value.length + afterOffset), - range: { - start: { - line: (range?.start.line || 0) + start.line, - character: (end.line === 0 ? range?.start.character || 0 : 0) + start.character, - }, - end: { - line: (range?.start.line || 0) + end.line, - character: (end.line === 0 ? range?.start.character || 0 : 0) + end.character, - }, - }, + .map((classList) => { + if (Array.isArray(classList)) { + return classList + .map((classList) => resolveClassList(classList, text, match, range)) + .filter((x) => x !== null) + } else { + return resolveClassList(classList, text, match, range) } }) .filter((x) => x !== null) @@ -277,14 +292,51 @@ return result } +function resolveClassList( + classList: { value: string; offset: number }, + text: string, + match: RegExpMatchArray, + range?: Range +): DocumentClassList { + let { value, offset } = classList + if (value.trim() === '') { + return null + } + + const before = value.match(/^\s*/) + const beforeOffset = before === null ? 0 : before[0].length + const after = value.match(/\s*$/) + const afterOffset = after === null ? 0 : -after[0].length + + const start = indexToPosition(text, match.index + match[0].length - 1 + offset + beforeOffset) + const end = indexToPosition( + text, + match.index + match[0].length - 1 + offset + value.length + afterOffset + ) + + return { + classList: value.substr(beforeOffset, value.length + afterOffset), + range: { + start: { + line: (range?.start.line || 0) + start.line, + character: (end.line === 0 ? range?.start.character || 0 : 0) + start.character, + }, + end: { + line: (range?.start.line || 0) + end.line, + character: (end.line === 0 ? range?.start.character || 0 : 0) + end.character, + }, + }, + } +} + export async function findClassListsInRange( state: State, doc: TextDocument, range?: Range, mode?: 'html' | 'css', includeCustom: boolean = true -): Promise { - let classLists: DocumentClassList[] +): Promise> { + let classLists: Array if (mode === 'css') { classLists = findClassListsInCssRange(doc, range) } else { @@ -296,7 +348,7 @@ export async function findClassListsInDocument( state: State, doc: TextDocument -): Promise { +): Promise> { if (isCssDoc(state, doc)) { return findClassListsInCssRange(doc) }