From 9b6420dbfc683d734620241b9eac90336c02c321 Mon Sep 17 00:00:00 2001 From: Ajay Bhargav Baaskaran Date: Tue, 22 Aug 2017 16:55:03 -0700 Subject: [PATCH] Include currently selected attribute in TagHelper completion results --- .../AttributeCompletionContext.cs | 4 + .../DefaultTagHelperCompletionService.cs | 7 +- .../DefaultTagHelperCompletionServiceTest.cs | 104 ++++++++++++++++++ 3 files changed, 113 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/AttributeCompletionContext.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/AttributeCompletionContext.cs index bbf5c60dac..4fec8d13bd 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/AttributeCompletionContext.cs +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/AttributeCompletionContext.cs @@ -13,6 +13,7 @@ namespace Microsoft.CodeAnalysis.Razor TagHelperDocumentContext documentContext, IEnumerable existingCompletions, string currentTagName, + string currentAttributeName, IEnumerable> attributes, string currentParentTagName, Func inHTMLSchema) @@ -45,6 +46,7 @@ namespace Microsoft.CodeAnalysis.Razor DocumentContext = documentContext; ExistingCompletions = existingCompletions; CurrentTagName = currentTagName; + CurrentAttributeName = currentAttributeName; Attributes = attributes; CurrentParentTagName = currentParentTagName; InHTMLSchema = inHTMLSchema; @@ -56,6 +58,8 @@ namespace Microsoft.CodeAnalysis.Razor public string CurrentTagName { get; } + public string CurrentAttributeName { get; } + public IEnumerable> Attributes { get; } public string CurrentParentTagName { get; } diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionService.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionService.cs index 09fd620fa8..e5c1ee2895 100644 --- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionService.cs +++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperCompletionService.cs @@ -109,9 +109,12 @@ namespace Microsoft.CodeAnalysis.Razor void UpdateCompletions(string attributeName, BoundAttributeDescriptor possibleDescriptor) { - if (completionContext.Attributes.Any(attribute => string.Equals(attribute.Key, attributeName, StringComparison.OrdinalIgnoreCase))) + if (completionContext.Attributes.Any(attribute => string.Equals(attribute.Key, attributeName, StringComparison.OrdinalIgnoreCase)) && + (completionContext.CurrentAttributeName == null || + !string.Equals(attributeName, completionContext.CurrentAttributeName, StringComparison.OrdinalIgnoreCase))) { - // Attribute is already present on this element it shouldn't exist in the completion list. + // Attribute is already present on this element and it is not the attribute in focus. + // It shouldn't exist in the completion list. return; } diff --git a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperCompletionServiceTest.cs b/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperCompletionServiceTest.cs index 8c79de0a5e..3b7ad930cf 100644 --- a/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperCompletionServiceTest.cs +++ b/test/Microsoft.CodeAnalysis.Razor.Workspaces.Test/DefaultTagHelperCompletionServiceTest.cs @@ -61,6 +61,108 @@ namespace Microsoft.CodeAnalysis.Razor AssertCompletionsAreEquivalent(expectedCompletions, completions); } + [Fact] + public void GetAttributeCompletions_ReturnsCompletionForAlreadySuppliedAttribute_IfCurrentAttributeMatches() + { + // Arrange + var documentDescriptors = new[] + { + TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly") + .TagMatchingRuleDescriptor(rule => rule + .RequireTagName("div") + .RequireAttributeDescriptor(attribute => attribute.Name("repeat"))) + .BoundAttributeDescriptor(attribute => attribute + .Name("visible") + .TypeName(typeof(bool).FullName) + .PropertyName("Visible")) + .Build(), + TagHelperDescriptorBuilder.Create("StyleTagHelper", "TestAssembly") + .TagMatchingRuleDescriptor(rule => rule.RequireTagName("*")) + .BoundAttributeDescriptor(attribute => attribute + .Name("class") + .TypeName(typeof(string).FullName) + .PropertyName("Class")) + .Build(), + }; + var expectedCompletions = AttributeCompletionResult.Create(new Dictionary>() + { + ["onclick"] = new HashSet(), + ["visible"] = new HashSet() + { + documentDescriptors[0].BoundAttributes.Last() + } + }); + + var existingCompletions = new[] { "onclick" }; + var completionContext = BuildAttributeCompletionContext( + documentDescriptors, + existingCompletions, + attributes: new Dictionary() + { + ["class"] = "something", + ["repeat"] = "4", + ["visible"] = "false", + }, + currentTagName: "div", + currentAttributeName: "visible"); + var service = CreateTagHelperCompletionFactsService(); + + // Act + var completions = service.GetAttributeCompletions(completionContext); + + // Assert + AssertCompletionsAreEquivalent(expectedCompletions, completions); + } + + [Fact] + public void GetAttributeCompletions_DoesNotReturnAlreadySuppliedAttribute_IfCurrentAttributeDoesNotMatch() + { + // Arrange + var documentDescriptors = new[] + { + TagHelperDescriptorBuilder.Create("DivTagHelper", "TestAssembly") + .TagMatchingRuleDescriptor(rule => rule + .RequireTagName("div") + .RequireAttributeDescriptor(attribute => attribute.Name("repeat"))) + .BoundAttributeDescriptor(attribute => attribute + .Name("visible") + .TypeName(typeof(bool).FullName) + .PropertyName("Visible")) + .Build(), + TagHelperDescriptorBuilder.Create("StyleTagHelper", "TestAssembly") + .TagMatchingRuleDescriptor(rule => rule.RequireTagName("*")) + .BoundAttributeDescriptor(attribute => attribute + .Name("class") + .TypeName(typeof(string).FullName) + .PropertyName("Class")) + .Build(), + }; + var expectedCompletions = AttributeCompletionResult.Create(new Dictionary>() + { + ["onclick"] = new HashSet() + }); + + var existingCompletions = new[] { "onclick" }; + var completionContext = BuildAttributeCompletionContext( + documentDescriptors, + existingCompletions, + attributes: new Dictionary() + { + ["class"] = "something", + ["repeat"] = "4", + ["visible"] = "false", + }, + currentTagName: "div", + currentAttributeName: "repeat"); + var service = CreateTagHelperCompletionFactsService(); + + // Act + var completions = service.GetAttributeCompletions(completionContext); + + // Assert + AssertCompletionsAreEquivalent(expectedCompletions, completions); + } + [Fact] public void GetAttributeCompletions_PossibleDescriptorsReturnUnboundRequiredAttributesWithExistingCompletions() { @@ -947,6 +1049,7 @@ namespace Microsoft.CodeAnalysis.Razor IEnumerable descriptors, IEnumerable existingCompletions, string currentTagName, + string currentAttributeName = null, IEnumerable> attributes = null, string tagHelperPrefix = "") { @@ -956,6 +1059,7 @@ namespace Microsoft.CodeAnalysis.Razor documentContext, existingCompletions, currentTagName, + currentAttributeName, attributes, currentParentTagName: "body", inHTMLSchema: (tag) => tag == "strong" || tag == "b" || tag == "bold" || tag == "li" || tag == "div");