From 1588613c40dad88174bae9518f4f426fec3663b0 Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Mon, 16 May 2022 10:55:24 +0100 Subject: [PATCH 01/10] Add `sortSelection` command --- .../tailwindcss-language-server/src/server.ts | 73 ++++++++++++++++++- packages/vscode-tailwindcss/package.json | 5 ++ packages/vscode-tailwindcss/src/extension.ts | 52 +++++++++++++ 3 files changed, 129 insertions(+), 1 deletion(-) diff --git a/packages/tailwindcss-language-server/src/server.ts b/packages/tailwindcss-language-server/src/server.ts index 615fd950..16c80bcf 100644 --- a/packages/tailwindcss-language-server/src/server.ts +++ b/packages/tailwindcss-language-server/src/server.ts @@ -73,7 +73,7 @@ import { getModuleDependencies } from './util/getModuleDependencies' import assert from 'assert' // import postcssLoadConfig from 'postcss-load-config' import * as parcel from './watcher/index.js' -import { generateRules } from 'tailwindcss-language-service/src/util/jit' +import { bigSign } from 'tailwindcss-language-service/src/util/jit' import { getColor } from 'tailwindcss-language-service/src/util/color' import * as culori from 'culori' import namedColors from 'color-name' @@ -186,6 +186,7 @@ interface ProjectService { onDocumentColor(params: DocumentColorParams): Promise onColorPresentation(params: ColorPresentationParams): Promise onCodeAction(params: CodeActionParams): Promise + sortClassLists(classLists: string[]): string[] } type ProjectConfig = { folder: string; configPath?: string; documentSelector?: string[] } @@ -1052,9 +1053,66 @@ async function createProjectService( .replace(/\d+\.\d+(%?)/g, (value, suffix) => `${Math.round(parseFloat(value))}${suffix}`), ].map((value) => ({ label: `${prefix}-[${value}]` })) }, + sortClassLists(classLists: string[]): string[] { + return classLists.map((classList) => { + let result = '' + let parts = classList.split(/(\s+)/) + let classes = parts.filter((_, i) => i % 2 === 0) + let whitespace = parts.filter((_, i) => i % 2 !== 0) + + if (classes[classes.length - 1] === '') { + classes.pop() + } + + let classNamesWithOrder = state.jitContext.getClassOrder + ? state.jitContext.getClassOrder(classes) + : getClassOrderPolyfill(state, classes) + + classes = classNamesWithOrder + .sort(([, a], [, z]) => { + if (a === z) return 0 + if (a === null) return -1 + if (z === null) return 1 + return bigSign(a - z) + }) + .map(([className]) => className) + + for (let i = 0; i < classes.length; i++) { + result += `${classes[i]}${whitespace[i] ?? ''}` + } + + return result + }) + }, } } +function prefixCandidate(state: State, selector: string) { + let prefix = state.config.prefix + return typeof prefix === 'function' ? prefix(selector) : prefix + selector +} + +function getClassOrderPolyfill(state: State, classes: string[]): Array<[string, bigint]> { + let parasiteUtilities = new Set([prefixCandidate(state, 'group'), prefixCandidate(state, 'peer')]) + + let classNamesWithOrder = [] + + for (let className of classes) { + let order = + state.modules.jit.generateRules + .module(new Set([className]), state.jitContext) + .sort(([a], [z]) => bigSign(z - a))[0]?.[0] ?? null + + if (order === null && parasiteUtilities.has(className)) { + order = state.jitContext.layerOrder.components + } + + classNamesWithOrder.push([className, order]) + } + + return classNamesWithOrder +} + function isObject(value: unknown): boolean { return Object.prototype.toString.call(value) === '[object Object]' } @@ -1449,6 +1507,19 @@ class TW { this.connection.onDocumentColor(this.onDocumentColor.bind(this)) this.connection.onColorPresentation(this.onColorPresentation.bind(this)) this.connection.onCodeAction(this.onCodeAction.bind(this)) + this.connection.onRequest((method, params: { uri: string; classLists: string[] }) => { + if (method === '@/tailwindCSS/sortSelection') { + let project = this.getProject({ uri: params.uri }) + if (!project) { + return { error: 'no-project' } + } + try { + return { result: project.sortClassLists(params.classLists) } + } catch { + return { error: 'unknown' } + } + } + }) } private updateCapabilities() { diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json index 50baee3c..ee280069 100755 --- a/packages/vscode-tailwindcss/package.json +++ b/packages/vscode-tailwindcss/package.json @@ -57,6 +57,11 @@ "command": "tailwindCSS.showOutput", "title": "Tailwind CSS: Show Output", "enablement": "tailwindCSS.hasOutputChannel" + }, + { + "command": "tailwindCSS.sortSelection", + "title": "Tailwind CSS: Sort Selection", + "enablement": "editorHasSelection && resourceScheme == file" } ], "grammars": [ diff --git a/packages/vscode-tailwindcss/src/extension.ts b/packages/vscode-tailwindcss/src/extension.ts index 35b4f759..f2b44363 100755 --- a/packages/vscode-tailwindcss/src/extension.ts +++ b/packages/vscode-tailwindcss/src/extension.ts @@ -149,6 +149,58 @@ export async function activate(context: ExtensionContext) { }) ) + async function sortSelection(): Promise { + let { document, selections } = Window.activeTextEditor + if (selections.length === 0) { + return + } + let selectionsJson = JSON.stringify(selections) + let uri = document.uri + let folder = Workspace.getWorkspaceFolder(uri) + if (clients.size === 0 || !folder || isExcluded(uri.fsPath, folder)) { + throw Error(`No active Tailwind project found for file ${document.uri.fsPath}`) + } + folder = getOuterMostWorkspaceFolder(folder) + let client = clients.get(folder.uri.toString()) + if (!client) { + throw Error(`No active Tailwind project found for file ${document.uri.fsPath}`) + } + let ranges = selections.map((selection) => new Range(selection.start, selection.end)) + let { error, result } = await client.sendRequest('@/tailwindCSS/sortSelection', { + uri: uri.toString(), + classLists: ranges.map((range) => document.getText(range)), + }) + if ( + Window.activeTextEditor.document.uri.toString() !== uri.toString() || + JSON.stringify(Window.activeTextEditor.selections) !== selectionsJson + ) { + return + } + if (error) { + throw Error( + { + 'no-project': `No active Tailwind project found for file ${document.uri.fsPath}`, + unknown: 'An unknown error occurred.', + }[error] + ) + } + Window.activeTextEditor.edit((builder) => { + for (let i = 0; i < ranges.length; i++) { + builder.replace(ranges[i], result[i]) + } + }) + } + + context.subscriptions.push( + commands.registerCommand('tailwindCSS.sortSelection', async () => { + try { + await sortSelection() + } catch (error) { + Window.showWarningMessage(`Couldn’t sort Tailwind classes: ${error.message}`) + } + }) + ) + let watcher = Workspace.createFileSystemWatcher(`**/${CONFIG_FILE_GLOB}`, false, true, true) watcher.onDidCreate((uri) => { From f23b3b18663aada99a7dd68f88d16baa87f3bcc9 Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Fri, 21 Oct 2022 14:41:52 +0100 Subject: [PATCH 02/10] wip --- packages/vscode-tailwindcss/src/extension.ts | 28 +++++++++++--------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/vscode-tailwindcss/src/extension.ts b/packages/vscode-tailwindcss/src/extension.ts index d4af6dbd..d574390b 100755 --- a/packages/vscode-tailwindcss/src/extension.ts +++ b/packages/vscode-tailwindcss/src/extension.ts @@ -156,29 +156,33 @@ export async function activate(context: ExtensionContext) { throw Error(`No active Tailwind project found for file ${document.uri.fsPath}`) } let ranges = selections.map((selection) => new Range(selection.start, selection.end)) - let { error, result } = await client.sendRequest('@/tailwindCSS/sortSelection', { - uri: uri.toString(), - classLists: ranges.map((range) => document.getText(range)), - }) + let result = await client.sendRequest<{ error: string } | { classLists: string[] }>( + '@/tailwindCSS/sortSelection', + { + uri: uri.toString(), + classLists: ranges.map((range) => document.getText(range)), + } + ) if ( Window.activeTextEditor.document.uri.toString() !== uri.toString() || JSON.stringify(Window.activeTextEditor.selections) !== selectionsJson ) { return } - if (error) { + if ('error' in result) { throw Error( { 'no-project': `No active Tailwind project found for file ${document.uri.fsPath}`, - unknown: 'An unknown error occurred.', - }[error] + }[result.error] ?? 'An unknown error occurred.' ) + } else { + let sortedClassLists = result.classLists + Window.activeTextEditor.edit((builder) => { + for (let i = 0; i < ranges.length; i++) { + builder.replace(ranges[i], sortedClassLists[i]) + } + }) } - Window.activeTextEditor.edit((builder) => { - for (let i = 0; i < ranges.length; i++) { - builder.replace(ranges[i], result[i]) - } - }) } context.subscriptions.push( From 4958f275cfde353dc105b2f8fbbb27c06a887dfb Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Fri, 21 Oct 2022 14:46:09 +0100 Subject: [PATCH 03/10] wip --- packages/tailwindcss-language-server/src/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tailwindcss-language-server/src/server.ts b/packages/tailwindcss-language-server/src/server.ts index 3e33ffc5..396a159b 100644 --- a/packages/tailwindcss-language-server/src/server.ts +++ b/packages/tailwindcss-language-server/src/server.ts @@ -2128,7 +2128,7 @@ class TW { return { error: 'no-project' } } try { - return { result: project.sortClassLists(params.classLists) } + return { classLists: project.sortClassLists(params.classLists) } } catch { return { error: 'unknown' } } From 93ad5e8b5bebc87d195afee414baff6be7dcc5b9 Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Fri, 21 Oct 2022 14:56:28 +0100 Subject: [PATCH 04/10] wip --- packages/vscode-tailwindcss/src/extension.ts | 27 +++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/vscode-tailwindcss/src/extension.ts b/packages/vscode-tailwindcss/src/extension.ts index d574390b..d8a9085e 100755 --- a/packages/vscode-tailwindcss/src/extension.ts +++ b/packages/vscode-tailwindcss/src/extension.ts @@ -27,6 +27,7 @@ import { SnippetString, TextEdit, TextEditorSelectionChangeKind, + Selection, } from 'vscode' import { LanguageClient, @@ -121,6 +122,21 @@ async function fileContainsAtConfig(uri: Uri) { return /@config\s*['"]/.test(contents) } +function selectionsAreEqual( + aSelections: readonly Selection[], + bSelections: readonly Selection[] +): boolean { + if (aSelections.length !== bSelections.length) { + return false + } + for (let i = 0; i < aSelections.length; i++) { + if (!aSelections[i].isEqual(bSelections[i])) { + return false + } + } + return true +} + export async function activate(context: ExtensionContext) { let module = context.asAbsolutePath(path.join('dist', 'server.js')) let prod = path.join('dist', 'tailwindServer.js') @@ -145,7 +161,7 @@ export async function activate(context: ExtensionContext) { if (selections.length === 0) { return } - let selectionsJson = JSON.stringify(selections) + let initialSelections = selections let uri = document.uri let folder = Workspace.getWorkspaceFolder(uri) if (clients.size === 0 || !folder || isExcluded(uri.fsPath, folder)) { @@ -155,17 +171,16 @@ export async function activate(context: ExtensionContext) { if (!client) { throw Error(`No active Tailwind project found for file ${document.uri.fsPath}`) } - let ranges = selections.map((selection) => new Range(selection.start, selection.end)) let result = await client.sendRequest<{ error: string } | { classLists: string[] }>( '@/tailwindCSS/sortSelection', { uri: uri.toString(), - classLists: ranges.map((range) => document.getText(range)), + classLists: selections.map((selection) => document.getText(selection)), } ) if ( Window.activeTextEditor.document.uri.toString() !== uri.toString() || - JSON.stringify(Window.activeTextEditor.selections) !== selectionsJson + !selectionsAreEqual(initialSelections, Window.activeTextEditor.selections) ) { return } @@ -178,8 +193,8 @@ export async function activate(context: ExtensionContext) { } else { let sortedClassLists = result.classLists Window.activeTextEditor.edit((builder) => { - for (let i = 0; i < ranges.length; i++) { - builder.replace(ranges[i], sortedClassLists[i]) + for (let i = 0; i < selections.length; i++) { + builder.replace(selections[i], sortedClassLists[i]) } }) } From 417288b7732f44b3775a488fa73ef1eb39196d17 Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Fri, 21 Oct 2022 14:57:17 +0100 Subject: [PATCH 05/10] wip --- packages/vscode-tailwindcss/src/extension.ts | 21 +++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/vscode-tailwindcss/src/extension.ts b/packages/vscode-tailwindcss/src/extension.ts index d8a9085e..2cf77de1 100755 --- a/packages/vscode-tailwindcss/src/extension.ts +++ b/packages/vscode-tailwindcss/src/extension.ts @@ -158,19 +158,24 @@ export async function activate(context: ExtensionContext) { async function sortSelection(): Promise { let { document, selections } = Window.activeTextEditor + if (selections.length === 0) { return } + let initialSelections = selections let uri = document.uri let folder = Workspace.getWorkspaceFolder(uri) + if (clients.size === 0 || !folder || isExcluded(uri.fsPath, folder)) { throw Error(`No active Tailwind project found for file ${document.uri.fsPath}`) } + let client = clients.get(folder.uri.toString()) if (!client) { throw Error(`No active Tailwind project found for file ${document.uri.fsPath}`) } + let result = await client.sendRequest<{ error: string } | { classLists: string[] }>( '@/tailwindCSS/sortSelection', { @@ -178,26 +183,28 @@ export async function activate(context: ExtensionContext) { classLists: selections.map((selection) => document.getText(selection)), } ) + if ( Window.activeTextEditor.document.uri.toString() !== uri.toString() || !selectionsAreEqual(initialSelections, Window.activeTextEditor.selections) ) { return } + if ('error' in result) { throw Error( { 'no-project': `No active Tailwind project found for file ${document.uri.fsPath}`, }[result.error] ?? 'An unknown error occurred.' ) - } else { - let sortedClassLists = result.classLists - Window.activeTextEditor.edit((builder) => { - for (let i = 0; i < selections.length; i++) { - builder.replace(selections[i], sortedClassLists[i]) - } - }) } + + let sortedClassLists = result.classLists + Window.activeTextEditor.edit((builder) => { + for (let i = 0; i < selections.length; i++) { + builder.replace(selections[i], sortedClassLists[i]) + } + }) } context.subscriptions.push( From c9b68a167cc5ec4b97b03577adc24a299e8f5c4e Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Fri, 21 Oct 2022 14:59:12 +0100 Subject: [PATCH 06/10] wip --- packages/vscode-tailwindcss/src/extension.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/vscode-tailwindcss/src/extension.ts b/packages/vscode-tailwindcss/src/extension.ts index 2cf77de1..b963d3a7 100755 --- a/packages/vscode-tailwindcss/src/extension.ts +++ b/packages/vscode-tailwindcss/src/extension.ts @@ -164,10 +164,9 @@ export async function activate(context: ExtensionContext) { } let initialSelections = selections - let uri = document.uri - let folder = Workspace.getWorkspaceFolder(uri) + let folder = Workspace.getWorkspaceFolder(document.uri) - if (clients.size === 0 || !folder || isExcluded(uri.fsPath, folder)) { + if (clients.size === 0 || !folder || isExcluded(document.uri.fsPath, folder)) { throw Error(`No active Tailwind project found for file ${document.uri.fsPath}`) } @@ -179,13 +178,13 @@ export async function activate(context: ExtensionContext) { let result = await client.sendRequest<{ error: string } | { classLists: string[] }>( '@/tailwindCSS/sortSelection', { - uri: uri.toString(), + uri: document.uri.toString(), classLists: selections.map((selection) => document.getText(selection)), } ) if ( - Window.activeTextEditor.document.uri.toString() !== uri.toString() || + Window.activeTextEditor.document !== document || !selectionsAreEqual(initialSelections, Window.activeTextEditor.selections) ) { return From df3e3250c91579a62ce530719a6ff5568de6ad51 Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Fri, 21 Oct 2022 17:45:07 +0100 Subject: [PATCH 07/10] wip --- .../tailwindcss-language-server/src/server.ts | 51 +++++++++++---- packages/vscode-tailwindcss/README.md | 10 +++ packages/vscode-tailwindcss/package.json | 4 +- packages/vscode-tailwindcss/src/extension.ts | 64 +++++++++++++++++++ 4 files changed, 115 insertions(+), 14 deletions(-) diff --git a/packages/tailwindcss-language-server/src/server.ts b/packages/tailwindcss-language-server/src/server.ts index 396a159b..c902c13d 100644 --- a/packages/tailwindcss-language-server/src/server.ts +++ b/packages/tailwindcss-language-server/src/server.ts @@ -512,6 +512,7 @@ async function createProjectService( state.enabled = false refreshDiagnostics() updateCapabilities() + connection.sendNotification('@/tailwindCSS/projectReset') } async function tryInit() { @@ -520,6 +521,7 @@ async function createProjectService( } try { await init() + connection.sendNotification('@/tailwindCSS/projectInitialized') } catch (error) { resetState() showError(connection, error) @@ -1212,6 +1214,10 @@ async function createProjectService( ].map((value) => ({ label: `${prefix}-[${value}]` })) }, sortClassLists(classLists: string[]): string[] { + if (!state.jit) { + return classLists + } + return classLists.map((classList) => { let result = '' let parts = classList.split(/(\s+)/) @@ -2121,19 +2127,39 @@ class TW { this.connection.onColorPresentation(this.onColorPresentation.bind(this)) this.connection.onCodeAction(this.onCodeAction.bind(this)) this.connection.onDocumentLinks(this.onDocumentLinks.bind(this)) - this.connection.onRequest((method, params: { uri: string; classLists: string[] }) => { - if (method === '@/tailwindCSS/sortSelection') { - let project = this.getProject({ uri: params.uri }) - if (!project) { - return { error: 'no-project' } - } - try { - return { classLists: project.sortClassLists(params.classLists) } - } catch { - return { error: 'unknown' } - } + this.connection.onRequest(this.onRequest.bind(this)) + } + + private onRequest( + method: '@/tailwindCSS/sortSelection', + params: { uri: string; classLists: string[] } + ): { error: string } | { classLists: string[] } + private onRequest( + method: '@/tailwindCSS/getProject', + params: { uri: string } + ): { version: string } | null + private onRequest(method: string, params: any): any { + if (method === '@/tailwindCSS/sortSelection') { + let project = this.getProject({ uri: params.uri }) + if (!project) { + return { error: 'no-project' } } - }) + try { + return { classLists: project.sortClassLists(params.classLists) } + } catch { + return { error: 'unknown' } + } + } + + if (method === '@/tailwindCSS/getProject') { + let project = this.getProject({ uri: params.uri }) + if (!project || !project.enabled() || !project.state?.enabled) { + return null + } + return { + version: project.state.version, + } + } } private updateCapabilities() { @@ -2243,6 +2269,7 @@ class TW { } dispose(): void { + connection.sendNotification('@/tailwindCSS/projectsDestroyed') for (let [, project] of this.projects) { project.dispose() } diff --git a/packages/vscode-tailwindcss/README.md b/packages/vscode-tailwindcss/README.md index 8c758865..12d613fd 100644 --- a/packages/vscode-tailwindcss/README.md +++ b/packages/vscode-tailwindcss/README.md @@ -54,6 +54,16 @@ By default VS Code will not trigger completions when editing "string" content, f } ``` +## Extension Commands + +### `Tailwind CSS: Show Output` + +Reveal the language server log panel. This command is only available when there is an active language server instance. + +### `Tailwind CSS: Sort Classes in Selection` + +Treat the current selection(s) as a list of classes and sort them based on [our recommended class order](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted). This command is only available when the current document belongs to an active Tailwind project and the `tailwindcss` version is `3.0.0` or greater. + ## Extension Settings ### `tailwindCSS.includeLanguages` diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json index 04454756..d332f9c9 100755 --- a/packages/vscode-tailwindcss/package.json +++ b/packages/vscode-tailwindcss/package.json @@ -60,8 +60,8 @@ }, { "command": "tailwindCSS.sortSelection", - "title": "Tailwind CSS: Sort Selection", - "enablement": "editorHasSelection && resourceScheme == file" + "title": "Tailwind CSS: Sort Classes in Selection", + "enablement": "editorHasSelection && resourceScheme == file && tailwindCSS.activeTextEditorSupportsClassSorting" } ], "grammars": [ diff --git a/packages/vscode-tailwindcss/src/extension.ts b/packages/vscode-tailwindcss/src/extension.ts index b963d3a7..98a9dc3d 100755 --- a/packages/vscode-tailwindcss/src/extension.ts +++ b/packages/vscode-tailwindcss/src/extension.ts @@ -39,6 +39,7 @@ import { Disposable, } from 'vscode-languageclient/node' import { languages as defaultLanguages } from 'tailwindcss-language-service/src/util/languages' +import * as semver from 'tailwindcss-language-service/src/util/semver' import isObject from 'tailwindcss-language-service/src/util/isObject' import { dedupe, equal } from 'tailwindcss-language-service/src/util/array' import namedColors from 'color-name' @@ -137,6 +138,53 @@ function selectionsAreEqual( return true } +async function getActiveTextEditorProject(): Promise<{ version: string } | null> { + if (clients.size === 0) { + return null + } + let editor = Window.activeTextEditor + if (!editor) { + return null + } + let uri = editor.document.uri + let folder = Workspace.getWorkspaceFolder(uri) + if (!folder) { + return null + } + let client = clients.get(folder.uri.toString()) + if (!client) { + return null + } + try { + let project = await client.sendRequest<{ version: string } | null>('@/tailwindCSS/getProject', { + uri: uri.toString(), + }) + return project + } catch { + return null + } +} + +async function activeTextEditorSupportsClassSorting(): Promise { + let project = await getActiveTextEditorProject() + if (!project) { + return false + } + return semver.gte(project.version, '3.0.0') +} + +async function updateActiveTextEditorContext(): Promise { + commands.executeCommand( + 'setContext', + 'tailwindCSS.activeTextEditorSupportsClassSorting', + await activeTextEditorSupportsClassSorting() + ) +} + +function resetActiveTextEditorContext(): void { + commands.executeCommand('setContext', 'tailwindCSS.activeTextEditorSupportsClassSorting', false) +} + export async function activate(context: ExtensionContext) { let module = context.asAbsolutePath(path.join('dist', 'server.js')) let prod = path.join('dist', 'tailwindServer.js') @@ -216,6 +264,12 @@ export async function activate(context: ExtensionContext) { }) ) + context.subscriptions.push( + Window.onDidChangeActiveTextEditor(async () => { + await updateActiveTextEditorContext() + }) + ) + // context.subscriptions.push( // commands.registerCommand( // 'tailwindCSS.onInsertArbitraryVariantSnippet', @@ -694,6 +748,16 @@ export async function activate(context: ExtensionContext) { client.onNotification('@/tailwindCSS/clearColors', () => clearColors()) + client.onNotification('@/tailwindCSS/projectInitialized', async () => { + await updateActiveTextEditorContext() + }) + client.onNotification('@/tailwindCSS/projectReset', async () => { + await updateActiveTextEditorContext() + }) + client.onNotification('@/tailwindCSS/projectsDestroyed', () => { + resetActiveTextEditorContext() + }) + client.onRequest('@/tailwindCSS/getDocumentSymbols', async ({ uri }) => { return commands.executeCommand( 'vscode.executeDocumentSymbolProvider', From 8d94ef77c8511b91ad66c073bcca693d74ede19b Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Thu, 31 Aug 2023 10:58:15 +0100 Subject: [PATCH 08/10] Add test --- .../tests/commands/commands.test.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 packages/tailwindcss-language-server/tests/commands/commands.test.js diff --git a/packages/tailwindcss-language-server/tests/commands/commands.test.js b/packages/tailwindcss-language-server/tests/commands/commands.test.js new file mode 100644 index 00000000..a9683254 --- /dev/null +++ b/packages/tailwindcss-language-server/tests/commands/commands.test.js @@ -0,0 +1,14 @@ +import { test, expect } from 'vitest' +import { withFixture } from '../common' + +withFixture('basic', (c) => { + test.concurrent('sortSelection', async () => { + let textDocument = await c.openDocument({ text: '
' }) + let res = await c.sendRequest('@/tailwindCSS/sortSelection', { + uri: textDocument.uri, + classLists: ['sm:p-0 p-0'], + }) + + expect(res).toEqual({ classLists: ['p-0 sm:p-0'] }) + }) +}) From a5b5dc903ed19b640b4ad65d936b351e72199258 Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Thu, 31 Aug 2023 10:58:40 +0100 Subject: [PATCH 09/10] Update command name and description --- packages/vscode-tailwindcss/README.md | 4 ++-- packages/vscode-tailwindcss/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/vscode-tailwindcss/README.md b/packages/vscode-tailwindcss/README.md index 47d759e7..714b3808 100644 --- a/packages/vscode-tailwindcss/README.md +++ b/packages/vscode-tailwindcss/README.md @@ -60,9 +60,9 @@ By default VS Code will not trigger completions when editing "string" content, f Reveal the language server log panel. This command is only available when there is an active language server instance. -### `Tailwind CSS: Sort Classes in Selection` +### `Tailwind CSS: Sort Selection` (pre-release) -Treat the current selection(s) as a list of classes and sort them based on [our recommended class order](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted). This command is only available when the current document belongs to an active Tailwind project and the `tailwindcss` version is `3.0.0` or greater. +When a list of CSS classes is selected this command can be used to sort them in [the same order that Tailwind orders them in your CSS](https://tailwindcss.com/blog/automatic-class-sorting-with-prettier#how-classes-are-sorted). This command is only available when the current document belongs to an active Tailwind project and the `tailwindcss` version is `3.0.0` or greater. ## Extension Settings diff --git a/packages/vscode-tailwindcss/package.json b/packages/vscode-tailwindcss/package.json index 68f2bedd..cb00b41e 100755 --- a/packages/vscode-tailwindcss/package.json +++ b/packages/vscode-tailwindcss/package.json @@ -63,7 +63,7 @@ }, { "command": "tailwindCSS.sortSelection", - "title": "Tailwind CSS: Sort Classes in Selection", + "title": "Tailwind CSS: Sort Selection", "enablement": "editorHasSelection && resourceScheme == file && tailwindCSS.activeTextEditorSupportsClassSorting" } ], From 273876c9cd2e891746fe121fa5eac6784f40300d Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Thu, 31 Aug 2023 11:26:08 +0100 Subject: [PATCH 10/10] Don't show sort command if file is excluded --- packages/vscode-tailwindcss/src/extension.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/vscode-tailwindcss/src/extension.ts b/packages/vscode-tailwindcss/src/extension.ts index 85ffb1eb..9139983d 100755 --- a/packages/vscode-tailwindcss/src/extension.ts +++ b/packages/vscode-tailwindcss/src/extension.ts @@ -157,6 +157,9 @@ async function getActiveTextEditorProject(): Promise<{ version: string } | null> if (!client) { return null } + if (isExcluded(uri.fsPath, folder)) { + return null + } try { let project = await client.sendRequest<{ version: string } | null>('@/tailwindCSS/getProject', { uri: uri.toString(),