From 02126aa3e2045ace32f9b920c0f5e9ebb77e9701 Mon Sep 17 00:00:00 2001 From: Alpha1337k Date: Tue, 5 Dec 2023 02:43:43 +0100 Subject: [PATCH 1/4] Feat: working ignore option --- .../tailwindcss-language-server/src/server.ts | 11 ++++ .../provideInvalidIdentifierCodeActions.ts | 57 +++++++++++-------- .../src/diagnostics/diagnosticsProvider.ts | 4 +- ...stics.ts => getInvalidValueDiagnostics.ts} | 53 ++++++++++------- .../src/diagnostics/types.ts | 1 + .../src/util/state.ts | 1 + packages/vscode-tailwindcss/package.json | 10 +++- packages/vscode-tailwindcss/src/extension.ts | 31 ++++++++++ 8 files changed, 123 insertions(+), 45 deletions(-) rename packages/tailwindcss-language-service/src/diagnostics/{getUnknownClassesDiagnostics.ts => getInvalidValueDiagnostics.ts} (76%) diff --git a/packages/tailwindcss-language-server/src/server.ts b/packages/tailwindcss-language-server/src/server.ts index f4c2b230c..b551ff43f 100644 --- a/packages/tailwindcss-language-server/src/server.ts +++ b/packages/tailwindcss-language-server/src/server.ts @@ -2211,6 +2211,10 @@ class TW { method: '@/tailwindCSS/getProject', params: { uri: string } ): { version: string } | null + private onRequest( + method: '@/tailwindCSS/setIgnoredCSS', + params: { ignoredKeys: string[] } + ): { version: string } | null private onRequest(method: string, params: any): any { if (method === '@/tailwindCSS/sortSelection') { let project = this.getProject({ uri: params.uri }) @@ -2233,6 +2237,13 @@ class TW { version: project.state.version, } } + + if (method === '@/tailwindCSS/setIgnoredCSS') { + this.projects.forEach((p) => { + p.state.ignoredKeys = params.ignoredKeys + }) + this.refreshDiagnostics() + } } private updateCapabilities() { diff --git a/packages/tailwindcss-language-service/src/codeActions/provideInvalidIdentifierCodeActions.ts b/packages/tailwindcss-language-service/src/codeActions/provideInvalidIdentifierCodeActions.ts index d3b30a3c1..5a521bc0a 100644 --- a/packages/tailwindcss-language-service/src/codeActions/provideInvalidIdentifierCodeActions.ts +++ b/packages/tailwindcss-language-service/src/codeActions/provideInvalidIdentifierCodeActions.ts @@ -1,35 +1,46 @@ import { State } from '../util/state' -import type { - CodeActionParams, - CodeAction, +import { + type CodeActionParams, CodeAction, + CodeActionKind, + Command, } from 'vscode-languageserver' import { CssConflictDiagnostic, InvalidIdentifierDiagnostic } from '../diagnostics/types' import { joinWithAnd } from '../util/joinWithAnd' import { removeRangesFromString } from '../util/removeRangesFromString' + export async function provideInvalidIdentifierCodeActions( _state: State, params: CodeActionParams, diagnostic: InvalidIdentifierDiagnostic ): Promise { - if (!diagnostic.suggestion) return []; - - debugger; - return [ - { - title: `Replace with '${diagnostic.suggestion}'`, - kind: 'quickfix', // CodeActionKind.QuickFix, - diagnostics: [diagnostic], - edit: { - changes: { - [params.textDocument.uri]: [ - { - range: diagnostic.range, - newText: diagnostic.suggestion, - }, - ], - }, - }, - }, - ] + const actions: CodeAction[] = [{ + title: `Ignore ${diagnostic.chunk} in workspace settings`, + kind: CodeActionKind.QuickFix, + diagnostics: [diagnostic], + command: Command.create(`Ignore '${diagnostic.chunk}' in workspace settings`, 'tailwindCSS.addWordToWorkspaceFileFromServer', diagnostic.chunk) + }]; + + if (typeof diagnostic.suggestion == 'string') { + actions.push({ + title: `Replace with '${diagnostic.suggestion}'`, + kind: CodeActionKind.QuickFix, + diagnostics: [diagnostic], + isPreferred: true, + edit: { + changes: { + [params.textDocument.uri]: [ + { + range: diagnostic.range, + newText: diagnostic.suggestion, + }, + ], + }, + }, + }) + } else { + // unimplemented. + } + + return actions; } diff --git a/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts b/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts index 71de07c87..400b3f315 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/diagnosticsProvider.ts @@ -8,7 +8,7 @@ import { getInvalidVariantDiagnostics } from './getInvalidVariantDiagnostics' import { getInvalidConfigPathDiagnostics } from './getInvalidConfigPathDiagnostics' import { getInvalidTailwindDirectiveDiagnostics } from './getInvalidTailwindDirectiveDiagnostics' import { getRecommendedVariantOrderDiagnostics } from './getRecommendedVariantOrderDiagnostics' -import { getUnknownClassesDiagnostics } from './getUnknownClassesDiagnostics' +import { getInvalidValueDiagnostics } from './getInvalidValueDiagnostics' export async function doValidate( state: State, @@ -29,7 +29,7 @@ export async function doValidate( return settings.tailwindCSS.validate ? [ ...(only.includes(DiagnosticKind.InvalidIdentifier) - ? await getUnknownClassesDiagnostics(state, document, settings) + ? await getInvalidValueDiagnostics(state, document, settings) : []), ...(only.includes(DiagnosticKind.CssConflict) ? await getCssConflictDiagnostics(state, document, settings) diff --git a/packages/tailwindcss-language-service/src/diagnostics/getUnknownClassesDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getInvalidValueDiagnostics.ts similarity index 76% rename from packages/tailwindcss-language-service/src/diagnostics/getUnknownClassesDiagnostics.ts rename to packages/tailwindcss-language-service/src/diagnostics/getInvalidValueDiagnostics.ts index 1982a4678..964529779 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/getUnknownClassesDiagnostics.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/getInvalidValueDiagnostics.ts @@ -4,14 +4,19 @@ import { findClassListsInDocument, getClassNamesInClassList } from '../util/find import type { TextDocument } from 'vscode-languageserver-textdocument' import { Range } from 'vscode-languageserver' -function createDiagnostic(className: DocumentClassName, range: Range, message: string, suggestion?: string): InvalidIdentifierDiagnostic +function createDiagnostic(className: DocumentClassName, range: Range, chunk: string, message: string, suggestion?: string): InvalidIdentifierDiagnostic { return({ code: DiagnosticKind.InvalidIdentifier, - severity: 3, + severity: 1, range: range, message, className, + chunk, + source: "TailwindCSS", + data: { + name: className.className + }, suggestion, otherClassNames: null }) @@ -96,6 +101,11 @@ function editDistance(s1: string, s2: string) { return costs[s2.length]; } +/** + * + * + */ + function handleClass(state: State, className: DocumentClassName, chunk: string, @@ -114,12 +124,12 @@ function handleClass(state: State, if (noNumericClasses[chunk]) { - return createDiagnostic(className, range, `${chunk} requires an postfix. Choose between ${noNumericClasses[chunk].join(', -')}.`) + return createDiagnostic(className, range, chunk, `${chunk} requires an postfix. Choose between ${noNumericClasses[chunk].join(', -')}.`) } if (classes[nonNumericValue]) { - return createDiagnostic(className, range, `${nonNumericValue} requires no postfix.`, nonNumericValue) + return createDiagnostic(className, range, chunk, `${nonNumericValue} requires no postfix.`, nonNumericValue) } if (nonNumericValue && noNumericClasses[nonNumericValue]) @@ -142,11 +152,11 @@ function handleClass(state: State, if (closestSuggestion.text) { - return createDiagnostic(className, range, `${chunk} is an invalid value. Did you mean ${nonNumericValue + '-' + closestSuggestion.text}? (${closestSuggestion.value})`, nonNumericValue + '-' + closestSuggestion.text) + return createDiagnostic(className, range, chunk, `${chunk} is an invalid value. Did you mean ${nonNumericValue + '-' + closestSuggestion.text}?`, nonNumericValue + '-' + closestSuggestion.text) } else { - return createDiagnostic(className, range, `${chunk} is an invalid value. Choose between ${noNumericClasses[nonNumericValue].join(', ')}.`) + return createDiagnostic(className, range, chunk, `${chunk} is an invalid value. Choose between ${noNumericClasses[nonNumericValue].join(', ')}.`) } } @@ -168,11 +178,11 @@ function handleClass(state: State, if (closestSuggestion.text) { - return createDiagnostic(className, range, `${chunk} was not found in the registry. Did you mean ${closestSuggestion.text} (${closestSuggestion.value})?`, closestSuggestion.text) + return createDiagnostic(className, range, chunk, `${chunk} was not found in the registry. Did you mean ${closestSuggestion.text}?`, closestSuggestion.text) } else { - return createDiagnostic(className, range, `${chunk} was not found in the registry.`) + return createDiagnostic(className, range, chunk, `${chunk} was not found in the registry.`) } } @@ -202,22 +212,26 @@ function handleVariant(state: State, className: DocumentClassName, chunk: string if (closestSuggestion.text) { - return createDiagnostic(className, range, `${chunk} is an invalid variant. Did you mean ${closestSuggestion.text} (${closestSuggestion.value})?`, closestSuggestion.text) + return createDiagnostic(className, range, chunk, `${chunk} is an invalid variant. Did you mean ${closestSuggestion.text}?`, closestSuggestion.text) } else { - return createDiagnostic(className, range, `${chunk} is an invalid variant.`); + return createDiagnostic(className, range, chunk, `${chunk} is an invalid variant.`); } } -export async function getUnknownClassesDiagnostics( +export async function getInvalidValueDiagnostics( state: State, document: TextDocument, settings: Settings ): Promise { let severity = settings.tailwindCSS.lint.invalidClass if (severity === 'ignore') return []; + + if (state.ignoredKeys) { + debugger + } const items = []; const { classes, variants, noNumericClasses} = generateHashMaps(state); @@ -239,15 +253,16 @@ export async function getUnknownClassesDiagnostics( character: className.range.start.character + offset + chunk.length, }} - if (index == splitted.length - 1) - { - items.push(handleClass(state, className, chunk, classes, noNumericClasses, range)); + if (!state.ignoredKeys || state.ignoredKeys.find(x => x == chunk) == undefined) { + if (index == splitted.length - 1) + { + items.push(handleClass(state, className, chunk, classes, noNumericClasses, range)); + } + else + { + items.push(handleVariant(state, className, chunk, variants, range)); + } } - else - { - items.push(handleVariant(state, className, chunk, variants, range)); - } - offset += chunk.length + 1; }) }); diff --git a/packages/tailwindcss-language-service/src/diagnostics/types.ts b/packages/tailwindcss-language-service/src/diagnostics/types.ts index 5722bb56f..2a5d260f0 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/types.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/types.ts @@ -16,6 +16,7 @@ export type InvalidIdentifierDiagnostic = Diagnostic & { code: DiagnosticKind.InvalidIdentifier className: DocumentClassName, suggestion?: string, + chunk: string, otherClassNames: DocumentClassName[] } diff --git a/packages/tailwindcss-language-service/src/util/state.ts b/packages/tailwindcss-language-service/src/util/state.ts index f55cae604..cb83144d1 100644 --- a/packages/tailwindcss-language-service/src/util/state.ts +++ b/packages/tailwindcss-language-service/src/util/state.ts @@ -101,6 +101,7 @@ export interface State { variants?: Variant[] corePlugins?: string[] blocklist?: unknown[] + ignoredKeys?: string[] modules?: { tailwindcss?: { version: string; module: any } postcss?: { version: string; module: Postcss } diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json index 0016e5c23..a17f342fe 100755 --- a/packages/vscode-tailwindcss/package.json +++ b/packages/vscode-tailwindcss/package.json @@ -320,7 +320,15 @@ ], "default": null, "markdownDescription": "Enable the Node.js inspector agent for the language server and listen on the specified port." - } + }, + "tailwindCSS.ignoredCSS": { + "items": { + "type": "string" + }, + "markdownDescription": "List of CSS classes to be considered correct.", + "scope": "resource", + "type": "array" + } } } }, diff --git a/packages/vscode-tailwindcss/src/extension.ts b/packages/vscode-tailwindcss/src/extension.ts index f38602711..1fed8b982 100755 --- a/packages/vscode-tailwindcss/src/extension.ts +++ b/packages/vscode-tailwindcss/src/extension.ts @@ -27,6 +27,8 @@ import { SnippetString, TextEdit, Selection, + workspace, + ConfigurationTarget, } from 'vscode' import { LanguageClient, @@ -678,6 +680,9 @@ export async function activate(context: ExtensionContext) { client.onNotification('@/tailwindCSS/projectInitialized', async () => { await updateActiveTextEditorContext() + + const ignoredKeys = workspace.getConfiguration().get('tailwindCSS.ignoredCSS') + client.sendRequest('@/tailwindCSS/setIgnoredCSS', {ignoredKeys}) }) client.onNotification('@/tailwindCSS/projectReset', async () => { await updateActiveTextEditorContext() @@ -703,6 +708,32 @@ export async function activate(context: ExtensionContext) { clients.set(folder.uri.toString(), client) } + workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration('tailwindCSS') == false) + return; + const ignoredKeys: string[] = workspace.getConfiguration().get('tailwindCSS.ignoredCSS') + + clients.forEach((c) => { + c.sendRequest('@/tailwindCSS/setIgnoredCSS', {ignoredKeys}).then(d => { + console.log(d); + }) + }) + }) + + + context.subscriptions.push( + commands.registerCommand('tailwindCSS.addWordToWorkspaceFileFromServer', (name) => { + const storedKeys: string[] = workspace.getConfiguration().get('tailwindCSS.ignoredCSS') + + storedKeys.push(name); + workspace.getConfiguration() + .update('tailwindCSS.ignoredCSS', [...new Set(storedKeys)], ConfigurationTarget.Workspace) + + // commands.executeCommand('@/tailwindCSS/reload', {ignoredKeys: storedKeys}); + + }) + ) + async function bootClientForFolderIfNeeded(folder: WorkspaceFolder): Promise { let settings = Workspace.getConfiguration('tailwindCSS', folder) if (settings.get('experimental.configFile') !== null) { From f60954972cfad3eaa729b4c7f4e277e4897553c1 Mon Sep 17 00:00:00 2001 From: Alpha1337k Date: Tue, 5 Dec 2023 03:21:55 +0100 Subject: [PATCH 2/4] Chore: cleanup && improve: better similarity finding --- .../tailwindcss-language-server/src/server.ts | 11 -- .../diagnostics/getInvalidValueDiagnostics.ts | 183 +++++++++++++----- .../src/util/state.ts | 6 +- packages/vscode-tailwindcss/package.json | 18 ++ packages/vscode-tailwindcss/src/extension.ts | 19 -- 5 files changed, 156 insertions(+), 81 deletions(-) diff --git a/packages/tailwindcss-language-server/src/server.ts b/packages/tailwindcss-language-server/src/server.ts index b551ff43f..f4c2b230c 100644 --- a/packages/tailwindcss-language-server/src/server.ts +++ b/packages/tailwindcss-language-server/src/server.ts @@ -2211,10 +2211,6 @@ class TW { method: '@/tailwindCSS/getProject', params: { uri: string } ): { version: string } | null - private onRequest( - method: '@/tailwindCSS/setIgnoredCSS', - params: { ignoredKeys: string[] } - ): { version: string } | null private onRequest(method: string, params: any): any { if (method === '@/tailwindCSS/sortSelection') { let project = this.getProject({ uri: params.uri }) @@ -2237,13 +2233,6 @@ class TW { version: project.state.version, } } - - if (method === '@/tailwindCSS/setIgnoredCSS') { - this.projects.forEach((p) => { - p.state.ignoredKeys = params.ignoredKeys - }) - this.refreshDiagnostics() - } } private updateCapabilities() { diff --git a/packages/tailwindcss-language-service/src/diagnostics/getInvalidValueDiagnostics.ts b/packages/tailwindcss-language-service/src/diagnostics/getInvalidValueDiagnostics.ts index 964529779..89be02312 100644 --- a/packages/tailwindcss-language-service/src/diagnostics/getInvalidValueDiagnostics.ts +++ b/packages/tailwindcss-language-service/src/diagnostics/getInvalidValueDiagnostics.ts @@ -2,22 +2,43 @@ import { State, Settings, DocumentClassName, Variant } from '../util/state' import { CssConflictDiagnostic, DiagnosticKind, InvalidIdentifierDiagnostic } from './types' import { findClassListsInDocument, getClassNamesInClassList } from '../util/find' import type { TextDocument } from 'vscode-languageserver-textdocument' -import { Range } from 'vscode-languageserver' +import { DiagnosticSeverity, Range } from 'vscode-languageserver' -function createDiagnostic(className: DocumentClassName, range: Range, chunk: string, message: string, suggestion?: string): InvalidIdentifierDiagnostic +function createDiagnostic(data: { + className: DocumentClassName, + range: Range, + chunk: string, + message: string, + suggestion?: string + severity: 'info' | 'warning' | 'error' | 'ignore' + }): InvalidIdentifierDiagnostic { + let severity: DiagnosticSeverity = 1; + + switch (data.severity) { + case "info": + severity = 3; + break + case "warning": + severity = 2; + break + case "error": + severity = 1; + break + } + return({ code: DiagnosticKind.InvalidIdentifier, - severity: 1, - range: range, - message, - className, - chunk, + severity, + range: data.range, + message: data.message, + className: data.className, + chunk: data.chunk, source: "TailwindCSS", data: { - name: className.className + name: data.className.className }, - suggestion, + suggestion: data.suggestion, otherClassNames: null }) } @@ -101,46 +122,64 @@ function editDistance(s1: string, s2: string) { return costs[s2.length]; } -/** - * - * - */ +function getMinimumSimilarity(str: string) { + if (str.length < 5) { + return 0.5 + } else { + return 0.7 + } +} + -function handleClass(state: State, +function handleClass(data: {state: State, + settings: Settings, className: DocumentClassName, chunk: string, classes: {[key: string]: State['classList'][0] }, noNumericClasses: {[key: string]: string[]}, range: Range - ) + }) { - if (chunk.indexOf('[') != -1 || classes[chunk] != undefined) { + if (data.chunk.indexOf('[') != -1 || data.classes[data.chunk] != undefined) { return null; } - let nonNumericChunk = chunk.split('-'); + let nonNumericChunk = data.chunk.split('-'); let nonNumericRemainder = nonNumericChunk.pop(); const nonNumericValue = nonNumericChunk.join('-'); - if (noNumericClasses[chunk]) + if (data.noNumericClasses[data.chunk]) { - return createDiagnostic(className, range, chunk, `${chunk} requires an postfix. Choose between ${noNumericClasses[chunk].join(', -')}.`) + return createDiagnostic({ + className: data.className, + range: data.range, + chunk: data.chunk, + message: `${data.chunk} requires an postfix. Choose between ${data.noNumericClasses[data.chunk].join(', -')}.`, + severity: data.settings.tailwindCSS.lint.validateClasses, + }) } - if (classes[nonNumericValue]) + if (data.classes[nonNumericValue]) { - return createDiagnostic(className, range, chunk, `${nonNumericValue} requires no postfix.`, nonNumericValue) + return createDiagnostic({ + className: data.className, + range: data.range, + chunk: data.chunk, + message: `${nonNumericValue} requires no postfix.`, + suggestion: nonNumericValue, + severity: data.settings.tailwindCSS.lint.validateClasses, + }) } - if (nonNumericValue && noNumericClasses[nonNumericValue]) + if (nonNumericValue && data.noNumericClasses[nonNumericValue]) { let closestSuggestion = { value: 0, text: "" }; - for (let i = 0; i < noNumericClasses[nonNumericValue].length; i++) { - const e = noNumericClasses[nonNumericValue][i]; + for (let i = 0; i < data.noNumericClasses[nonNumericValue].length; i++) { + const e = data.noNumericClasses[nonNumericValue][i]; const match = similarity(e, nonNumericRemainder); if (match > 0.5 && match > closestSuggestion.value) { closestSuggestion = { @@ -152,11 +191,24 @@ function handleClass(state: State, if (closestSuggestion.text) { - return createDiagnostic(className, range, chunk, `${chunk} is an invalid value. Did you mean ${nonNumericValue + '-' + closestSuggestion.text}?`, nonNumericValue + '-' + closestSuggestion.text) + return createDiagnostic({ + className: data.className, + range: data.range, + chunk: data.chunk, + message: `${data.chunk} is an invalid value. Did you mean ${nonNumericValue + '-' + closestSuggestion.text}?`, + suggestion: nonNumericValue + '-' + closestSuggestion.text, + severity: data.settings.tailwindCSS.lint.validateClasses, + }) } else { - return createDiagnostic(className, range, chunk, `${chunk} is an invalid value. Choose between ${noNumericClasses[nonNumericValue].join(', ')}.`) + return createDiagnostic({ + className: data.className, + range: data.range, + chunk: data.chunk, + message: `${data.chunk} is an invalid value. Choose between ${data.noNumericClasses[nonNumericValue].join(', ')}.`, + severity: data.settings.tailwindCSS.lint.validateClasses, + }) } } @@ -165,10 +217,12 @@ function handleClass(state: State, value: 0, text: "" }; - for (let i = 0; i < state.classList.length; i++) { - const e = state.classList[i]; - const match = similarity(e[0], className.className); - if (match > 0.5 && match > closestSuggestion.value) { + + let minimumSimilarity = getMinimumSimilarity(data.className.className) + for (let i = 0; i < data.state.classList.length; i++) { + const e = data.state.classList[i]; + const match = similarity(e[0], data.className.className); + if (match >= minimumSimilarity && match > closestSuggestion.value) { closestSuggestion = { value: match, text: e[0] @@ -178,17 +232,38 @@ function handleClass(state: State, if (closestSuggestion.text) { - return createDiagnostic(className, range, chunk, `${chunk} was not found in the registry. Did you mean ${closestSuggestion.text}?`, closestSuggestion.text) + return createDiagnostic({ + className: data.className, + range: data.range, + chunk: data.chunk, + message: `${data.chunk} was not found in the registry. Did you mean ${closestSuggestion.text}?`, + severity: data.settings.tailwindCSS.lint.validateClasses, + suggestion: closestSuggestion.text + }) } - else + else if (data.settings.tailwindCSS.lint.onlyAllowTailwindCSS) { - return createDiagnostic(className, range, chunk, `${chunk} was not found in the registry.`) + return createDiagnostic({ + className: data.className, + range: data.range, + chunk: data.chunk, + message: `${data.chunk} was not found in the registry.`, + severity: data.settings.tailwindCSS.lint.validateClasses + }) } + return null } -function handleVariant(state: State, className: DocumentClassName, chunk: string, variants: {[key: string]: Variant }, range: Range) +function handleVariant(data: { + state: State, + settings: Settings, + className: DocumentClassName, + chunk: string, + variants: {[key: string]: Variant }, + range: Range + }) { - if (chunk.indexOf('[') != -1 || variants[chunk]) { + if (data.chunk.indexOf('[') != -1 || data.variants[data.chunk]) { return null; } @@ -197,11 +272,12 @@ function handleVariant(state: State, className: DocumentClassName, chunk: string value: 0, text: "" }; + let minimumSimilarity = getMinimumSimilarity(data.className.className) - Object.keys(variants).forEach(key => { - const variant = variants[key]; - const match = similarity(variant.name, chunk); - if (match >= 0.5 && match > closestSuggestion.value) { + Object.keys(data.variants).forEach(key => { + const variant = data.variants[key]; + const match = similarity(variant.name, data.chunk); + if (match >= minimumSimilarity && match > closestSuggestion.value) { closestSuggestion = { value: match, text: variant.name @@ -212,11 +288,24 @@ function handleVariant(state: State, className: DocumentClassName, chunk: string if (closestSuggestion.text) { - return createDiagnostic(className, range, chunk, `${chunk} is an invalid variant. Did you mean ${closestSuggestion.text}?`, closestSuggestion.text) + return createDiagnostic({ + className: data.className, + range: data.range, + chunk: data.chunk, + message: `${data.chunk} is an invalid variant. Did you mean ${closestSuggestion.text}?`, + suggestion: closestSuggestion.text, + severity: data.settings.tailwindCSS.lint.validateClasses + }) } else { - return createDiagnostic(className, range, chunk, `${chunk} is an invalid variant.`); + return createDiagnostic({ + className: data.className, + range: data.range, + chunk: data.chunk, + message: `${data.chunk} is an invalid variant.`, + severity: data.settings.tailwindCSS.lint.validateClasses + }); } } @@ -226,12 +315,8 @@ export async function getInvalidValueDiagnostics( document: TextDocument, settings: Settings ): Promise { - let severity = settings.tailwindCSS.lint.invalidClass + let severity = settings.tailwindCSS.lint.validateClasses if (severity === 'ignore') return []; - - if (state.ignoredKeys) { - debugger - } const items = []; const { classes, variants, noNumericClasses} = generateHashMaps(state); @@ -253,14 +338,14 @@ export async function getInvalidValueDiagnostics( character: className.range.start.character + offset + chunk.length, }} - if (!state.ignoredKeys || state.ignoredKeys.find(x => x == chunk) == undefined) { + if (!settings.tailwindCSS.ignoredCSS.find(x => x == chunk)) { if (index == splitted.length - 1) { - items.push(handleClass(state, className, chunk, classes, noNumericClasses, range)); + items.push(handleClass({state, settings, className, chunk, classes, noNumericClasses, range})); } else { - items.push(handleVariant(state, className, chunk, variants, range)); + items.push(handleVariant({state, settings, className, chunk, variants, range})); } } offset += chunk.length + 1; diff --git a/packages/tailwindcss-language-service/src/util/state.ts b/packages/tailwindcss-language-service/src/util/state.ts index cb83144d1..b0c4dfee8 100644 --- a/packages/tailwindcss-language-service/src/util/state.ts +++ b/packages/tailwindcss-language-service/src/util/state.ts @@ -51,12 +51,15 @@ export type TailwindCssSettings = { showPixelEquivalents: boolean rootFontSize: number colorDecorators: boolean + ignoredCSS: string[] lint: { invalidClass: DiagnosticSeveritySetting cssConflict: DiagnosticSeveritySetting invalidApply: DiagnosticSeveritySetting invalidScreen: DiagnosticSeveritySetting - invalidVariant: DiagnosticSeveritySetting + invalidVariant: DiagnosticSeveritySetting, + validateClasses: DiagnosticSeveritySetting, + onlyAllowTailwindCSS: boolean, invalidConfigPath: DiagnosticSeveritySetting invalidTailwindDirective: DiagnosticSeveritySetting recommendedVariantOrder: DiagnosticSeveritySetting @@ -101,7 +104,6 @@ export interface State { variants?: Variant[] corePlugins?: string[] blocklist?: unknown[] - ignoredKeys?: string[] modules?: { tailwindcss?: { version: string; module: any } postcss?: { version: string; module: Postcss } diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json index a17f342fe..4c126b4b1 100755 --- a/packages/vscode-tailwindcss/package.json +++ b/packages/vscode-tailwindcss/package.json @@ -290,6 +290,24 @@ "markdownDescription": "Class variants not in the recommended order (applies in [JIT mode](https://tailwindcss.com/docs/just-in-time-mode) only)", "scope": "language-overridable" }, + "tailwindCSS.lint.validateClasses": { + "type": "string", + "enum": [ + "ignore", + "info", + "warning", + "error" + ], + "default": "warning", + "markdownDescription": "Validate CSS for wrongly typed classes.", + "scope": "language-overridable" + }, + "tailwindCSS.lint.onlyAllowTailwindCSS": { + "type": "boolean", + "default": true, + "markdownDescription": "Validate CSS for non / invalid tailwindCSS classes. You are able to ignore on an case-by-case basis. Requires `invalidVariant` to be active.", + "scope": "language-overridable" + }, "tailwindCSS.experimental.classRegex": { "type": "array", "scope": "language-overridable" diff --git a/packages/vscode-tailwindcss/src/extension.ts b/packages/vscode-tailwindcss/src/extension.ts index 1fed8b982..4f1943176 100755 --- a/packages/vscode-tailwindcss/src/extension.ts +++ b/packages/vscode-tailwindcss/src/extension.ts @@ -680,9 +680,6 @@ export async function activate(context: ExtensionContext) { client.onNotification('@/tailwindCSS/projectInitialized', async () => { await updateActiveTextEditorContext() - - const ignoredKeys = workspace.getConfiguration().get('tailwindCSS.ignoredCSS') - client.sendRequest('@/tailwindCSS/setIgnoredCSS', {ignoredKeys}) }) client.onNotification('@/tailwindCSS/projectReset', async () => { await updateActiveTextEditorContext() @@ -708,19 +705,6 @@ export async function activate(context: ExtensionContext) { clients.set(folder.uri.toString(), client) } - workspace.onDidChangeConfiguration((e) => { - if (e.affectsConfiguration('tailwindCSS') == false) - return; - const ignoredKeys: string[] = workspace.getConfiguration().get('tailwindCSS.ignoredCSS') - - clients.forEach((c) => { - c.sendRequest('@/tailwindCSS/setIgnoredCSS', {ignoredKeys}).then(d => { - console.log(d); - }) - }) - }) - - context.subscriptions.push( commands.registerCommand('tailwindCSS.addWordToWorkspaceFileFromServer', (name) => { const storedKeys: string[] = workspace.getConfiguration().get('tailwindCSS.ignoredCSS') @@ -728,9 +712,6 @@ export async function activate(context: ExtensionContext) { storedKeys.push(name); workspace.getConfiguration() .update('tailwindCSS.ignoredCSS', [...new Set(storedKeys)], ConfigurationTarget.Workspace) - - // commands.executeCommand('@/tailwindCSS/reload', {ignoredKeys: storedKeys}); - }) ) From 09f80033b2e7d7ed8c40520dd572792e4eab6361 Mon Sep 17 00:00:00 2001 From: Alpha1337k Date: Tue, 5 Dec 2023 03:28:35 +0100 Subject: [PATCH 3/4] Chore: more styling and wording changes --- packages/vscode-tailwindcss/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json index 4c126b4b1..167ab3c7d 100755 --- a/packages/vscode-tailwindcss/package.json +++ b/packages/vscode-tailwindcss/package.json @@ -299,13 +299,13 @@ "error" ], "default": "warning", - "markdownDescription": "Validate CSS for wrongly typed classes.", + "markdownDescription": "Validate CSS for wrongly typed tailwind classes.", "scope": "language-overridable" }, "tailwindCSS.lint.onlyAllowTailwindCSS": { "type": "boolean", "default": true, - "markdownDescription": "Validate CSS for non / invalid tailwindCSS classes. You are able to ignore on an case-by-case basis. Requires `invalidVariant` to be active.", + "markdownDescription": "Validate CSS for non / invalid tailwindCSS classes. You are able to ignore on an case-by-case basis. Requires `tailwindCSS.lint.validateClasses` to be active.", "scope": "language-overridable" }, "tailwindCSS.experimental.classRegex": { From cce837ef168ae996ce1817a0431d5d44bdc03de0 Mon Sep 17 00:00:00 2001 From: Alpha1337k Date: Tue, 5 Dec 2023 03:32:22 +0100 Subject: [PATCH 4/4] Chore: more wording changes.. --- .../src/codeActions/provideInvalidIdentifierCodeActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/tailwindcss-language-service/src/codeActions/provideInvalidIdentifierCodeActions.ts b/packages/tailwindcss-language-service/src/codeActions/provideInvalidIdentifierCodeActions.ts index 5a521bc0a..2aef1047a 100644 --- a/packages/tailwindcss-language-service/src/codeActions/provideInvalidIdentifierCodeActions.ts +++ b/packages/tailwindcss-language-service/src/codeActions/provideInvalidIdentifierCodeActions.ts @@ -15,10 +15,10 @@ export async function provideInvalidIdentifierCodeActions( diagnostic: InvalidIdentifierDiagnostic ): Promise { const actions: CodeAction[] = [{ - title: `Ignore ${diagnostic.chunk} in workspace settings`, + title: `Ignore '${diagnostic.chunk}' in this workspace`, kind: CodeActionKind.QuickFix, diagnostics: [diagnostic], - command: Command.create(`Ignore '${diagnostic.chunk}' in workspace settings`, 'tailwindCSS.addWordToWorkspaceFileFromServer', diagnostic.chunk) + command: Command.create(`Ignore '${diagnostic.chunk}' in this workspace`, 'tailwindCSS.addWordToWorkspaceFileFromServer', diagnostic.chunk) }]; if (typeof diagnostic.suggestion == 'string') {