private static void AssociateRequest(LanguageServerKind requestKind, CompletionItem item, CompletionRequestContextCache cache, object originalData = null) { var documentUri = new Uri("C:/path/to/file.razor"); var projectedUri = new Uri("C:/path/to/file.razor.g.xyz"); var requestContext = new CompletionRequestContext(documentUri, projectedUri, requestKind); var resultId = cache.Set(requestContext); var data = new CompletionResolveData() { ResultId = resultId, OriginalData = originalData, }; item.Data = data; }
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 { var completionContext = RewriteContext(request.Context, projectionResult.LanguageKind); // This is a valid non-provisional completion request. var completionParams = new CompletionParams() { Context = completionContext, Position = projectionResult.Position, TextDocument = new TextDocumentIdentifier() { Uri = projectionResult.Uri } }; result = await _requestInvoker.ReinvokeRequestOnServerAsync <CompletionParams, SumType <CompletionItem[], CompletionList>?>( Methods.TextDocumentCompletionName, serverKind.ToContentType(), completionParams, cancellationToken).ConfigureAwait(false); } if (TryConvertToCompletionList(result, out var completionList)) { var wordExtent = documentSnapshot.Snapshot.GetWordExtent(request.Position.Line, request.Position.Character, _textStructureNavigator); if (serverKind == LanguageServerKind.CSharp) { completionList = PostProcessCSharpCompletionList(request, documentSnapshot, wordExtent, completionList); } completionList = TranslateTextEdits(request.Position, projectionResult.Position, wordExtent, completionList); var requestContext = new CompletionRequestContext(documentSnapshot.Uri, projectionResult.Uri, serverKind); var resultId = _completionRequestContextCache.Set(requestContext); SetResolveData(resultId, completionList); } return(completionList);
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);