55import * as vscode from 'vscode' ;
66import * as path from 'path' ;
77import * as Telemetry from '../../telemetry' ;
8- import { DefaultClient , CancelReferencesNotification , workspaceReferences } from '../client' ;
8+ import { DefaultClient , workspaceReferences } from '../client' ;
99import { processDelayedDidOpen } from '../extension' ;
10- import { CallHierarchyResultCallback } from '../references' ;
10+ import { CancellationSender } from '../references' ;
1111import { Position , Range , RequestType , TextDocumentIdentifier } from 'vscode-languageclient' ;
1212import { makeVscodeRange } from '../utils' ;
1313
@@ -54,7 +54,7 @@ interface CallHierarchyItemResult {
5454
5555 /**
5656 * If a request is cancelled, `succeeded` will be undefined to indicate no result was returned.
57- * Therfore , object is not defined as optional on the language server.
57+ * Therefore , object is not defined as optional on the language server.
5858 */
5959 succeeded : boolean ;
6060}
@@ -77,45 +77,68 @@ export interface CallHierarchyCallsItemResult {
7777 calls : CallHierarchyCallsItem [ ] ;
7878}
7979
80- export enum CallHierarchyRequestStatus {
80+ enum CallHierarchyRequestStatus {
8181 Unknown ,
8282 Succeeded ,
8383 Canceled ,
84- CaneledByUser ,
84+ CanceledByUser ,
8585 Failed
8686}
8787
8888const CallHierarchyItemRequest : RequestType < CallHierarchyParams , CallHierarchyItemResult , void > =
8989 new RequestType < CallHierarchyParams , CallHierarchyItemResult , void > ( 'cpptools/prepareCallHierarchy' ) ;
9090
91+ const CallHierarchyCallsToRequest : RequestType < CallHierarchyParams , CallHierarchyCallsItemResult , void > =
92+ new RequestType < CallHierarchyParams , CallHierarchyCallsItemResult , void > ( 'cpptools/callHierarchyCallsTo' ) ;
93+
9194const CallHierarchyCallsFromRequest : RequestType < CallHierarchyParams , CallHierarchyCallsItemResult , void > =
9295 new RequestType < CallHierarchyParams , CallHierarchyCallsItemResult , void > ( 'cpptools/callHierarchyCallsFrom' ) ;
9396
9497export class CallHierarchyProvider implements vscode . CallHierarchyProvider {
9598 // Indicates whether a request is from an entry root node (e.g. top function in the call tree).
9699 private isEntryRootNodeTelemetry : boolean = false ;
97100 private client : DefaultClient ;
101+
98102 constructor ( client : DefaultClient ) {
99103 this . client = client ;
100104 }
101105
102106 public async prepareCallHierarchy ( document : vscode . TextDocument , position : vscode . Position , token : vscode . CancellationToken ) :
103107 Promise < vscode . CallHierarchyItem | undefined > {
108+ await this . client . requestWhenReady ( ( ) => processDelayedDidOpen ( document ) ) ;
109+ workspaceReferences . cancelCurrentReferenceRequest ( CancellationSender . NewRequest ) ;
104110 workspaceReferences . clearViews ( ) ;
111+
105112 const range : vscode . Range | undefined = document . getWordRangeAtPosition ( position ) ;
106113 if ( range === undefined ) {
107114 return undefined ;
108115 }
109116
110- await this . client . requestWhenReady ( ( ) => processDelayedDidOpen ( document ) ) ;
117+ // Listen to a cancellation for this request. When this request is cancelled,
118+ // use a local cancellation source to explicitly cancel a token.
119+ const cancelSource : vscode . CancellationTokenSource = new vscode . CancellationTokenSource ( ) ;
120+ const cancellationTokenListener : vscode . Disposable = token . onCancellationRequested ( ( ) => {
121+ cancelSource . cancel ( ) ;
122+ } ) ;
123+ const requestCanceledListener : vscode . Disposable = workspaceReferences . onCancellationRequested ( sender => {
124+ cancelSource . cancel ( ) ;
125+ } ) ;
111126
112127 const params : CallHierarchyParams = {
113128 textDocument : { uri : document . uri . toString ( ) } ,
114129 position : Position . create ( position . line , position . character )
115130 } ;
116- const response : CallHierarchyItemResult = await this . client . languageClient . sendRequest ( CallHierarchyItemRequest , params , token ) ;
117- if ( token . isCancellationRequested || response . succeeded === undefined ) {
118- throw new vscode . CancellationError ( ) ;
131+ const response : CallHierarchyItemResult = await this . client . languageClient . sendRequest ( CallHierarchyItemRequest , params , cancelSource . token ) ;
132+
133+ cancellationTokenListener . dispose ( ) ;
134+ requestCanceledListener . dispose ( ) ;
135+
136+ if ( cancelSource . token . isCancellationRequested || response . succeeded === undefined ) {
137+ // Return undefined instead of vscode.CancellationError to avoid the following error message from VS Code:
138+ // "MISSING provider."
139+ // TODO: per issue https://github.com/microsoft/vscode/issues/169698 vscode.CancellationError is expected,
140+ // so when VS Code fixes the error use vscode.CancellationError again.
141+ return undefined ;
119142 } else if ( response . item === undefined ) {
120143 return undefined ;
121144 }
@@ -125,76 +148,56 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider {
125148 }
126149
127150 public async provideCallHierarchyIncomingCalls ( item : vscode . CallHierarchyItem , token : vscode . CancellationToken ) :
128- Promise < vscode . CallHierarchyIncomingCall [ ] | undefined | any > {
129- return new Promise < vscode . CallHierarchyIncomingCall [ ] | undefined | any > ( ( resolve , reject ) => {
130- const CallHierarchyCallsToEvent : string = "CallHierarchyCallsTo" ;
131- if ( item === undefined ) {
132- this . logTelemetry ( CallHierarchyCallsToEvent , CallHierarchyRequestStatus . Failed ) ;
133- resolve ( undefined ) ;
134- return ;
135- }
136-
137- const callback : ( ) => Promise < void > = async ( ) => {
138- await this . client . awaitUntilLanguageClientReady ( ) ;
139- const params : CallHierarchyParams = {
140- textDocument : { uri : item . uri . toString ( ) } ,
141- position : Position . create ( item . range . start . line , item . range . start . character )
142- } ;
143- DefaultClient . referencesParams = params ;
144- // The current request is represented by referencesParams. If a request detects
145- // referencesParams does not match the object used when creating the request, abort it.
146- if ( params !== DefaultClient . referencesParams ) {
147- // Complete with nothing instead of rejecting, to avoid an error message from VS Code
148- resolve ( undefined ) ;
149- }
150- DefaultClient . referencesRequestPending = true ;
151-
152- // Register a single-fire handler for the reply.
153- const resultCallback : CallHierarchyResultCallback =
154- ( result : CallHierarchyCallsItemResult | null , canceledByUser : boolean , progressBarDuration ?: number ) => {
155- DefaultClient . referencesRequestPending = false ;
156- if ( result === null || result ?. calls === undefined ) {
157- const requestStatus : CallHierarchyRequestStatus = canceledByUser ? CallHierarchyRequestStatus . CaneledByUser : CallHierarchyRequestStatus . Canceled ;
158- this . logTelemetry ( CallHierarchyCallsToEvent , requestStatus , progressBarDuration ) ;
159- reject ( new vscode . CancellationError ( ) ) ;
160- } else {
161- this . logTelemetry ( CallHierarchyCallsToEvent , CallHierarchyRequestStatus . Succeeded , progressBarDuration ) ;
162- if ( result ?. calls . length === 0 ) {
163- resolve ( undefined ) ;
164- } else {
165- resolve ( this . createIncomingCalls ( result . calls ) ) ;
166- }
167- }
168-
169- this . client . clearPendingReferencesCancellations ( ) ;
170- } ;
171-
172- workspaceReferences . setCallHierarchyResultsCallback ( resultCallback ) ;
173- workspaceReferences . startCallHierarchyIncomingCalls ( params ) ;
174-
175- token . onCancellationRequested ( e => {
176- if ( params === DefaultClient . referencesParams ) {
177- this . client . cancelReferences ( ) ;
178- }
179- } ) ;
180- } ;
181-
182- if ( DefaultClient . referencesRequestPending || workspaceReferences . symbolSearchInProgress ) {
183- const cancelling : boolean = DefaultClient . referencesPendingCancellations . length > 0 ;
184- DefaultClient . referencesPendingCancellations . push ( {
185- reject : ( ) => {
186- // Complete with nothing instead of rejecting, to avoid an error message from VS Code
187- resolve ( undefined ) ;
188- } , callback
189- } ) ;
190- if ( ! cancelling ) {
191- workspaceReferences . referencesCanceled = true ;
192- this . client . languageClient . sendNotification ( CancelReferencesNotification ) ;
193- }
194- } else {
195- callback ( ) ;
196- }
151+ Promise < vscode . CallHierarchyIncomingCall [ ] | undefined > {
152+ await this . client . awaitUntilLanguageClientReady ( ) ;
153+ workspaceReferences . cancelCurrentReferenceRequest ( CancellationSender . NewRequest ) ;
154+
155+ const CallHierarchyCallsToEvent : string = "CallHierarchyCallsTo" ;
156+ if ( item === undefined ) {
157+ this . logTelemetry ( CallHierarchyCallsToEvent , CallHierarchyRequestStatus . Failed ) ;
158+ return undefined ;
159+ }
160+
161+ // Listen to a cancellation for this request. When this request is cancelled,
162+ // use a local cancellation source to explicitly cancel a token.
163+ let requestCanceled : CancellationSender | undefined ;
164+ const cancelSource : vscode . CancellationTokenSource = new vscode . CancellationTokenSource ( ) ;
165+ const cancellationTokenListener : vscode . Disposable = token . onCancellationRequested ( ( ) => {
166+ requestCanceled = CancellationSender . ProviderToken ;
167+ cancelSource . cancel ( ) ;
197168 } ) ;
169+ const requestCanceledListener : vscode . Disposable = workspaceReferences . onCancellationRequested ( sender => {
170+ requestCanceled = sender ;
171+ cancelSource . cancel ( ) ;
172+ } ) ;
173+
174+ // Send the request to the language server.
175+ let result : vscode . CallHierarchyIncomingCall [ ] | undefined ;
176+ const params : CallHierarchyParams = {
177+ textDocument : { uri : item . uri . toString ( ) } ,
178+ position : Position . create ( item . range . start . line , item . range . start . character )
179+ } ;
180+ const response : CallHierarchyCallsItemResult = await this . client . languageClient . sendRequest ( CallHierarchyCallsToRequest , params , cancelSource . token ) ;
181+
182+ // Reset anything that can be cleared before processing the result.
183+ const progressBarDuration : number | undefined = workspaceReferences . getCallHierarchyProgressBarDuration ( ) ;
184+ workspaceReferences . resetProgressBar ( ) ;
185+ workspaceReferences . resetReferences ( ) ;
186+ cancellationTokenListener . dispose ( ) ;
187+ requestCanceledListener . dispose ( ) ;
188+
189+ // Process the result.
190+ if ( cancelSource . token . isCancellationRequested || response . calls === undefined || requestCanceled !== undefined ) {
191+ const requestStatus : CallHierarchyRequestStatus = requestCanceled === CancellationSender . User ?
192+ CallHierarchyRequestStatus . CanceledByUser : CallHierarchyRequestStatus . Canceled ;
193+ this . logTelemetry ( CallHierarchyCallsToEvent , requestStatus , progressBarDuration ) ;
194+ throw new vscode . CancellationError ( ) ;
195+ } else if ( response . calls . length !== 0 ) {
196+ result = this . createIncomingCalls ( response . calls ) ;
197+ }
198+
199+ this . logTelemetry ( CallHierarchyCallsToEvent , CallHierarchyRequestStatus . Succeeded , progressBarDuration ) ;
200+ return result ;
198201 }
199202
200203 public async provideCallHierarchyOutgoingCalls ( item : vscode . CallHierarchyItem , token : vscode . CancellationToken ) :
@@ -212,8 +215,8 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider {
212215 textDocument : { uri : item . uri . toString ( ) } ,
213216 position : Position . create ( item . range . start . line , item . range . start . character )
214217 } ;
215-
216218 const response : CallHierarchyCallsItemResult = await this . client . languageClient . sendRequest ( CallHierarchyCallsFromRequest , params , token ) ;
219+
217220 if ( token . isCancellationRequested || response . calls === undefined ) {
218221 this . logTelemetry ( CallHierarchyCallsFromEvent , CallHierarchyRequestStatus . Canceled ) ;
219222 throw new vscode . CancellationError ( ) ;
@@ -280,7 +283,7 @@ export class CallHierarchyProvider implements vscode.CallHierarchyProvider {
280283 case CallHierarchyRequestStatus . Unknown : status = "Unknown" ; break ;
281284 case CallHierarchyRequestStatus . Succeeded : status = "Succeeded" ; break ;
282285 case CallHierarchyRequestStatus . Canceled : status = "Canceled" ; break ;
283- case CallHierarchyRequestStatus . CaneledByUser : status = "CaneledByUser " ; break ;
286+ case CallHierarchyRequestStatus . CanceledByUser : status = "CanceledByUser " ; break ;
284287 case CallHierarchyRequestStatus . Failed : status = "Failed" ; break ;
285288 }
286289
0 commit comments