Ejemplo n.º 1
0
        public async Task <SignatureHelp?> HandleRequestAsync(TextDocumentPositionParams request, ClientCapabilities clientCapabilities, 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 is null)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var textDocumentPositionParams = new TextDocumentPositionParams()
            {
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                },
            };

            _logger.LogInformation($"Requesting signature help for {projectionResult.Uri}.");

            var serverKind         = projectionResult.LanguageKind.ToLanguageServerKind();
            var languageServerName = serverKind.ToLanguageServerName();
            var textBuffer         = serverKind.GetTextBuffer(documentSnapshot);
            var response           = await _requestInvoker.ReinvokeRequestOnServerAsync <TextDocumentPositionParams, SignatureHelp>(
                textBuffer,
                Methods.TextDocumentSignatureHelpName,
                languageServerName,
                textDocumentPositionParams,
                cancellationToken).ConfigureAwait(false);

            if (!ReinvocationResponseHelper.TryExtractResultOrLog(response, _logger, languageServerName, out var signatureHelp))
            {
                return(null);
            }

            _logger.LogInformation("Returning result.");

            return(signatureHelp);
        }
        // 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),
                    DelayAfterLastNotifyAsync,
                    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);

            // 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
                }
            }
        }
        public async Task <DocumentHighlight[]> HandleRequestAsync(DocumentHighlightParams request, ClientCapabilities clientCapabilities, 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);
            }

            var serverKind = projectionResult.LanguageKind == RazorLanguageKind.CSharp ? LanguageServerKind.CSharp : LanguageServerKind.Html;

            cancellationToken.ThrowIfCancellationRequested();

            var documentHighlightParams = new DocumentHighlightParams()
            {
                Position     = projectionResult.Position,
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = projectionResult.Uri
                }
            };

            _logger.LogInformation($"Requesting highlights for {projectionResult.Uri} at ({projectionResult.Position?.Line}, {projectionResult.Position?.Character}).");

            var languageServerName = serverKind.ToLanguageServerName();
            var textBuffer         = serverKind.GetTextBuffer(documentSnapshot);
            var response           = await _requestInvoker.ReinvokeRequestOnServerAsync <DocumentHighlightParams, DocumentHighlight[]>(
                textBuffer,
                Methods.TextDocumentDocumentHighlightName,
                languageServerName,
                documentHighlightParams,
                cancellationToken).ConfigureAwait(false);

            if (!ReinvocationResponseHelper.TryExtractResultOrLog(response, _logger, languageServerName, out var highlights))
            {
                return(null);
            }

            if (highlights.Length == 0)
            {
                _logger.LogInformation("Received no results.");
                return(highlights);
            }

            _logger.LogInformation($"Received {highlights.Length} results, remapping.");

            var remappedHighlights = new List <DocumentHighlight>();

            var rangesToMap   = highlights.Select(r => r.Range).ToArray();
            var mappingResult = await _documentMappingProvider.MapToDocumentRangesAsync(
                projectionResult.LanguageKind,
                request.TextDocument.Uri,
                rangesToMap,
                cancellationToken).ConfigureAwait(false);

            if (mappingResult == null || mappingResult.HostDocumentVersion != documentSnapshot.Version)
            {
                // Couldn't remap the range or the document changed in the meantime. Discard this highlight.
                _logger.LogInformation($"Mapping failed. Versions: {documentSnapshot.Version} -> {mappingResult?.HostDocumentVersion}.");
                return(Array.Empty <DocumentHighlight>());
            }

            for (var i = 0; i < highlights.Length; i++)
            {
                var highlight = highlights[i];
                var range     = mappingResult.Ranges[i];
                if (range.IsUndefined())
                {
                    // Couldn't remap the range correctly. Discard this range.
                    continue;
                }

                var remappedHighlight = new DocumentHighlight()
                {
                    Range = range,
                    Kind  = highlight.Kind
                };

                remappedHighlights.Add(remappedHighlight);
            }

            _logger.LogInformation($"Returning {remappedHighlights.Count} highlights.");
            return(remappedHighlights.ToArray());
        }
Ejemplo n.º 4
0
        public async Task <SumType <CompletionItem[], CompletionList>?> HandleRequestAsync(CompletionParams request, ClientCapabilities clientCapabilities, 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);
            }

            var serverKind = projectionResult.LanguageKind == RazorLanguageKind.CSharp ? LanguageServerKind.CSharp : LanguageServerKind.Html;

            var(succeeded, result) = await TryGetProvisionalCompletionsAsync(request, documentSnapshot, projectionResult, cancellationToken).ConfigureAwait(false);

            if (succeeded)
            {
                // This means the user has just typed a dot after some identifier such as (cursor is pipe): "DateTime.| "
                // In this case Razor interprets after the dot as Html and before it as C#.
                // We use this criteria to provide a better completion experience for what we call provisional changes.
            }
            else if (!TriggerAppliesToProjection(request.Context, projectionResult.LanguageKind))
            {
                return(null);
            }
            else
            {
                // This is a valid non-provisional completion request.
                var completionParams = new CompletionParams()
                {
                    Context      = request.Context,
                    Position     = projectionResult.Position,
                    TextDocument = new TextDocumentIdentifier()
                    {
                        Uri = projectionResult.Uri
                    }
                };

                result = await _requestInvoker.ReinvokeRequestOnServerAsync <CompletionParams, SumType <CompletionItem[], CompletionList>?>(
                    Methods.TextDocumentCompletionName,
                    serverKind,
                    completionParams,
                    cancellationToken).ConfigureAwait(false);
            }

            if (result.HasValue)
            {
                // Set some context on the CompletionItem so the CompletionResolveHandler can handle it accordingly.
                result = SetResolveData(result.Value, serverKind);
            }

            return(result);
        }
Ejemplo n.º 5
0
        public async Task <SumType <CompletionItem[], CompletionList>?> HandleRequestAsync(CompletionParams request, ClientCapabilities clientCapabilities, 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);
            }

            if (request.Context is null)
            {
                _logger.LogWarning($"No Context available when document was found.");
                return(null);
            }

            if (!TryGetWordExtent(request, documentSnapshot, out var wordExtent))
            {
                return(null);
            }

            var projectionResult = await _projectionProvider.GetProjectionForCompletionAsync(
                documentSnapshot,
                request.Position,
                cancellationToken).ConfigureAwait(false);

            if (projectionResult is null)
            {
                return(null);
            }

            var projectedPosition    = projectionResult.Position;
            var projectedDocumentUri = projectionResult.Uri;
            var serverKind           = projectionResult.LanguageKind == RazorLanguageKind.CSharp ? LanguageServerKind.CSharp : LanguageServerKind.Html;
            var languageServerName   = projectionResult.LanguageKind == RazorLanguageKind.CSharp ? RazorLSPConstants.RazorCSharpLanguageServerName : RazorLSPConstants.HtmlLanguageServerName;
            SumType <CompletionItem[], CompletionList>?result = null;
            var provisionalCompletionResult = await TryGetProvisionalCompletionsAsync(request, documentSnapshot, projectionResult, cancellationToken).ConfigureAwait(false);

            if (provisionalCompletionResult.Success)
            {
                // This means the user has just typed a dot after some identifier such as (cursor is pipe): "DateTime.| "
                // In this case Razor interprets after the dot as Html and before it as C#.
                // We use this criteria to provide a better completion experience for what we call provisional changes.
                serverKind = LanguageServerKind.CSharp;
                if (documentSnapshot.TryGetVirtualDocument <CSharpVirtualDocumentSnapshot>(out var csharpVirtualDocumentSnapshot))
                {
                    result = provisionalCompletionResult.Result;
                    projectedDocumentUri = csharpVirtualDocumentSnapshot.Uri;
                    projectionResult     = provisionalCompletionResult.ProvisionalProjection;
                    projectedPosition    = projectionResult !.Position;
                }
                else
                {
                    _logger.LogError("Could not acquire C# virtual document snapshot after provisional completion.");
                }
            }
            else if (!TriggerAppliesToProjection(request.Context, projectionResult.LanguageKind))
            {
                _logger.LogInformation("Trigger does not apply to projection.");
                return(null);
            }
            else
            {
                // This is a valid non-provisional completion request.
                _logger.LogInformation("Searching for non-provisional completions, rewriting context.");

                var completionContext = RewriteContext(request.Context, projectionResult.LanguageKind);

                var completionParams = new CompletionParams()
                {
                    Context      = completionContext,
                    Position     = projectedPosition,
                    TextDocument = new TextDocumentIdentifier()
                    {
                        Uri = projectedDocumentUri
                    }
                };

                _logger.LogInformation($"Requesting non-provisional completions for {projectedDocumentUri}.");

                var textBuffer = serverKind.GetTextBuffer(documentSnapshot);
                var response   = await _requestInvoker.ReinvokeRequestOnServerAsync <CompletionParams, SumType <CompletionItem[], CompletionList>?>(
                    textBuffer,
                    Methods.TextDocumentCompletionName,
                    languageServerName,
                    completionParams,
                    cancellationToken).ConfigureAwait(false);

                if (!ReinvocationResponseHelper.TryExtractResultOrLog(response, _logger, languageServerName, out result))
                {
                    return(null);
                }

                _logger.LogInformation("Found non-provisional completion");
            }

            if (TryConvertToCompletionList(result, out var completionList))
            {
                if (serverKind == LanguageServerKind.CSharp)
                {
                    completionList = PostProcessCSharpCompletionList(request, documentSnapshot, wordExtent.Value, completionList);
                }

                var wordRange = wordExtent.HasValue && wordExtent.Value.IsSignificant ? wordExtent?.Span.AsRange() : null;
                completionList = TranslateTextEdits(request.Position, projectedPosition, wordRange, completionList);

                if (completionList.ItemDefaults?.EditRange != null)
                {
                    completionList.ItemDefaults.EditRange = TranslateRange(request.Position, projectedPosition, wordRange, completionList.ItemDefaults.EditRange);
                }

                var requestContext = new CompletionRequestContext(documentSnapshot.Uri, projectedDocumentUri, serverKind);
                var resultId       = _completionRequestContextCache.Set(requestContext);
                SetResolveData(resultId, completionList);
            }

            if (completionList != null)
            {
                completionList = completionList is VSInternalCompletionList vsCompletionList
                    ? new OptimizedVSCompletionList(vsCompletionList)
                    : new OptimizedVSCompletionList(completionList);
            }

            _logger.LogInformation("Returning completion list.");
            return(completionList);