// 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.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 : IRequestHandler?> { public async Task?> HandleRequestAsync(Solution solution, LSP.CompletionParams request, LSP.ClientCapabilities clientCapabilities, CancellationToken cancellationToken) { var document = solution.GetDocumentFromURI(request.TextDocument.Uri); if (document == null) { return Array.Empty(); } var position = await document.GetPositionFromLinePositionAsync(ProtocolConversions.PositionToLinePosition(request.Position), cancellationToken).ConfigureAwait(false); var completionService = document.Project.LanguageServices.GetRequiredService(); var list = await completionService.GetCompletionsAsync(document, position, 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; } } }