1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
import {
TextDocument,
Diagnostic,
DiagnosticSeverity,
} from 'vscode-languageserver'
import { State } from '../util/state'
import { isCssDoc } from '../util/css'
import {
findClassNamesInRange,
findClassListsInDocument,
getClassNamesInClassList,
} from '../util/find'
import { getClassNameMeta } from '../util/getClassNameMeta'
import { getClassNameDecls } from '../util/getClassNameDecls'
import { equal } from '../../util/array'
function getCssDiagnostics(state: State, document: TextDocument): Diagnostic[] {
const classNames = findClassNamesInRange(document, undefined, 'css')
let diagnostics: Diagnostic[] = classNames
.map(({ className, range }) => {
const meta = getClassNameMeta(state, className)
if (!meta) return null
let message: string
if (Array.isArray(meta)) {
message = `\`@apply\` cannot be used with \`.${className}\` because it is included in multiple rulesets.`
} else if (meta.source !== 'utilities') {
message = `\`@apply\` cannot be used with \`.${className}\` because it is not a utility.`
} else if (meta.context && meta.context.length > 0) {
if (meta.context.length === 1) {
message = `\`@apply\` cannot be used with \`.${className}\` because it is nested inside of an at-rule (${meta.context[0]}).`
} else {
message = `\`@apply\` cannot be used with \`.${className}\` because it is nested inside of at-rules (${meta.context.join(
', '
)}).`
}
} else if (meta.pseudo && meta.pseudo.length > 0) {
if (meta.pseudo.length === 1) {
message = `\`@apply\` cannot be used with \`.${className}\` because its definition includes a pseudo-selector (${meta.pseudo[0]})`
} else {
message = `\`@apply\` cannot be used with \`.${className}\` because its definition includes pseudo-selectors (${meta.pseudo.join(
', '
)})`
}
}
if (!message) return null
return {
severity: DiagnosticSeverity.Error,
range,
message,
}
})
.filter(Boolean)
return 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(
state: State,
document: TextDocument
): Promise<void> {
state.editor.connection.sendDiagnostics({
uri: document.uri,
diagnostics: [
...getConflictDiagnostics(state, document),
...(isCssDoc(state, document) ? getCssDiagnostics(state, document) : []),
],
})
}
|