// 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
                }
            }
        }
Example #2
0
        // 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);
        }