From 539085d15667c936006c9ed7232a97534ce75dd8 Mon Sep 17 00:00:00 2001 From: Ajay Bhargav Baaskaran Date: Mon, 27 Apr 2020 14:50:54 -0700 Subject: [PATCH] Make Razor tag helper icons show up correctly --- .../CompletionItemExtensions.cs | 64 ++++++++++++++ .../DefaultRazorLanguageClientMiddleLayer.cs | 84 ++++++++++++++++++- 2 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/CompletionItemExtensions.cs diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/CompletionItemExtensions.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/CompletionItemExtensions.cs new file mode 100644 index 0000000000..c136d42419 --- /dev/null +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/CompletionItemExtensions.cs @@ -0,0 +1,64 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using Microsoft.CodeAnalysis.Razor.Completion; +using Microsoft.VisualStudio.LanguageServer.Protocol; +using Newtonsoft.Json.Linq; + +namespace Microsoft.VisualStudio.LanguageServerClient.Razor +{ + internal static class CompletionItemExtensions + { + private const string TagHelperElementDataKey = "_TagHelperElementData_"; + private const string TagHelperAttributeDataKey = "_TagHelperAttributes_"; + private const string RazorCompletionItemKind = "_CompletionItemKind_"; + + public static bool TryGetRazorCompletionKind(this CompletionItem completion, out RazorCompletionItemKind completionItemKind) + { + if (completion is null) + { + throw new ArgumentNullException(nameof(completion)); + } + + if (completion.Data is JObject dataObject && + dataObject.TryGetValue("data", out var dataToken) && + dataToken is JObject data && + data.ContainsKey(RazorCompletionItemKind)) + { + completionItemKind = data[RazorCompletionItemKind].ToObject(); + return true; + } + + completionItemKind = default; + return false; + } + + public static bool IsTagHelperElementCompletion(this CompletionItem completion) + { + if (completion.Data is JObject dataObject && + dataObject.TryGetValue("data", out var dataToken) && + dataToken is JObject data && + data.ContainsKey(TagHelperElementDataKey)) + { + return true; + } + + return false; + } + + public static bool IsTagHelperAttributeCompletion(this CompletionItem completion) + { + if (completion.Data is JObject dataObject && + dataObject.TryGetValue("data", out var dataToken) && + dataToken is JObject data && + data.ContainsKey(TagHelperAttributeDataKey)) + { + return true; + } + + return false; + } + } +} diff --git a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/DefaultRazorLanguageClientMiddleLayer.cs b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/DefaultRazorLanguageClientMiddleLayer.cs index 42104c6cfb..0c08a3a0ad 100644 --- a/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/DefaultRazorLanguageClientMiddleLayer.cs +++ b/src/Razor/src/Microsoft.VisualStudio.LanguageServerClient.Razor/DefaultRazorLanguageClientMiddleLayer.cs @@ -4,9 +4,14 @@ using System; using System.Collections.Generic; using System.Composition; +using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Razor.LanguageServer.Common; +using Microsoft.CodeAnalysis.Razor.Completion; +using Microsoft.VisualStudio.Core.Imaging; +using Microsoft.VisualStudio.Imaging; using Microsoft.VisualStudio.LanguageServer.Protocol; +using Microsoft.VisualStudio.Text.Adornments; using Microsoft.VisualStudio.Threading; using Newtonsoft.Json.Linq; using Task = System.Threading.Tasks.Task; @@ -49,7 +54,8 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor public override bool CanHandle(string methodName) { - return methodName == Methods.TextDocumentOnTypeFormattingName; + return methodName == Methods.TextDocumentOnTypeFormattingName || + methodName == Methods.TextDocumentCompletionName; } public override Task HandleNotificationAsync(string methodName, JToken methodParam, Func sendNotification) @@ -89,10 +95,86 @@ namespace Microsoft.VisualStudio.LanguageServerClient.Razor // We would have already applied the edits and moved the cursor. Return empty. return emptyResult; } + else if (methodName == Methods.TextDocumentCompletionName) + { + var response = await sendRequest(methodParam).ConfigureAwait(false); + var result = response?.ToObject?>(); + if (!result.HasValue) + { + return response; + } + + result = SetCompletionIcon(result.Value); + + return JToken.FromObject(result); + } else { return await sendRequest(methodParam).ConfigureAwait(false); } } + + // Internal for testing + internal SumType? SetCompletionIcon(SumType completionResult) + { + var result = completionResult.Match?>( + items => + { + var newItems = items.Select(item => SetIcon(item)).ToArray(); + return newItems; + }, + list => + { + var newItems = list.Items.Select(item => SetIcon(item)).ToArray(); + return new CompletionList() + { + Items = newItems, + IsIncomplete = list.IsIncomplete, + }; + }, + () => null); + + return result; + + static CompletionItem SetIcon(CompletionItem item) + { + ImageElement icon = null; + if (item.IsTagHelperElementCompletion() || item.IsTagHelperAttributeCompletion()) + { + icon = new ImageElement(new ImageId(KnownMonikers.XMLAttribute.Guid, KnownMonikers.XMLAttribute.Id)); + } + else if (item.TryGetRazorCompletionKind(out var kind) && + (kind == RazorCompletionItemKind.DirectiveAttribute || + kind == RazorCompletionItemKind.DirectiveAttributeParameter || + kind == RazorCompletionItemKind.MarkupTransition)) + { + icon = new ImageElement(new ImageId(KnownMonikers.XMLAttribute.Guid, KnownMonikers.XMLAttribute.Id)); + } + + if (icon == null) + { + return item; + } + + return new VSCompletionItem() + { + Label = item.Label, + Kind = item.Kind, + Detail = item.Detail, + Documentation = item.Documentation, + Preselect = item.Preselect, + SortText = item.SortText, + FilterText = item.FilterText, + InsertText = item.InsertText, + InsertTextFormat = item.InsertTextFormat, + TextEdit = item.TextEdit, + AdditionalTextEdits = item.AdditionalTextEdits, + CommitCharacters = item.CommitCharacters, + Command = item.Command, + Data = item.Data, + Icon = icon, + }; + } + } } }