@@ -176,11 +176,11 @@ namespace ts.projectSystem {
176
176
}
177
177
} ;
178
178
179
- export function createSession ( host : server . ServerHost , typingsInstaller ?: server . ITypingsInstaller , projectServiceEventHandler ?: server . ProjectServiceEventHandler , cancellationToken ?: server . ServerCancellationToken ) {
179
+ export function createSession ( host : server . ServerHost , typingsInstaller ?: server . ITypingsInstaller , projectServiceEventHandler ?: server . ProjectServiceEventHandler , cancellationToken ?: server . ServerCancellationToken , throttleWaitMilliseconds ?: number ) {
180
180
if ( typingsInstaller === undefined ) {
181
181
typingsInstaller = new TestTypingsInstaller ( "/a/data/" , /*throttleLimit*/ 5 , host ) ;
182
182
}
183
- return new TestSession ( host , cancellationToken || server . 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 , throttleWaitMilliseconds ) ;
184
184
}
185
185
186
186
export interface CreateProjectServiceParameters {
@@ -547,6 +547,49 @@ namespace ts.projectSystem {
547
547
readonly getEnvironmentVariable = notImplemented ;
548
548
}
549
549
550
+ /**
551
+ * Test server cancellation token used to mock host token cancellation requests.
552
+ * The cancelAfterRequest constructor param specifies how many isCancellationRequested() calls
553
+ * should be made before canceling the token. The id of the request to cancel should be set with
554
+ * setRequestToCancel();
555
+ */
556
+ export class TestServerCancellationToken implements server . ServerCancellationToken {
557
+ private currentId = - 1 ;
558
+ private requestToCancel = - 1 ;
559
+ private isCancellationRequestedCount = 0 ;
560
+
561
+ constructor ( private cancelAfterRequest = 0 ) {
562
+ }
563
+
564
+ setRequest ( requestId : number ) {
565
+ this . currentId = requestId ;
566
+ }
567
+
568
+ setRequestToCancel ( requestId : number ) {
569
+ this . resetToken ( ) ;
570
+ this . requestToCancel = requestId ;
571
+ }
572
+
573
+ resetRequest ( requestId : number ) {
574
+ assert . equal ( requestId , this . currentId , "unexpected request id in cancellation" ) ;
575
+ this . currentId = undefined ;
576
+ }
577
+
578
+ isCancellationRequested ( ) {
579
+ this . isCancellationRequestedCount ++ ;
580
+ // If the request id is the request to cancel and isCancellationRequestedCount
581
+ // has been met then cancel the request. Ex: cancel the request if it is a
582
+ // nav bar request & isCancellationRequested() has already been called three times.
583
+ return this . requestToCancel === this . currentId && this . isCancellationRequestedCount >= this . cancelAfterRequest ;
584
+ }
585
+
586
+ resetToken ( ) {
587
+ this . currentId = - 1 ;
588
+ this . isCancellationRequestedCount = 0 ;
589
+ this . requestToCancel = - 1 ;
590
+ }
591
+ }
592
+
550
593
export function makeSessionRequest < T > ( command : string , args : T ) {
551
594
const newRequest : protocol . Request = {
552
595
seq : 0 ,
@@ -3384,6 +3427,7 @@ namespace ts.projectSystem {
3384
3427
} ,
3385
3428
resetRequest : noop
3386
3429
} ;
3430
+
3387
3431
const session = createSession ( host , /*typingsInstaller*/ undefined , /*projectServiceEventHandler*/ undefined , cancellationToken ) ;
3388
3432
3389
3433
expectedRequestId = session . getNextSeq ( ) ;
@@ -3422,22 +3466,7 @@ namespace ts.projectSystem {
3422
3466
} )
3423
3467
} ;
3424
3468
3425
- let requestToCancel = - 1 ;
3426
- const cancellationToken : server . ServerCancellationToken = ( function ( ) {
3427
- let currentId : number ;
3428
- return < server . ServerCancellationToken > {
3429
- setRequest ( requestId ) {
3430
- currentId = requestId ;
3431
- } ,
3432
- resetRequest ( requestId ) {
3433
- assert . equal ( requestId , currentId , "unexpected request id in cancellation" ) ;
3434
- currentId = undefined ;
3435
- } ,
3436
- isCancellationRequested ( ) {
3437
- return requestToCancel === currentId ;
3438
- }
3439
- } ;
3440
- } ) ( ) ;
3469
+ const cancellationToken = new TestServerCancellationToken ( ) ;
3441
3470
const host = createServerHost ( [ f1 , config ] ) ;
3442
3471
const session = createSession ( host , /*typingsInstaller*/ undefined , ( ) => { } , cancellationToken ) ;
3443
3472
{
@@ -3472,13 +3501,13 @@ namespace ts.projectSystem {
3472
3501
host . clearOutput ( ) ;
3473
3502
3474
3503
// cancel previously issued Geterr
3475
- requestToCancel = getErrId ;
3504
+ cancellationToken . setRequestToCancel ( getErrId ) ;
3476
3505
host . runQueuedTimeoutCallbacks ( ) ;
3477
3506
3478
3507
assert . equal ( host . getOutput ( ) . length , 1 , "expect 1 message" ) ;
3479
3508
verifyRequestCompleted ( getErrId , 0 ) ;
3480
3509
3481
- requestToCancel = - 1 ;
3510
+ cancellationToken . resetToken ( ) ;
3482
3511
}
3483
3512
{
3484
3513
const getErrId = session . getNextSeq ( ) ;
@@ -3495,12 +3524,12 @@ namespace ts.projectSystem {
3495
3524
assert . equal ( e1 . event , "syntaxDiag" ) ;
3496
3525
host . clearOutput ( ) ;
3497
3526
3498
- requestToCancel = getErrId ;
3527
+ cancellationToken . setRequestToCancel ( getErrId ) ;
3499
3528
host . runQueuedImmediateCallbacks ( ) ;
3500
3529
assert . equal ( host . getOutput ( ) . length , 1 , "expect 1 message" ) ;
3501
3530
verifyRequestCompleted ( getErrId , 0 ) ;
3502
3531
3503
- requestToCancel = - 1 ;
3532
+ cancellationToken . resetToken ( ) ;
3504
3533
}
3505
3534
{
3506
3535
const getErrId = session . getNextSeq ( ) ;
@@ -3523,7 +3552,7 @@ namespace ts.projectSystem {
3523
3552
assert . equal ( e2 . event , "semanticDiag" ) ;
3524
3553
verifyRequestCompleted ( getErrId , 1 ) ;
3525
3554
3526
- requestToCancel = - 1 ;
3555
+ cancellationToken . resetToken ( ) ;
3527
3556
}
3528
3557
{
3529
3558
const getErr1 = session . getNextSeq ( ) ;
@@ -3558,6 +3587,68 @@ namespace ts.projectSystem {
3558
3587
return JSON . parse ( server . extractMessage ( host . getOutput ( ) [ n ] ) ) ;
3559
3588
}
3560
3589
} ) ;
3590
+ it ( "Lower priority tasks are cancellable" , ( ) => {
3591
+ const f1 = {
3592
+ path : "/a/app.ts" ,
3593
+ content : `{ let x = 1; } var foo = "foo"; var bar = "bar"; var fooBar = "fooBar";`
3594
+ } ;
3595
+ const config = {
3596
+ path : "/a/tsconfig.json" ,
3597
+ content : JSON . stringify ( {
3598
+ compilerOptions : { }
3599
+ } )
3600
+ } ;
3601
+ const cancellationToken = new TestServerCancellationToken ( /*cancelAfterRequest*/ 3 ) ;
3602
+ const host = createServerHost ( [ f1 , config ] ) ;
3603
+ const session = createSession ( host , /*typingsInstaller*/ undefined , ( ) => { } , cancellationToken , /*throttleWaitMilliseconds*/ 0 ) ;
3604
+ {
3605
+ session . executeCommandSeq ( < protocol . OpenRequest > {
3606
+ command : "open" ,
3607
+ arguments : { file : f1 . path }
3608
+ } ) ;
3609
+
3610
+ // send navbar request (normal priority)
3611
+ session . executeCommandSeq ( < protocol . NavBarRequest > {
3612
+ command : "navbar" ,
3613
+ arguments : { file : f1 . path }
3614
+ } ) ;
3615
+
3616
+ // ensure the nav bar request can be canceled
3617
+ verifyExecuteCommandSeqIsCancellable ( < protocol . NavBarRequest > {
3618
+ command : "navbar" ,
3619
+ arguments : { file : f1 . path }
3620
+ } ) ;
3621
+
3622
+ // send outlining spans request (normal priority)
3623
+ session . executeCommandSeq ( < protocol . OutliningSpansRequest > {
3624
+ command : "outliningSpans" ,
3625
+ arguments : { file : f1 . path }
3626
+ } ) ;
3627
+
3628
+ // ensure the outlining spans request can be canceled
3629
+ verifyExecuteCommandSeqIsCancellable ( < protocol . OutliningSpansRequest > {
3630
+ command : "outliningSpans" ,
3631
+ arguments : { file : f1 . path }
3632
+ } ) ;
3633
+ }
3634
+
3635
+ function verifyExecuteCommandSeqIsCancellable < T extends server . protocol . Request > ( request : Partial < T > ) {
3636
+ // Set the next request to be cancellable
3637
+ // The cancellation token will cancel the request the third time
3638
+ // isCancellationRequested() is called.
3639
+ cancellationToken . setRequestToCancel ( session . getNextSeq ( ) ) ;
3640
+ let operationCanceledExceptionThrown = false ;
3641
+
3642
+ try {
3643
+ session . executeCommandSeq ( request ) ;
3644
+ }
3645
+ catch ( e ) {
3646
+ assert ( e instanceof OperationCanceledException ) ;
3647
+ operationCanceledExceptionThrown = true ;
3648
+ }
3649
+ assert ( operationCanceledExceptionThrown , "Operation Canceled Exception not thrown for request: " + JSON . stringify ( request ) ) ;
3650
+ }
3651
+ } ) ;
3561
3652
} ) ;
3562
3653
3563
3654
describe ( "occurence highlight on string" , ( ) => {
0 commit comments