diff --git a/src/lsp/providers/diagnosticsProvider.ts b/src/lsp/providers/diagnosticsProvider.ts index bb59ff571ca903793d4eb34a45d4775e9e9beaca..866c07d56dabc00b1759e3d154f43154c872931c 100644 --- a/src/lsp/providers/diagnosticsProvider.ts +++ b/src/lsp/providers/diagnosticsProvider.ts @@ -2,7 +2,6 @@ import { TextDocument, Diagnostic, DiagnosticSeverity, - Range, } from 'vscode-languageserver' import { State, Settings } from '../util/state' import { isCssDoc } from '../util/css' @@ -19,8 +18,6 @@ import { equal, flatten } from '../../util/array' import { getDocumentSettings } from '../util/getDocumentSettings' const dlv = require('dlv') import semver from 'semver' -import { getLanguageBoundaries } from '../util/getLanguageBoundaries' -import { absoluteRange } from '../util/absoluteRange' function getUnsupportedApplyDiagnostics( state: State, @@ -143,51 +140,35 @@ ): Diagnostic[] { let severity = settings.lint.unknownScreen if (severity === 'ignore') return [] - let diagnostics: Diagnostic[] = [] - let ranges: Range[] = [] - - if (isCssDoc(state, document)) { - ranges.push(undefined) - } else { - let boundaries = getLanguageBoundaries(state, document) - if (!boundaries) return [] - ranges.push(...boundaries.css) - } - - ranges.forEach((range) => { - let text = document.getText(range) - let matches = findAll(/(?:\s|^)@screen\s+(?[^\s{]+)/g, text) + let text = document.getText() + let matches = findAll(/(?:\s|^)@screen\s+(?[^\s{]+)/g, text) - let screens = Object.keys( - dlv(state.config, 'theme.screens', dlv(state.config, 'screens', {})) - ) + let screens = Object.keys( + dlv(state.config, 'theme.screens', dlv(state.config, 'screens', {})) + ) - matches.forEach((match) => { + return matches + .map((match) => { if (screens.includes(match.groups.screen)) { return null } - diagnostics.push({ - range: absoluteRange( - { - start: indexToPosition( - text, - match.index + match[0].length - match.groups.screen.length - ), - end: indexToPosition(text, match.index + match[0].length), - }, - range - ), + return { + range: { + start: indexToPosition( + text, + match.index + match[0].length - match.groups.screen.length + ), + end: indexToPosition(text, match.index + match[0].length), + }, severity: severity === 'error' ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning, message: 'Unknown screen', - }) + } }) - }) - - return diagnostics + .filter(Boolean) } function getUnknownVariantDiagnostics( @@ -198,54 +179,43 @@ ): Diagnostic[] { let severity = settings.lint.unknownVariant if (severity === 'ignore') return [] - let diagnostics: Diagnostic[] = [] - let ranges: Range[] = [] + let text = document.getText() + let matches = findAll(/(?:\s|^)@variants\s+(?[^{]+)/g, text) - if (isCssDoc(state, document)) { - ranges.push(undefined) - } else { - let boundaries = getLanguageBoundaries(state, document) - if (!boundaries) return [] - ranges.push(...boundaries.css) - } + return flatten( + matches + .map((match) => { + let diagnostics: Diagnostic[] = [] + let variants = match.groups.variants.split(/(\s*,\s*)/) + let listStartIndex = + match.index + match[0].length - match.groups.variants.length - ranges.forEach((range) => { - let text = document.getText(range) - let matches = findAll(/(?:\s|^)@variants\s+(?[^{]+)/g, text) + for (let i = 0; i < variants.length; i += 2) { + let variant = variants[i].trim() + if (state.variants.includes(variant)) { + continue + } - matches.forEach((match) => { - let variants = match.groups.variants.split(/(\s*,\s*)/) - let listStartIndex = - match.index + match[0].length - match.groups.variants.length + let variantStartIndex = + listStartIndex + variants.slice(0, i).join('').length - for (let i = 0; i < variants.length; i += 2) { - let variant = variants[i].trim() - if (state.variants.includes(variant)) { - continue - } - - let variantStartIndex = - listStartIndex + variants.slice(0, i).join('').length - - diagnostics.push({ - range: absoluteRange( - { + diagnostics.push({ + range: { start: indexToPosition(text, variantStartIndex), end: indexToPosition(text, variantStartIndex + variant.length), }, - range - ), - severity: - severity === 'error' - ? DiagnosticSeverity.Error - : DiagnosticSeverity.Warning, - message: `Unknown variant: ${variant}`, - }) - } - }) - }) + severity: + severity === 'error' + ? DiagnosticSeverity.Error + : DiagnosticSeverity.Warning, + message: `Unknown variant: ${variant}`, + }) + } - return diagnostics + return diagnostics + }) + .filter(Boolean) + ) } function getUnknownConfigKeyDiagnostics( @@ -256,25 +226,14 @@ ): Diagnostic[] { let severity = settings.lint.unknownConfigKey if (severity === 'ignore') return [] - let diagnostics: Diagnostic[] = [] - let ranges: Range[] = [] - - if (isCssDoc(state, document)) { - ranges.push(undefined) - } else { - let boundaries = getLanguageBoundaries(state, document) - if (!boundaries) return [] - ranges.push(...boundaries.css) - } + let text = document.getText() + let matches = findAll( + /(?\s|^)(?config|theme)\((?['"])(?[^)]+)\k\)/g, + text + ) - ranges.forEach((range) => { - let text = document.getText(range) - let matches = findAll( - /(?\s|^)(?config|theme)\((?['"])(?[^)]+)\k\)/g, - text - ) - - matches.forEach((match) => { + return matches + .map((match) => { let base = match.groups.helper === 'theme' ? ['theme'] : [] let keys = match.groups.key.split(/[.\[\]]/).filter(Boolean) let value = dlv(state.config, [...base, ...keys]) @@ -292,24 +251,19 @@ match.groups.helper.length + 1 + // open paren match.groups.quote.length - diagnostics.push({ - range: absoluteRange( - { - start: indexToPosition(text, startIndex), - end: indexToPosition(text, startIndex + match.groups.key.length), - }, - range - ), + return { + range: { + start: indexToPosition(text, startIndex), + end: indexToPosition(text, startIndex + match.groups.key.length), + }, severity: severity === 'error' ? DiagnosticSeverity.Error : DiagnosticSeverity.Warning, message: `Unknown ${match.groups.helper} key: ${match.groups.key}`, - }) + } }) - }) - - return diagnostics + .filter(Boolean) } function getUnsupportedTailwindDirectiveDiagnostics( @@ -320,44 +274,30 @@ ): Diagnostic[] { let severity = settings.lint.unsupportedTailwindDirective if (severity === 'ignore') return [] - let diagnostics: Diagnostic[] = [] - let ranges: Range[] = [] + let text = document.getText() + let matches = findAll(/(?:\s|^)@tailwind\s+(?[^;]+)/g, text) - if (isCssDoc(state, document)) { - ranges.push(undefined) - } else { - let boundaries = getLanguageBoundaries(state, document) - if (!boundaries) return [] - ranges.push(...boundaries.css) - } + let allowed = [ + 'utilities', + 'components', + 'screens', + semver.gte(state.version, '1.0.0-beta.1') ? 'base' : 'preflight', + ] - ranges.forEach((range) => { - let text = document.getText(range) - let matches = findAll(/(?:\s|^)@tailwind\s+(?[^;]+)/g, text) - - let valid = [ - 'utilities', - 'components', - 'screens', - semver.gte(state.version, '1.0.0-beta.1') ? 'base' : 'preflight', - ] - - matches.forEach((match) => { - if (valid.includes(match.groups.value)) { + return matches + .map((match) => { + if (allowed.includes(match.groups.value)) { return null } - diagnostics.push({ - range: absoluteRange( - { - start: indexToPosition( - text, - match.index + match[0].length - match.groups.value.length - ), - end: indexToPosition(text, match.index + match[0].length), - }, - range - ), + return { + range: { + start: indexToPosition( + text, + match.index + match[0].length - match.groups.value.length + ), + end: indexToPosition(text, match.index + match[0].length), + }, severity: severity === 'error' ? DiagnosticSeverity.Error @@ -365,11 +305,9 @@ : DiagnosticSeverity.Warning, message: `Unsupported value: ${match.groups.value}${ match.groups.value === 'preflight' ? '. Use base instead.' : '' }`, - }) + } }) - }) - - return diagnostics + .filter(Boolean) } export async function provideDiagnostics( @@ -381,15 +319,19 @@ const diagnostics: Diagnostic[] = settings.validate ? [ ...getUtilityConflictDiagnostics(state, document, settings), - ...getUnsupportedApplyDiagnostics(state, document, settings), - ...getUnknownScreenDiagnostics(state, document, settings), - ...getUnknownVariantDiagnostics(state, document, settings), - ...getUnknownConfigKeyDiagnostics(state, document, settings), - ...getUnsupportedTailwindDirectiveDiagnostics( - state, - document, - settings - ), + ...(isCssDoc(state, document) + ? [ + ...getUnsupportedApplyDiagnostics(state, document, settings), + ...getUnknownScreenDiagnostics(state, document, settings), + ...getUnknownVariantDiagnostics(state, document, settings), + ...getUnknownConfigKeyDiagnostics(state, document, settings), + ...getUnsupportedTailwindDirectiveDiagnostics( + state, + document, + settings + ), + ] + : []), ] : [] diff --git a/src/lsp/util/absoluteRange.ts b/src/lsp/util/absoluteRange.ts deleted file mode 100644 index 9250e4fb9ec9b13ff3124072808f2013ef5009a0..0000000000000000000000000000000000000000 --- a/src/lsp/util/absoluteRange.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Range } from 'vscode-languageserver' - -export function absoluteRange(range: Range, reference?: Range) { - return { - start: { - line: (reference?.start.line || 0) + range.start.line, - character: - (range.end.line === 0 ? reference?.start.character || 0 : 0) + - range.start.character, - }, - end: { - line: (reference?.start.line || 0) + range.end.line, - character: - (range.end.line === 0 ? reference?.start.character || 0 : 0) + - range.end.character, - }, - } -} diff --git a/src/lsp/util/find.ts b/src/lsp/util/find.ts index 609de6207d066862bb645e4d9ca199c915010b61..0f996a43f914e393b546ae08a4410908687126b3 100644 --- a/src/lsp/util/find.ts +++ b/src/lsp/util/find.ts @@ -10,7 +10,6 @@ import { getClassAttributeLexer, getComputedClassAttributeLexer, } from './lexers' -import { getLanguageBoundaries } from './getLanguageBoundaries' export function findAll(re: RegExp, str: string): RegExpMatchArray[] { let match: RegExpMatchArray @@ -231,13 +230,70 @@ if (isCssDoc(state, doc)) { return findClassListsInCssRange(doc) } - let boundaries = getLanguageBoundaries(state, doc) - if (!boundaries) return [] + if (isVueDoc(doc)) { + let text = doc.getText() + let blocks = findAll( + /<(?template|style|script)\b[^>]*>.*?(<\/\k>|$)/gis, + text + ) + let htmlRanges: Range[] = [] + let cssRanges: Range[] = [] + for (let i = 0; i < blocks.length; i++) { + let range = { + start: indexToPosition(text, blocks[i].index), + end: indexToPosition(text, blocks[i].index + blocks[i][0].length), + } + if (blocks[i].groups.type === 'style') { + cssRanges.push(range) + } else { + htmlRanges.push(range) + } + } + return [].concat.apply( + [], + [ + ...htmlRanges.map((range) => findClassListsInHtmlRange(doc, range)), + ...cssRanges.map((range) => findClassListsInCssRange(doc, range)), + ] + ) + } + + if (isHtmlDoc(state, doc) || isJsDoc(state, doc) || isSvelteDoc(doc)) { + let text = doc.getText() + let styleBlocks = findAll(/]*>|>).*?(<\/style>|$)/gis, text) + let htmlRanges: Range[] = [] + let cssRanges: Range[] = [] + let currentIndex = 0 + + for (let i = 0; i < styleBlocks.length; i++) { + htmlRanges.push({ + start: indexToPosition(text, currentIndex), + end: indexToPosition(text, styleBlocks[i].index), + }) + cssRanges.push({ + start: indexToPosition(text, styleBlocks[i].index), + end: indexToPosition( + text, + styleBlocks[i].index + styleBlocks[i][0].length + ), + }) + currentIndex = styleBlocks[i].index + styleBlocks[i][0].length + } + htmlRanges.push({ + start: indexToPosition(text, currentIndex), + end: indexToPosition(text, text.length), + }) - return flatten([ - ...boundaries.html.map((range) => findClassListsInHtmlRange(doc, range)), - ...boundaries.css.map((range) => findClassListsInCssRange(doc, range)), - ]) + return [].concat.apply( + [], + [ + ...htmlRanges.map((range) => findClassListsInHtmlRange(doc, range)), + ...cssRanges.map((range) => findClassListsInCssRange(doc, range)), + ] + ) + } + + return [] } export function indexToPosition(str: string, index: number): Position { diff --git a/src/lsp/util/getLanguageBoundaries.ts b/src/lsp/util/getLanguageBoundaries.ts deleted file mode 100644 index dfef2300d3a9052cb3f60970538858b81d8af39b..0000000000000000000000000000000000000000 --- a/src/lsp/util/getLanguageBoundaries.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { TextDocument, Range } from 'vscode-languageserver' -import { isVueDoc, isHtmlDoc, isSvelteDoc } from './html' -import { State } from './state' -import { findAll, indexToPosition } from './find' -import { isJsDoc } from './js' - -export interface LanguageBoundaries { - html: Range[] - css: Range[] -} - -export function getLanguageBoundaries( - state: State, - doc: TextDocument -): LanguageBoundaries | null { - if (isVueDoc(doc)) { - let text = doc.getText() - let blocks = findAll( - /<(?template|style|script)\b[^>]*>.*?(<\/\k>|$)/gis, - text - ) - let htmlRanges: Range[] = [] - let cssRanges: Range[] = [] - for (let i = 0; i < blocks.length; i++) { - let range = { - start: indexToPosition(text, blocks[i].index), - end: indexToPosition(text, blocks[i].index + blocks[i][0].length), - } - if (blocks[i].groups.type === 'style') { - cssRanges.push(range) - } else { - htmlRanges.push(range) - } - } - - return { - html: htmlRanges, - css: cssRanges, - } - } - - if (isHtmlDoc(state, doc) || isJsDoc(state, doc) || isSvelteDoc(doc)) { - let text = doc.getText() - let styleBlocks = findAll(/]*>|>).*?(<\/style>|$)/gis, text) - let htmlRanges: Range[] = [] - let cssRanges: Range[] = [] - let currentIndex = 0 - - for (let i = 0; i < styleBlocks.length; i++) { - htmlRanges.push({ - start: indexToPosition(text, currentIndex), - end: indexToPosition(text, styleBlocks[i].index), - }) - cssRanges.push({ - start: indexToPosition(text, styleBlocks[i].index), - end: indexToPosition( - text, - styleBlocks[i].index + styleBlocks[i][0].length - ), - }) - currentIndex = styleBlocks[i].index + styleBlocks[i][0].length - } - htmlRanges.push({ - start: indexToPosition(text, currentIndex), - end: indexToPosition(text, text.length), - }) - - return { - html: htmlRanges, - css: cssRanges, - } - } - - return null -}