1
1
import * as path from 'node:path'
2
2
import { 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'
18
5
import 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 '
21
8
22
9
type Settings = any
23
10
24
11
interface FixtureContext
25
12
extends Pick < ProtocolConnection , 'sendRequest' | 'sendNotification' | 'onNotification' > {
26
- client : ProtocolConnection
13
+ client : Client
27
14
openDocument : ( params : {
28
15
text : string
29
16
lang ?: string
30
17
dir ?: string
18
+ name ?: string | null
31
19
settings ?: Settings
32
20
} ) => Promise < { uri : string ; updateSettings : ( settings : Settings ) => Promise < void > } >
33
21
updateSettings : ( settings : Settings ) => Promise < void >
@@ -57,117 +45,22 @@ export interface InitOptions {
57
45
* Extra initialization options to pass to the LSP
58
46
*/
59
47
options ?: Record < string , any >
48
+
49
+ /**
50
+ * Settings to provide the server immediately when it starts
51
+ */
52
+ settings ?: Settings
60
53
}
61
54
62
55
export async function init (
63
56
fixture : string | string [ ] ,
64
57
opts : InitOptions = { } ,
65
58
) : 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 ]
168
61
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 )
171
64
}
172
65
173
66
function resolveUri ( ...parts : string [ ] ) {
@@ -176,86 +69,44 @@ export async function init(
176
69
? path . resolve ( './tests/fixtures' , ...parts )
177
70
: path . resolve ( './tests/fixtures' , fixtures [ 0 ] , ...parts )
178
71
179
- return ` file:// ${ filepath } `
72
+ return URI . file ( filepath ) . toString ( )
180
73
}
181
74
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
+ }
217
78
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 ,
220
85
} )
221
86
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
228
88
let projectDetails : any = null
229
89
230
- client . onNotification ( '@/tailwindCSS/projectDetails' , ( params ) => {
231
- console . log ( '[TEST] Project detailed changed' )
232
- projectDetails = params
90
+ client . project ( ) . then ( ( project ) => {
91
+ projectDetails = project
233
92
} )
234
93
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
-
245
94
return {
246
95
client,
247
- fixtureUri,
96
+ fixtureUri ( fixture : string ) {
97
+ return URI . file ( fixturePath ( fixture ) ) . toString ( )
98
+ } ,
248
99
get project ( ) {
249
100
return projectDetails
250
101
} ,
251
102
sendRequest ( type : any , params : any ) {
252
- return client . sendRequest ( type , params )
103
+ return client . conn . sendRequest ( type , params )
253
104
} ,
254
105
sendNotification ( type : any , params ?: any ) {
255
- return client . sendNotification ( type , params )
106
+ return client . conn . sendNotification ( type , params )
256
107
} ,
257
108
onNotification ( type : any , callback : any ) {
258
- return client . onNotification ( type , callback )
109
+ return client . conn . onNotification ( type , callback )
259
110
} ,
260
111
async openDocument ( {
261
112
text,
@@ -267,59 +118,35 @@ export async function init(
267
118
text : string
268
119
lang ?: string
269
120
dir ?: string
270
- name ?: string
121
+ name ?: string | null
271
122
settings ?: Settings
272
123
} ) {
273
124
let uri = resolveUri ( dir , name ?? `file-${ counter ++ } ` )
274
- docSettings . set ( uri , settings )
275
125
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,
289
131
} )
290
132
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
-
305
133
return {
306
- uri,
134
+ get uri ( ) {
135
+ return doc . uri . toString ( )
136
+ } ,
307
137
async updateSettings ( settings : Settings ) {
308
- docSettings . set ( uri , settings )
309
- await client . sendNotification ( DidChangeConfigurationNotification . type )
138
+ await doc . update ( { settings } )
310
139
} ,
311
140
}
312
141
} ,
313
142
314
143
async updateSettings ( newSettings : Settings ) {
315
- settings = newSettings
316
- await client . sendNotification ( DidChangeConfigurationNotification . type )
144
+ await client . updateSettings ( newSettings )
317
145
} ,
318
146
319
147
async updateFile ( file : string , text : string ) {
320
148
let uri = resolveUri ( file )
321
-
322
- await client . sendNotification ( DidChangeTextDocumentNotification . type , {
149
+ await client . conn . sendNotification ( DidChangeTextDocumentNotification . type , {
323
150
textDocument : { uri, version : counter ++ } ,
324
151
contentChanges : [ { text } ] ,
325
152
} )
@@ -337,7 +164,7 @@ export function withFixture(fixture: string, callback: (c: FixtureContext) => vo
337
164
// to the connection object without having to resort to using a Proxy
338
165
Object . setPrototypeOf ( c , await init ( fixture ) )
339
166
340
- return ( ) => c . client . dispose ( )
167
+ return ( ) => c . client . conn . dispose ( )
341
168
} )
342
169
343
170
callback ( c )
@@ -360,7 +187,7 @@ export function withWorkspace({
360
187
// to the connection object without having to resort to using a Proxy
361
188
Object . setPrototypeOf ( c , await init ( fixtures ) )
362
189
363
- return ( ) => c . client . dispose ( )
190
+ return ( ) => c . client . conn . dispose ( )
364
191
} )
365
192
366
193
run ( c )
0 commit comments