From 8c6ba844172f50a95583c34700903b3b03d382c7 Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Sun, 21 Jun 2020 01:38:08 +0100 Subject: [PATCH] reuse @apply validation in hover provider --- .../diagnostics/getInvalidApplyDiagnostics.ts | 37 +++------------- src/lsp/providers/hoverProvider.ts | 10 ++++- src/lsp/util/validateApply.ts | 44 +++++++++++++++++++ 3 files changed, 58 insertions(+), 33 deletions(-) create mode 100644 src/lsp/util/validateApply.ts diff --git a/src/lsp/providers/diagnostics/getInvalidApplyDiagnostics.ts b/src/lsp/providers/diagnostics/getInvalidApplyDiagnostics.ts index 66fc540..e829b57 100644 --- a/src/lsp/providers/diagnostics/getInvalidApplyDiagnostics.ts +++ b/src/lsp/providers/diagnostics/getInvalidApplyDiagnostics.ts @@ -2,7 +2,7 @@ import { findClassNamesInRange } from '../../util/find' import { InvalidApplyDiagnostic, DiagnosticKind } from './types' import { Settings, State } from '../../util/state' import { TextDocument, DiagnosticSeverity } from 'vscode-languageserver' -import { getClassNameMeta } from '../../util/getClassNameMeta' +import { validateApply } from '../../util/validateApply' export function getInvalidApplyDiagnostics( state: State, @@ -15,39 +15,12 @@ export function getInvalidApplyDiagnostics( const classNames = findClassNamesInRange(document, undefined, 'css') let diagnostics: InvalidApplyDiagnostic[] = classNames.map((className) => { - const meta = getClassNameMeta(state, className.className) - if (!meta) return null + let result = validateApply(state, className.className) - let message: string - - if (Array.isArray(meta)) { - message = `'@apply' cannot be used with '${className.className}' because it is included in multiple rulesets.` - } else if (meta.source !== 'utilities') { - message = `'@apply' cannot be used with '${className.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.className}' because it is nested inside of an at-rule ('${meta.context[0]}').` - } else { - message = `'@apply' cannot be used with '${ - className.className - }' because it is nested inside of at-rules (${meta.context - .map((c) => `'${c}'`) - .join(', ')}).` - } - } else if (meta.pseudo && meta.pseudo.length > 0) { - if (meta.pseudo.length === 1) { - message = `'@apply' cannot be used with '${className.className}' because its definition includes a pseudo-selector ('${meta.pseudo[0]}')` - } else { - message = `'@apply' cannot be used with '${ - className.className - }' because its definition includes pseudo-selectors (${meta.pseudo - .map((p) => `'${p}'`) - .join(', ')}).` - } + if (result === null || result.isApplyable === true) { + return null } - if (!message) return null - return { code: DiagnosticKind.InvalidApply, severity: @@ -55,7 +28,7 @@ export function getInvalidApplyDiagnostics( ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning, range: className.range, - message, + message: result.reason, className, } }) diff --git a/src/lsp/providers/hoverProvider.ts b/src/lsp/providers/hoverProvider.ts index a9010a3..2084ec4 100644 --- a/src/lsp/providers/hoverProvider.ts +++ b/src/lsp/providers/hoverProvider.ts @@ -1,10 +1,11 @@ import { State } from '../util/state' import { Hover, TextDocumentPositionParams } from 'vscode-languageserver' -import { getClassNameParts } from '../util/getClassNameAtPosition' import { stringifyCss, stringifyConfigValue } from '../util/stringify' const dlv = require('dlv') import { isCssContext } from '../util/css' import { findClassNameAtPosition } from '../util/find' +import { validateApply } from '../util/validateApply' +import { getClassNameParts } from '../util/getClassNameAtPosition' export function provideHover( state: State, @@ -81,6 +82,13 @@ function provideClassNameHover( const parts = getClassNameParts(state, className.className) if (!parts) return null + if (isCssContext(state, doc, position)) { + let validated = validateApply(state, parts) + if (validated === null || validated.isApplyable === false) { + return null + } + } + return { contents: { language: 'css', diff --git a/src/lsp/util/validateApply.ts b/src/lsp/util/validateApply.ts new file mode 100644 index 0000000..52f2b2c --- /dev/null +++ b/src/lsp/util/validateApply.ts @@ -0,0 +1,44 @@ +import { State } from './state' +import { getClassNameMeta } from './getClassNameMeta' + +export function validateApply( + state: State, + classNameOrParts: string | string[] +): { isApplyable: true } | { isApplyable: false; reason: string } | null { + const meta = getClassNameMeta(state, classNameOrParts) + if (!meta) return null + + const className = Array.isArray(classNameOrParts) + ? classNameOrParts.join(state.separator) + : classNameOrParts + + let reason: string + + if (Array.isArray(meta)) { + reason = `'@apply' cannot be used with '${className}' because it is included in multiple rulesets.` + } else if (meta.source !== 'utilities') { + reason = `'@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) { + reason = `'@apply' cannot be used with '${className}' because it is nested inside of an at-rule ('${meta.context[0]}').` + } else { + reason = `'@apply' cannot be used with '${className}' because it is nested inside of at-rules (${meta.context + .map((c) => `'${c}'`) + .join(', ')}).` + } + } else if (meta.pseudo && meta.pseudo.length > 0) { + if (meta.pseudo.length === 1) { + reason = `'@apply' cannot be used with '${className}' because its definition includes a pseudo-selector ('${meta.pseudo[0]}')` + } else { + reason = `'@apply' cannot be used with '${className}' because its definition includes pseudo-selectors (${meta.pseudo + .map((p) => `'${p}'`) + .join(', ')}).` + } + } + + if (reason) { + return { isApplyable: false, reason } + } + + return { isApplyable: true } +}