зеркало из https://github.com/dotnet/razor.git
Update TagHelper completion to understand dictionary attributes.
- Since dictionaries fill `TagHelperDescriptor`s with two ways to bind to a `TagHelper` (prefix or full match) we need to take into account their indexer name prefix when providing attribute completions. - Added tests to ensure that bound and unbound scenarios work as expected. #7759
This commit is contained in:
Родитель
4c8e63e610
Коммит
e3d89c262e
|
@ -88,11 +88,25 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
foreach (var attributeDescriptor in descriptor.BoundAttributes)
|
foreach (var attributeDescriptor in descriptor.BoundAttributes)
|
||||||
{
|
{
|
||||||
UpdateCompletions(attributeDescriptor.Name, attributeDescriptor);
|
UpdateCompletions(attributeDescriptor.Name, attributeDescriptor);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(attributeDescriptor.IndexerNamePrefix))
|
||||||
|
{
|
||||||
|
UpdateCompletions(attributeDescriptor.IndexerNamePrefix + "...", attributeDescriptor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var htmlNameToBoundAttribute = descriptor.BoundAttributes.ToDictionary(attribute => attribute.Name, StringComparer.OrdinalIgnoreCase);
|
var htmlNameToBoundAttribute = new Dictionary<string, BoundAttributeDescriptor>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var attributeDescriptor in descriptor.BoundAttributes)
|
||||||
|
{
|
||||||
|
htmlNameToBoundAttribute[attributeDescriptor.Name] = attributeDescriptor;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(attributeDescriptor.IndexerNamePrefix))
|
||||||
|
{
|
||||||
|
htmlNameToBoundAttribute[attributeDescriptor.IndexerNamePrefix] = attributeDescriptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var rule in descriptor.TagMatchingRules)
|
foreach (var rule in descriptor.TagMatchingRules)
|
||||||
{
|
{
|
||||||
|
@ -100,11 +114,11 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
if (htmlNameToBoundAttribute.TryGetValue(requiredAttribute.Name, out var attributeDescriptor))
|
if (htmlNameToBoundAttribute.TryGetValue(requiredAttribute.Name, out var attributeDescriptor))
|
||||||
{
|
{
|
||||||
UpdateCompletions(requiredAttribute.Name, attributeDescriptor);
|
UpdateCompletions(requiredAttribute.DisplayName, attributeDescriptor);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UpdateCompletions(requiredAttribute.Name, possibleDescriptor: null);
|
UpdateCompletions(requiredAttribute.DisplayName, possibleDescriptor: null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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 System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Razor.Language;
|
using Microsoft.AspNetCore.Razor.Language;
|
||||||
|
@ -11,6 +12,96 @@ namespace Microsoft.VisualStudio.Editor.Razor
|
||||||
{
|
{
|
||||||
public class DefaultTagHelperCompletionServiceTest
|
public class DefaultTagHelperCompletionServiceTest
|
||||||
{
|
{
|
||||||
|
[Fact]
|
||||||
|
public void GetAttributeCompletions_BoundDictionaryAttribute_ReturnsPrefixIndexerAndFullSetter()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var documentDescriptors = new[]
|
||||||
|
{
|
||||||
|
TagHelperDescriptorBuilder.Create("FormTagHelper", "TestAssembly")
|
||||||
|
.TagMatchingRuleDescriptor(rule => rule
|
||||||
|
.RequireTagName("form"))
|
||||||
|
.BoundAttributeDescriptor(attribute => attribute
|
||||||
|
.Name("asp-all-route-data")
|
||||||
|
.TypeName("System.Collections.Generic.IDictionary<System.String, System.String>")
|
||||||
|
.PropertyName("RouteValues").AsDictionary("asp-route-", typeof(string).FullName))
|
||||||
|
.Build(),
|
||||||
|
};
|
||||||
|
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
||||||
|
{
|
||||||
|
["asp-all-route-data"] = new HashSet<BoundAttributeDescriptor>()
|
||||||
|
{
|
||||||
|
documentDescriptors[0].BoundAttributes.Last(),
|
||||||
|
},
|
||||||
|
["asp-route-..."] = new HashSet<BoundAttributeDescriptor>()
|
||||||
|
{
|
||||||
|
documentDescriptors[0].BoundAttributes.Last(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var completionContext = BuildAttributeCompletionContext(
|
||||||
|
documentDescriptors,
|
||||||
|
Array.Empty<string>(),
|
||||||
|
attributes: new Dictionary<string, string>(),
|
||||||
|
currentTagName: "form");
|
||||||
|
var service = CreateTagHelperCompletionFactsService();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var completions = service.GetAttributeCompletions(completionContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetAttributeCompletions_RequiredBoundDictionaryAttribute_ReturnsPrefixIndexerAndFullSetter()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var documentDescriptors = new[]
|
||||||
|
{
|
||||||
|
TagHelperDescriptorBuilder.Create("FormTagHelper", "TestAssembly")
|
||||||
|
.TagMatchingRuleDescriptor(rule => rule
|
||||||
|
.RequireTagName("form")
|
||||||
|
.RequireAttributeDescriptor(builder =>
|
||||||
|
{
|
||||||
|
builder.Name = "asp-route-";
|
||||||
|
builder.NameComparisonMode = RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch;
|
||||||
|
}))
|
||||||
|
.TagMatchingRuleDescriptor(rule => rule
|
||||||
|
.RequireTagName("form")
|
||||||
|
.RequireAttributeDescriptor(builder => builder.Name = "asp-all-route-data"))
|
||||||
|
.BoundAttributeDescriptor(attribute => attribute
|
||||||
|
.Name("asp-all-route-data")
|
||||||
|
.TypeName("System.Collections.Generic.IDictionary<System.String, System.String>")
|
||||||
|
.PropertyName("RouteValues").AsDictionary("asp-route-", typeof(string).FullName))
|
||||||
|
.Build(),
|
||||||
|
};
|
||||||
|
var expectedCompletions = AttributeCompletionResult.Create(new Dictionary<string, HashSet<BoundAttributeDescriptor>>()
|
||||||
|
{
|
||||||
|
["asp-all-route-data"] = new HashSet<BoundAttributeDescriptor>()
|
||||||
|
{
|
||||||
|
documentDescriptors[0].BoundAttributes.Last(),
|
||||||
|
},
|
||||||
|
["asp-route-..."] = new HashSet<BoundAttributeDescriptor>()
|
||||||
|
{
|
||||||
|
documentDescriptors[0].BoundAttributes.Last(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var completionContext = BuildAttributeCompletionContext(
|
||||||
|
documentDescriptors,
|
||||||
|
Array.Empty<string>(),
|
||||||
|
attributes: new Dictionary<string, string>(),
|
||||||
|
currentTagName: "form");
|
||||||
|
var service = CreateTagHelperCompletionFactsService();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var completions = service.GetAttributeCompletions(completionContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
AssertCompletionsAreEquivalent(expectedCompletions, completions);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetAttributeCompletions_DoesNotReturnCompletionsForAlreadySuppliedAttributes()
|
public void GetAttributeCompletions_DoesNotReturnCompletionsForAlreadySuppliedAttributes()
|
||||||
{
|
{
|
||||||
|
|
Загрузка…
Ссылка в новой задаче