add initial class name conflict diagnostics
parent
d5b5176756
commit
de6273dad2
|
@ -5,10 +5,16 @@ import {
|
||||||
} from 'vscode-languageserver'
|
} from 'vscode-languageserver'
|
||||||
import { State } from '../util/state'
|
import { State } from '../util/state'
|
||||||
import { isCssDoc } from '../util/css'
|
import { isCssDoc } from '../util/css'
|
||||||
import { findClassNamesInRange } from '../util/find'
|
import {
|
||||||
|
findClassNamesInRange,
|
||||||
|
findClassListsInDocument,
|
||||||
|
getClassNamesInClassList,
|
||||||
|
} from '../util/find'
|
||||||
import { getClassNameMeta } from '../util/getClassNameMeta'
|
import { getClassNameMeta } from '../util/getClassNameMeta'
|
||||||
|
import { getClassNameDecls } from '../util/getClassNameDecls'
|
||||||
|
import { equal } from '../../util/array'
|
||||||
|
|
||||||
function provideCssDiagnostics(state: State, document: TextDocument): void {
|
function getCssDiagnostics(state: State, document: TextDocument): Diagnostic[] {
|
||||||
const classNames = findClassNamesInRange(document, undefined, 'css')
|
const classNames = findClassNamesInRange(document, undefined, 'css')
|
||||||
|
|
||||||
let diagnostics: Diagnostic[] = classNames
|
let diagnostics: Diagnostic[] = classNames
|
||||||
|
@ -46,38 +52,73 @@ function provideCssDiagnostics(state: State, document: TextDocument): void {
|
||||||
severity: DiagnosticSeverity.Error,
|
severity: DiagnosticSeverity.Error,
|
||||||
range,
|
range,
|
||||||
message,
|
message,
|
||||||
// source: 'ex',
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
|
|
||||||
// if (state.editor.capabilities.diagnosticRelatedInformation) {
|
return diagnostics
|
||||||
// diagnostic.relatedInformation = [
|
}
|
||||||
// {
|
|
||||||
// location: {
|
|
||||||
// uri: document.uri,
|
|
||||||
// range: Object.assign({}, diagnostic.range),
|
|
||||||
// },
|
|
||||||
// message: '',
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// location: {
|
|
||||||
// uri: document.uri,
|
|
||||||
// range: Object.assign({}, diagnostic.range),
|
|
||||||
// },
|
|
||||||
// message: '',
|
|
||||||
// },
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
|
|
||||||
state.editor.connection.sendDiagnostics({ uri: document.uri, diagnostics })
|
function getConflictDiagnostics(
|
||||||
|
state: State,
|
||||||
|
document: TextDocument
|
||||||
|
): Diagnostic[] {
|
||||||
|
let diagnostics: Diagnostic[] = []
|
||||||
|
const classLists = findClassListsInDocument(state, document)
|
||||||
|
|
||||||
|
classLists.forEach((classList) => {
|
||||||
|
const classNames = getClassNamesInClassList(classList)
|
||||||
|
|
||||||
|
classNames.forEach((className, index) => {
|
||||||
|
let otherClassNames = classNames.filter((_className, i) => i !== index)
|
||||||
|
otherClassNames.forEach((otherClassName) => {
|
||||||
|
let decls = getClassNameDecls(state, className.className)
|
||||||
|
if (!decls) return
|
||||||
|
|
||||||
|
let otherDecls = getClassNameDecls(state, otherClassName.className)
|
||||||
|
if (!otherDecls) return
|
||||||
|
|
||||||
|
let meta = getClassNameMeta(state, className.className)
|
||||||
|
let otherMeta = getClassNameMeta(state, otherClassName.className)
|
||||||
|
|
||||||
|
if (
|
||||||
|
equal(Object.keys(decls), Object.keys(otherDecls)) &&
|
||||||
|
!Array.isArray(meta) &&
|
||||||
|
!Array.isArray(otherMeta) &&
|
||||||
|
equal(meta.context, otherMeta.context) &&
|
||||||
|
equal(meta.pseudo, otherMeta.pseudo)
|
||||||
|
) {
|
||||||
|
diagnostics.push({
|
||||||
|
range: className.range,
|
||||||
|
severity: DiagnosticSeverity.Warning,
|
||||||
|
message: `You can’t use \`${className.className}\` and \`${otherClassName.className}\` together`,
|
||||||
|
relatedInformation: [
|
||||||
|
{
|
||||||
|
message: otherClassName.className,
|
||||||
|
location: {
|
||||||
|
uri: document.uri,
|
||||||
|
range: otherClassName.range,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return diagnostics
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function provideDiagnostics(
|
export async function provideDiagnostics(
|
||||||
state: State,
|
state: State,
|
||||||
document: TextDocument
|
document: TextDocument
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (isCssDoc(state, document)) {
|
state.editor.connection.sendDiagnostics({
|
||||||
return provideCssDiagnostics(state, document)
|
uri: document.uri,
|
||||||
}
|
diagnostics: [
|
||||||
|
...getConflictDiagnostics(state, document),
|
||||||
|
...(isCssDoc(state, document) ? getCssDiagnostics(state, document) : []),
|
||||||
|
],
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { isHtmlContext, isHtmlDoc, isSvelteDoc, isVueDoc } from './html'
|
||||||
import { isWithinRange } from './isWithinRange'
|
import { isWithinRange } from './isWithinRange'
|
||||||
import { isJsContext, isJsDoc } from './js'
|
import { isJsContext, isJsDoc } from './js'
|
||||||
import { getClassAttributeLexer } from './lexers'
|
import { getClassAttributeLexer } from './lexers'
|
||||||
|
import { flatten } from '../../util/array'
|
||||||
|
|
||||||
export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
|
export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
|
||||||
let match: RegExpMatchArray
|
let match: RegExpMatchArray
|
||||||
|
@ -24,44 +25,53 @@ export function findLast(re: RegExp, str: string): RegExpMatchArray {
|
||||||
return matches[matches.length - 1]
|
return matches[matches.length - 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getClassNamesInClassList({
|
||||||
|
classList,
|
||||||
|
range,
|
||||||
|
}: DocumentClassList): DocumentClassName[] {
|
||||||
|
const parts = classList.split(/(\s+)/)
|
||||||
|
const names: DocumentClassName[] = []
|
||||||
|
let index = 0
|
||||||
|
for (let i = 0; i < parts.length; i++) {
|
||||||
|
if (i % 2 === 0) {
|
||||||
|
const start = indexToPosition(classList, index)
|
||||||
|
const end = indexToPosition(classList, index + parts[i].length)
|
||||||
|
names.push({
|
||||||
|
className: parts[i],
|
||||||
|
range: {
|
||||||
|
start: {
|
||||||
|
line: range.start.line + start.line,
|
||||||
|
character:
|
||||||
|
(end.line === 0 ? range.start.character : 0) + start.character,
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
line: range.start.line + end.line,
|
||||||
|
character:
|
||||||
|
(end.line === 0 ? range.start.character : 0) + end.character,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
index += parts[i].length
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
export function findClassNamesInRange(
|
export function findClassNamesInRange(
|
||||||
doc: TextDocument,
|
doc: TextDocument,
|
||||||
range?: Range,
|
range?: Range,
|
||||||
mode?: 'html' | 'css'
|
mode?: 'html' | 'css'
|
||||||
): DocumentClassName[] {
|
): DocumentClassName[] {
|
||||||
const classLists = findClassListsInRange(doc, range, mode)
|
const classLists = findClassListsInRange(doc, range, mode)
|
||||||
return [].concat.apply(
|
return flatten(classLists.map(getClassNamesInClassList))
|
||||||
[],
|
}
|
||||||
classLists.map(({ classList, range }) => {
|
|
||||||
const parts = classList.split(/(\s+)/)
|
export function findClassNamesInDocument(
|
||||||
const names: DocumentClassName[] = []
|
state: State,
|
||||||
let index = 0
|
doc: TextDocument
|
||||||
for (let i = 0; i < parts.length; i++) {
|
): DocumentClassName[] {
|
||||||
if (i % 2 === 0) {
|
const classLists = findClassListsInDocument(state, doc)
|
||||||
const start = indexToPosition(classList, index)
|
return flatten(classLists.map(getClassNamesInClassList))
|
||||||
const end = indexToPosition(classList, index + parts[i].length)
|
|
||||||
names.push({
|
|
||||||
className: parts[i],
|
|
||||||
range: {
|
|
||||||
start: {
|
|
||||||
line: range.start.line + start.line,
|
|
||||||
character:
|
|
||||||
(end.line === 0 ? range.start.character : 0) +
|
|
||||||
start.character,
|
|
||||||
},
|
|
||||||
end: {
|
|
||||||
line: range.start.line + end.line,
|
|
||||||
character:
|
|
||||||
(end.line === 0 ? range.start.character : 0) + end.character,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
index += parts[i].length
|
|
||||||
}
|
|
||||||
return names
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findClassListsInCssRange(
|
export function findClassListsInCssRange(
|
||||||
|
@ -98,7 +108,7 @@ export function findClassListsInCssRange(
|
||||||
|
|
||||||
export function findClassListsInHtmlRange(
|
export function findClassListsInHtmlRange(
|
||||||
doc: TextDocument,
|
doc: TextDocument,
|
||||||
range: Range
|
range?: Range
|
||||||
): DocumentClassList[] {
|
): DocumentClassList[] {
|
||||||
const text = doc.getText(range)
|
const text = doc.getText(range)
|
||||||
const matches = findAll(/[\s:]class(?:Name)?=['"`{]/g, text)
|
const matches = findAll(/[\s:]class(?:Name)?=['"`{]/g, text)
|
||||||
|
@ -174,15 +184,16 @@ export function findClassListsInHtmlRange(
|
||||||
classList: value,
|
classList: value,
|
||||||
range: {
|
range: {
|
||||||
start: {
|
start: {
|
||||||
line: range.start.line + start.line,
|
line: (range?.start.line || 0) + start.line,
|
||||||
character:
|
character:
|
||||||
(end.line === 0 ? range.start.character : 0) +
|
(end.line === 0 ? range?.start.character || 0 : 0) +
|
||||||
start.character,
|
start.character,
|
||||||
},
|
},
|
||||||
end: {
|
end: {
|
||||||
line: range.start.line + end.line,
|
line: (range?.start.line || 0) + end.line,
|
||||||
character:
|
character:
|
||||||
(end.line === 0 ? range.start.character : 0) + end.character,
|
(end.line === 0 ? range?.start.character || 0 : 0) +
|
||||||
|
end.character,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -196,8 +207,8 @@ export function findClassListsInHtmlRange(
|
||||||
|
|
||||||
export function findClassListsInRange(
|
export function findClassListsInRange(
|
||||||
doc: TextDocument,
|
doc: TextDocument,
|
||||||
range: Range,
|
range?: Range,
|
||||||
mode: 'html' | 'css'
|
mode?: 'html' | 'css'
|
||||||
): DocumentClassList[] {
|
): DocumentClassList[] {
|
||||||
if (mode === 'css') {
|
if (mode === 'css') {
|
||||||
return findClassListsInCssRange(doc, range)
|
return findClassListsInCssRange(doc, range)
|
||||||
|
|
Loading…
Reference in New Issue