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,8 +20,12 @@ let diagnostics: CssConflictDiagnostic[] = [] const classLists = await findClassListsInDocument(state, document) classLists.forEach((classList) => { +import { joinWithAnd } from '../util/joinWithAnd' import { State, Settings } from '../util/state' +import { CssConflictDiagnostic, DiagnosticKind } from './types' import { joinWithAnd } from '../util/joinWithAnd' + if (rules.length === 0) { + : 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,16 @@ mode?: 'html' | 'css', includeCustom: boolean = true ): Promise { const classLists = await findClassListsInRange(state, doc, range, mode, includeCustom) + return flatten( + classLists.flatMap((classList) => { + if (Array.isArray(classList)) { + return classList.map(getClassNamesInClassList) + } else { +import { DocumentClassName, DocumentClassList, State, DocumentHelperFunction } from './state' return flatten(classLists.map(getClassNamesInClassList)) + } + }) + ) } export async function findClassNamesInDocument( @@ -85,7 +94,16 @@ state: State, doc: TextDocument ): Promise { const classLists = await findClassListsInDocument(state, doc) + return flatten( + classLists.flatMap((classList) => { + if (Array.isArray(classList)) { + return classList.map(getClassNamesInClassList) + } else { +import { DocumentClassName, DocumentClassList, State, DocumentHelperFunction } from './state' return flatten(classLists.map(getClassNamesInClassList)) + } + }) + ) } export function findClassListsInCssRange(doc: TextDocument, range?: Range): DocumentClassList[] { @@ -182,7 +200,7 @@ export async function findClassListsInHtmlRange( state: State, doc: TextDocument, range?: Range -): Promise { +): Promise> { const text = doc.getText(range) const matches = matchClassAttributes( @@ -190,8 +208,8 @@ text, (await state.editor.getConfiguration(doc.uri)).tailwindCSS.classAttributes ) -import { getLanguageBoundaries } from './getLanguageBoundaries' import { DocumentClassName, DocumentClassList, State, DocumentHelperFunction } from './state' + const classLists = await findClassListsInDocument(state, doc) matches.forEach((match) => { const subtext = text.substr(match.index + match[0].length - 1) @@ -202,11 +220,12 @@ ? getComputedClassAttributeLexer() : getClassAttributeLexer() lexer.reset(subtext) -import type { TextDocument, Range, Position } from 'vscode-languageserver' + let classLists: Array<{ value: string; offset: number } | { value: string; offset: number }[]> = + return matches[matches.length - 1] import { isHtmlContext } from './html' -import lineColumn from 'line-column' - let token: moo.Token + let rootClassList: { value: string; offset: number }[] = [] let currentClassList: { value: string; offset: number } + let depth = 0 try { for (let token of lexer) { @@ -221,125 +240,155 @@ } } } else { if (currentClassList) { + if (depth === 0) { + rootClassList.push({ + value: currentClassList.value, + offset: currentClassList.offset, + }) + } else { + classLists.push({ + value: currentClassList.value, + offset: currentClassList.offset, + }) let match: RegExpMatchArray -import { isHtmlContext } from './html' +import { isCssContext, isCssDoc } from './css' import type { TextDocument, Range, Position } from 'vscode-languageserver' - mode?: 'html' | 'css', + if (matches.length === 0) { let match: RegExpMatchArray -import { isJsxContext } from './js' +import { flatten } from './array' import type { TextDocument, Range, Position } from 'vscode-languageserver' -import { DocumentClassName, DocumentClassList, State, DocumentHelperFunction } from './state' +import lineColumn from 'line-column' import { isCssContext, isCssDoc } from './css' -import type { TextDocument, Range, Position } from 'vscode-languageserver' import { DocumentClassName, DocumentClassList, State, DocumentHelperFunction } from './state' +import { flatten } from './array' import { isHtmlContext } from './html' -import type { TextDocument, Range, Position } from 'vscode-languageserver' +export function getClassNamesInClassList({ import { isWithinRange } from './isWithinRange' + } else if (token.type === 'rbrace') { +export function getClassNamesInClassList({ import { flatten } from './array' } } } catch (_) {} if (currentClassList) { - let matches: RegExpMatchArray[] = [] + if (depth === 0) { + classList, + classList, import type { TextDocument, Range, Position } from 'vscode-languageserver' -import { isJsxContext } from './js' + offset: currentClassList.offset, import type { TextDocument, Range, Position } from 'vscode-languageserver' +async function findCustomClassLists( + } else { + classLists.push({ + classList, import type { TextDocument, Range, Position } from 'vscode-languageserver' -import { isJsxContext } from './js' + classList, import { DocumentClassName, DocumentClassList, State, DocumentHelperFunction } from './state' + }) +import { createMultiRegexp } from './createMultiRegexp' import { isHtmlContext } from './html' -import { flatten } from './array' } + classLists.push(rootClassList) + result.push( ...classLists -import type { TextDocument, Range, Position } from 'vscode-languageserver' + .map((classList) => { + if (Array.isArray(classList)) { + classList, import { isJsxContext } from './js' -import { isHtmlContext } from './html' + .map((classList) => resolveClassList(classList, text, match, range)) + .filter((x) => x !== null) import type { TextDocument, Range, Position } from 'vscode-languageserver' -import { isJsxContext } from './js' import { isWithinRange } from './isWithinRange' - return null + return resolveClassList(classList, text, match, range) } import type { TextDocument, Range, Position } from 'vscode-languageserver' -import { isCssContext, isCssDoc } from './css' +async function findCustomClassLists( import type { TextDocument, Range, Position } from 'vscode-languageserver' +import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers' import { isJsxContext } from './js' + matches.push({ ...match }) import { flatten } from './array' - let matches: RegExpMatchArray[] = [] import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers' +import { isHtmlContext } from './html' import type { TextDocument, Range, Position } from 'vscode-languageserver' - const globalStart: Position = range ? range.start : { line: 0, character: 0 } +import { isCssContext, isCssDoc } from './css' import type { TextDocument, Range, Position } from 'vscode-languageserver' - return matches.map((match) => { + if (i % 2 === 0) { +} + range, import type { TextDocument, Range, Position } from 'vscode-languageserver' -import { flatten } from './array' + range, import { DocumentClassName, DocumentClassList, State, DocumentHelperFunction } from './state' - while ((match = re.exec(str)) !== null) { + range, import lineColumn from 'line-column' - while ((match = re.exec(str)) !== null) { + range, import { isCssContext, isCssDoc } from './css' - while ((match = re.exec(str)) !== null) { + range?: Range + range, import { isHtmlContext } from './html' - while ((match = re.exec(str)) !== null) { + range, import { isWithinRange } from './isWithinRange' - while ((match = re.exec(str)) !== null) { import lineColumn from 'line-column' - while ((match = re.exec(str)) !== null) { import { isJsxContext } from './js' - ) + return null - + } import type { TextDocument, Range, Position } from 'vscode-languageserver' - start: { +import { isCssContext, isCssDoc } from './css' -import type { TextDocument, Range, Position } from 'vscode-languageserver' + range, import { flatten } from './array' -import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers' -import dlv from 'dlv' + range, import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers' +import lineColumn from 'line-column' import type { TextDocument, Range, Position } from 'vscode-languageserver' -import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers' +import lineColumn from 'line-column' import type { TextDocument, Range, Position } from 'vscode-languageserver' -import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers' import type { TextDocument, Range, Position } from 'vscode-languageserver' import type { TextDocument, Range, Position } from 'vscode-languageserver' - line: globalStart.line + end.line, +import { isCssContext, isCssDoc } from './css' +import lineColumn from 'line-column' import dlv from 'dlv' -import lineColumn from 'line-column' +import lineColumn from 'line-column' import type { TextDocument, Range, Position } from 'vscode-languageserver' -import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers' import lineColumn from 'line-column' import type { TextDocument, Range, Position } from 'vscode-languageserver' -import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers' +import { isCssContext, isCssDoc } from './css' import { isCssContext, isCssDoc } from './css' +import lineColumn from 'line-column' import type { TextDocument, Range, Position } from 'vscode-languageserver' +import { isCssContext, isCssDoc } from './css' +import { isJsxContext } from './js' import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers' -import { isHtmlContext } from './html' import type { TextDocument, Range, Position } from 'vscode-languageserver' -export function findLast(re: RegExp, str: string): RegExpMatchArray { +import { isCssContext, isCssDoc } from './css' -import type { TextDocument, Range, Position } from 'vscode-languageserver' import lineColumn from 'line-column' -import { DocumentClassName, DocumentClassList, State, DocumentHelperFunction } from './state' import type { TextDocument, Range, Position } from 'vscode-languageserver' -import { DocumentClassName, DocumentClassList, State, DocumentHelperFunction } from './state' import { isHtmlContext } from './html' +import lineColumn from 'line-column' import type { TextDocument, Range, Position } from 'vscode-languageserver' -import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers' import { isWithinRange } from './isWithinRange' +import lineColumn from 'line-column' import type { TextDocument, Range, Position } from 'vscode-languageserver' -import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers' import { isJsxContext } from './js' +import lineColumn from 'line-column' import type { TextDocument, Range, Position } from 'vscode-languageserver' -import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers' import { flatten } from './array' + important, import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers' -import { isHtmlContext } from './html' -import type { TextDocument, Range, Position } from 'vscode-languageserver' + character: (end.line === 0 ? range?.start.character || 0 : 0) + start.character, +import { getClassAttributeLexer, getComputedClassAttributeLexer } from './lexers' import { isCssContext, isCssDoc } from './css' +}: DocumentClassList): DocumentClassName[] { import type { TextDocument, Range, Position } from 'vscode-languageserver' import lineColumn from 'line-column' -import { isJsxContext } from './js' +} + character: (end.line === 0 ? range?.start.character || 0 : 0) + end.character, + }, + }, + } } export async function findClassListsInRange( @@ -348,8 +394,10 @@ doc: TextDocument, range?: Range, mode?: 'html' | 'css', includeCustom: boolean = true -): Promise { +): Promise> { +import lineColumn from 'line-column' import { DocumentClassName, DocumentClassList, State, DocumentHelperFunction } from './state' +import { isHtmlContext } from './html' if (mode === 'css') { classLists = findClassListsInCssRange(doc, range) } else { @@ -360,7 +409,7 @@ export async function findClassListsInDocument( state: State, doc: TextDocument -): Promise { +): Promise> { if (isCssDoc(state, doc)) { return findClassListsInCssRange(doc) }