// 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.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Completion;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServer.CustomProtocol;
using Microsoft.VisualStudio.Text.Adornments;
using Newtonsoft.Json.Linq;
using LSP = Microsoft.VisualStudio.LanguageServer.Protocol;
namespace Microsoft.CodeAnalysis.LanguageServer.Handler
{
///
/// Handle a completion resolve request to add description.
///
[Shared]
[ExportLspMethod(LSP.Methods.TextDocumentCompletionResolveName)]
internal class CompletionResolveHandler : AbstractRequestHandler
{
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public CompletionResolveHandler(ILspSolutionProvider solutionProvider) : base(solutionProvider)
{
}
public override async Task HandleRequestAsync(LSP.CompletionItem completionItem, LSP.ClientCapabilities clientCapabilities,
string? clientName, CancellationToken cancellationToken)
{
CompletionResolveData data;
if (completionItem.Data is CompletionResolveData)
{
data = (CompletionResolveData)completionItem.Data;
}
else
{
data = ((JToken)completionItem.Data).ToObject();
}
var request = data.CompletionParams;
var document = SolutionProvider.GetDocument(request.TextDocument, clientName);
if (document == null)
{
return completionItem;
}
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 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 = 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;
}
private static LSP.VSCompletionItem CloneVSCompletionItem(LSP.CompletionItem completionItem)
{
return new LSP.VSCompletionItem
{
AdditionalTextEdits = completionItem.AdditionalTextEdits,
Command = completionItem.Command,
CommitCharacters = completionItem.CommitCharacters,
Data = completionItem.Data,
Detail = completionItem.Detail,
Documentation = completionItem.Documentation,
FilterText = completionItem.FilterText,
InsertText = completionItem.InsertText,
InsertTextFormat = completionItem.InsertTextFormat,
Kind = completionItem.Kind,
Label = completionItem.Label,
SortText = completionItem.SortText,
TextEdit = completionItem.TextEdit
};
}
}
}