Improve conflict diagnostics (#503)

master
Brad Cornes 2022-03-04 15:41:44 +00:00 committed by GitHub
parent 86497bb380
commit ddfaea21cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 105 additions and 51 deletions

View File

@ -20,7 +20,9 @@ export async function getCssConflictDiagnostics(
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) {

View File

@ -22,7 +22,7 @@ export async function getRecommendedVariantOrderDiagnostics(
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])

View File

@ -20,7 +20,7 @@ export async function getDocumentColors(
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)

View File

@ -77,7 +77,15 @@ export async function findClassNamesInRange(
includeCustom: boolean = true
): Promise<DocumentClassName[]> {
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 @@ export async function findClassNamesInDocument(
doc: TextDocument
): Promise<DocumentClassName[]> {
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<DocumentClassList[]> {
): Promise<Array<DocumentClassList | DocumentClassList[]>> {
const text = doc.getText(range)
const matches = matchClassAttributes(
@ -190,7 +206,7 @@ export async function findClassListsInHtmlRange(
(await state.editor.getConfiguration(doc.uri)).tailwindCSS.classAttributes
)
const result: DocumentClassList[] = []
const result: Array<DocumentClassList | DocumentClassList[]> = []
matches.forEach((match) => {
const subtext = text.substr(match.index + match[0].length - 1)
@ -201,9 +217,11 @@ export async function findClassListsInHtmlRange(
: 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 @@ export async function findClassListsInHtmlRange(
}
} 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 @@ export async function findClassListsInHtmlRange(
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<DocumentClassList[]> {
let classLists: DocumentClassList[]
): Promise<Array<DocumentClassList | DocumentClassList[]>> {
let classLists: Array<DocumentClassList | DocumentClassList[]>
if (mode === 'css') {
classLists = findClassListsInCssRange(doc, range)
} else {
@ -296,7 +348,7 @@ export async function findClassListsInRange(
export async function findClassListsInDocument(
state: State,
doc: TextDocument
): Promise<DocumentClassList[]> {
): Promise<Array<DocumentClassList | DocumentClassList[]>> {
if (isCssDoc(state, doc)) {
return findClassListsInCssRange(doc)
}