// Internal for testing internal async override Task <VSInternalReferenceItem[]> HandleRequestAsync(ReferenceParams request, ClientCapabilities clientCapabilities, string token, CancellationToken cancellationToken) { if (request is null) { throw new ArgumentNullException(nameof(request)); } if (clientCapabilities is null) { throw new ArgumentNullException(nameof(clientCapabilities)); } _logger.LogInformation($"Starting request for {request.TextDocument.Uri}."); if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot)) { _logger.LogWarning($"Failed to find document {request.TextDocument.Uri}."); return(null); } var projectionResult = await _projectionProvider.GetProjectionAsync( documentSnapshot, request.Position, cancellationToken).ConfigureAwait(false); if (projectionResult == null) { return(null); } cancellationToken.ThrowIfCancellationRequested(); var referenceParams = new SerializableReferenceParams() { Position = projectionResult.Position, TextDocument = new TextDocumentIdentifier() { Uri = projectionResult.Uri }, Context = request.Context, PartialResultToken = token // request.PartialResultToken }; _logger.LogInformation("Attaching to progress listener."); if (!_lspProgressListener.TryListenForProgress( token, onProgressNotifyAsync: (value, ct) => ProcessReferenceItemsAsync(value, request.PartialResultToken, ct), DelayAfterLastNotifyAsync, cancellationToken, out var onCompleted)) { _logger.LogWarning("Failed to attach to progress listener."); return(null); } _logger.LogInformation($"Requesting references for {projectionResult.Uri}."); var response = await _requestInvoker.ReinvokeRequestOnServerAsync <SerializableReferenceParams, VSInternalReferenceItem[]>( Methods.TextDocumentReferencesName, projectionResult.LanguageKind.ToContainedLanguageServerName(), referenceParams, cancellationToken).ConfigureAwait(false); var result = response.Result; if (result is null) { _logger.LogInformation("Received no results from initial request."); return(null); } cancellationToken.ThrowIfCancellationRequested(); _logger.LogInformation("Waiting on progress notifications."); // We must not return till we have received the progress notifications // and reported the results via the PartialResultToken await onCompleted.ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); _logger.LogInformation("Finished waiting, remapping results."); // Results returned through Progress notification var remappedResults = await RemapReferenceItemsAsync(result, cancellationToken).ConfigureAwait(false); _logger.LogInformation($"Returning {remappedResults.Length} results."); return(remappedResults); // Local functions async Task DelayAfterLastNotifyAsync(CancellationToken cancellationToken) { using var combined = ImmediateNotificationTimeout.CombineWith(cancellationToken); try { await Task.Delay(WaitForProgressNotificationTimeout, combined.Token).ConfigureAwait(false); } catch (TaskCanceledException) when(ImmediateNotificationTimeout.IsCancellationRequested) { // The delay was requested to complete immediately } } }
// Internal for testing internal async override Task <VSReferenceItem[]> HandleRequestAsync(ReferenceParams request, ClientCapabilities clientCapabilities, string token, CancellationToken cancellationToken) { if (request is null) { throw new ArgumentNullException(nameof(request)); } if (clientCapabilities is null) { throw new ArgumentNullException(nameof(clientCapabilities)); } if (!_documentManager.TryGetDocument(request.TextDocument.Uri, out var documentSnapshot)) { return(null); } var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, request.Position, cancellationToken).ConfigureAwait(false); if (projectionResult == null) { return(null); } cancellationToken.ThrowIfCancellationRequested(); var referenceParams = new SerializableReferenceParams() { Position = projectionResult.Position, TextDocument = new TextDocumentIdentifier() { Uri = projectionResult.Uri }, Context = request.Context, PartialResultToken = token // request.PartialResultToken }; if (!_lspProgressListener.TryListenForProgress( token, onProgressNotifyAsync: (value, ct) => ProcessReferenceItemsAsync(value, request.PartialResultToken, ct), WaitForProgressNotificationTimeout, cancellationToken, out var onCompleted)) { return(null); } var result = await _requestInvoker.ReinvokeRequestOnServerAsync <SerializableReferenceParams, VSReferenceItem[]>( Methods.TextDocumentReferencesName, projectionResult.LanguageKind.ToContainedLanguageContentType(), referenceParams, cancellationToken).ConfigureAwait(false); if (result == null) { return(null); } cancellationToken.ThrowIfCancellationRequested(); // We must not return till we have received the progress notifications // and reported the results via the PartialResultToken await onCompleted.ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); // Results returned through Progress notification var remappedResults = await RemapReferenceItemsAsync(result, cancellationToken).ConfigureAwait(false); return(remappedResults); }