tailwind-ctp-intellisense @master -
refs -
log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
add initial class name conflict diagnostics
2 changed files, 116 additions(+), 64 deletions(-)
diff --git a/src/lsp/providers/diagnosticsProvider.ts b/src/lsp/providers/diagnosticsProvider.ts
index e76bf49249f86a753313ebc8aecd65c29403d89a..9f483ac9f69be7fbc5deeae70607194fafacf66b 100644
--- a/src/lsp/providers/diagnosticsProvider.ts
+++ b/src/lsp/providers/diagnosticsProvider.ts
@@ -5,10 +5,16 @@ DiagnosticSeverity,
} from 'vscode-languageserver'
import { State } from '../util/state'
import { isCssDoc } from '../util/css'
-import { findClassNamesInRange } from '../util/find'
+import {
+ findClassNamesInRange,
+ findClassListsInDocument,
+ getClassNamesInClassList,
+} from '../util/find'
import { getClassNameMeta } from '../util/getClassNameMeta'
+import { getClassNameDecls } from '../util/getClassNameDecls'
+import { equal } from '../../util/array'
-function provideCssDiagnostics(state: State, document: TextDocument): void {
+function getCssDiagnostics(state: State, document: TextDocument): Diagnostic[] {
const classNames = findClassNamesInRange(document, undefined, 'css')
let diagnostics: Diagnostic[] = classNames
@@ -46,38 +52,73 @@ return {
severity: DiagnosticSeverity.Error,
range,
message,
- // source: 'ex',
}
})
.filter(Boolean)
- // if (state.editor.capabilities.diagnosticRelatedInformation) {
- // diagnostic.relatedInformation = [
- // {
- // location: {
- // uri: document.uri,
- // range: Object.assign({}, diagnostic.range),
- // },
- // message: '',
- // },
- // {
- // location: {
- // uri: document.uri,
- // range: Object.assign({}, diagnostic.range),
- // },
- // message: '',
- // },
- // ]
- // }
+ return diagnostics
+}
+
+function getConflictDiagnostics(
+ state: State,
+ document: TextDocument
+): Diagnostic[] {
+ let diagnostics: Diagnostic[] = []
+ const classLists = findClassListsInDocument(state, document)
+
+ classLists.forEach((classList) => {
+ const classNames = getClassNamesInClassList(classList)
+
+ classNames.forEach((className, index) => {
+ let otherClassNames = classNames.filter((_className, i) => i !== index)
+ otherClassNames.forEach((otherClassName) => {
+ let decls = getClassNameDecls(state, className.className)
+ if (!decls) return
+
+ let otherDecls = getClassNameDecls(state, otherClassName.className)
+ if (!otherDecls) return
+
+ let meta = getClassNameMeta(state, className.className)
+ let otherMeta = getClassNameMeta(state, otherClassName.className)
+
+ if (
+ equal(Object.keys(decls), Object.keys(otherDecls)) &&
+ !Array.isArray(meta) &&
+ !Array.isArray(otherMeta) &&
+ equal(meta.context, otherMeta.context) &&
+ equal(meta.pseudo, otherMeta.pseudo)
+ ) {
+ diagnostics.push({
+ range: className.range,
+ severity: DiagnosticSeverity.Warning,
+ message: `You can’t use \`${className.className}\` and \`${otherClassName.className}\` together`,
+ relatedInformation: [
+ {
+ message: otherClassName.className,
+ location: {
+ uri: document.uri,
+ range: otherClassName.range,
+ },
+ },
+ ],
+ })
+ }
+ })
+ })
+ })
- state.editor.connection.sendDiagnostics({ uri: document.uri, diagnostics })
+ return diagnostics
}
export async function provideDiagnostics(
state: State,
document: TextDocument
): Promise<void> {
- if (isCssDoc(state, document)) {
- return provideCssDiagnostics(state, document)
- }
+ state.editor.connection.sendDiagnostics({
+ uri: document.uri,
+ diagnostics: [
+ ...getConflictDiagnostics(state, document),
+ ...(isCssDoc(state, document) ? getCssDiagnostics(state, document) : []),
+ ],
+ })
}
diff --git a/src/lsp/util/find.ts b/src/lsp/util/find.ts
index 6b1bfca7fbce3737b86c7529b4e7141715d17f58..9a04ac4de58771e60379719acd0b50a5e1ebcdf2 100644
--- a/src/lsp/util/find.ts
+++ b/src/lsp/util/find.ts
@@ -6,6 +6,7 @@ import { isHtmlContext, isHtmlDoc, isSvelteDoc, isVueDoc } from './html'
import { isWithinRange } from './isWithinRange'
import { isJsContext, isJsDoc } from './js'
import { getClassAttributeLexer } from './lexers'
+import { flatten } from '../../util/array'
export function findAll(re: RegExp, str: string): RegExpMatchArray[] {
let match: RegExpMatchArray
@@ -24,44 +25,53 @@ }
return matches[matches.length - 1]
}
+export function getClassNamesInClassList({
+ classList,
+ range,
+}: DocumentClassList): DocumentClassName[] {
+ const parts = classList.split(/(\s+)/)
+ const names: DocumentClassName[] = []
+ let index = 0
+ for (let i = 0; i < parts.length; i++) {
+ if (i % 2 === 0) {
+ const start = indexToPosition(classList, index)
+ const end = indexToPosition(classList, index + parts[i].length)
+ names.push({
+ className: parts[i],
+ range: {
+ start: {
+ line: range.start.line + start.line,
+ character:
+ (end.line === 0 ? range.start.character : 0) + start.character,
+ },
+ end: {
+ line: range.start.line + end.line,
+ character:
+ (end.line === 0 ? range.start.character : 0) + end.character,
+ },
+ },
+ })
+ }
+ index += parts[i].length
+ }
+ return names
+}
+
export function findClassNamesInRange(
doc: TextDocument,
range?: Range,
mode?: 'html' | 'css'
): DocumentClassName[] {
const classLists = findClassListsInRange(doc, range, mode)
- return [].concat.apply(
- [],
- classLists.map(({ classList, range }) => {
- const parts = classList.split(/(\s+)/)
- const names: DocumentClassName[] = []
- let index = 0
- for (let i = 0; i < parts.length; i++) {
- if (i % 2 === 0) {
- const start = indexToPosition(classList, index)
- const end = indexToPosition(classList, index + parts[i].length)
- names.push({
- className: parts[i],
- range: {
- start: {
- line: range.start.line + start.line,
- character:
- (end.line === 0 ? range.start.character : 0) +
- start.character,
- },
- end: {
- line: range.start.line + end.line,
- character:
- (end.line === 0 ? range.start.character : 0) + end.character,
- },
- },
- })
- }
- index += parts[i].length
- }
- return names
- })
- )
+ return flatten(classLists.map(getClassNamesInClassList))
+}
+
+export function findClassNamesInDocument(
+ state: State,
+ doc: TextDocument
+): DocumentClassName[] {
+ const classLists = findClassListsInDocument(state, doc)
+ return flatten(classLists.map(getClassNamesInClassList))
}
export function findClassListsInCssRange(
@@ -98,7 +108,7 @@ }
export function findClassListsInHtmlRange(
doc: TextDocument,
- range: Range
+ range?: Range
): DocumentClassList[] {
const text = doc.getText(range)
const matches = findAll(/[\s:]class(?:Name)?=['"`{]/g, text)
@@ -174,15 +184,16 @@ return {
classList: value,
range: {
start: {
- line: range.start.line + start.line,
+ line: (range?.start.line || 0) + start.line,
character:
- (end.line === 0 ? range.start.character : 0) +
+ (end.line === 0 ? range?.start.character || 0 : 0) +
start.character,
},
end: {
- line: range.start.line + end.line,
+ line: (range?.start.line || 0) + end.line,
character:
- (end.line === 0 ? range.start.character : 0) + end.character,
+ (end.line === 0 ? range?.start.character || 0 : 0) +
+ end.character,
},
},
}
@@ -196,8 +207,8 @@ }
export function findClassListsInRange(
doc: TextDocument,
- range: Range,
- mode: 'html' | 'css'
+ range?: Range,
+ mode?: 'html' | 'css'
): DocumentClassList[] {
if (mode === 'css') {
return findClassListsInCssRange(doc, range)