public override async Task <RazorDiagnosticsResponse?> TranslateAsync(
            RazorLanguageKind languageKind,
            Uri razorDocumentUri,
            Diagnostic[] diagnostics,
            CancellationToken cancellationToken)
        {
            if (!_documentManager.TryGetDocument(razorDocumentUri, out var documentSnapshot))
            {
                return(new RazorDiagnosticsResponse()
                {
                    Diagnostics = Array.Empty <Diagnostic>(),
                });
            }

            var diagnosticsParams = new RazorDiagnosticsParams()
            {
                Kind             = languageKind,
                RazorDocumentUri = razorDocumentUri,
                Diagnostics      = diagnostics
            };

            var response = await _requestInvoker.ReinvokeRequestOnServerAsync <RazorDiagnosticsParams, RazorDiagnosticsResponse>(
                documentSnapshot.Snapshot.TextBuffer,
                LanguageServerConstants.RazorTranslateDiagnosticsEndpoint,
                RazorLSPConstants.RazorLanguageServerName,
                diagnosticsParams,
                cancellationToken).ConfigureAwait(false);

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

            return(result);
        }
Example #2
0
        public async Task <CompletionItem?> HandleRequestAsync(CompletionItem request, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
        {
            if (request.Data is null)
            {
                _logger.LogInformation("Received no completion resolve data.");
                return(request);
            }

            _logger.LogInformation("Starting request to resolve completion.");

            var resolveData = request.Data is CompletionResolveData data ? data : ((JToken)request.Data).ToObject <CompletionResolveData>();

            if (resolveData is null)
            {
                _logger.LogInformation("CompletionResolveData failed to serialize.");
                return(request);
            }

            // Set the original resolve data back so the language server deserializes it correctly.
            request.Data = resolveData.OriginalData;

            if (!_completionRequestContextCache.TryGet(resolveData.ResultId, out var requestContext))
            {
                _logger.LogInformation("Could not find the associated request context.");
                return(request);
            }

            if (!_documentManager.TryGetDocument(requestContext.HostDocumentUri, out var documentSnapshot))
            {
                _logger.LogError("Could not find the associated host document for completion resolve: {0}.", requestContext.HostDocumentUri);
                return(request);
            }

            var serverKind         = requestContext.LanguageServerKind;
            var languageServerName = serverKind.ToLanguageServerName();
            var textBuffer         = serverKind.GetTextBuffer(documentSnapshot);
            var response           = await _requestInvoker.ReinvokeRequestOnServerAsync <CompletionItem, CompletionItem>(
                textBuffer,
                Methods.TextDocumentCompletionResolveName,
                languageServerName,
                request,
                cancellationToken).ConfigureAwait(false);

            if (!ReinvocationResponseHelper.TryExtractResultOrLog(response, _logger, RazorLSPConstants.RazorCSharpLanguageServerName, out var result))
            {
                return(request);
            }

            _logger.LogInformation("Received result, post-processing.");

            var postProcessedResult = await PostProcessCompletionItemAsync(request, result, requestContext, documentSnapshot, cancellationToken).ConfigureAwait(false);

            _logger.LogInformation("Returning resolved completion.");
            return(postProcessedResult);
        }
Example #3
0
        public async Task <Location[]> 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 == null)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

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

            var serverKind         = projectionResult.LanguageKind.ToLanguageServerKind();
            var languageServerName = serverKind.ToLanguageServerName();

            _logger.LogInformation($"Requesting {languageServerName} implementation for {projectionResult.Uri}.");

            var textBuffer = serverKind.GetTextBuffer(documentSnapshot);
            var response   = await _requestInvoker.ReinvokeRequestOnServerAsync <TextDocumentPositionParams, Location[]>(
                textBuffer,
                Methods.TextDocumentImplementationName,
                languageServerName,
                textDocumentPositionParams,
                cancellationToken).ConfigureAwait(false);

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

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

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

            cancellationToken.ThrowIfCancellationRequested();

            var remappedLocations = await _documentMappingProvider.RemapLocationsAsync(locations, cancellationToken).ConfigureAwait(false);

            _logger.LogInformation($"Returning {remappedLocations?.Length} locations.");
            return(remappedLocations);
        }
Example #4
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);
        }
Example #5
0
        public async Task <Hover?> 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);
            }

            var languageServerName = projectionResult.LanguageKind.ToContainedLanguageServerName();

            cancellationToken.ThrowIfCancellationRequested();

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

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

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

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

            if (result.Range is null)
            {
                _logger.LogInformation("Received no results.");
                return(null);
            }

            _logger.LogInformation("Received result, remapping.");

            var mappingResult = await _documentMappingProvider.MapToDocumentRangesAsync(
                projectionResult.LanguageKind,
                request.TextDocument.Uri,
                new[] { result.Range },
                cancellationToken).ConfigureAwait(false);

            if (mappingResult is null || mappingResult.Ranges[0].IsUndefined())
            {
                // Couldn't remap the edits properly. Returning hover at initial request position.
                _logger.LogInformation("Mapping failed");
                return(CreateHover(result, new Range
                {
                    Start = request.Position,
                    End = request.Position
                }));
            }
        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 is 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?.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());
        }
        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 (!TryGetWordExtent(request, documentSnapshot, out var wordExtent))
            {
                return(null);
            }

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

            if (projectionResult == null)
            {
                if (IsRazorCompilerBugWithCSharpKeywords(request, wordExtent))
                {
                    var csharpPolyfilledCompletionList = new CompletionList()
                    {
                        Items        = Array.Empty <CompletionItem>(),
                        IsIncomplete = true,
                    };
                    csharpPolyfilledCompletionList = IncludeCSharpKeywords(csharpPolyfilledCompletionList);
                    return(csharpPolyfilledCompletionList);
                }

                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;

            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.
                serverKind = LanguageServerKind.CSharp;
                if (documentSnapshot.TryGetVirtualDocument <CSharpVirtualDocumentSnapshot>(out var csharpVirtualDocumentSnapshot))
                {
                    projectedDocumentUri = csharpVirtualDocumentSnapshot.Uri;
                }
                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, completionList);
                }

                completionList = TranslateTextEdits(request.Position, projectedPosition, wordExtent, completionList);

                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);
        public async Task <SumType <Location[]?, VSInternalReferenceItem[]?> > 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(new());
            }

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

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

            cancellationToken.ThrowIfCancellationRequested();

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

            var serverKind         = projectionResult.LanguageKind.ToLanguageServerKind();
            var languageServerName = serverKind.ToLanguageServerName();

            _logger.LogInformation($"Requesting {languageServerName} implementation for {projectionResult.Uri}.");

            var textBuffer = serverKind.GetTextBuffer(documentSnapshot);
            var response   = await _requestInvoker.ReinvokeRequestOnServerAsync <TextDocumentPositionParams, SumType <Location[]?, VSInternalReferenceItem[]?> >(
                textBuffer,
                Methods.TextDocumentImplementationName,
                languageServerName,
                textDocumentPositionParams,
                cancellationToken).ConfigureAwait(false);

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

            cancellationToken.ThrowIfCancellationRequested();

            // From some language servers we get VSInternalReferenceItem results, and from some we get Location results.
            // We check for the _vs_id property, which is required in VSInternalReferenceItem, to know which is which.
            if (result.Value is VSInternalReferenceItem[] referenceItems)
            {
                var remappedLocations = await FindAllReferencesHandler.RemapReferenceItemsAsync(referenceItems, _documentMappingProvider, _documentManager, cancellationToken).ConfigureAwait(false);

                _logger.LogInformation($"Returning {remappedLocations?.Length} internal reference items.");
                return(remappedLocations);
            }
            else if (result.Value is Location[] locations)
            {
                var remappedLocations = await _documentMappingProvider.RemapLocationsAsync(locations, cancellationToken).ConfigureAwait(false);

                _logger.LogInformation($"Returning {remappedLocations?.Length} locations.");

                return(remappedLocations);
            }

            _logger.LogInformation("Received no results.");
            return(Array.Empty <VSInternalReferenceItem>());
        }