@@ -34,10 +34,6 @@ namespace ts.projectSystem {
34
34
getLogFileName : ( ) : string => undefined
35
35
} ;
36
36
37
- export const nullCancellationToken : HostCancellationToken = {
38
- isCancellationRequested : ( ) => false
39
- } ;
40
-
41
37
export const { content : libFileContent } = Harness . getDefaultLibraryFile ( Harness . IO ) ;
42
38
export const libFile : FileOrFolder = {
43
39
path : "/a/lib/lib.d.ts" ,
@@ -158,17 +154,33 @@ namespace ts.projectSystem {
158
154
}
159
155
160
156
class TestSession extends server . Session {
157
+ private seq = 0 ;
158
+
161
159
getProjectService ( ) {
162
160
return this . projectService ;
163
161
}
162
+
163
+ public getSeq ( ) {
164
+ return this . seq ;
165
+ }
166
+
167
+ public getNextSeq ( ) {
168
+ return this . seq + 1 ;
169
+ }
170
+
171
+ public executeCommandSeq < T extends server . protocol . Request > ( request : Partial < T > ) {
172
+ this . seq ++ ;
173
+ request . seq = this . seq ;
174
+ request . type = "request" ;
175
+ return this . executeCommand ( < T > request ) ;
176
+ }
164
177
} ;
165
178
166
- export function createSession ( host : server . ServerHost , typingsInstaller ?: server . ITypingsInstaller , projectServiceEventHandler ?: server . ProjectServiceEventHandler ) {
179
+ export function createSession ( host : server . ServerHost , typingsInstaller ?: server . ITypingsInstaller , projectServiceEventHandler ?: server . ProjectServiceEventHandler , cancellationToken ?: server . ServerCancellationToken ) {
167
180
if ( typingsInstaller === undefined ) {
168
181
typingsInstaller = new TestTypingsInstaller ( "/a/data/" , /*throttleLimit*/ 5 , host ) ;
169
182
}
170
-
171
- return new TestSession ( host , nullCancellationToken , /*useSingleInferredProject*/ false , typingsInstaller , Utils . byteLength , process . hrtime , nullLogger , /*canUseEvents*/ projectServiceEventHandler !== undefined , projectServiceEventHandler ) ;
183
+ return new TestSession ( host , cancellationToken || server . nullCancellationToken , /*useSingleInferredProject*/ false , typingsInstaller , Utils . byteLength , process . hrtime , nullLogger , /*canUseEvents*/ projectServiceEventHandler !== undefined , projectServiceEventHandler ) ;
172
184
}
173
185
174
186
export interface CreateProjectServiceParameters {
@@ -191,7 +203,7 @@ namespace ts.projectSystem {
191
203
}
192
204
}
193
205
export function createProjectService ( host : server . ServerHost , parameters : CreateProjectServiceParameters = { } ) {
194
- const cancellationToken = parameters . cancellationToken || nullCancellationToken ;
206
+ const cancellationToken = parameters . cancellationToken || server . nullCancellationToken ;
195
207
const logger = parameters . logger || nullLogger ;
196
208
const useSingleInferredProject = parameters . useSingleInferredProject !== undefined ? parameters . useSingleInferredProject : false ;
197
209
return new TestProjectService ( host , logger , cancellationToken , useSingleInferredProject , parameters . typingsInstaller , parameters . eventHandler ) ;
@@ -328,6 +340,8 @@ namespace ts.projectSystem {
328
340
export class TestServerHost implements server . ServerHost {
329
341
args : string [ ] = [ ] ;
330
342
343
+ private readonly output : string [ ] = [ ] ;
344
+
331
345
private fs : ts . FileMap < FSEntry > ;
332
346
private getCanonicalFileName : ( s : string ) => string ;
333
347
private toPath : ( f : string ) => Path ;
@@ -477,6 +491,10 @@ namespace ts.projectSystem {
477
491
this . timeoutCallbacks . invoke ( ) ;
478
492
}
479
493
494
+ runQueuedImmediateCallbacks ( ) {
495
+ this . immediateCallbacks . invoke ( ) ;
496
+ }
497
+
480
498
setImmediate ( callback : TimeOutCallback , _time : number , ...args : any [ ] ) {
481
499
return this . immediateCallbacks . register ( callback , args ) ;
482
500
}
@@ -509,7 +527,17 @@ namespace ts.projectSystem {
509
527
this . reloadFS ( filesOrFolders ) ;
510
528
}
511
529
512
- write ( ) { }
530
+ write ( message : string ) {
531
+ this . output . push ( message ) ;
532
+ }
533
+
534
+ getOutput ( ) : ReadonlyArray < string > {
535
+ return this . output ;
536
+ }
537
+
538
+ clearOutput ( ) {
539
+ this . output . length = 0 ;
540
+ }
513
541
514
542
readonly readFile = ( s : string ) => ( < File > this . fs . get ( this . toPath ( s ) ) ) . content ;
515
543
readonly resolvePath = ( s : string ) => s ;
@@ -3131,6 +3159,200 @@ namespace ts.projectSystem {
3131
3159
} ) ;
3132
3160
} ) ;
3133
3161
3162
+ describe ( "cancellationToken" , ( ) => {
3163
+ it ( "is attached to request" , ( ) => {
3164
+ const f1 = {
3165
+ path : "/a/b/app.ts" ,
3166
+ content : "let xyz = 1;"
3167
+ } ;
3168
+ const host = createServerHost ( [ f1 ] ) ;
3169
+ let expectedRequestId : number ;
3170
+ const cancellationToken : server . ServerCancellationToken = {
3171
+ isCancellationRequested : ( ) => false ,
3172
+ setRequest : requestId => {
3173
+ if ( expectedRequestId === undefined ) {
3174
+ assert . isTrue ( false , "unexpected call" )
3175
+ }
3176
+ assert . equal ( requestId , expectedRequestId ) ;
3177
+ } ,
3178
+ resetRequest : noop
3179
+ }
3180
+ const session = createSession ( host , /*typingsInstaller*/ undefined , /*projectServiceEventHandler*/ undefined , cancellationToken ) ;
3181
+
3182
+ expectedRequestId = session . getNextSeq ( ) ;
3183
+ session . executeCommandSeq ( < server . protocol . OpenRequest > {
3184
+ command : "open" ,
3185
+ arguments : { file : f1 . path }
3186
+ } ) ;
3187
+
3188
+ expectedRequestId = session . getNextSeq ( ) ;
3189
+ session . executeCommandSeq ( < server . protocol . GeterrRequest > {
3190
+ command : "geterr" ,
3191
+ arguments : { files : [ f1 . path ] }
3192
+ } ) ;
3193
+
3194
+ expectedRequestId = session . getNextSeq ( ) ;
3195
+ session . executeCommandSeq ( < server . protocol . OccurrencesRequest > {
3196
+ command : "occurrences" ,
3197
+ arguments : { file : f1 . path , line : 1 , offset : 6 }
3198
+ } ) ;
3199
+
3200
+ expectedRequestId = 2 ;
3201
+ host . runQueuedImmediateCallbacks ( ) ;
3202
+ expectedRequestId = 2 ;
3203
+ host . runQueuedImmediateCallbacks ( ) ;
3204
+ } ) ;
3205
+
3206
+ it ( "Geterr is cancellable" , ( ) => {
3207
+ const f1 = {
3208
+ path : "/a/app.ts" ,
3209
+ content : "let x = 1"
3210
+ } ;
3211
+ const config = {
3212
+ path : "/a/tsconfig.json" ,
3213
+ content : JSON . stringify ( {
3214
+ compilerOptions : { }
3215
+ } )
3216
+ } ;
3217
+
3218
+ let requestToCancel = - 1 ;
3219
+ const cancellationToken : server . ServerCancellationToken = ( function ( ) {
3220
+ let currentId : number ;
3221
+ return < server . ServerCancellationToken > {
3222
+ setRequest ( requestId ) {
3223
+ currentId = requestId ;
3224
+ } ,
3225
+ resetRequest ( requestId ) {
3226
+ assert . equal ( requestId , currentId , "unexpected request id in cancellation" )
3227
+ currentId = undefined ;
3228
+ } ,
3229
+ isCancellationRequested ( ) {
3230
+ return requestToCancel === currentId ;
3231
+ }
3232
+ }
3233
+ } ) ( ) ;
3234
+ const host = createServerHost ( [ f1 , config ] ) ;
3235
+ const session = createSession ( host , /*typingsInstaller*/ undefined , ( ) => { } , cancellationToken ) ;
3236
+ {
3237
+ session . executeCommandSeq ( < protocol . OpenRequest > {
3238
+ command : "open" ,
3239
+ arguments : { file : f1 . path }
3240
+ } ) ;
3241
+ // send geterr for missing file
3242
+ session . executeCommandSeq ( < protocol . GeterrRequest > {
3243
+ command : "geterr" ,
3244
+ arguments : { files : [ "/a/missing" ] }
3245
+ } ) ;
3246
+ // no files - expect 'completed' event
3247
+ assert . equal ( host . getOutput ( ) . length , 1 , "expect 1 message" ) ;
3248
+ verifyRequestCompleted ( session . getSeq ( ) , 0 ) ;
3249
+ }
3250
+ {
3251
+ const getErrId = session . getNextSeq ( ) ;
3252
+ // send geterr for a valid file
3253
+ session . executeCommandSeq ( < protocol . GeterrRequest > {
3254
+ command : "geterr" ,
3255
+ arguments : { files : [ f1 . path ] }
3256
+ } ) ;
3257
+
3258
+ assert . equal ( host . getOutput ( ) . length , 0 , "expect 0 messages" ) ;
3259
+
3260
+ // run new request
3261
+ session . executeCommandSeq ( < protocol . ProjectInfoRequest > {
3262
+ command : "projectInfo" ,
3263
+ arguments : { file : f1 . path }
3264
+ } ) ;
3265
+ host . clearOutput ( ) ;
3266
+
3267
+ // cancel previously issued Geterr
3268
+ requestToCancel = getErrId ;
3269
+ host . runQueuedTimeoutCallbacks ( ) ;
3270
+
3271
+ assert . equal ( host . getOutput ( ) . length , 1 , "expect 1 message" ) ;
3272
+ verifyRequestCompleted ( getErrId , 0 ) ;
3273
+
3274
+ requestToCancel = - 1 ;
3275
+ }
3276
+ {
3277
+ const getErrId = session . getNextSeq ( ) ;
3278
+ session . executeCommandSeq ( < protocol . GeterrRequest > {
3279
+ command : "geterr" ,
3280
+ arguments : { files : [ f1 . path ] }
3281
+ } ) ;
3282
+ assert . equal ( host . getOutput ( ) . length , 0 , "expect 0 messages" ) ;
3283
+
3284
+ // run first step
3285
+ host . runQueuedTimeoutCallbacks ( ) ;
3286
+ assert . equal ( host . getOutput ( ) . length , 1 , "expect 1 messages" ) ;
3287
+ const e1 = < protocol . Event > getMessage ( 0 ) ;
3288
+ assert . equal ( e1 . event , "syntaxDiag" ) ;
3289
+ host . clearOutput ( ) ;
3290
+
3291
+ requestToCancel = getErrId ;
3292
+ host . runQueuedImmediateCallbacks ( ) ;
3293
+ assert . equal ( host . getOutput ( ) . length , 1 , "expect 1 message" ) ;
3294
+ verifyRequestCompleted ( getErrId , 0 ) ;
3295
+
3296
+ requestToCancel = - 1 ;
3297
+ }
3298
+ {
3299
+ const getErrId = session . getNextSeq ( ) ;
3300
+ session . executeCommandSeq ( < protocol . GeterrRequest > {
3301
+ command : "geterr" ,
3302
+ arguments : { files : [ f1 . path ] }
3303
+ } ) ;
3304
+ assert . equal ( host . getOutput ( ) . length , 0 , "expect 0 messages" ) ;
3305
+
3306
+ // run first step
3307
+ host . runQueuedTimeoutCallbacks ( ) ;
3308
+ assert . equal ( host . getOutput ( ) . length , 1 , "expect 1 messages" ) ;
3309
+ const e1 = < protocol . Event > getMessage ( 0 ) ;
3310
+ assert . equal ( e1 . event , "syntaxDiag" ) ;
3311
+ host . clearOutput ( ) ;
3312
+
3313
+ host . runQueuedImmediateCallbacks ( ) ;
3314
+ assert . equal ( host . getOutput ( ) . length , 2 , "expect 2 messages" ) ;
3315
+ const e2 = < protocol . Event > getMessage ( 0 ) ;
3316
+ assert . equal ( e2 . event , "semanticDiag" ) ;
3317
+ verifyRequestCompleted ( getErrId , 1 ) ;
3318
+
3319
+ requestToCancel = - 1 ;
3320
+ }
3321
+ {
3322
+ const getErr1 = session . getNextSeq ( ) ;
3323
+ session . executeCommandSeq ( < protocol . GeterrRequest > {
3324
+ command : "geterr" ,
3325
+ arguments : { files : [ f1 . path ] }
3326
+ } ) ;
3327
+ assert . equal ( host . getOutput ( ) . length , 0 , "expect 0 messages" ) ;
3328
+ // run first step
3329
+ host . runQueuedTimeoutCallbacks ( ) ;
3330
+ assert . equal ( host . getOutput ( ) . length , 1 , "expect 1 messages" ) ;
3331
+ const e1 = < protocol . Event > getMessage ( 0 ) ;
3332
+ assert . equal ( e1 . event , "syntaxDiag" ) ;
3333
+ host . clearOutput ( ) ;
3334
+
3335
+ session . executeCommandSeq ( < protocol . GeterrRequest > {
3336
+ command : "geterr" ,
3337
+ arguments : { files : [ f1 . path ] }
3338
+ } ) ;
3339
+ // make sure that getErr1 is completed
3340
+ verifyRequestCompleted ( getErr1 , 0 ) ;
3341
+ }
3342
+
3343
+ function verifyRequestCompleted ( expectedSeq : number , n : number ) {
3344
+ const event = < protocol . RequestCompletedEvent > getMessage ( n ) ;
3345
+ assert . equal ( event . event , "requestCompleted" ) ;
3346
+ assert . equal ( event . body . request_seq , expectedSeq , "expectedSeq" ) ;
3347
+ host . clearOutput ( ) ;
3348
+ }
3349
+
3350
+ function getMessage ( n : number ) {
3351
+ return JSON . parse ( server . extractMessage ( host . getOutput ( ) [ n ] ) ) ;
3352
+ }
3353
+ } ) ;
3354
+ } ) ;
3355
+
3134
3356
describe ( "maxNodeModuleJsDepth for inferred projects" , ( ) => {
3135
3357
it ( "should be set to 2 if the project has js root files" , ( ) => {
3136
3358
const file1 : FileOrFolder = {
@@ -3184,5 +3406,4 @@ namespace ts.projectSystem {
3184
3406
assert . isUndefined ( project . getCompilerOptions ( ) . maxNodeModuleJsDepth ) ;
3185
3407
} ) ;
3186
3408
} ) ;
3187
-
3188
3409
}
0 commit comments