@@ -176,11 +176,11 @@ namespace ts.projectSystem {
176176 }
177177 } ;
178178
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 ) {
180180 if ( typingsInstaller === undefined ) {
181181 typingsInstaller = new TestTypingsInstaller ( "/a/data/" , /*throttleLimit*/ 5 , host ) ;
182182 }
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 ) ;
184184 }
185185
186186 export interface CreateProjectServiceParameters {
@@ -547,6 +547,49 @@ namespace ts.projectSystem {
547547 readonly getEnvironmentVariable = notImplemented ;
548548 }
549549
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+
550593 export function makeSessionRequest < T > ( command : string , args : T ) {
551594 const newRequest : protocol . Request = {
552595 seq : 0 ,
@@ -3384,6 +3427,7 @@ namespace ts.projectSystem {
33843427 } ,
33853428 resetRequest : noop
33863429 } ;
3430+
33873431 const session = createSession ( host , /*typingsInstaller*/ undefined , /*projectServiceEventHandler*/ undefined , cancellationToken ) ;
33883432
33893433 expectedRequestId = session . getNextSeq ( ) ;
@@ -3422,22 +3466,7 @@ namespace ts.projectSystem {
34223466 } )
34233467 } ;
34243468
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 ( ) ;
34413470 const host = createServerHost ( [ f1 , config ] ) ;
34423471 const session = createSession ( host , /*typingsInstaller*/ undefined , ( ) => { } , cancellationToken ) ;
34433472 {
@@ -3472,13 +3501,13 @@ namespace ts.projectSystem {
34723501 host . clearOutput ( ) ;
34733502
34743503 // cancel previously issued Geterr
3475- requestToCancel = getErrId ;
3504+ cancellationToken . setRequestToCancel ( getErrId ) ;
34763505 host . runQueuedTimeoutCallbacks ( ) ;
34773506
34783507 assert . equal ( host . getOutput ( ) . length , 1 , "expect 1 message" ) ;
34793508 verifyRequestCompleted ( getErrId , 0 ) ;
34803509
3481- requestToCancel = - 1 ;
3510+ cancellationToken . resetToken ( ) ;
34823511 }
34833512 {
34843513 const getErrId = session . getNextSeq ( ) ;
@@ -3495,12 +3524,12 @@ namespace ts.projectSystem {
34953524 assert . equal ( e1 . event , "syntaxDiag" ) ;
34963525 host . clearOutput ( ) ;
34973526
3498- requestToCancel = getErrId ;
3527+ cancellationToken . setRequestToCancel ( getErrId ) ;
34993528 host . runQueuedImmediateCallbacks ( ) ;
35003529 assert . equal ( host . getOutput ( ) . length , 1 , "expect 1 message" ) ;
35013530 verifyRequestCompleted ( getErrId , 0 ) ;
35023531
3503- requestToCancel = - 1 ;
3532+ cancellationToken . resetToken ( ) ;
35043533 }
35053534 {
35063535 const getErrId = session . getNextSeq ( ) ;
@@ -3523,7 +3552,7 @@ namespace ts.projectSystem {
35233552 assert . equal ( e2 . event , "semanticDiag" ) ;
35243553 verifyRequestCompleted ( getErrId , 1 ) ;
35253554
3526- requestToCancel = - 1 ;
3555+ cancellationToken . resetToken ( ) ;
35273556 }
35283557 {
35293558 const getErr1 = session . getNextSeq ( ) ;
@@ -3558,6 +3587,68 @@ namespace ts.projectSystem {
35583587 return JSON . parse ( server . extractMessage ( host . getOutput ( ) [ n ] ) ) ;
35593588 }
35603589 } ) ;
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+ } ) ;
35613652 } ) ;
35623653
35633654 describe ( "occurence highlight on string" , ( ) => {
0 commit comments