public async Task <LSP.CompletionItem> HandleRequestAsync(LSP.CompletionItem completionItem, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document == null) { return(completionItem); } var completionService = document.Project.LanguageServices.GetRequiredService <CompletionService>(); var data = GetCompletionResolveData(completionItem); CompletionList?list = null; // See if we have a cache of the completion list we need if (data.ResultId.HasValue) { list = await _completionListCache.GetCachedCompletionListAsync(data.ResultId.Value, cancellationToken).ConfigureAwait(false); } if (list == null) { // We don't have a cache, so we need to recompute the list var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(data.Position), cancellationToken).ConfigureAwait(false); var completionOptions = await CompletionHandler.GetCompletionOptionsAsync(document, cancellationToken).ConfigureAwait(false); list = await completionService.GetCompletionsAsync(document, position, data.CompletionTrigger, options : completionOptions, cancellationToken : cancellationToken).ConfigureAwait(false); if (list == null) { return(completionItem); } } var selectedItem = list.Items.FirstOrDefault(i => i.DisplayText == data.DisplayText); if (selectedItem == null) { return(completionItem); } var description = await completionService.GetDescriptionAsync(document, selectedItem, cancellationToken).ConfigureAwait(false); var lspVSClientCapability = context.ClientCapabilities?.HasVisualStudioLspCapability() == true; LSP.CompletionItem resolvedCompletionItem; if (lspVSClientCapability) { resolvedCompletionItem = CloneVSCompletionItem(completionItem); ((LSP.VSCompletionItem)resolvedCompletionItem).Description = new ClassifiedTextElement(description.TaggedParts .Select(tp => new ClassifiedTextRun(tp.Tag.ToClassificationTypeName(), tp.Text))); } else { resolvedCompletionItem = RoslynCompletionItem.From(completionItem); ((RoslynCompletionItem)resolvedCompletionItem).Description = description.TaggedParts.Select( tp => new RoslynTaggedText { Tag = tp.Tag, Text = tp.Text }).ToArray(); } resolvedCompletionItem.Detail = description.TaggedParts.GetFullText(); return(resolvedCompletionItem); }
public async Task <LSP.CompletionItem> HandleRequestAsync(LSP.CompletionItem completionItem, RequestContext context, CancellationToken cancellationToken) { var document = context.Document; if (document == null) { return(completionItem); } var completionService = document.Project.LanguageServices.GetRequiredService <CompletionService>(); var data = GetCompletionResolveData(completionItem); CompletionList?list = null; // See if we have a cache of the completion list we need if (data.ResultId.HasValue) { list = await _completionListCache.GetCachedCompletionListAsync(data.ResultId.Value, cancellationToken).ConfigureAwait(false); } if (list == null) { // We don't have a cache, so we need to recompute the list var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(data.Position), cancellationToken).ConfigureAwait(false); var completionOptions = await CompletionHandler.GetCompletionOptionsAsync(document, cancellationToken).ConfigureAwait(false); list = await completionService.GetCompletionsAsync(document, position, data.CompletionTrigger, options : completionOptions, cancellationToken : cancellationToken).ConfigureAwait(false); if (list == null) { return(completionItem); } } // Find the matching completion item in the completion list var selectedItem = list.Items.FirstOrDefault( i => data.DisplayText == i.DisplayText && completionItem.Label.StartsWith(i.DisplayTextPrefix) && completionItem.Label.EndsWith(i.DisplayTextSuffix)); if (selectedItem == null) { return(completionItem); } var description = await completionService.GetDescriptionAsync(document, selectedItem, cancellationToken).ConfigureAwait(false); if (completionItem is LSP.VSCompletionItem vsCompletionItem) { vsCompletionItem.Description = new ClassifiedTextElement(description.TaggedParts .Select(tp => new ClassifiedTextRun(tp.Tag.ToClassificationTypeName(), tp.Text))); } // We compute the TextEdit resolves for complex text edits (e.g. override and partial // method completions) here. Lazily resolving TextEdits is technically a violation of // the LSP spec, but is currently supported by the VS client anyway. Once the VS client // adheres to the spec, this logic will need to change and VS will need to provide // official support for TextEdit resolution in some form. if (selectedItem.IsComplexTextEdit) { Contract.ThrowIfTrue(completionItem.InsertText != null); Contract.ThrowIfTrue(completionItem.TextEdit != null); var snippetsSupported = context.ClientCapabilities.TextDocument?.Completion?.CompletionItem?.SnippetSupport ?? false; completionItem.TextEdit = await GenerateTextEditAsync( document, completionService, selectedItem, snippetsSupported, cancellationToken).ConfigureAwait(false); } completionItem.Detail = description.TaggedParts.GetFullText(); return(completionItem); }