From 38d7a12f8d088e297470aa427db7f3d02317ea6f Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 9 May 2025 19:28:14 -0400 Subject: [PATCH 1/6] Rewrite test --- .../tests/env/multi-config-content.test.js | 97 ++++++++++++++----- 1 file changed, 72 insertions(+), 25 deletions(-) diff --git a/packages/tailwindcss-language-server/tests/env/multi-config-content.test.js b/packages/tailwindcss-language-server/tests/env/multi-config-content.test.js index 44b4cb64..f9e7da2f 100644 --- a/packages/tailwindcss-language-server/tests/env/multi-config-content.test.js +++ b/packages/tailwindcss-language-server/tests/env/multi-config-content.test.js @@ -1,38 +1,85 @@ -import { test } from 'vitest' -import { withFixture } from '../common' +import { expect } from 'vitest' +import { css, defineTest, html, js, json, symlinkTo } from '../../src/testing' +import dedent from 'dedent' +import { createClient } from '../utils/client' -withFixture('multi-config-content', (c) => { - test.concurrent('multi-config with content config - 1', async ({ expect }) => { - let textDocument = await c.openDocument({ text: '
', dir: 'one' }) - let res = await c.sendRequest('textDocument/hover', { - textDocument, - position: { line: 0, character: 13 }, +defineTest({ + name: 'multi-config with content config', + fs: { + 'tailwind.config.one.js': js` + module.exports = { + content: ['./one/**/*'], + theme: { + extend: { + colors: { + foo: 'red', + }, + }, + }, + } + `, + 'tailwind.config.two.js': js` + module.exports = { + content: ['./two/**/*'], + theme: { + extend: { + colors: { + foo: 'blue', + }, + }, + }, + } + `, + }, + prepare: async ({ root }) => ({ client: await createClient({ root }) }), + handle: async ({ client }) => { + let one = await client.open({ + lang: 'html', + name: 'one/index.html', + text: '
', }) - expect(res).toEqual({ + let two = await client.open({ + lang: 'html', + name: 'two/index.html', + text: '
', + }) + + //
+ // ^ + let hoverOne = await one.hover({ line: 0, character: 13 }) + let hoverTwo = await two.hover({ line: 0, character: 13 }) + + expect(hoverOne).toEqual({ contents: { language: 'css', - value: - '.bg-foo {\n --tw-bg-opacity: 1;\n background-color: rgb(255 0 0 / var(--tw-bg-opacity, 1)) /* #ff0000 */;\n}', + value: dedent` + .bg-foo { + --tw-bg-opacity: 1; + background-color: rgb(255 0 0 / var(--tw-bg-opacity, 1)) /* #ff0000 */; + } + `, + }, + range: { + start: { line: 0, character: 12 }, + end: { line: 0, character: 18 }, }, - range: { start: { line: 0, character: 12 }, end: { line: 0, character: 18 } }, - }) - }) - - test.concurrent('multi-config with content config - 2', async ({ expect }) => { - let textDocument = await c.openDocument({ text: '
', dir: 'two' }) - let res = await c.sendRequest('textDocument/hover', { - textDocument, - position: { line: 0, character: 13 }, }) - expect(res).toEqual({ + expect(hoverTwo).toEqual({ contents: { language: 'css', - value: - '.bg-foo {\n --tw-bg-opacity: 1;\n background-color: rgb(0 0 255 / var(--tw-bg-opacity, 1)) /* #0000ff */;\n}', + value: dedent` + .bg-foo { + --tw-bg-opacity: 1; + background-color: rgb(0 0 255 / var(--tw-bg-opacity, 1)) /* #0000ff */; + } + `, + }, + range: { + start: { line: 0, character: 12 }, + end: { line: 0, character: 18 }, }, - range: { start: { line: 0, character: 12 }, end: { line: 0, character: 18 } }, }) - }) + }, }) From 9313d47ca7148a1daa43133e0fd6346a172d1e1e Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 14 May 2025 10:39:12 -0400 Subject: [PATCH 2/6] Refactor Compute correct document selectors when a project is initialized Refactor --- .../src/project-locator.ts | 120 ++++++++++-------- .../src/projects.ts | 4 +- 2 files changed, 67 insertions(+), 57 deletions(-) diff --git a/packages/tailwindcss-language-server/src/project-locator.ts b/packages/tailwindcss-language-server/src/project-locator.ts index 4e3c0453..b31f29f8 100644 --- a/packages/tailwindcss-language-server/src/project-locator.ts +++ b/packages/tailwindcss-language-server/src/project-locator.ts @@ -206,62 +206,7 @@ export class ProjectLocator { // Look for the package root for the config config.packageRoot = await getPackageRoot(path.dirname(config.path), this.base) - let selectors: DocumentSelector[] = [] - - // selectors: - // - CSS files - for (let entry of config.entries) { - if (entry.type !== 'css') continue - selectors.push({ - pattern: entry.path, - priority: DocumentSelectorPriority.CSS_FILE, - }) - } - - // - Config File - selectors.push({ - pattern: config.path, - priority: DocumentSelectorPriority.CONFIG_FILE, - }) - - // - Content patterns from config - for await (let selector of contentSelectorsFromConfig( - config, - tailwind.features, - this.resolver, - )) { - selectors.push(selector) - } - - // - Directories containing the CSS files - for (let entry of config.entries) { - if (entry.type !== 'css') continue - selectors.push({ - pattern: normalizePath(path.join(path.dirname(entry.path), '**')), - priority: DocumentSelectorPriority.CSS_DIRECTORY, - }) - } - - // - Directory containing the config - selectors.push({ - pattern: normalizePath(path.join(path.dirname(config.path), '**')), - priority: DocumentSelectorPriority.CONFIG_DIRECTORY, - }) - - // - Root of package that contains the config - selectors.push({ - pattern: normalizePath(path.join(config.packageRoot, '**')), - priority: DocumentSelectorPriority.PACKAGE_DIRECTORY, - }) - - // Reorder selectors from most specific to least specific - selectors.sort((a, z) => a.priority - z.priority) - - // Eliminate duplicate selector patterns - selectors = selectors.filter( - ({ pattern }, index, documentSelectors) => - documentSelectors.findIndex(({ pattern: p }) => p === pattern) === index, - ) + let selectors = await calculateDocumentSelectors(config, tailwind.features, this.resolver) return { config, @@ -793,3 +738,66 @@ function requiresPreprocessor(filepath: string) { return ext === '.scss' || ext === '.sass' || ext === '.less' || ext === '.styl' || ext === '.pcss' } + +export async function calculateDocumentSelectors( + config: ConfigEntry, + features: Feature[], + resolver: Resolver, +) { + let selectors: DocumentSelector[] = [] + + // selectors: + // - CSS files + for (let entry of config.entries) { + if (entry.type !== 'css') continue + + selectors.push({ + pattern: entry.path, + priority: DocumentSelectorPriority.CSS_FILE, + }) + } + + // - Config File + selectors.push({ + pattern: config.path, + priority: DocumentSelectorPriority.CONFIG_FILE, + }) + + // - Content patterns from config + for await (let selector of contentSelectorsFromConfig(config, features, resolver)) { + selectors.push(selector) + } + + // - Directories containing the CSS files + for (let entry of config.entries) { + if (entry.type !== 'css') continue + + selectors.push({ + pattern: normalizePath(path.join(path.dirname(entry.path), '**')), + priority: DocumentSelectorPriority.CSS_DIRECTORY, + }) + } + + // - Directory containing the config + selectors.push({ + pattern: normalizePath(path.join(path.dirname(config.path), '**')), + priority: DocumentSelectorPriority.CONFIG_DIRECTORY, + }) + + // - Root of package that contains the config + selectors.push({ + pattern: normalizePath(path.join(config.packageRoot, '**')), + priority: DocumentSelectorPriority.PACKAGE_DIRECTORY, + }) + + // Reorder selectors from most specific to least specific + selectors.sort((a, z) => a.priority - z.priority) + + // Eliminate duplicate selector patterns + selectors = selectors.filter( + ({ pattern }, index, documentSelectors) => + documentSelectors.findIndex(({ pattern: p }) => p === pattern) === index, + ) + + return selectors +} diff --git a/packages/tailwindcss-language-server/src/projects.ts b/packages/tailwindcss-language-server/src/projects.ts index 21e7bb3b..d1dff9ac 100644 --- a/packages/tailwindcss-language-server/src/projects.ts +++ b/packages/tailwindcss-language-server/src/projects.ts @@ -286,7 +286,9 @@ export async function createProjectService( ) } - function onFileEvents(changes: Array<{ file: string; type: FileChangeType }>): void { + async function onFileEvents( + changes: Array<{ file: string; type: FileChangeType }>, + ): Promise { let needsInit = false let needsRebuild = false From b4cf6a7879faca599356aa9b766fdcf4e0300e9e Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 14 May 2025 10:39:26 -0400 Subject: [PATCH 3/6] Allow passing resolved config to `calculateDocumentSelectors` --- packages/tailwindcss-language-server/src/project-locator.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/tailwindcss-language-server/src/project-locator.ts b/packages/tailwindcss-language-server/src/project-locator.ts index b31f29f8..1a32dee0 100644 --- a/packages/tailwindcss-language-server/src/project-locator.ts +++ b/packages/tailwindcss-language-server/src/project-locator.ts @@ -490,13 +490,14 @@ function contentSelectorsFromConfig( entry: ConfigEntry, features: Feature[], resolver: Resolver, + actualConfig?: any, ): AsyncIterable { if (entry.type === 'css') { return contentSelectorsFromCssConfig(entry, resolver) } if (entry.type === 'js') { - return contentSelectorsFromJsConfig(entry, features) + return contentSelectorsFromJsConfig(entry, features, actualConfig) } } @@ -743,6 +744,7 @@ export async function calculateDocumentSelectors( config: ConfigEntry, features: Feature[], resolver: Resolver, + actualConfig?: any, ) { let selectors: DocumentSelector[] = [] @@ -764,7 +766,7 @@ export async function calculateDocumentSelectors( }) // - Content patterns from config - for await (let selector of contentSelectorsFromConfig(config, features, resolver)) { + for await (let selector of contentSelectorsFromConfig(config, features, resolver, actualConfig)) { selectors.push(selector) } From 57318296dae4f20e48369dbd141a707c6fba50c5 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Wed, 14 May 2025 10:39:34 -0400 Subject: [PATCH 4/6] Compute correct document selectors when a project is initialized --- .../src/projects.ts | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/packages/tailwindcss-language-server/src/projects.ts b/packages/tailwindcss-language-server/src/projects.ts index d1dff9ac..0c54942c 100644 --- a/packages/tailwindcss-language-server/src/projects.ts +++ b/packages/tailwindcss-language-server/src/projects.ts @@ -80,7 +80,7 @@ import { normalizeDriveLetter, } from './utils' import type { DocumentService } from './documents' -import type { ProjectConfig } from './project-locator' +import { calculateDocumentSelectors, type ProjectConfig } from './project-locator' import { supportedFeatures } from '@tailwindcss/language-service/src/features' import { loadDesignSystem } from './util/v4' import { readCssFile } from './util/css' @@ -309,16 +309,11 @@ export async function createProjectService( projectConfig.configPath && (isConfigFile || isDependency) ) { - documentSelector = [ - ...documentSelector.filter( - ({ priority }) => priority !== DocumentSelectorPriority.CONTENT_FILE, - ), - ...getContentDocumentSelectorFromConfigFile( - projectConfig.configPath, - initialTailwindVersion, - projectConfig.folder, - ), - ] + documentSelector = await calculateDocumentSelectors( + projectConfig.config, + state.features, + resolver, + ) checkOpenDocuments() } @@ -965,17 +960,12 @@ export async function createProjectService( ///////////////////// if (!projectConfig.isUserConfigured) { - documentSelector = [ - ...documentSelector.filter( - ({ priority }) => priority !== DocumentSelectorPriority.CONTENT_FILE, - ), - ...getContentDocumentSelectorFromConfigFile( - state.configPath, - tailwindcss.version, - projectConfig.folder, - originalConfig, - ), - ] + documentSelector = await calculateDocumentSelectors( + projectConfig.config, + state.features, + resolver, + originalConfig, + ) } ////////////////////// From fc0d986c0f8e87d8c71cc0fbd50fe35a422f7bde Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 9 May 2025 20:26:43 -0400 Subject: [PATCH 5/6] Ensure all document selectors have normalized drive letters --- .../src/project-locator.ts | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/packages/tailwindcss-language-server/src/project-locator.ts b/packages/tailwindcss-language-server/src/project-locator.ts index 1a32dee0..0f3751ed 100644 --- a/packages/tailwindcss-language-server/src/project-locator.ts +++ b/packages/tailwindcss-language-server/src/project-locator.ts @@ -532,11 +532,18 @@ async function* contentSelectorsFromJsConfig( if (typeof item !== 'string') continue let filepath = item.startsWith('!') - ? `!${path.resolve(contentBase, item.slice(1))}` + ? path.resolve(contentBase, item.slice(1)) : path.resolve(contentBase, item) + filepath = normalizePath(filepath) + filepath = normalizeDriveLetter(filepath) + + if (item.startsWith('!')) { + filepath = `!${filepath}` + } + yield { - pattern: normalizePath(filepath), + pattern: filepath, priority: DocumentSelectorPriority.CONTENT_FILE, } } @@ -549,8 +556,11 @@ async function* contentSelectorsFromCssConfig( let auto = false for (let item of entry.content) { if (item.kind === 'file') { + let filepath = item.file + filepath = normalizePath(filepath) + filepath = normalizeDriveLetter(filepath) yield { - pattern: normalizePath(item.file), + pattern: filepath, priority: DocumentSelectorPriority.CONTENT_FILE, } } else if (item.kind === 'auto' && !auto) { @@ -603,12 +613,16 @@ async function* detectContentFiles( if (!result) return for (let file of result.files) { - yield normalizePath(file) + file = normalizePath(file) + file = normalizeDriveLetter(file) + yield file } for (let { base, pattern } of result.globs) { // Do not normalize the glob itself as it may contain escape sequences - yield normalizePath(base) + '/' + pattern + base = normalizePath(base) + base = normalizeDriveLetter(base) + yield `${base}/${pattern}` } } catch { // @@ -754,14 +768,14 @@ export async function calculateDocumentSelectors( if (entry.type !== 'css') continue selectors.push({ - pattern: entry.path, + pattern: normalizeDriveLetter(normalizePath(entry.path)), priority: DocumentSelectorPriority.CSS_FILE, }) } // - Config File selectors.push({ - pattern: config.path, + pattern: normalizeDriveLetter(normalizePath(config.path)), priority: DocumentSelectorPriority.CONFIG_FILE, }) @@ -775,20 +789,20 @@ export async function calculateDocumentSelectors( if (entry.type !== 'css') continue selectors.push({ - pattern: normalizePath(path.join(path.dirname(entry.path), '**')), + pattern: normalizeDriveLetter(normalizePath(path.join(path.dirname(entry.path), '**'))), priority: DocumentSelectorPriority.CSS_DIRECTORY, }) } // - Directory containing the config selectors.push({ - pattern: normalizePath(path.join(path.dirname(config.path), '**')), + pattern: normalizeDriveLetter(normalizePath(path.join(path.dirname(config.path), '**'))), priority: DocumentSelectorPriority.CONFIG_DIRECTORY, }) // - Root of package that contains the config selectors.push({ - pattern: normalizePath(path.join(config.packageRoot, '**')), + pattern: normalizeDriveLetter(normalizePath(path.join(config.packageRoot, '**'))), priority: DocumentSelectorPriority.PACKAGE_DIRECTORY, }) From b886d49ccf7ec31ea094fbaea08c62df872adb26 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Fri, 9 May 2025 20:35:18 -0400 Subject: [PATCH 6/6] Update changelog --- packages/vscode-tailwindcss/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/vscode-tailwindcss/CHANGELOG.md b/packages/vscode-tailwindcss/CHANGELOG.md index 14775414..a80f579a 100644 --- a/packages/vscode-tailwindcss/CHANGELOG.md +++ b/packages/vscode-tailwindcss/CHANGELOG.md @@ -13,6 +13,8 @@ - Handle helper function lookups in nested parens ([#1354](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1354)) - Hide `@property` declarations from completion details ([#1356](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1356)) - Hide variant-provided declarations from completion details for a utility ([#1356](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1356)) +- Compute correct document selectors when a project is initialized ([#1335](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1335)) +- Fix matching of some content file paths on Windows ([#1335](https://github.com/tailwindlabs/tailwindcss-intellisense/pull/1335)) # 0.14.16