Fix CSS conflict diagnostics in semicolonless CSS documents (#771)

master
Brad Cornes 2023-04-27 18:17:13 +01:00 committed by GitHub
parent 8266d6b0aa
commit 9b47dd0597
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 29 additions and 14 deletions

View File

@ -8,6 +8,7 @@ import * as semver from '../util/semver'
import { closest } from '../util/closest' import { closest } from '../util/closest'
import { absoluteRange } from '../util/absoluteRange' import { absoluteRange } from '../util/absoluteRange'
import { getTextWithoutComments } from '../util/doc' import { getTextWithoutComments } from '../util/doc'
import { isSemicolonlessCssLanguage } from '../util/languages'
export function getInvalidTailwindDirectiveDiagnostics( export function getInvalidTailwindDirectiveDiagnostics(
state: State, state: State,
@ -28,13 +29,8 @@ export function getInvalidTailwindDirectiveDiagnostics(
ranges.push(...boundaries.filter((b) => b.type === 'css').map(({ range }) => range)) ranges.push(...boundaries.filter((b) => b.type === 'css').map(({ range }) => range))
} }
let notSemicolonLanguages = ['sass', 'sugarss', 'stylus']
let regex: RegExp let regex: RegExp
if ( if (isSemicolonlessCssLanguage(document.languageId, state.editor?.userLanguages)) {
notSemicolonLanguages.includes(document.languageId) ||
(state.editor &&
notSemicolonLanguages.includes(state.editor.userLanguages[document.languageId]))
) {
regex = /(?:\s|^)@tailwind\s+(?<value>[^\r\n]+)/g regex = /(?:\s|^)@tailwind\s+(?<value>[^\r\n]+)/g
} else { } else {
regex = /(?:\s|^)@tailwind\s+(?<value>[^;]+)/g regex = /(?:\s|^)@tailwind\s+(?<value>[^;]+)/g

View File

@ -11,6 +11,7 @@ import { getLanguageBoundaries } from './getLanguageBoundaries'
import { resolveRange } from './resolveRange' import { resolveRange } from './resolveRange'
import Regex from 'becke-ch--regex--s0-0-v1--base--pl--lib' import Regex from 'becke-ch--regex--s0-0-v1--base--pl--lib'
import { getTextWithoutComments } from './doc' import { getTextWithoutComments } from './doc'
import { isSemicolonlessCssLanguage } from './languages'
export function findAll(re: RegExp, str: string): RegExpMatchArray[] { export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
let match: 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 text = getTextWithoutComments(doc, 'css', range)
const matches = findAll( let regex = isSemicolonlessCssLanguage(doc.languageId, state.editor?.userLanguages)
/(@apply\s+)(?<classList>[^;}]+?)(?<important>\s*!important)?\s*[;}]/g, ? /(@apply\s+)(?<classList>[^}\r\n]+?)(?<important>\s*!important)?(?:\r|\n|}|$)/g
text : /(@apply\s+)(?<classList>[^;}]+?)(?<important>\s*!important)?\s*[;}]/g
) const matches = findAll(regex, text)
const globalStart: Position = range ? range.start : { line: 0, character: 0 } const globalStart: Position = range ? range.start : { line: 0, character: 0 }
return matches.map((match) => { return matches.map((match) => {
@ -292,7 +297,7 @@ export async function findClassListsInRange(
): Promise<DocumentClassList[]> { ): Promise<DocumentClassList[]> {
let classLists: DocumentClassList[] let classLists: DocumentClassList[]
if (mode === 'css') { if (mode === 'css') {
classLists = findClassListsInCssRange(doc, range) classLists = findClassListsInCssRange(state, doc, range)
} else { } else {
classLists = await findClassListsInHtmlRange(state, doc, mode, range) classLists = await findClassListsInHtmlRange(state, doc, mode, range)
} }
@ -307,7 +312,7 @@ export async function findClassListsInDocument(
doc: TextDocument doc: TextDocument
): Promise<DocumentClassList[]> { ): Promise<DocumentClassList[]> {
if (isCssDoc(state, doc)) { if (isCssDoc(state, doc)) {
return findClassListsInCssRange(doc) return findClassListsInCssRange(state, doc)
} }
let boundaries = getLanguageBoundaries(state, doc) let boundaries = getLanguageBoundaries(state, doc)
@ -324,7 +329,7 @@ export async function findClassListsInDocument(
)), )),
...boundaries ...boundaries
.filter((b) => b.type === 'css') .filter((b) => b.type === 'css')
.map(({ range }) => findClassListsInCssRange(doc, range)), .map(({ range }) => findClassListsInCssRange(state, doc, range)),
await findCustomClassLists(state, doc), await findCustomClassLists(state, doc),
]) ])
) )

View File

@ -1,3 +1,5 @@
import type { EditorState } from './state'
export const htmlLanguages = [ export const htmlLanguages = [
'aspnetcorerazor', 'aspnetcorerazor',
'astro', 'astro',
@ -57,3 +59,15 @@ export const jsLanguages = [
export const specialLanguages = ['vue', 'svelte'] export const specialLanguages = ['vue', 'svelte']
export const languages = [...cssLanguages, ...htmlLanguages, ...jsLanguages, ...specialLanguages] 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])
)
}