Add `TagHelperFactsService`.
- Add API to enable the editor to query information on the state of `TagHelper`s within a Razor document. - Refactored methods from `TagHelperDescriptorProvider` to be in a `TagHelperDescriptorConventions` class so the language service could use them. - Added `DefaultTagHelperFactService` tests. #1120
This commit is contained in:
parent
c1500da2a8
commit
22c7c90b5a
|
|
@ -530,7 +530,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
var attributeValueNode = attribute.Value;
|
var attributeValueNode = attribute.Value;
|
||||||
var associatedDescriptors = descriptors.Where(descriptor =>
|
var associatedDescriptors = descriptors.Where(descriptor =>
|
||||||
descriptor.BoundAttributes.Any(attributeDescriptor => attributeDescriptor.CanMatchName(attribute.Name)));
|
descriptor.BoundAttributes.Any(attributeDescriptor => TagHelperMatchingConventions.CanSatisfyBoundAttribute(attribute.Name, attributeDescriptor)));
|
||||||
|
|
||||||
if (associatedDescriptors.Any() && renderedBoundAttributeNames.Add(attribute.Name))
|
if (associatedDescriptors.Any() && renderedBoundAttributeNames.Add(attribute.Name))
|
||||||
{
|
{
|
||||||
|
|
@ -544,7 +544,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
foreach (var associatedDescriptor in associatedDescriptors)
|
foreach (var associatedDescriptor in associatedDescriptors)
|
||||||
{
|
{
|
||||||
var associatedAttributeDescriptor = associatedDescriptor.BoundAttributes.First(
|
var associatedAttributeDescriptor = associatedDescriptor.BoundAttributes.First(
|
||||||
attributeDescriptor => attributeDescriptor.CanMatchName(attribute.Name));
|
attributeDescriptor => TagHelperMatchingConventions.CanSatisfyBoundAttribute(attribute.Name, attributeDescriptor));
|
||||||
var tagHelperTypeName = associatedDescriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
|
var tagHelperTypeName = associatedDescriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
|
||||||
var attributePropertyName = associatedAttributeDescriptor.Metadata[ITagHelperBoundAttributeDescriptorBuilder.PropertyNameKey];
|
var attributePropertyName = associatedAttributeDescriptor.Metadata[ITagHelperBoundAttributeDescriptorBuilder.PropertyNameKey];
|
||||||
|
|
||||||
|
|
@ -557,7 +557,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
Binding = tagHelperBinding,
|
Binding = tagHelperBinding,
|
||||||
ValueStyle = attribute.ValueStyle,
|
ValueStyle = attribute.ValueStyle,
|
||||||
Source = BuildSourceSpanFromNode(attributeValueNode),
|
Source = BuildSourceSpanFromNode(attributeValueNode),
|
||||||
IsIndexerNameMatch = associatedAttributeDescriptor.IsIndexerNameMatch(attribute.Name),
|
IsIndexerNameMatch = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(attribute.Name, associatedAttributeDescriptor),
|
||||||
};
|
};
|
||||||
|
|
||||||
_builder.Push(setTagHelperProperty);
|
_builder.Push(setTagHelperProperty);
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
|
||||||
yield return diagnostic;
|
yield return diagnostic;
|
||||||
}
|
}
|
||||||
else if (name != TagHelperDescriptorProvider.ElementCatchAllTarget)
|
else if (name != TagHelperMatchingConventions.ElementCatchAllName)
|
||||||
{
|
{
|
||||||
foreach (var character in name)
|
foreach (var character in name)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -655,7 +655,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
private static string GetPropertyType(string name, IEnumerable<TagHelperDescriptor> descriptors)
|
private static string GetPropertyType(string name, IEnumerable<TagHelperDescriptor> descriptors)
|
||||||
{
|
{
|
||||||
var firstBoundAttribute = FindFirstBoundAttribute(name, descriptors);
|
var firstBoundAttribute = FindFirstBoundAttribute(name, descriptors);
|
||||||
var isBoundToIndexer = firstBoundAttribute.IsIndexerNameMatch(name);
|
var isBoundToIndexer = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(name, firstBoundAttribute);
|
||||||
|
|
||||||
if (isBoundToIndexer)
|
if (isBoundToIndexer)
|
||||||
{
|
{
|
||||||
|
|
@ -674,7 +674,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
var isBoundAttribute = firstBoundAttribute != null;
|
var isBoundAttribute = firstBoundAttribute != null;
|
||||||
var isBoundNonStringAttribute = isBoundAttribute &&
|
var isBoundNonStringAttribute = isBoundAttribute &&
|
||||||
!(firstBoundAttribute.IsStringProperty ||
|
!(firstBoundAttribute.IsStringProperty ||
|
||||||
(firstBoundAttribute.IsIndexerNameMatch(name) && firstBoundAttribute.IsIndexerStringProperty));
|
(TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(name, firstBoundAttribute) && firstBoundAttribute.IsIndexerStringProperty));
|
||||||
var isMissingDictionaryKey = isBoundAttribute &&
|
var isMissingDictionaryKey = isBoundAttribute &&
|
||||||
firstBoundAttribute.IndexerNamePrefix != null &&
|
firstBoundAttribute.IndexerNamePrefix != null &&
|
||||||
name.Length == firstBoundAttribute.IndexerNamePrefix.Length;
|
name.Length == firstBoundAttribute.IndexerNamePrefix.Length;
|
||||||
|
|
@ -695,7 +695,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
{
|
{
|
||||||
var firstBoundAttribute = descriptors
|
var firstBoundAttribute = descriptors
|
||||||
.SelectMany(descriptor => descriptor.BoundAttributes)
|
.SelectMany(descriptor => descriptor.BoundAttributes)
|
||||||
.FirstOrDefault(attributeDescriptor => attributeDescriptor.CanMatchName(name));
|
.FirstOrDefault(attributeDescriptor => TagHelperMatchingConventions.CanSatisfyBoundAttribute(name, attributeDescriptor));
|
||||||
|
|
||||||
return firstBoundAttribute;
|
return firstBoundAttribute;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class TagHelperDescriptorProvider
|
internal class TagHelperDescriptorProvider
|
||||||
{
|
{
|
||||||
public const string ElementCatchAllTarget = "*";
|
|
||||||
|
|
||||||
private IDictionary<string, HashSet<TagHelperDescriptor>> _registrations;
|
private IDictionary<string, HashSet<TagHelperDescriptor>> _registrations;
|
||||||
private readonly string _tagHelperPrefix;
|
private readonly string _tagHelperPrefix;
|
||||||
|
|
||||||
|
|
@ -60,7 +58,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
IEnumerable<TagHelperDescriptor> descriptors;
|
IEnumerable<TagHelperDescriptor> descriptors;
|
||||||
|
|
||||||
// Ensure there's a HashSet to use.
|
// Ensure there's a HashSet to use.
|
||||||
if (!_registrations.TryGetValue(ElementCatchAllTarget, out HashSet<TagHelperDescriptor> catchAllDescriptors))
|
if (!_registrations.TryGetValue(TagHelperMatchingConventions.ElementCatchAllName, out HashSet<TagHelperDescriptor> catchAllDescriptors))
|
||||||
{
|
{
|
||||||
descriptors = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
|
descriptors = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
|
||||||
}
|
}
|
||||||
|
|
@ -81,7 +79,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
foreach (var descriptor in descriptors)
|
foreach (var descriptor in descriptors)
|
||||||
{
|
{
|
||||||
var applicableRules = descriptor.TagMatchingRules.Where(
|
var applicableRules = descriptor.TagMatchingRules.Where(
|
||||||
rule => MatchesRule(rule, attributes, tagNameWithoutPrefix, parentTagName));
|
rule => TagHelperMatchingConventions.SatisfiesRule(tagNameWithoutPrefix, parentTagName, attributes, rule));
|
||||||
|
|
||||||
if (applicableRules.Any())
|
if (applicableRules.Any())
|
||||||
{
|
{
|
||||||
|
|
@ -104,43 +102,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
return tagMappingResult;
|
return tagMappingResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool MatchesRule(
|
|
||||||
TagMatchingRule rule,
|
|
||||||
IEnumerable<KeyValuePair<string, string>> tagAttributes,
|
|
||||||
string tagNameWithoutPrefix,
|
|
||||||
string parentTagName)
|
|
||||||
{
|
|
||||||
// Verify tag name
|
|
||||||
if (rule.TagName != ElementCatchAllTarget &&
|
|
||||||
rule.TagName != null &&
|
|
||||||
!string.Equals(tagNameWithoutPrefix, rule.TagName, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify parent tag
|
|
||||||
if (rule.ParentTag != null && !string.Equals(parentTagName, rule.ParentTag, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rule.Attributes.All(
|
|
||||||
requiredAttribute => tagAttributes.Any(
|
|
||||||
attribute => requiredAttribute.IsMatch(attribute.Key, attribute.Value))))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Register(TagHelperDescriptor descriptor)
|
private void Register(TagHelperDescriptor descriptor)
|
||||||
{
|
{
|
||||||
foreach (var rule in descriptor.TagMatchingRules)
|
foreach (var rule in descriptor.TagMatchingRules)
|
||||||
{
|
{
|
||||||
var registrationKey =
|
var registrationKey =
|
||||||
string.Equals(rule.TagName, ElementCatchAllTarget, StringComparison.Ordinal) ?
|
string.Equals(rule.TagName, TagHelperMatchingConventions.ElementCatchAllName, StringComparison.Ordinal) ?
|
||||||
ElementCatchAllTarget :
|
TagHelperMatchingConventions.ElementCatchAllName :
|
||||||
_tagHelperPrefix + rule.TagName;
|
_tagHelperPrefix + rule.TagName;
|
||||||
|
|
||||||
// Ensure there's a HashSet to add the descriptor to.
|
// Ensure there's a HashSet to add the descriptor to.
|
||||||
|
|
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
// 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.Diagnostics;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
|
||||||
{
|
|
||||||
internal static class TagHelperDescriptorMatchingConventions
|
|
||||||
{
|
|
||||||
public static bool CanMatchName(this BoundAttributeDescriptor descriptor, string name)
|
|
||||||
{
|
|
||||||
return IsFullNameMatch(descriptor, name) || IsIndexerNameMatch(descriptor, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsFullNameMatch(this BoundAttributeDescriptor descriptor, string name)
|
|
||||||
{
|
|
||||||
return string.Equals(descriptor.Name, name, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsIndexerNameMatch(this BoundAttributeDescriptor descriptor, string name)
|
|
||||||
{
|
|
||||||
return descriptor.IndexerNamePrefix != null &&
|
|
||||||
!IsFullNameMatch(descriptor, name) &&
|
|
||||||
name.StartsWith(descriptor.IndexerNamePrefix, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsMatch(this RequiredAttributeDescriptor descriptor, string attributeName, string attributeValue)
|
|
||||||
{
|
|
||||||
var nameMatches = false;
|
|
||||||
if (descriptor.NameComparison == RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
|
||||||
{
|
|
||||||
nameMatches = string.Equals(descriptor.Name, attributeName, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
else if (descriptor.NameComparison == RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)
|
|
||||||
{
|
|
||||||
// attributeName cannot equal the Name if comparing as a PrefixMatch.
|
|
||||||
nameMatches = attributeName.Length != descriptor.Name.Length &&
|
|
||||||
attributeName.StartsWith(descriptor.Name, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.Assert(false, "Unknown name comparison.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nameMatches)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (descriptor.ValueComparison)
|
|
||||||
{
|
|
||||||
case RequiredAttributeDescriptor.ValueComparisonMode.None:
|
|
||||||
return true;
|
|
||||||
case RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch: // Value starts with
|
|
||||||
return attributeValue.StartsWith(descriptor.Value, StringComparison.Ordinal);
|
|
||||||
case RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch: // Value ends with
|
|
||||||
return attributeValue.EndsWith(descriptor.Value, StringComparison.Ordinal);
|
|
||||||
case RequiredAttributeDescriptor.ValueComparisonMode.FullMatch: // Value equals
|
|
||||||
return string.Equals(attributeValue, descriptor.Value, StringComparison.Ordinal);
|
|
||||||
default:
|
|
||||||
Debug.Assert(false, "Unknown value comparison.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,173 @@
|
||||||
|
// 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 System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
internal static class TagHelperMatchingConventions
|
||||||
|
{
|
||||||
|
public const string ElementCatchAllName = "*";
|
||||||
|
|
||||||
|
public static bool SatisfiesRule(
|
||||||
|
string tagNameWithoutPrefix,
|
||||||
|
string parentTagName,
|
||||||
|
IEnumerable<KeyValuePair<string, string>> tagAttributes,
|
||||||
|
TagMatchingRule rule)
|
||||||
|
{
|
||||||
|
if (tagNameWithoutPrefix == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(tagNameWithoutPrefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagAttributes == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(tagAttributes));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rule == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(rule));
|
||||||
|
}
|
||||||
|
|
||||||
|
var satisfiesTagName = SatisfiesTagName(tagNameWithoutPrefix, rule);
|
||||||
|
if (!satisfiesTagName)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var satisfiesParentTag = SatisfiesParentTag(parentTagName, rule);
|
||||||
|
if (!satisfiesParentTag)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var satisfiesAttributes = SatisfiesAttributes(tagAttributes, rule);
|
||||||
|
if (!satisfiesAttributes)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool SatisfiesTagName(string tagNameWithoutPrefix, TagMatchingRule rule)
|
||||||
|
{
|
||||||
|
if (tagNameWithoutPrefix == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(tagNameWithoutPrefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rule == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(rule));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rule.TagName != ElementCatchAllName &&
|
||||||
|
rule.TagName != null &&
|
||||||
|
!string.Equals(tagNameWithoutPrefix, rule.TagName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool SatisfiesParentTag(string parentTagName, TagMatchingRule rule)
|
||||||
|
{
|
||||||
|
if (rule == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(rule));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rule.ParentTag != null && !string.Equals(parentTagName, rule.ParentTag, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool SatisfiesAttributes(IEnumerable<KeyValuePair<string, string>> tagAttributes, TagMatchingRule rule)
|
||||||
|
{
|
||||||
|
if (tagAttributes == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(tagAttributes));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rule == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(rule));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rule.Attributes.All(
|
||||||
|
requiredAttribute => tagAttributes.Any(
|
||||||
|
attribute => SatisfiesRequiredAttribute(attribute.Key, attribute.Value, requiredAttribute))))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool CanSatisfyBoundAttribute(string name, BoundAttributeDescriptor descriptor)
|
||||||
|
{
|
||||||
|
return SatisfiesBoundAttributeName(name, descriptor) || SatisfiesBoundAttributeIndexer(name, descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool SatisfiesBoundAttributeIndexer(string name, BoundAttributeDescriptor descriptor)
|
||||||
|
{
|
||||||
|
return descriptor.IndexerNamePrefix != null &&
|
||||||
|
!SatisfiesBoundAttributeName(name, descriptor) &&
|
||||||
|
name.StartsWith(descriptor.IndexerNamePrefix, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool SatisfiesBoundAttributeName(string name, BoundAttributeDescriptor descriptor)
|
||||||
|
{
|
||||||
|
return string.Equals(descriptor.Name, name, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal for testing
|
||||||
|
internal static bool SatisfiesRequiredAttribute(string attributeName, string attributeValue, RequiredAttributeDescriptor descriptor)
|
||||||
|
{
|
||||||
|
var nameMatches = false;
|
||||||
|
if (descriptor.NameComparison == RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
|
{
|
||||||
|
nameMatches = string.Equals(descriptor.Name, attributeName, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
else if (descriptor.NameComparison == RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)
|
||||||
|
{
|
||||||
|
// attributeName cannot equal the Name if comparing as a PrefixMatch.
|
||||||
|
nameMatches = attributeName.Length != descriptor.Name.Length &&
|
||||||
|
attributeName.StartsWith(descriptor.Name, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Assert(false, "Unknown name comparison.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nameMatches)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (descriptor.ValueComparison)
|
||||||
|
{
|
||||||
|
case RequiredAttributeDescriptor.ValueComparisonMode.None:
|
||||||
|
return true;
|
||||||
|
case RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch: // Value starts with
|
||||||
|
return attributeValue.StartsWith(descriptor.Value, StringComparison.Ordinal);
|
||||||
|
case RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch: // Value ends with
|
||||||
|
return attributeValue.EndsWith(descriptor.Value, StringComparison.Ordinal);
|
||||||
|
case RequiredAttributeDescriptor.ValueComparisonMode.FullMatch: // Value equals
|
||||||
|
return string.Equals(attributeValue, descriptor.Value, StringComparison.Ordinal);
|
||||||
|
default:
|
||||||
|
Debug.Assert(false, "Unknown value comparison.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -122,7 +122,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
|
||||||
yield return diagnostic;
|
yield return diagnostic;
|
||||||
}
|
}
|
||||||
else if (_tagName != TagHelperDescriptorProvider.ElementCatchAllTarget)
|
else if (_tagName != TagHelperMatchingConventions.ElementCatchAllName)
|
||||||
{
|
{
|
||||||
foreach (var character in _tagName)
|
foreach (var character in _tagName)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -371,7 +371,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
if (attibute.ConstructorArguments.Length == 0)
|
if (attibute.ConstructorArguments.Length == 0)
|
||||||
{
|
{
|
||||||
return TagHelperDescriptorProvider.ElementCatchAllTarget;
|
return TagHelperMatchingConventions.ElementCatchAllName;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
// 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.ComponentModel.Composition;
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
[Export(typeof(RazorTagHelperFactsService))]
|
|
||||||
internal class DefaultRazorTagHelperFactsService : RazorTagHelperFactsService
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,170 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Razor.Evolution;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.Composition;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
|
{
|
||||||
|
[Export(typeof(TagHelperFactsService))]
|
||||||
|
internal class DefaultTagHelperFactsService : TagHelperFactsService
|
||||||
|
{
|
||||||
|
public override TagHelperBinding GetTagHelperBinding(
|
||||||
|
TagHelperDocumentContext documentContext,
|
||||||
|
string tagName,
|
||||||
|
IEnumerable<KeyValuePair<string, string>> attributes,
|
||||||
|
string parentTag)
|
||||||
|
{
|
||||||
|
if (documentContext == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(documentContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagName == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(tagName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributes == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(attributes));
|
||||||
|
}
|
||||||
|
|
||||||
|
var descriptors = documentContext.TagHelpers;
|
||||||
|
if (descriptors == null || descriptors.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var prefix = documentContext.Prefix;
|
||||||
|
var provider = new TagHelperDescriptorProvider(prefix, descriptors);
|
||||||
|
var binding = provider.GetTagHelperBinding(tagName, attributes, parentTag);
|
||||||
|
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<BoundAttributeDescriptor> GetBoundTagHelperAttributes(
|
||||||
|
TagHelperDocumentContext documentContext,
|
||||||
|
string attributeName,
|
||||||
|
TagHelperBinding binding)
|
||||||
|
{
|
||||||
|
if (documentContext == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(documentContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attributeName == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(attributeName));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (binding == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(binding));
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchingBoundAttributes = new List<BoundAttributeDescriptor>();
|
||||||
|
foreach (var descriptor in binding.Descriptors)
|
||||||
|
{
|
||||||
|
foreach (var boundAttributeDescriptor in descriptor.BoundAttributes)
|
||||||
|
{
|
||||||
|
if (TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, boundAttributeDescriptor))
|
||||||
|
{
|
||||||
|
matchingBoundAttributes.Add(boundAttributeDescriptor);
|
||||||
|
|
||||||
|
// Only one bound attribute can match an attribute
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchingBoundAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenTag(
|
||||||
|
TagHelperDocumentContext documentContext,
|
||||||
|
string tagName,
|
||||||
|
string parentTag)
|
||||||
|
{
|
||||||
|
if (documentContext == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(documentContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagName == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(tagName));
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchingDescriptors = new List<TagHelperDescriptor>();
|
||||||
|
var descriptors = documentContext?.TagHelpers;
|
||||||
|
if (descriptors?.Count == 0)
|
||||||
|
{
|
||||||
|
return matchingDescriptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
var prefix = documentContext.Prefix ?? string.Empty;
|
||||||
|
if (!tagName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
// Can't possibly match TagHelpers, it doesn't start with the TagHelperPrefix.
|
||||||
|
return matchingDescriptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tagNameWithoutPrefix = tagName.Substring(prefix.Length);
|
||||||
|
for (var i = 0; i < descriptors.Count; i++)
|
||||||
|
{
|
||||||
|
var descriptor = descriptors[i];
|
||||||
|
foreach (var rule in descriptor.TagMatchingRules)
|
||||||
|
{
|
||||||
|
if (TagHelperMatchingConventions.SatisfiesTagName(tagNameWithoutPrefix, rule) &&
|
||||||
|
TagHelperMatchingConventions.SatisfiesParentTag(parentTag, rule))
|
||||||
|
{
|
||||||
|
matchingDescriptors.Add(descriptor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchingDescriptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag)
|
||||||
|
{
|
||||||
|
if (documentContext == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(documentContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentTag == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(parentTag));
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchingDescriptors = new List<TagHelperDescriptor>();
|
||||||
|
var descriptors = documentContext?.TagHelpers;
|
||||||
|
if (descriptors?.Count == 0)
|
||||||
|
{
|
||||||
|
return matchingDescriptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < descriptors.Count; i++)
|
||||||
|
{
|
||||||
|
var descriptor = descriptors[i];
|
||||||
|
foreach (var rule in descriptor.TagMatchingRules)
|
||||||
|
{
|
||||||
|
if (TagHelperMatchingConventions.SatisfiesParentTag(parentTag, rule))
|
||||||
|
{
|
||||||
|
matchingDescriptors.Add(descriptor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchingDescriptors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
// 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.
|
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|
||||||
{
|
|
||||||
public abstract class RazorTagHelperFactsService
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Razor.Evolution;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
|
{
|
||||||
|
public abstract class TagHelperFactsService
|
||||||
|
{
|
||||||
|
public abstract TagHelperBinding GetTagHelperBinding(TagHelperDocumentContext documentContext, string tagName, IEnumerable<KeyValuePair<string, string>> attributes, string parentTag);
|
||||||
|
|
||||||
|
public abstract IEnumerable<BoundAttributeDescriptor> GetBoundTagHelperAttributes(TagHelperDocumentContext documentContext, string attributeName, TagHelperBinding binding);
|
||||||
|
|
||||||
|
public abstract IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenTag(TagHelperDocumentContext documentContext, string tagName, string parentTag);
|
||||||
|
|
||||||
|
public abstract IReadOnlyList<TagHelperDescriptor> GetTagHelpersGivenParent(TagHelperDocumentContext documentContext, string parentTag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -115,20 +115,20 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("CatchAllTagHelper", "SomeAssembly")
|
var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("CatchAllTagHelper", "SomeAssembly")
|
||||||
.TagMatchingRule(rule =>
|
.TagMatchingRule(rule =>
|
||||||
rule
|
rule
|
||||||
.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
|
.RequireTagName(TagHelperMatchingConventions.ElementCatchAllName)
|
||||||
.RequireAttribute(attribute => attribute.Name("class")))
|
.RequireAttribute(attribute => attribute.Name("class")))
|
||||||
.Build();
|
.Build();
|
||||||
var catchAllDescriptor2 = ITagHelperDescriptorBuilder.Create("CatchAllTagHelper2", "SomeAssembly")
|
var catchAllDescriptor2 = ITagHelperDescriptorBuilder.Create("CatchAllTagHelper2", "SomeAssembly")
|
||||||
.TagMatchingRule(rule =>
|
.TagMatchingRule(rule =>
|
||||||
rule
|
rule
|
||||||
.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
|
.RequireTagName(TagHelperMatchingConventions.ElementCatchAllName)
|
||||||
.RequireAttribute(attribute => attribute.Name("custom"))
|
.RequireAttribute(attribute => attribute.Name("custom"))
|
||||||
.RequireAttribute(attribute => attribute.Name("class")))
|
.RequireAttribute(attribute => attribute.Name("class")))
|
||||||
.Build();
|
.Build();
|
||||||
var catchAllWildcardPrefixDescriptor = ITagHelperDescriptorBuilder.Create("CatchAllWildCardAttribute", "SomeAssembly")
|
var catchAllWildcardPrefixDescriptor = ITagHelperDescriptorBuilder.Create("CatchAllWildCardAttribute", "SomeAssembly")
|
||||||
.TagMatchingRule(rule =>
|
.TagMatchingRule(rule =>
|
||||||
rule
|
rule
|
||||||
.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
|
.RequireTagName(TagHelperMatchingConventions.ElementCatchAllName)
|
||||||
.RequireAttribute(attribute =>
|
.RequireAttribute(attribute =>
|
||||||
attribute
|
attribute
|
||||||
.Name("prefix-")
|
.Name("prefix-")
|
||||||
|
|
@ -254,7 +254,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||||
.TagMatchingRule(rule => rule.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget))
|
.TagMatchingRule(rule => rule.RequireTagName(TagHelperMatchingConventions.ElementCatchAllName))
|
||||||
.Build();
|
.Build();
|
||||||
var descriptors = new[] { catchAllDescriptor };
|
var descriptors = new[] { catchAllDescriptor };
|
||||||
var provider = new TagHelperDescriptorProvider("th", descriptors);
|
var provider = new TagHelperDescriptorProvider("th", descriptors);
|
||||||
|
|
@ -274,7 +274,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||||
.TagMatchingRule(rule => rule.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget))
|
.TagMatchingRule(rule => rule.RequireTagName(TagHelperMatchingConventions.ElementCatchAllName))
|
||||||
.Build();
|
.Build();
|
||||||
var descriptors = new[] { catchAllDescriptor };
|
var descriptors = new[] { catchAllDescriptor };
|
||||||
var provider = new TagHelperDescriptorProvider("th:", descriptors);
|
var provider = new TagHelperDescriptorProvider("th:", descriptors);
|
||||||
|
|
@ -373,7 +373,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
.TagMatchingRule(rule => rule.RequireTagName("span"))
|
.TagMatchingRule(rule => rule.RequireTagName("span"))
|
||||||
.Build();
|
.Build();
|
||||||
var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("foo3", "SomeAssembly")
|
var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("foo3", "SomeAssembly")
|
||||||
.TagMatchingRule(rule => rule.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget))
|
.TagMatchingRule(rule => rule.RequireTagName(TagHelperMatchingConventions.ElementCatchAllName))
|
||||||
.Build();
|
.Build();
|
||||||
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor };
|
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor };
|
||||||
var provider = new TagHelperDescriptorProvider(null, descriptors);
|
var provider = new TagHelperDescriptorProvider(null, descriptors);
|
||||||
|
|
@ -427,7 +427,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
// Arrange
|
// Arrange
|
||||||
var multiRuleDescriptor = ITagHelperDescriptorBuilder.Create("foo", "SomeAssembly")
|
var multiRuleDescriptor = ITagHelperDescriptorBuilder.Create("foo", "SomeAssembly")
|
||||||
.TagMatchingRule(rule => rule
|
.TagMatchingRule(rule => rule
|
||||||
.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
|
.RequireTagName(TagHelperMatchingConventions.ElementCatchAllName)
|
||||||
.RequireParentTag("body"))
|
.RequireParentTag("body"))
|
||||||
.TagMatchingRule(rule => rule
|
.TagMatchingRule(rule => rule
|
||||||
.RequireTagName("div"))
|
.RequireTagName("div"))
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
bool expectedResult)
|
bool expectedResult)
|
||||||
{
|
{
|
||||||
// Act
|
// Act
|
||||||
var result = ((RequiredAttributeDescriptor)requiredAttributeDescriptor).IsMatch(attributeName, attributeValue);
|
var result = TagHelperMatchingConventions.SatisfiesRequiredAttribute(attributeName, attributeValue, (RequiredAttributeDescriptor)requiredAttributeDescriptor);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(expectedResult, result);
|
Assert.Equal(expectedResult, result);
|
||||||
|
|
|
||||||
|
|
@ -720,7 +720,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
||||||
{
|
{
|
||||||
typeof(AttributeTargetingTagHelper),
|
typeof(AttributeTargetingTagHelper),
|
||||||
CreateTagHelperDescriptor(
|
CreateTagHelperDescriptor(
|
||||||
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
TagHelperMatchingConventions.ElementCatchAllName,
|
||||||
typeof(AttributeTargetingTagHelper).FullName,
|
typeof(AttributeTargetingTagHelper).FullName,
|
||||||
AssemblyName,
|
AssemblyName,
|
||||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
|
|
@ -731,7 +731,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
||||||
{
|
{
|
||||||
typeof(MultiAttributeTargetingTagHelper),
|
typeof(MultiAttributeTargetingTagHelper),
|
||||||
CreateTagHelperDescriptor(
|
CreateTagHelperDescriptor(
|
||||||
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
TagHelperMatchingConventions.ElementCatchAllName,
|
||||||
typeof(MultiAttributeTargetingTagHelper).FullName,
|
typeof(MultiAttributeTargetingTagHelper).FullName,
|
||||||
AssemblyName,
|
AssemblyName,
|
||||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
|
|
@ -747,7 +747,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
||||||
{
|
{
|
||||||
typeof(MultiAttributeAttributeTargetingTagHelper),
|
typeof(MultiAttributeAttributeTargetingTagHelper),
|
||||||
CreateTagHelperDescriptor(
|
CreateTagHelperDescriptor(
|
||||||
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
TagHelperMatchingConventions.ElementCatchAllName,
|
||||||
typeof(MultiAttributeAttributeTargetingTagHelper).FullName,
|
typeof(MultiAttributeAttributeTargetingTagHelper).FullName,
|
||||||
AssemblyName,
|
AssemblyName,
|
||||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
|
|
@ -764,7 +764,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
||||||
{
|
{
|
||||||
typeof(InheritedAttributeTargetingTagHelper),
|
typeof(InheritedAttributeTargetingTagHelper),
|
||||||
CreateTagHelperDescriptor(
|
CreateTagHelperDescriptor(
|
||||||
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
TagHelperMatchingConventions.ElementCatchAllName,
|
||||||
typeof(InheritedAttributeTargetingTagHelper).FullName,
|
typeof(InheritedAttributeTargetingTagHelper).FullName,
|
||||||
AssemblyName,
|
AssemblyName,
|
||||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
|
|
@ -856,7 +856,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
||||||
{
|
{
|
||||||
typeof(AttributeWildcardTargetingTagHelper),
|
typeof(AttributeWildcardTargetingTagHelper),
|
||||||
CreateTagHelperDescriptor(
|
CreateTagHelperDescriptor(
|
||||||
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
TagHelperMatchingConventions.ElementCatchAllName,
|
||||||
typeof(AttributeWildcardTargetingTagHelper).FullName,
|
typeof(AttributeWildcardTargetingTagHelper).FullName,
|
||||||
AssemblyName,
|
AssemblyName,
|
||||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
|
|
@ -870,7 +870,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
||||||
{
|
{
|
||||||
typeof(MultiAttributeWildcardTargetingTagHelper),
|
typeof(MultiAttributeWildcardTargetingTagHelper),
|
||||||
CreateTagHelperDescriptor(
|
CreateTagHelperDescriptor(
|
||||||
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
TagHelperMatchingConventions.ElementCatchAllName,
|
||||||
typeof(MultiAttributeWildcardTargetingTagHelper).FullName,
|
typeof(MultiAttributeWildcardTargetingTagHelper).FullName,
|
||||||
AssemblyName,
|
AssemblyName,
|
||||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,302 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Razor.Evolution;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
|
{
|
||||||
|
public class DefaultTagHelperFactsServiceTest
|
||||||
|
{
|
||||||
|
// Purposefully not thoroughly testing DefaultTagHelperFactsService.GetTagHelperBinding because it's a pass through
|
||||||
|
// into TagHelperDescriptorProvider.GetTagHelperBinding.
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTagHelperBinding_WorksAsExpected()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var documentDescriptors = new[]
|
||||||
|
{
|
||||||
|
ITagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||||
|
.TagMatchingRule(rule =>
|
||||||
|
rule
|
||||||
|
.RequireTagName("a")
|
||||||
|
.RequireAttribute(attribute => attribute.Name("asp-for")))
|
||||||
|
.BindAttribute(attribute =>
|
||||||
|
attribute
|
||||||
|
.Name("asp-for")
|
||||||
|
.TypeName(typeof(string).FullName)
|
||||||
|
.PropertyName("AspFor"))
|
||||||
|
.BindAttribute(attribute =>
|
||||||
|
attribute
|
||||||
|
.Name("asp-route")
|
||||||
|
.TypeName(typeof(IDictionary<string, string>).Namespace + "IDictionary<string, string>")
|
||||||
|
.PropertyName("AspRoute")
|
||||||
|
.AsDictionary("asp-route-", typeof(string).FullName))
|
||||||
|
.Build(),
|
||||||
|
ITagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||||
|
.TagMatchingRule(rule => rule.RequireTagName("input"))
|
||||||
|
.BindAttribute(attribute =>
|
||||||
|
attribute
|
||||||
|
.Name("asp-for")
|
||||||
|
.TypeName(typeof(string).FullName)
|
||||||
|
.PropertyName("AspFor"))
|
||||||
|
.Build(),
|
||||||
|
};
|
||||||
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
|
var service = new DefaultTagHelperFactsService();
|
||||||
|
var attributes = new[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, string>("asp-for", "Name")
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var binding = service.GetTagHelperBinding(documentContext, "a", attributes, parentTag: "p");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var descriptor = Assert.Single(binding.Descriptors);
|
||||||
|
Assert.Equal(documentDescriptors[0], descriptor, TagHelperDescriptorComparer.CaseSensitive);
|
||||||
|
var boundRule = Assert.Single(binding.GetBoundRules(descriptor));
|
||||||
|
Assert.Equal(documentDescriptors[0].TagMatchingRules.First(), boundRule, TagMatchingRuleComparer.CaseSensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetBoundTagHelperAttributes_MatchesPrefixedAttributeName()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var documentDescriptors = new[]
|
||||||
|
{
|
||||||
|
ITagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||||
|
.TagMatchingRule(rule => rule.RequireTagName("a"))
|
||||||
|
.BindAttribute(attribute =>
|
||||||
|
attribute
|
||||||
|
.Name("asp-for")
|
||||||
|
.TypeName(typeof(string).FullName)
|
||||||
|
.PropertyName("AspFor"))
|
||||||
|
.BindAttribute(attribute =>
|
||||||
|
attribute
|
||||||
|
.Name("asp-route")
|
||||||
|
.TypeName(typeof(IDictionary<string, string>).Namespace + "IDictionary<string, string>")
|
||||||
|
.PropertyName("AspRoute")
|
||||||
|
.AsDictionary("asp-route-", typeof(string).FullName))
|
||||||
|
.Build()
|
||||||
|
};
|
||||||
|
var expectedAttributeDescriptors = new[]
|
||||||
|
{
|
||||||
|
documentDescriptors[0].BoundAttributes.Last()
|
||||||
|
};
|
||||||
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
|
var service = new DefaultTagHelperFactsService();
|
||||||
|
var binding = service.GetTagHelperBinding(documentContext, "a", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptors = service.GetBoundTagHelperAttributes(documentContext, "asp-route-something", binding);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedAttributeDescriptors, descriptors, BoundAttributeDescriptorComparer.CaseSensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetBoundTagHelperAttributes_MatchesAttributeName()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var documentDescriptors = new[]
|
||||||
|
{
|
||||||
|
ITagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||||
|
.TagMatchingRule(rule => rule.RequireTagName("input"))
|
||||||
|
.BindAttribute(attribute =>
|
||||||
|
attribute
|
||||||
|
.Name("asp-for")
|
||||||
|
.TypeName(typeof(string).FullName)
|
||||||
|
.PropertyName("AspFor"))
|
||||||
|
.BindAttribute(attribute =>
|
||||||
|
attribute
|
||||||
|
.Name("asp-extra")
|
||||||
|
.TypeName(typeof(string).FullName)
|
||||||
|
.PropertyName("AspExtra"))
|
||||||
|
.Build()
|
||||||
|
};
|
||||||
|
var expectedAttributeDescriptors = new[]
|
||||||
|
{
|
||||||
|
documentDescriptors[0].BoundAttributes.First()
|
||||||
|
};
|
||||||
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
|
var service = new DefaultTagHelperFactsService();
|
||||||
|
var binding = service.GetTagHelperBinding(documentContext, "input", Enumerable.Empty<KeyValuePair<string, string>>(), parentTag: null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptors = service.GetBoundTagHelperAttributes(documentContext, "asp-for", binding);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedAttributeDescriptors, descriptors, BoundAttributeDescriptorComparer.CaseSensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTagHelpersGivenTag_RequiresTagName()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var documentDescriptors = new[]
|
||||||
|
{
|
||||||
|
ITagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||||
|
.TagMatchingRule(rule => rule.RequireTagName("strong"))
|
||||||
|
.Build()
|
||||||
|
};
|
||||||
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
|
var service = new DefaultTagHelperFactsService();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptors = service.GetTagHelpersGivenTag(documentContext, "strong", "p");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(documentDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTagHelpersGivenTag_RestrictsTagHelpersBasedOnTagName()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedDescriptors = new[]
|
||||||
|
{
|
||||||
|
ITagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||||
|
.TagMatchingRule(
|
||||||
|
rule => rule
|
||||||
|
.RequireTagName("a")
|
||||||
|
.RequireParentTag("div"))
|
||||||
|
.Build()
|
||||||
|
};
|
||||||
|
var documentDescriptors = new[]
|
||||||
|
{
|
||||||
|
expectedDescriptors[0],
|
||||||
|
ITagHelperDescriptorBuilder.Create("TestType2", "TestAssembly")
|
||||||
|
.TagMatchingRule(
|
||||||
|
rule => rule
|
||||||
|
.RequireTagName("strong")
|
||||||
|
.RequireParentTag("div"))
|
||||||
|
.Build()
|
||||||
|
};
|
||||||
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
|
var service = new DefaultTagHelperFactsService();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptors = service.GetTagHelpersGivenTag(documentContext, "a", "div");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTagHelpersGivenTag_RestrictsTagHelpersBasedOnTagHelperPrefix()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedDescriptors = new[]
|
||||||
|
{
|
||||||
|
ITagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||||
|
.TagMatchingRule(rule => rule.RequireTagName("strong"))
|
||||||
|
.Build()
|
||||||
|
};
|
||||||
|
var documentDescriptors = new[]
|
||||||
|
{
|
||||||
|
expectedDescriptors[0],
|
||||||
|
ITagHelperDescriptorBuilder.Create("TestType2", "TestAssembly")
|
||||||
|
.TagMatchingRule(rule => rule.RequireTagName("thstrong"))
|
||||||
|
.Build()
|
||||||
|
};
|
||||||
|
var documentContext = TagHelperDocumentContext.Create("th", documentDescriptors);
|
||||||
|
var service = new DefaultTagHelperFactsService();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptors = service.GetTagHelpersGivenTag(documentContext, "thstrong", "div");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTagHelpersGivenTag_RestrictsTagHelpersBasedOnParent()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedDescriptors = new[]
|
||||||
|
{
|
||||||
|
ITagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||||
|
.TagMatchingRule(
|
||||||
|
rule => rule
|
||||||
|
.RequireTagName("strong")
|
||||||
|
.RequireParentTag("div"))
|
||||||
|
.Build()
|
||||||
|
};
|
||||||
|
var documentDescriptors = new[]
|
||||||
|
{
|
||||||
|
expectedDescriptors[0],
|
||||||
|
ITagHelperDescriptorBuilder.Create("TestType2", "TestAssembly")
|
||||||
|
.TagMatchingRule(
|
||||||
|
rule => rule
|
||||||
|
.RequireTagName("strong")
|
||||||
|
.RequireParentTag("p"))
|
||||||
|
.Build()
|
||||||
|
};
|
||||||
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
|
var service = new DefaultTagHelperFactsService();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptors = service.GetTagHelpersGivenTag(documentContext, "strong", "div");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTagHelpersGivenParent_AllowsUnspecifiedParentTagHelpers()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var documentDescriptors = new[]
|
||||||
|
{
|
||||||
|
ITagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||||
|
.TagMatchingRule(rule => rule.RequireTagName("div"))
|
||||||
|
.Build()
|
||||||
|
};
|
||||||
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
|
var service = new DefaultTagHelperFactsService();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptors = service.GetTagHelpersGivenParent(documentContext, "p");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(documentDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTagHelpersGivenParent_RestrictsTagHelpersBasedOnParent()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedDescriptors = new[]
|
||||||
|
{
|
||||||
|
ITagHelperDescriptorBuilder.Create("TestType", "TestAssembly")
|
||||||
|
.TagMatchingRule(
|
||||||
|
rule => rule
|
||||||
|
.RequireTagName("p")
|
||||||
|
.RequireParentTag("div"))
|
||||||
|
.Build()
|
||||||
|
};
|
||||||
|
var documentDescriptors = new[]
|
||||||
|
{
|
||||||
|
expectedDescriptors[0],
|
||||||
|
ITagHelperDescriptorBuilder.Create("TestType2", "TestAssembly")
|
||||||
|
.TagMatchingRule(
|
||||||
|
rule => rule
|
||||||
|
.RequireTagName("strong")
|
||||||
|
.RequireParentTag("p"))
|
||||||
|
.Build()
|
||||||
|
};
|
||||||
|
var documentContext = TagHelperDocumentContext.Create(string.Empty, documentDescriptors);
|
||||||
|
var service = new DefaultTagHelperFactsService();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptors = service.GetTagHelpersGivenParent(documentContext, "div");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedDescriptors, descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue