diff --git a/packages/tailwindcss-language-server/src/config.ts b/packages/tailwindcss-language-server/src/config.ts index 90fb3207..d8364d06 100644 --- a/packages/tailwindcss-language-server/src/config.ts +++ b/packages/tailwindcss-language-server/src/config.ts @@ -12,6 +12,7 @@ function getDefaultSettings(): Settings { return { editor: { tabSize: 2 }, tailwindCSS: { + inspectPort: null, emmetCompletions: false, classAttributes: ['class', 'className', 'ngClass', 'class:list'], codeActions: true, diff --git a/packages/tailwindcss-language-server/src/testing/index.ts b/packages/tailwindcss-language-server/src/testing/index.ts index 92976755..2435ca0f 100644 --- a/packages/tailwindcss-language-server/src/testing/index.ts +++ b/packages/tailwindcss-language-server/src/testing/index.ts @@ -1,4 +1,4 @@ -import { afterAll, onTestFinished, test, TestOptions } from 'vitest' +import { onTestFinished, test, TestOptions } from 'vitest' import * as fs from 'node:fs/promises' import * as path from 'node:path' import * as proc from 'node:child_process' @@ -16,7 +16,7 @@ export interface Storage { export interface TestConfig { name: string - fs: Storage + fs?: Storage prepare?(utils: TestUtils): Promise handle(utils: TestUtils & Extras): void | Promise @@ -43,8 +43,10 @@ async function setup(config: TestConfig): Promise { await fs.mkdir(baseDir, { recursive: true }) - await prepareFileSystem(baseDir, config.fs) - await installDependencies(baseDir, config.fs) + if (config.fs) { + await prepareFileSystem(baseDir, config.fs) + await installDependencies(baseDir, config.fs) + } onTestFinished(async (result) => { // Once done, move all the files to a new location diff --git a/packages/tailwindcss-language-server/src/tw.ts b/packages/tailwindcss-language-server/src/tw.ts index b795be5c..efb12a34 100644 --- a/packages/tailwindcss-language-server/src/tw.ts +++ b/packages/tailwindcss-language-server/src/tw.ts @@ -553,6 +553,7 @@ export class TW { configTailwindVersionMap.get(projectConfig.configPath), userLanguages, resolver, + baseUri, ), ), ) @@ -663,6 +664,11 @@ export class TW { }), ) } + + // TODO: This is a hack and shouldn't be necessary + if (isTestMode) { + await this.connection.sendNotification('@/tailwindCSS/serverReady') + } } private filterNewWatchPatterns(patterns: string[]) { @@ -684,6 +690,7 @@ export class TW { tailwindVersion: string, userLanguages: Record, resolver: Resolver, + baseUri: URI, ): Promise { let key = String(this.projectCounter++) const project = await createProjectService( @@ -717,6 +724,7 @@ export class TW { } this.connection.sendNotification('@/tailwindCSS/projectDetails', { + uri: baseUri.toString(), config: projectConfig.configPath, tailwind: projectConfig.tailwind, }) diff --git a/packages/tailwindcss-language-server/tests/common.ts b/packages/tailwindcss-language-server/tests/common.ts index 37339159..9b40b3d3 100644 --- a/packages/tailwindcss-language-server/tests/common.ts +++ b/packages/tailwindcss-language-server/tests/common.ts @@ -1,33 +1,21 @@ import * as path from 'node:path' import { beforeAll, describe } from 'vitest' -import { connect, launch } from './connection' -import { - CompletionRequest, - ConfigurationRequest, - DidChangeConfigurationNotification, - DidChangeTextDocumentNotification, - DidOpenTextDocumentNotification, - InitializeRequest, - InitializedNotification, - RegistrationRequest, - InitializeParams, - DidOpenTextDocumentParams, - MessageType, -} from 'vscode-languageserver-protocol' -import type { ClientCapabilities, ProtocolConnection } from 'vscode-languageclient' +import { DidChangeTextDocumentNotification } from 'vscode-languageserver' +import type { ProtocolConnection } from 'vscode-languageclient' import type { Feature } from '@tailwindcss/language-service/src/features' -import { clearLanguageBoundariesCache } from '@tailwindcss/language-service/src/util/getLanguageBoundaries' -import { CacheMap } from '../src/cache-map' +import { URI } from 'vscode-uri' +import { Client, createClient } from './utils/client' type Settings = any interface FixtureContext extends Pick { - client: ProtocolConnection + client: Client openDocument: (params: { text: string lang?: string dir?: string + name?: string | null settings?: Settings }) => Promise<{ uri: string; updateSettings: (settings: Settings) => Promise }> updateSettings: (settings: Settings) => Promise @@ -57,117 +45,22 @@ export interface InitOptions { * Extra initialization options to pass to the LSP */ options?: Record + + /** + * Settings to provide the server immediately when it starts + */ + settings?: Settings } export async function init( fixture: string | string[], opts: InitOptions = {}, ): Promise { - let settings = {} - let docSettings = new Map() - - const { client } = opts?.mode === 'spawn' ? await launch() : await connect() - - if (opts?.mode === 'spawn') { - client.onNotification('window/logMessage', ({ message, type }) => { - if (type === MessageType.Error) { - console.error(message) - } else if (type === MessageType.Warning) { - console.warn(message) - } else if (type === MessageType.Info) { - console.info(message) - } else if (type === MessageType.Log) { - console.log(message) - } else if (type === MessageType.Debug) { - console.debug(message) - } - }) - } - - const capabilities: ClientCapabilities = { - textDocument: { - codeAction: { dynamicRegistration: true }, - codeLens: { dynamicRegistration: true }, - colorProvider: { dynamicRegistration: true }, - completion: { - completionItem: { - commitCharactersSupport: true, - documentationFormat: ['markdown', 'plaintext'], - snippetSupport: true, - }, - completionItemKind: { - valueSet: [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, - ], - }, - contextSupport: true, - dynamicRegistration: true, - }, - definition: { dynamicRegistration: true }, - documentHighlight: { dynamicRegistration: true }, - documentLink: { dynamicRegistration: true }, - documentSymbol: { - dynamicRegistration: true, - symbolKind: { - valueSet: [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, - ], - }, - }, - formatting: { dynamicRegistration: true }, - hover: { - contentFormat: ['markdown', 'plaintext'], - dynamicRegistration: true, - }, - implementation: { dynamicRegistration: true }, - onTypeFormatting: { dynamicRegistration: true }, - publishDiagnostics: { relatedInformation: true }, - rangeFormatting: { dynamicRegistration: true }, - references: { dynamicRegistration: true }, - rename: { dynamicRegistration: true }, - signatureHelp: { - dynamicRegistration: true, - signatureInformation: { documentationFormat: ['markdown', 'plaintext'] }, - }, - synchronization: { - didSave: true, - dynamicRegistration: true, - willSave: true, - willSaveWaitUntil: true, - }, - typeDefinition: { dynamicRegistration: true }, - }, - workspace: { - applyEdit: true, - configuration: true, - didChangeConfiguration: { dynamicRegistration: true }, - didChangeWatchedFiles: { dynamicRegistration: true }, - executeCommand: { dynamicRegistration: true }, - symbol: { - dynamicRegistration: true, - symbolKind: { - valueSet: [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, - ], - }, - }, - workspaceEdit: { documentChanges: true }, - workspaceFolders: true, - }, - experimental: { - tailwind: { - projectDetails: true, - }, - }, - } - - const fixtures = Array.isArray(fixture) ? fixture : [fixture] + let workspaces: Record = {} + let fixtures = Array.isArray(fixture) ? fixture : [fixture] - function fixtureUri(fixture: string) { - return `file://${path.resolve('./tests/fixtures', fixture)}` + function fixturePath(fixture: string) { + return path.resolve('./tests/fixtures', fixture) } function resolveUri(...parts: string[]) { @@ -176,86 +69,44 @@ export async function init( ? path.resolve('./tests/fixtures', ...parts) : path.resolve('./tests/fixtures', fixtures[0], ...parts) - return `file://${filepath}` + return URI.file(filepath).toString() } - const workspaceFolders = fixtures.map((fixture) => ({ - name: `Fixture ${fixture}`, - uri: fixtureUri(fixture), - })) - - const rootUri = fixtures.length > 1 ? null : workspaceFolders[0].uri - - await client.sendRequest(InitializeRequest.type, { - processId: -1, - rootUri, - capabilities, - trace: 'off', - workspaceFolders, - initializationOptions: { - testMode: true, - ...(opts.options ?? {}), - }, - } as InitializeParams) - - await client.sendNotification(InitializedNotification.type) - - client.onRequest(ConfigurationRequest.type, (params) => { - return params.items.map((item) => { - if (docSettings.has(item.scopeUri!)) { - return docSettings.get(item.scopeUri!)[item.section!] ?? {} - } - return settings[item.section!] ?? {} - }) - }) - - let initPromise = new Promise((resolve) => { - client.onRequest(RegistrationRequest.type, ({ registrations }) => { - if (registrations.some((r) => r.method === CompletionRequest.method)) { - resolve() - } + for (let [idx, fixture] of fixtures.entries()) { + workspaces[`Fixture ${idx}`] = fixturePath(fixture) + } - return null - }) + let client = await createClient({ + server: 'tailwindcss', + mode: opts.mode, + options: opts.options, + root: workspaces, + settings: opts.settings, }) - interface PromiseWithResolvers extends Promise { - resolve: (value?: T | PromiseLike) => void - reject: (reason?: any) => void - } - - let openingDocuments = new CacheMap>() + let counter = 0 let projectDetails: any = null - client.onNotification('@/tailwindCSS/projectDetails', (params) => { - console.log('[TEST] Project detailed changed') - projectDetails = params + client.project().then((project) => { + projectDetails = project }) - client.onNotification('@/tailwindCSS/documentReady', (params) => { - console.log('[TEST] Document ready', params.uri) - openingDocuments.get(params.uri)?.resolve() - }) - - // This is a global cache that must be reset between tests for accurate results - clearLanguageBoundariesCache() - - let counter = 0 - return { client, - fixtureUri, + fixtureUri(fixture: string) { + return URI.file(fixturePath(fixture)).toString() + }, get project() { return projectDetails }, sendRequest(type: any, params: any) { - return client.sendRequest(type, params) + return client.conn.sendRequest(type, params) }, sendNotification(type: any, params?: any) { - return client.sendNotification(type, params) + return client.conn.sendNotification(type, params) }, onNotification(type: any, callback: any) { - return client.onNotification(type, callback) + return client.conn.onNotification(type, callback) }, async openDocument({ text, @@ -267,59 +118,35 @@ export async function init( text: string lang?: string dir?: string - name?: string + name?: string | null settings?: Settings }) { let uri = resolveUri(dir, name ?? `file-${counter++}`) - docSettings.set(uri, settings) - let openPromise = openingDocuments.remember(uri, () => { - let resolve = () => {} - let reject = () => {} - - let p = new Promise((_resolve, _reject) => { - resolve = _resolve - reject = _reject - }) - - return Object.assign(p, { - resolve, - reject, - }) + let doc = await client.open({ + lang, + text, + uri, + settings, }) - await client.sendNotification(DidOpenTextDocumentNotification.type, { - textDocument: { - uri, - languageId: lang, - version: 1, - text, - }, - } as DidOpenTextDocumentParams) - - // If opening a document stalls then it's probably because this promise is not being resolved - // This can happen if a document is not covered by one of the selectors because of it's URI - await initPromise - await openPromise - return { - uri, + get uri() { + return doc.uri.toString() + }, async updateSettings(settings: Settings) { - docSettings.set(uri, settings) - await client.sendNotification(DidChangeConfigurationNotification.type) + await doc.update({ settings }) }, } }, async updateSettings(newSettings: Settings) { - settings = newSettings - await client.sendNotification(DidChangeConfigurationNotification.type) + await client.updateSettings(newSettings) }, async updateFile(file: string, text: string) { let uri = resolveUri(file) - - await client.sendNotification(DidChangeTextDocumentNotification.type, { + await client.conn.sendNotification(DidChangeTextDocumentNotification.type, { textDocument: { uri, version: counter++ }, contentChanges: [{ text }], }) @@ -337,7 +164,7 @@ export function withFixture(fixture: string, callback: (c: FixtureContext) => vo // to the connection object without having to resort to using a Proxy Object.setPrototypeOf(c, await init(fixture)) - return () => c.client.dispose() + return () => c.client.conn.dispose() }) callback(c) @@ -360,7 +187,7 @@ export function withWorkspace({ // to the connection object without having to resort to using a Proxy Object.setPrototypeOf(c, await init(fixtures)) - return () => c.client.dispose() + return () => c.client.conn.dispose() }) run(c) diff --git a/packages/tailwindcss-language-server/tests/connection.ts b/packages/tailwindcss-language-server/tests/connection.ts deleted file mode 100644 index 21e56766..00000000 --- a/packages/tailwindcss-language-server/tests/connection.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { fork } from 'node:child_process' -import { createConnection } from 'vscode-languageserver/node' -import type { ProtocolConnection } from 'vscode-languageclient/node' - -import { Duplex } from 'node:stream' -import { TW } from '../src/tw' - -class TestStream extends Duplex { - _write(chunk: string, _encoding: string, done: () => void) { - this.emit('data', chunk) - done() - } - - _read(_size: number) {} -} - -export async function connect() { - let input = new TestStream() - let output = new TestStream() - - let server = createConnection(input, output) - let tw = new TW(server) - tw.setup() - tw.listen() - - let client = createConnection(output, input) as unknown as ProtocolConnection - client.listen() - - return { - client, - } -} - -export async function launch() { - let child = fork('./bin/tailwindcss-language-server', { silent: true }) - - let client = createConnection(child.stdout!, child.stdin!) as unknown as ProtocolConnection - - client.listen() - - return { - client, - } -} diff --git a/packages/tailwindcss-language-server/tests/env/custom-languages.test.js b/packages/tailwindcss-language-server/tests/env/custom-languages.test.js index 75663051..b5e60a59 100644 --- a/packages/tailwindcss-language-server/tests/env/custom-languages.test.js +++ b/packages/tailwindcss-language-server/tests/env/custom-languages.test.js @@ -1,24 +1,20 @@ +// @ts-check import { test } from 'vitest' import { init } from '../common' -import { CompletionRequest, HoverRequest } from 'vscode-languageserver' test('Unknown languages do not provide completions', async ({ expect }) => { - let c = await init('basic') + let { client } = await init('basic') - let textDocument = await c.openDocument({ + let doc = await client.open({ lang: 'some-lang', text: '
', }) - let hover = await c.sendRequest(HoverRequest.type, { - textDocument, - position: { line: 0, character: 13 }, - }) + let hover = await doc.hover({ line: 0, character: 13 }) expect(hover).toEqual(null) - let completion = await c.sendRequest(CompletionRequest.type, { - textDocument, + let completion = await doc.completions({ position: { line: 0, character: 13 }, context: { triggerKind: 1 }, }) @@ -27,7 +23,7 @@ test('Unknown languages do not provide completions', async ({ expect }) => { }) test('Custom languages may be specified via init options (deprecated)', async ({ expect }) => { - let c = await init('basic', { + let { client } = await init('basic', { options: { userLanguages: { 'some-lang': 'html', @@ -35,15 +31,12 @@ test('Custom languages may be specified via init options (deprecated)', async ({ }, }) - let textDocument = await c.openDocument({ + let doc = await client.open({ lang: 'some-lang', text: '
', }) - let hover = await c.sendRequest(HoverRequest.type, { - textDocument, - position: { line: 0, character: 13 }, - }) + let hover = await doc.hover({ line: 0, character: 13 }) expect(hover).toEqual({ contents: { @@ -54,35 +47,31 @@ test('Custom languages may be specified via init options (deprecated)', async ({ range: { start: { line: 0, character: 12 }, end: { line: 0, character: 21 } }, }) - let completion = await c.sendRequest(CompletionRequest.type, { - textDocument, + let completion = await doc.completions({ position: { line: 0, character: 13 }, context: { triggerKind: 1 }, }) - expect(completion.items.length).toBe(11509) + expect(completion?.items.length).toBe(11509) }) test('Custom languages may be specified via settings', async ({ expect }) => { - let c = await init('basic') - - await c.updateSettings({ - tailwindCSS: { - includeLanguages: { - 'some-lang': 'html', + let { client } = await init('basic', { + settings: { + tailwindCSS: { + includeLanguages: { + 'some-lang': 'html', + }, }, }, }) - let textDocument = await c.openDocument({ + let doc = await client.open({ lang: 'some-lang', text: '
', }) - let hover = await c.sendRequest(HoverRequest.type, { - textDocument, - position: { line: 0, character: 13 }, - }) + let hover = await doc.hover({ line: 0, character: 13 }) expect(hover).toEqual({ contents: { @@ -93,60 +82,51 @@ test('Custom languages may be specified via settings', async ({ expect }) => { range: { start: { line: 0, character: 12 }, end: { line: 0, character: 21 } }, }) - let completion = await c.sendRequest(CompletionRequest.type, { - textDocument, + let completion = await doc.completions({ position: { line: 0, character: 13 }, context: { triggerKind: 1 }, }) - expect(completion.items.length).toBe(11509) + expect(completion?.items.length).toBe(11509) }) test('Custom languages are merged from init options and settings', async ({ expect }) => { - let c = await init('basic', { + let { client } = await init('basic', { options: { userLanguages: { 'some-lang': 'html', }, }, - }) - await c.updateSettings({ - tailwindCSS: { - includeLanguages: { - 'other-lang': 'html', + settings: { + tailwindCSS: { + includeLanguages: { + 'other-lang': 'html', + }, }, }, }) - let textDocument = await c.openDocument({ + let doc = await client.open({ lang: 'some-lang', text: '
', }) - let hover = await c.sendRequest(HoverRequest.type, { - textDocument, - position: { line: 0, character: 13 }, - }) + let hover = await doc.hover({ line: 0, character: 13 }) - let completion = await c.sendRequest(CompletionRequest.type, { - textDocument, + let completion = await doc.completions({ position: { line: 0, character: 13 }, context: { triggerKind: 1 }, }) - textDocument = await c.openDocument({ + let doc2 = await client.open({ lang: 'other-lang', text: '
', }) - let hover2 = await c.sendRequest(HoverRequest.type, { - textDocument, - position: { line: 0, character: 13 }, - }) + let hover2 = await doc2.hover({ line: 0, character: 13 }) - let completion2 = await c.sendRequest(CompletionRequest.type, { - textDocument, + let completion2 = await doc2.completions({ position: { line: 0, character: 13 }, context: { triggerKind: 1 }, }) @@ -169,36 +149,33 @@ test('Custom languages are merged from init options and settings', async ({ expe range: { start: { line: 0, character: 12 }, end: { line: 0, character: 21 } }, }) - expect(completion.items.length).toBe(11509) - expect(completion2.items.length).toBe(11509) + expect(completion?.items.length).toBe(11509) + expect(completion2?.items.length).toBe(11509) }) test('Language mappings from settings take precedence', async ({ expect }) => { - let c = await init('basic', { + let { client } = await init('basic', { options: { userLanguages: { 'some-lang': 'css', }, }, - }) - await c.updateSettings({ - tailwindCSS: { - includeLanguages: { - 'some-lang': 'html', + settings: { + tailwindCSS: { + includeLanguages: { + 'some-lang': 'html', + }, }, }, }) - let textDocument = await c.openDocument({ + let doc = await client.open({ lang: 'some-lang', text: '
', }) - let hover = await c.sendRequest(HoverRequest.type, { - textDocument, - position: { line: 0, character: 13 }, - }) + let hover = await doc.hover({ line: 0, character: 13 }) expect(hover).toEqual({ contents: { @@ -209,11 +186,10 @@ test('Language mappings from settings take precedence', async ({ expect }) => { range: { start: { line: 0, character: 12 }, end: { line: 0, character: 21 } }, }) - let completion = await c.sendRequest(CompletionRequest.type, { - textDocument, + let completion = await doc.completions({ position: { line: 0, character: 13 }, context: { triggerKind: 1 }, }) - expect(completion.items.length).toBe(11509) + expect(completion?.items.length).toBe(11509) }) diff --git a/packages/tailwindcss-language-server/tests/env/v4.test.js b/packages/tailwindcss-language-server/tests/env/v4.test.js index 310c9a34..1ae5caf4 100644 --- a/packages/tailwindcss-language-server/tests/env/v4.test.js +++ b/packages/tailwindcss-language-server/tests/env/v4.test.js @@ -1,9 +1,9 @@ +// @ts-check + import { expect } from 'vitest' -import { init } from '../common' -import { HoverRequest } from 'vscode-languageserver' import { css, defineTest, html, js, json } from '../../src/testing' import dedent from 'dedent' -import { CompletionRequest } from 'vscode-languageserver-protocol' +import { createClient } from '../utils/client' defineTest({ name: 'v4, no npm, uses fallback', @@ -12,36 +12,27 @@ defineTest({ @import 'tailwindcss'; `, }, - prepare: async ({ root }) => ({ c: await init(root) }), - handle: async ({ c }) => { - let textDocument = await c.openDocument({ + prepare: async ({ root }) => ({ client: await createClient({ root }) }), + handle: async ({ client }) => { + let doc = await client.open({ lang: 'html', text: '
', }) - expect(c.project).toMatchObject({ + expect(await client.project()).toMatchObject({ tailwind: { version: '4.0.6', isDefaultVersion: true, }, }) - let hover = await c.sendRequest(HoverRequest.type, { - textDocument, - - //
({ - c: await init(root, { mode: 'spawn' }), - }), + prepare: async ({ root }) => ({ client: await createClient({ root, mode: 'spawn' }) }), - handle: async ({ c }) => { - let textDocument = await c.openDocument({ + handle: async ({ client }) => { + let doc = await client.open({ lang: 'html', text: '
', }) - expect(c.project).toMatchObject({ + expect(await client.project()).toMatchObject({ tailwind: { version: '4.0.6', isDefaultVersion: true, }, }) - let hover = await c.sendRequest(HoverRequest.type, { - textDocument, - - //
- // ^ - position: { line: 0, character: 13 }, - }) + //
+ // ^ + let hover = await doc.hover({ line: 0, character: 13 }) expect(hover).toEqual({ contents: { @@ -137,13 +122,9 @@ defineTest({ }, }) - let hoverFromPlugin = await c.sendRequest(HoverRequest.type, { - textDocument, - - //
- // ^ - position: { line: 0, character: 23 }, - }) + //
+ // ^ + let hoverFromPlugin = await doc.hover({ line: 0, character: 23 }) expect(hoverFromPlugin).toEqual({ contents: { @@ -176,36 +157,27 @@ defineTest({ @import 'tailwindcss'; `, }, - prepare: async ({ root }) => ({ c: await init(root) }), - handle: async ({ c }) => { - let textDocument = await c.openDocument({ + prepare: async ({ root }) => ({ client: await createClient({ root }) }), + handle: async ({ client }) => { + let doc = await client.open({ lang: 'html', text: '
', }) - expect(c.project).toMatchObject({ + expect(await client.project()).toMatchObject({ tailwind: { version: '4.0.1', isDefaultVersion: false, }, }) - let hover = await c.sendRequest(HoverRequest.type, { - textDocument, - - //
+ // ^ + let hover = await doc.hover({ line: 0, character: 13 }) - let completion = await c.sendRequest(CompletionRequest.type, { - textDocument, - context: { triggerKind: 1 }, - - //
+ // ^ + let completion = await doc.completions({ line: 0, character: 31 }) expect(hover).toEqual({ contents: { @@ -222,7 +194,7 @@ defineTest({ }, }) - expect(completion.items.length).toBe(12288) + expect(completion?.items.length).toBe(12288) }, }) @@ -250,27 +222,23 @@ defineTest({ } `, }, - prepare: async ({ root }) => ({ c: await init(root) }), - handle: async ({ c }) => { - let textDocument = await c.openDocument({ + prepare: async ({ root }) => ({ client: await createClient({ root }) }), + handle: async ({ client }) => { + let doc = await client.open({ lang: 'html', text: '
', }) - expect(c.project).toMatchObject({ + expect(await client.project()).toMatchObject({ tailwind: { version: '4.0.1', isDefaultVersion: false, }, }) - let hover = await c.sendRequest(HoverRequest.type, { - textDocument, - - //
- // ^ - position: { line: 0, character: 13 }, - }) + //
+ // ^ + let hover = await doc.hover({ line: 0, character: 13 }) expect(hover).toEqual({ contents: { @@ -306,27 +274,23 @@ defineTest({ } `, }, - prepare: async ({ root }) => ({ c: await init(root) }), - handle: async ({ c }) => { - let textDocument = await c.openDocument({ + prepare: async ({ root }) => ({ client: await createClient({ root }) }), + handle: async ({ client }) => { + let doc = await client.open({ lang: 'html', text: '
', }) - expect(c.project).toMatchObject({ + expect(await client.project()).toMatchObject({ tailwind: { version: '4.0.6', isDefaultVersion: true, }, }) - let hover = await c.sendRequest(HoverRequest.type, { - textDocument, - - //
- // ^ - position: { line: 0, character: 13 }, - }) + //
+ // ^ + let hover = await doc.hover({ line: 0, character: 13 }) expect(hover).toEqual({ contents: { @@ -368,46 +332,41 @@ defineTest({ } `, }, - prepare: async ({ root }) => ({ c: await init(root) }), - handle: async ({ c }) => { - await c.updateSettings({ - tailwindCSS: { - experimental: { - configFile: { - 'a/app.css': 'c/a/**', - 'b/app.css': 'c/b/**', + prepare: async ({ root }) => ({ + client: await createClient({ + root, + settings: { + tailwindCSS: { + experimental: { + configFile: { + 'a/app.css': 'c/a/**', + 'b/app.css': 'c/b/**', + }, }, }, }, - }) - - let documentA = await c.openDocument({ + }), + }), + handle: async ({ client }) => { + let documentA = await client.open({ lang: 'html', text: '
', name: 'c/a/index.html', }) - let documentB = await c.openDocument({ + let documentB = await client.open({ lang: 'html', text: '
', name: 'c/b/index.html', }) - let hoverA = await c.sendRequest(HoverRequest.type, { - textDocument: documentA, - - //
- // ^ - position: { line: 0, character: 13 }, - }) - - let hoverB = await c.sendRequest(HoverRequest.type, { - textDocument: documentB, + //
+ // ^ + let hoverA = await documentA.hover({ line: 0, character: 13 }) - //
- // ^ - position: { line: 0, character: 13 }, - }) + //
+ // ^ + let hoverB = await documentB.hover({ line: 0, character: 13 }) expect(hoverA).toEqual({ contents: { @@ -457,46 +416,41 @@ defineTest({ } `, }, - prepare: async ({ root }) => ({ c: await init(root) }), - handle: async ({ c }) => { - await c.updateSettings({ - tailwindCSS: { - experimental: { - configFile: { - 'a/app.css': 'c/a/**', - 'b/app.css': 'c/b/**', + prepare: async ({ root }) => ({ + client: await createClient({ + root, + settings: { + tailwindCSS: { + experimental: { + configFile: { + 'a/app.css': 'c/a/**', + 'b/app.css': 'c/b/**', + }, }, }, }, - }) - - let documentA = await c.openDocument({ + }), + }), + handle: async ({ client }) => { + let documentA = await client.open({ lang: 'html', text: '
', name: 'c/a/index.html', }) - let documentB = await c.openDocument({ + let documentB = await client.open({ lang: 'html', text: '
', name: 'c/b/index.html', }) - let hoverA = await c.sendRequest(HoverRequest.type, { - textDocument: documentA, - - //
- // ^ - position: { line: 0, character: 13 }, - }) - - let hoverB = await c.sendRequest(HoverRequest.type, { - textDocument: documentB, + //
+ // ^ + let hoverA = await documentA.hover({ line: 0, character: 13 }) - //
- // ^ - position: { line: 0, character: 13 }, - }) + //
+ // ^ + let hoverB = await documentB.hover({ line: 0, character: 13 }) expect(hoverA).toEqual({ contents: { @@ -537,9 +491,9 @@ defineTest({ @import 'tailwindcss'; `, }, - prepare: async ({ root }) => ({ c: await init(root) }), - handle: async ({ c }) => { - let document = await c.openDocument({ + prepare: async ({ root }) => ({ client: await createClient({ root }) }), + handle: async ({ client }) => { + let document = await client.open({ lang: 'vue', text: html`