From 9b47dd0597447e2967b1de3f8cbecb42349d36c1 Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Thu, 27 Apr 2023 18:17:13 +0100 Subject: [PATCH] Fix CSS conflict diagnostics in semicolonless CSS documents (#771) --- .../getInvalidTailwindDirectiveDiagnostics.ts | 8 ++----- .../src/util/find.ts | 21 ++++++++++++------- .../src/util/languages.ts | 14 +++++++++++++ 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/packages/tailwindcss-language-service/src/diagnostics/getInvalidTailwindDirectiveDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getInvalidTailwindDirectiveDiagnostics.ts index 74e26f9..307c1f4 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/getInvalidTailwindDirectiveDiagnostics.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/getInvalidTailwindDirectiveDiagnostics.ts @@ -8,6 +8,7 @@ import * as semver from '../util/semver' import { closest } from '../util/closest' import { absoluteRange } from '../util/absoluteRange' import { getTextWithoutComments } from '../util/doc' +import { isSemicolonlessCssLanguage } from '../util/languages' export function getInvalidTailwindDirectiveDiagnostics( state: State, @@ -28,13 +29,8 @@ export function getInvalidTailwindDirectiveDiagnostics( ranges.push(...boundaries.filter((b) => b.type === 'css').map(({ range }) => range)) } - let notSemicolonLanguages = ['sass', 'sugarss', 'stylus'] let regex: RegExp - if ( - notSemicolonLanguages.includes(document.languageId) || - (state.editor && - notSemicolonLanguages.includes(state.editor.userLanguages[document.languageId])) - ) { + if (isSemicolonlessCssLanguage(document.languageId, state.editor?.userLanguages)) { regex = /(?:\s|^)@tailwind\s+(?[^\r\n]+)/g } else { regex = /(?:\s|^)@tailwind\s+(?[^;]+)/g diff --git a/packages/tailwindcss-language-service/src/util/find.ts b/packages/tailwindcss-language-service/src/util/find.ts index 50a7d8f..7d67cb8 100644 --- a/packages/tailwindcss-language-service/src/util/find.ts +++ b/packages/tailwindcss-language-service/src/util/find.ts @@ -11,6 +11,7 @@ import { getLanguageBoundaries } from './getLanguageBoundaries' import { resolveRange } from './resolveRange' import Regex from 'becke-ch--regex--s0-0-v1--base--pl--lib' import { getTextWithoutComments } from './doc' +import { isSemicolonlessCssLanguage } from './languages' export function findAll(re: RegExp, str: string): RegExpMatchArray[] { let match: RegExpMatchArray @@ -91,12 +92,16 @@ export async function findClassNamesInDocument( ) } -export function findClassListsInCssRange(doc: TextDocument, range?: Range): DocumentClassList[] { +export function findClassListsInCssRange( + state: State, + doc: TextDocument, + range?: Range +): DocumentClassList[] { const text = getTextWithoutComments(doc, 'css', range) - const matches = findAll( - /(@apply\s+)(?[^;}]+?)(?\s*!important)?\s*[;}]/g, - text - ) + let regex = isSemicolonlessCssLanguage(doc.languageId, state.editor?.userLanguages) + ? /(@apply\s+)(?[^}\r\n]+?)(?\s*!important)?(?:\r|\n|}|$)/g + : /(@apply\s+)(?[^;}]+?)(?\s*!important)?\s*[;}]/g + const matches = findAll(regex, text) const globalStart: Position = range ? range.start : { line: 0, character: 0 } return matches.map((match) => { @@ -292,7 +297,7 @@ export async function findClassListsInRange( ): Promise { let classLists: DocumentClassList[] if (mode === 'css') { - classLists = findClassListsInCssRange(doc, range) + classLists = findClassListsInCssRange(state, doc, range) } else { classLists = await findClassListsInHtmlRange(state, doc, mode, range) } @@ -307,7 +312,7 @@ export async function findClassListsInDocument( doc: TextDocument ): Promise { if (isCssDoc(state, doc)) { - return findClassListsInCssRange(doc) + return findClassListsInCssRange(state, doc) } let boundaries = getLanguageBoundaries(state, doc) @@ -324,7 +329,7 @@ export async function findClassListsInDocument( )), ...boundaries .filter((b) => b.type === 'css') - .map(({ range }) => findClassListsInCssRange(doc, range)), + .map(({ range }) => findClassListsInCssRange(state, doc, range)), await findCustomClassLists(state, doc), ]) ) diff --git a/packages/tailwindcss-language-service/src/util/languages.ts b/packages/tailwindcss-language-service/src/util/languages.ts index 7548cf0..565f7e1 100644 --- a/packages/tailwindcss-language-service/src/util/languages.ts +++ b/packages/tailwindcss-language-service/src/util/languages.ts @@ -1,3 +1,5 @@ +import type { EditorState } from './state' + export const htmlLanguages = [ 'aspnetcorerazor', 'astro', @@ -57,3 +59,15 @@ export const jsLanguages = [ export const specialLanguages = ['vue', 'svelte'] export const languages = [...cssLanguages, ...htmlLanguages, ...jsLanguages, ...specialLanguages] + +const semicolonlessLanguages = ['sass', 'sugarss', 'stylus'] + +export function isSemicolonlessCssLanguage( + languageId: string, + userLanguages: EditorState['userLanguages'] = {} +) { + return ( + semicolonlessLanguages.includes(languageId) || + semicolonlessLanguages.includes(userLanguages[languageId]) + ) +}