Skip to content

Commit 8c6ba84

Browse files
committed
reuse @apply validation in hover provider
1 parent 2df8544 commit 8c6ba84

File tree

3 files changed

+58
-33
lines changed

3 files changed

+58
-33
lines changed

src/lsp/providers/diagnostics/getInvalidApplyDiagnostics.ts

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { findClassNamesInRange } from '../../util/find'
22
import { InvalidApplyDiagnostic, DiagnosticKind } from './types'
33
import { Settings, State } from '../../util/state'
44
import { TextDocument, DiagnosticSeverity } from 'vscode-languageserver'
5-
import { getClassNameMeta } from '../../util/getClassNameMeta'
5+
import { validateApply } from '../../util/validateApply'
66

77
export function getInvalidApplyDiagnostics(
88
state: State,
@@ -15,47 +15,20 @@ export function getInvalidApplyDiagnostics(
1515
const classNames = findClassNamesInRange(document, undefined, 'css')
1616

1717
let diagnostics: InvalidApplyDiagnostic[] = classNames.map((className) => {
18-
const meta = getClassNameMeta(state, className.className)
19-
if (!meta) return null
18+
let result = validateApply(state, className.className)
2019

21-
let message: string
22-
23-
if (Array.isArray(meta)) {
24-
message = `'@apply' cannot be used with '${className.className}' because it is included in multiple rulesets.`
25-
} else if (meta.source !== 'utilities') {
26-
message = `'@apply' cannot be used with '${className.className}' because it is not a utility.`
27-
} else if (meta.context && meta.context.length > 0) {
28-
if (meta.context.length === 1) {
29-
message = `'@apply' cannot be used with '${className.className}' because it is nested inside of an at-rule ('${meta.context[0]}').`
30-
} else {
31-
message = `'@apply' cannot be used with '${
32-
className.className
33-
}' because it is nested inside of at-rules (${meta.context
34-
.map((c) => `'${c}'`)
35-
.join(', ')}).`
36-
}
37-
} else if (meta.pseudo && meta.pseudo.length > 0) {
38-
if (meta.pseudo.length === 1) {
39-
message = `'@apply' cannot be used with '${className.className}' because its definition includes a pseudo-selector ('${meta.pseudo[0]}')`
40-
} else {
41-
message = `'@apply' cannot be used with '${
42-
className.className
43-
}' because its definition includes pseudo-selectors (${meta.pseudo
44-
.map((p) => `'${p}'`)
45-
.join(', ')}).`
46-
}
20+
if (result === null || result.isApplyable === true) {
21+
return null
4722
}
4823

49-
if (!message) return null
50-
5124
return {
5225
code: DiagnosticKind.InvalidApply,
5326
severity:
5427
severity === 'error'
5528
? DiagnosticSeverity.Error
5629
: DiagnosticSeverity.Warning,
5730
range: className.range,
58-
message,
31+
message: result.reason,
5932
className,
6033
}
6134
})

src/lsp/providers/hoverProvider.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { State } from '../util/state'
22
import { Hover, TextDocumentPositionParams } from 'vscode-languageserver'
3-
import { getClassNameParts } from '../util/getClassNameAtPosition'
43
import { stringifyCss, stringifyConfigValue } from '../util/stringify'
54
const dlv = require('dlv')
65
import { isCssContext } from '../util/css'
76
import { findClassNameAtPosition } from '../util/find'
7+
import { validateApply } from '../util/validateApply'
8+
import { getClassNameParts } from '../util/getClassNameAtPosition'
89

910
export function provideHover(
1011
state: State,
@@ -81,6 +82,13 @@ function provideClassNameHover(
8182
const parts = getClassNameParts(state, className.className)
8283
if (!parts) return null
8384

85+
if (isCssContext(state, doc, position)) {
86+
let validated = validateApply(state, parts)
87+
if (validated === null || validated.isApplyable === false) {
88+
return null
89+
}
90+
}
91+
8492
return {
8593
contents: {
8694
language: 'css',

src/lsp/util/validateApply.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { State } from './state'
2+
import { getClassNameMeta } from './getClassNameMeta'
3+
4+
export function validateApply(
5+
state: State,
6+
classNameOrParts: string | string[]
7+
): { isApplyable: true } | { isApplyable: false; reason: string } | null {
8+
const meta = getClassNameMeta(state, classNameOrParts)
9+
if (!meta) return null
10+
11+
const className = Array.isArray(classNameOrParts)
12+
? classNameOrParts.join(state.separator)
13+
: classNameOrParts
14+
15+
let reason: string
16+
17+
if (Array.isArray(meta)) {
18+
reason = `'@apply' cannot be used with '${className}' because it is included in multiple rulesets.`
19+
} else if (meta.source !== 'utilities') {
20+
reason = `'@apply' cannot be used with '${className}' because it is not a utility.`
21+
} else if (meta.context && meta.context.length > 0) {
22+
if (meta.context.length === 1) {
23+
reason = `'@apply' cannot be used with '${className}' because it is nested inside of an at-rule ('${meta.context[0]}').`
24+
} else {
25+
reason = `'@apply' cannot be used with '${className}' because it is nested inside of at-rules (${meta.context
26+
.map((c) => `'${c}'`)
27+
.join(', ')}).`
28+
}
29+
} else if (meta.pseudo && meta.pseudo.length > 0) {
30+
if (meta.pseudo.length === 1) {
31+
reason = `'@apply' cannot be used with '${className}' because its definition includes a pseudo-selector ('${meta.pseudo[0]}')`
32+
} else {
33+
reason = `'@apply' cannot be used with '${className}' because its definition includes pseudo-selectors (${meta.pseudo
34+
.map((p) => `'${p}'`)
35+
.join(', ')}).`
36+
}
37+
}
38+
39+
if (reason) {
40+
return { isApplyable: false, reason }
41+
}
42+
43+
return { isApplyable: true }
44+
}

0 commit comments

Comments
 (0)