// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable enable using System; using System.Collections.Immutable; using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Editor.Shared.Extensions; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.LanguageServer.CustomProtocol; using Microsoft.VisualStudio.Text.Adornments; using LSP = Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.CodeAnalysis.LanguageServer.Handler { /// /// Handle a completion request. /// [Shared] [ExportLspMethod(LSP.Methods.TextDocumentCompletionName)] internal class CompletionHandler : AbstractRequestHandler { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public CompletionHandler(ILspSolutionProvider solutionProvider) : base(solutionProvider) { } public override async Task HandleRequestAsync(LSP.CompletionParams request, LSP.ClientCapabilities clientCapabilities, string? clientName, CancellationToken cancellationToken) { var document = SolutionProvider.GetDocument(request.TextDocument, clientName); if (document == null) { return Array.Empty(); } var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); // Filter out unimported types for now as there are two issues with providing them: // 1. LSP client does not currently provide a way to provide detail text on the completion item to show the namespace. // https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1076759 // 2. We need to figure out how to provide the text edits along with the completion item or provide them in the resolve request. // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/985860/ // 3. LSP client should support completion filters / expanders var documentOptions = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); var completionOptions = documentOptions .WithChangedOption(CompletionOptions.ShowItemsFromUnimportedNamespaces, false) .WithChangedOption(CompletionServiceOptions.IsExpandedCompletion, false); var completionService = document.Project.LanguageServices.GetRequiredService(); var list = await completionService.GetCompletionsAsync(document, position, options: completionOptions, cancellationToken: cancellationToken).ConfigureAwait(false); if (list == null) { return Array.Empty(); } var lspVSClientCapability = clientCapabilities?.HasVisualStudioLspCapability() == true; return list.Items.Select(item => CreateLSPCompletionItem(request, item, lspVSClientCapability)).ToArray(); // local functions static LSP.CompletionItem CreateLSPCompletionItem(LSP.CompletionParams request, CompletionItem item, bool useVSCompletionItem) { if (useVSCompletionItem) { var vsCompletionItem = CreateCompletionItem(request, item); vsCompletionItem.Icon = new ImageElement(item.Tags.GetFirstGlyph().GetImageId()); return vsCompletionItem; } else { var roslynCompletionItem = CreateCompletionItem(request, item); roslynCompletionItem.Tags = item.Tags.ToArray(); return roslynCompletionItem; } } static TCompletionItem CreateCompletionItem(LSP.CompletionParams request, CompletionItem item) where TCompletionItem : LSP.CompletionItem, new() => new TCompletionItem { Label = item.DisplayTextPrefix + item.DisplayText + item.DisplayTextSuffix, InsertText = item.Properties.ContainsKey("InsertionText") ? item.Properties["InsertionText"] : item.DisplayText, SortText = item.SortText, FilterText = item.FilterText, Kind = GetCompletionKind(item.Tags), Data = new CompletionResolveData { CompletionParams = request, DisplayText = item.DisplayText } }; } private static LSP.CompletionItemKind GetCompletionKind(ImmutableArray tags) { foreach (var tag in tags) { if (ProtocolConversions.RoslynTagToCompletionItemKind.TryGetValue(tag, out var completionItemKind)) { return completionItemKind; } } return LSP.CompletionItemKind.Text; } } }