11import * as path from 'node:path'
22import { beforeAll , describe } from 'vitest'
3- import { connect , launch } from './connection'
4- import {
5- CompletionRequest ,
6- ConfigurationRequest ,
7- DidChangeConfigurationNotification ,
8- DidChangeTextDocumentNotification ,
9- DidOpenTextDocumentNotification ,
10- InitializeRequest ,
11- InitializedNotification ,
12- RegistrationRequest ,
13- InitializeParams ,
14- DidOpenTextDocumentParams ,
15- MessageType ,
16- } from 'vscode-languageserver-protocol'
17- import type { ClientCapabilities , ProtocolConnection } from 'vscode-languageclient'
3+ import { DidChangeTextDocumentNotification } from 'vscode-languageserver'
4+ import type { ProtocolConnection } from 'vscode-languageclient'
185import type { Feature } from '@tailwindcss/language-service/src/features'
19- import { clearLanguageBoundariesCache } from '@tailwindcss/language-service/src/util/getLanguageBoundaries '
20- import { CacheMap } from '../src/cache-map '
6+ import { URI } from 'vscode-uri '
7+ import { Client , createClient } from './utils/client '
218
229type Settings = any
2310
2411interface FixtureContext
2512 extends Pick < ProtocolConnection , 'sendRequest' | 'sendNotification' | 'onNotification' > {
26- client : ProtocolConnection
13+ client : Client
2714 openDocument : ( params : {
2815 text : string
2916 lang ?: string
3017 dir ?: string
18+ name ?: string | null
3119 settings ?: Settings
3220 } ) => Promise < { uri : string ; updateSettings : ( settings : Settings ) => Promise < void > } >
3321 updateSettings : ( settings : Settings ) => Promise < void >
@@ -57,117 +45,22 @@ export interface InitOptions {
5745 * Extra initialization options to pass to the LSP
5846 */
5947 options ?: Record < string , any >
48+
49+ /**
50+ * Settings to provide the server immediately when it starts
51+ */
52+ settings ?: Settings
6053}
6154
6255export async function init (
6356 fixture : string | string [ ] ,
6457 opts : InitOptions = { } ,
6558) : Promise < FixtureContext > {
66- let settings = { }
67- let docSettings = new Map < string , Settings > ( )
68-
69- const { client } = opts ?. mode === 'spawn' ? await launch ( ) : await connect ( )
70-
71- if ( opts ?. mode === 'spawn' ) {
72- client . onNotification ( 'window/logMessage' , ( { message, type } ) => {
73- if ( type === MessageType . Error ) {
74- console . error ( message )
75- } else if ( type === MessageType . Warning ) {
76- console . warn ( message )
77- } else if ( type === MessageType . Info ) {
78- console . info ( message )
79- } else if ( type === MessageType . Log ) {
80- console . log ( message )
81- } else if ( type === MessageType . Debug ) {
82- console . debug ( message )
83- }
84- } )
85- }
86-
87- const capabilities : ClientCapabilities = {
88- textDocument : {
89- codeAction : { dynamicRegistration : true } ,
90- codeLens : { dynamicRegistration : true } ,
91- colorProvider : { dynamicRegistration : true } ,
92- completion : {
93- completionItem : {
94- commitCharactersSupport : true ,
95- documentationFormat : [ 'markdown' , 'plaintext' ] ,
96- snippetSupport : true ,
97- } ,
98- completionItemKind : {
99- valueSet : [
100- 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 ,
101- 25 ,
102- ] ,
103- } ,
104- contextSupport : true ,
105- dynamicRegistration : true ,
106- } ,
107- definition : { dynamicRegistration : true } ,
108- documentHighlight : { dynamicRegistration : true } ,
109- documentLink : { dynamicRegistration : true } ,
110- documentSymbol : {
111- dynamicRegistration : true ,
112- symbolKind : {
113- valueSet : [
114- 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 ,
115- 25 , 26 ,
116- ] ,
117- } ,
118- } ,
119- formatting : { dynamicRegistration : true } ,
120- hover : {
121- contentFormat : [ 'markdown' , 'plaintext' ] ,
122- dynamicRegistration : true ,
123- } ,
124- implementation : { dynamicRegistration : true } ,
125- onTypeFormatting : { dynamicRegistration : true } ,
126- publishDiagnostics : { relatedInformation : true } ,
127- rangeFormatting : { dynamicRegistration : true } ,
128- references : { dynamicRegistration : true } ,
129- rename : { dynamicRegistration : true } ,
130- signatureHelp : {
131- dynamicRegistration : true ,
132- signatureInformation : { documentationFormat : [ 'markdown' , 'plaintext' ] } ,
133- } ,
134- synchronization : {
135- didSave : true ,
136- dynamicRegistration : true ,
137- willSave : true ,
138- willSaveWaitUntil : true ,
139- } ,
140- typeDefinition : { dynamicRegistration : true } ,
141- } ,
142- workspace : {
143- applyEdit : true ,
144- configuration : true ,
145- didChangeConfiguration : { dynamicRegistration : true } ,
146- didChangeWatchedFiles : { dynamicRegistration : true } ,
147- executeCommand : { dynamicRegistration : true } ,
148- symbol : {
149- dynamicRegistration : true ,
150- symbolKind : {
151- valueSet : [
152- 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 ,
153- 25 , 26 ,
154- ] ,
155- } ,
156- } ,
157- workspaceEdit : { documentChanges : true } ,
158- workspaceFolders : true ,
159- } ,
160- experimental : {
161- tailwind : {
162- projectDetails : true ,
163- } ,
164- } ,
165- }
166-
167- const fixtures = Array . isArray ( fixture ) ? fixture : [ fixture ]
59+ let workspaces : Record < string , string > = { }
60+ let fixtures = Array . isArray ( fixture ) ? fixture : [ fixture ]
16861
169- function fixtureUri ( fixture : string ) {
170- return `file:// ${ path . resolve ( './tests/fixtures' , fixture ) } `
62+ function fixturePath ( fixture : string ) {
63+ return path . resolve ( './tests/fixtures' , fixture )
17164 }
17265
17366 function resolveUri ( ...parts : string [ ] ) {
@@ -176,86 +69,44 @@ export async function init(
17669 ? path . resolve ( './tests/fixtures' , ...parts )
17770 : path . resolve ( './tests/fixtures' , fixtures [ 0 ] , ...parts )
17871
179- return ` file:// ${ filepath } `
72+ return URI . file ( filepath ) . toString ( )
18073 }
18174
182- const workspaceFolders = fixtures . map ( ( fixture ) => ( {
183- name : `Fixture ${ fixture } ` ,
184- uri : fixtureUri ( fixture ) ,
185- } ) )
186-
187- const rootUri = fixtures . length > 1 ? null : workspaceFolders [ 0 ] . uri
188-
189- await client . sendRequest ( InitializeRequest . type , {
190- processId : - 1 ,
191- rootUri,
192- capabilities,
193- trace : 'off' ,
194- workspaceFolders,
195- initializationOptions : {
196- testMode : true ,
197- ...( opts . options ?? { } ) ,
198- } ,
199- } as InitializeParams )
200-
201- await client . sendNotification ( InitializedNotification . type )
202-
203- client . onRequest ( ConfigurationRequest . type , ( params ) => {
204- return params . items . map ( ( item ) => {
205- if ( docSettings . has ( item . scopeUri ! ) ) {
206- return docSettings . get ( item . scopeUri ! ) [ item . section ! ] ?? { }
207- }
208- return settings [ item . section ! ] ?? { }
209- } )
210- } )
211-
212- let initPromise = new Promise < void > ( ( resolve ) => {
213- client . onRequest ( RegistrationRequest . type , ( { registrations } ) => {
214- if ( registrations . some ( ( r ) => r . method === CompletionRequest . method ) ) {
215- resolve ( )
216- }
75+ for ( let [ idx , fixture ] of fixtures . entries ( ) ) {
76+ workspaces [ `Fixture ${ idx } ` ] = fixturePath ( fixture )
77+ }
21778
218- return null
219- } )
79+ let client = await createClient ( {
80+ server : 'tailwindcss' ,
81+ mode : opts . mode ,
82+ options : opts . options ,
83+ root : workspaces ,
84+ settings : opts . settings ,
22085 } )
22186
222- interface PromiseWithResolvers < T > extends Promise < T > {
223- resolve : ( value ?: T | PromiseLike < T > ) => void
224- reject : ( reason ?: any ) => void
225- }
226-
227- let openingDocuments = new CacheMap < string , PromiseWithResolvers < void > > ( )
87+ let counter = 0
22888 let projectDetails : any = null
22989
230- client . onNotification ( '@/tailwindCSS/projectDetails' , ( params ) => {
231- console . log ( '[TEST] Project detailed changed' )
232- projectDetails = params
90+ client . project ( ) . then ( ( project ) => {
91+ projectDetails = project
23392 } )
23493
235- client . onNotification ( '@/tailwindCSS/documentReady' , ( params ) => {
236- console . log ( '[TEST] Document ready' , params . uri )
237- openingDocuments . get ( params . uri ) ?. resolve ( )
238- } )
239-
240- // This is a global cache that must be reset between tests for accurate results
241- clearLanguageBoundariesCache ( )
242-
243- let counter = 0
244-
24594 return {
24695 client,
247- fixtureUri,
96+ fixtureUri ( fixture : string ) {
97+ return URI . file ( fixturePath ( fixture ) ) . toString ( )
98+ } ,
24899 get project ( ) {
249100 return projectDetails
250101 } ,
251102 sendRequest ( type : any , params : any ) {
252- return client . sendRequest ( type , params )
103+ return client . conn . sendRequest ( type , params )
253104 } ,
254105 sendNotification ( type : any , params ?: any ) {
255- return client . sendNotification ( type , params )
106+ return client . conn . sendNotification ( type , params )
256107 } ,
257108 onNotification ( type : any , callback : any ) {
258- return client . onNotification ( type , callback )
109+ return client . conn . onNotification ( type , callback )
259110 } ,
260111 async openDocument ( {
261112 text,
@@ -267,59 +118,35 @@ export async function init(
267118 text : string
268119 lang ?: string
269120 dir ?: string
270- name ?: string
121+ name ?: string | null
271122 settings ?: Settings
272123 } ) {
273124 let uri = resolveUri ( dir , name ?? `file-${ counter ++ } ` )
274- docSettings . set ( uri , settings )
275125
276- let openPromise = openingDocuments . remember ( uri , ( ) => {
277- let resolve = ( ) => { }
278- let reject = ( ) => { }
279-
280- let p = new Promise < void > ( ( _resolve , _reject ) => {
281- resolve = _resolve
282- reject = _reject
283- } )
284-
285- return Object . assign ( p , {
286- resolve,
287- reject,
288- } )
126+ let doc = await client . open ( {
127+ lang,
128+ text,
129+ uri,
130+ settings,
289131 } )
290132
291- await client . sendNotification ( DidOpenTextDocumentNotification . type , {
292- textDocument : {
293- uri,
294- languageId : lang ,
295- version : 1 ,
296- text,
297- } ,
298- } as DidOpenTextDocumentParams )
299-
300- // If opening a document stalls then it's probably because this promise is not being resolved
301- // This can happen if a document is not covered by one of the selectors because of it's URI
302- await initPromise
303- await openPromise
304-
305133 return {
306- uri,
134+ get uri ( ) {
135+ return doc . uri . toString ( )
136+ } ,
307137 async updateSettings ( settings : Settings ) {
308- docSettings . set ( uri , settings )
309- await client . sendNotification ( DidChangeConfigurationNotification . type )
138+ await doc . update ( { settings } )
310139 } ,
311140 }
312141 } ,
313142
314143 async updateSettings ( newSettings : Settings ) {
315- settings = newSettings
316- await client . sendNotification ( DidChangeConfigurationNotification . type )
144+ await client . updateSettings ( newSettings )
317145 } ,
318146
319147 async updateFile ( file : string , text : string ) {
320148 let uri = resolveUri ( file )
321-
322- await client . sendNotification ( DidChangeTextDocumentNotification . type , {
149+ await client . conn . sendNotification ( DidChangeTextDocumentNotification . type , {
323150 textDocument : { uri, version : counter ++ } ,
324151 contentChanges : [ { text } ] ,
325152 } )
@@ -337,7 +164,7 @@ export function withFixture(fixture: string, callback: (c: FixtureContext) => vo
337164 // to the connection object without having to resort to using a Proxy
338165 Object . setPrototypeOf ( c , await init ( fixture ) )
339166
340- return ( ) => c . client . dispose ( )
167+ return ( ) => c . client . conn . dispose ( )
341168 } )
342169
343170 callback ( c )
@@ -360,7 +187,7 @@ export function withWorkspace({
360187 // to the connection object without having to resort to using a Proxy
361188 Object . setPrototypeOf ( c , await init ( fixtures ) )
362189
363- return ( ) => c . client . dispose ( )
190+ return ( ) => c . client . conn . dispose ( )
364191 } )
365192
366193 run ( c )
0 commit comments