diff --git a/shared/Microsoft.AspNetCore.Razor.TagHelpers.Testing.Sources/CaseSensitiveTagHelperAttributeComparer.cs b/shared/Microsoft.AspNetCore.Razor.TagHelpers.Testing.Sources/CaseSensitiveBoundAttributeComparer.cs
similarity index 100%
rename from shared/Microsoft.AspNetCore.Razor.TagHelpers.Testing.Sources/CaseSensitiveTagHelperAttributeComparer.cs
rename to shared/Microsoft.AspNetCore.Razor.TagHelpers.Testing.Sources/CaseSensitiveBoundAttributeComparer.cs
diff --git a/shared/Microsoft.AspNetCore.Razor.TagHelpers.Testing.Sources/TagHelperAttributeDescriptorComparer.cs b/shared/Microsoft.AspNetCore.Razor.TagHelpers.Testing.Sources/CaseSensitiveBoundAttributeDescriptorComparer.cs
similarity index 100%
rename from shared/Microsoft.AspNetCore.Razor.TagHelpers.Testing.Sources/TagHelperAttributeDescriptorComparer.cs
rename to shared/Microsoft.AspNetCore.Razor.TagHelpers.Testing.Sources/CaseSensitiveBoundAttributeDescriptorComparer.cs
diff --git a/shared/Microsoft.AspNetCore.Razor.TagHelpers.Testing.Sources/CaseSensitiveTagHelperRequiredAttributeDescriptorComparer.cs b/shared/Microsoft.AspNetCore.Razor.TagHelpers.Testing.Sources/CaseSensitiveRequiredAttributeDescriptorComparer.cs
similarity index 100%
rename from shared/Microsoft.AspNetCore.Razor.TagHelpers.Testing.Sources/CaseSensitiveTagHelperRequiredAttributeDescriptorComparer.cs
rename to shared/Microsoft.AspNetCore.Razor.TagHelpers.Testing.Sources/CaseSensitiveRequiredAttributeDescriptorComparer.cs
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/BoundAttributeDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Evolution/BoundAttributeDescriptor.cs
new file mode 100644
index 0000000000..768a4eddf2
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/BoundAttributeDescriptor.cs
@@ -0,0 +1,53 @@
+// 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.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ ///
+ /// A metadata class describing a tag helper attribute.
+ ///
+ public abstract class BoundAttributeDescriptor
+ {
+ protected BoundAttributeDescriptor(string kind)
+ {
+ Kind = kind;
+ }
+
+ public string Kind { get; }
+
+ public bool IsIndexerStringProperty { get; protected set; }
+
+ public bool IsEnum { get; protected set; }
+
+ public bool IsStringProperty { get; protected set; }
+
+ public string Name { get; protected set; }
+
+ public string IndexerNamePrefix { get; set; }
+
+ public string TypeName { get; protected set; }
+
+ public string IndexerTypeName { get; protected set; }
+
+ public string Documentation { get; protected set; }
+
+ public string DisplayName { get; protected set; }
+
+ public IReadOnlyList Diagnostics { get; protected set; }
+
+ public IReadOnlyDictionary Metadata { get; protected set; }
+
+ public bool HasAnyErrors
+ {
+ get
+ {
+ var anyErrors = Diagnostics.Any(diagnostic => diagnostic.Severity == RazorDiagnosticSeverity.Error);
+
+ return anyErrors;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/BoundAttributeDescriptorComparer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/BoundAttributeDescriptorComparer.cs
new file mode 100644
index 0000000000..43ef4abb66
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/BoundAttributeDescriptorComparer.cs
@@ -0,0 +1,90 @@
+// 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.Linq;
+using Microsoft.Extensions.Internal;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ internal class BoundAttributeDescriptorComparer : IEqualityComparer
+ {
+ ///
+ /// A default instance of the .
+ ///
+ public static readonly BoundAttributeDescriptorComparer Default = new BoundAttributeDescriptorComparer();
+
+ ///
+ /// A default instance of the that does case-sensitive comparison.
+ ///
+ internal static readonly BoundAttributeDescriptorComparer CaseSensitive =
+ new BoundAttributeDescriptorComparer(caseSensitive: true);
+
+ private readonly StringComparer _stringComparer;
+ private readonly StringComparison _stringComparison;
+
+ private BoundAttributeDescriptorComparer(bool caseSensitive = false)
+ {
+ if (caseSensitive)
+ {
+ _stringComparer = StringComparer.Ordinal;
+ _stringComparison = StringComparison.Ordinal;
+ }
+ else
+ {
+ _stringComparer = StringComparer.OrdinalIgnoreCase;
+ _stringComparison = StringComparison.OrdinalIgnoreCase;
+ }
+ }
+
+ public virtual bool Equals(BoundAttributeDescriptor descriptorX, BoundAttributeDescriptor descriptorY)
+ {
+ if (object.ReferenceEquals(descriptorX, descriptorY))
+ {
+ return true;
+ }
+
+ if (descriptorX == null ^ descriptorY == null)
+ {
+ return false;
+ }
+
+ return descriptorX != null &&
+ string.Equals(descriptorX.Kind, descriptorY.Kind, StringComparison.Ordinal) &&
+ descriptorX.IsIndexerStringProperty == descriptorY.IsIndexerStringProperty &&
+ descriptorX.IsEnum == descriptorY.IsEnum &&
+ string.Equals(descriptorX.Name, descriptorY.Name, _stringComparison) &&
+ string.Equals(descriptorX.IndexerNamePrefix, descriptorY.IndexerNamePrefix, _stringComparison) &&
+ string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal) &&
+ string.Equals(descriptorX.IndexerTypeName, descriptorY.IndexerTypeName, StringComparison.Ordinal) &&
+ string.Equals(descriptorX.Documentation, descriptorY.Documentation, StringComparison.Ordinal) &&
+ string.Equals(descriptorX.DisplayName, descriptorY.DisplayName, StringComparison.Ordinal) &&
+ Enumerable.SequenceEqual(descriptorX.Diagnostics, descriptorY.Diagnostics) &&
+ Enumerable.SequenceEqual(
+ descriptorX.Metadata.OrderBy(propertyX => propertyX.Key, StringComparer.Ordinal),
+ descriptorY.Metadata.OrderBy(propertyY => propertyY.Key, StringComparer.Ordinal));
+ }
+
+ public virtual int GetHashCode(BoundAttributeDescriptor descriptor)
+ {
+ if (descriptor == null)
+ {
+ throw new ArgumentNullException(nameof(descriptor));
+ }
+
+ var hashCodeCombiner = HashCodeCombiner.Start();
+ hashCodeCombiner.Add(descriptor.Kind);
+ hashCodeCombiner.Add(descriptor.IsIndexerStringProperty);
+ hashCodeCombiner.Add(descriptor.IsEnum);
+ hashCodeCombiner.Add(descriptor.Name, _stringComparer);
+ hashCodeCombiner.Add(descriptor.IndexerNamePrefix, _stringComparer);
+ hashCodeCombiner.Add(descriptor.TypeName, StringComparer.Ordinal);
+ hashCodeCombiner.Add(descriptor.IndexerTypeName, StringComparer.Ordinal);
+ hashCodeCombiner.Add(descriptor.Documentation, StringComparer.Ordinal);
+ hashCodeCombiner.Add(descriptor.DisplayName, StringComparer.Ordinal);
+
+ return hashCodeCombiner.CombinedHash;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeCSharpRenderer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeCSharpRenderer.cs
index 98933e212a..33ecbfc972 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeCSharpRenderer.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeCSharpRenderer.cs
@@ -191,7 +191,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
{
var tagHelperVariableName = GetTagHelperVariableName(node.TagHelperTypeName);
var tagHelperRenderingContext = Context.TagHelperRenderingContext;
- var propertyValueAccessor = GetTagHelperPropertyAccessor(tagHelperVariableName, node.AttributeName, node.Descriptor);
+ var propertyValueAccessor = GetTagHelperPropertyAccessor(node.IsIndexerNameMatch, tagHelperVariableName, node.AttributeName, node.Descriptor);
string previousValueAccessor;
if (tagHelperRenderingContext.RenderedBoundAttributes.TryGetValue(node.AttributeName, out previousValueAccessor))
@@ -208,7 +208,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
tagHelperRenderingContext.RenderedBoundAttributes[node.AttributeName] = propertyValueAccessor;
}
- if (node.Descriptor.IsStringProperty)
+ if (node.Descriptor.IsStringProperty || (node.IsIndexerNameMatch && node.Descriptor.IsIndexerStringProperty))
{
VisitDefault(node);
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/PageStructureCSharpRenderer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/PageStructureCSharpRenderer.cs
index 756bcf2269..05d1572ba1 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/PageStructureCSharpRenderer.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/PageStructureCSharpRenderer.cs
@@ -83,15 +83,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
protected static string GetTagHelperVariableName(string tagHelperTypeName) => "__" + tagHelperTypeName.Replace('.', '_');
protected static string GetTagHelperPropertyAccessor(
+ bool isIndexerNameMatch,
string tagHelperVariableName,
string attributeName,
- TagHelperAttributeDescriptor descriptor)
+ BoundAttributeDescriptor descriptor)
{
- var propertyAccessor = $"{tagHelperVariableName}.{descriptor.PropertyName}";
+ var propertyAccessor = $"{tagHelperVariableName}.{descriptor.Metadata[ITagHelperBoundAttributeDescriptorBuilder.PropertyNameKey]}";
- if (descriptor.IsIndexer)
+ if (isIndexerNameMatch)
{
- var dictionaryKey = attributeName.Substring(descriptor.Name.Length);
+ var dictionaryKey = attributeName.Substring(descriptor.IndexerNamePrefix.Length);
propertyAccessor += $"[\"{dictionaryKey}\"]";
}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs
index c904c64841..1d7e494a9b 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs
@@ -373,7 +373,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
public override void VisitSetPreallocatedTagHelperProperty(SetPreallocatedTagHelperPropertyIRNode node)
{
var tagHelperVariableName = GetTagHelperVariableName(node.TagHelperTypeName);
- var propertyValueAccessor = GetTagHelperPropertyAccessor(tagHelperVariableName, node.AttributeName, node.Descriptor);
+ var propertyValueAccessor = GetTagHelperPropertyAccessor(node.IsIndexerNameMatch, tagHelperVariableName, node.AttributeName, node.Descriptor);
var attributeValueAccessor = $"{node.VariableName}.Value" /* ORIGINAL: TagHelperAttributeValuePropertyName */;
Context.Writer
.WriteStartAssignment(propertyValueAccessor)
@@ -391,17 +391,18 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
{
var tagHelperVariableName = GetTagHelperVariableName(node.TagHelperTypeName);
var tagHelperRenderingContext = Context.TagHelperRenderingContext;
+ var propertyName = node.Descriptor.Metadata[ITagHelperBoundAttributeDescriptorBuilder.PropertyNameKey];
// Ensure that the property we're trying to set has initialized its dictionary bound properties.
- if (node.Descriptor.IsIndexer &&
- tagHelperRenderingContext.VerifiedPropertyDictionaries.Add(node.Descriptor.PropertyName))
+ if (node.IsIndexerNameMatch &&
+ tagHelperRenderingContext.VerifiedPropertyDictionaries.Add(propertyName))
{
// Throw a reasonable Exception at runtime if the dictionary property is null.
Context.Writer
.Write("if (")
.Write(tagHelperVariableName)
.Write(".")
- .Write(node.Descriptor.PropertyName)
+ .Write(propertyName)
.WriteLine(" == null)");
using (Context.Writer.BuildScope())
{
@@ -415,13 +416,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
.WriteParameterSeparator()
.WriteStringLiteral(node.TagHelperTypeName)
.WriteParameterSeparator()
- .WriteStringLiteral(node.Descriptor.PropertyName)
+ .WriteStringLiteral(propertyName)
.WriteEndMethodInvocation(endLine: false) // End of method call
.WriteEndMethodInvocation(); // End of new expression / throw statement
}
}
- var propertyValueAccessor = GetTagHelperPropertyAccessor(tagHelperVariableName, node.AttributeName, node.Descriptor);
+ var propertyValueAccessor = GetTagHelperPropertyAccessor(node.IsIndexerNameMatch, tagHelperVariableName, node.AttributeName, node.Descriptor);
string previousValueAccessor;
if (tagHelperRenderingContext.RenderedBoundAttributes.TryGetValue(node.AttributeName, out previousValueAccessor))
@@ -438,7 +439,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
tagHelperRenderingContext.RenderedBoundAttributes[node.AttributeName] = propertyValueAccessor;
}
- if (node.Descriptor.IsStringProperty)
+ if (node.Descriptor.IsStringProperty || (node.IsIndexerNameMatch && node.Descriptor.IsIndexerStringProperty))
{
Context.Writer.WriteMethodInvocation("BeginWriteTagHelperAttribute" /* ORIGINAL: BeginWriteTagHelperAttributeMethodName */);
@@ -683,8 +684,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
else if (node is TemplateIRNode)
{
var attributeValueNode = (SetTagHelperPropertyIRNode)node.Parent;
+ var expectedTypeName = attributeValueNode.IsIndexerNameMatch ?
+ attributeValueNode.Descriptor.IndexerTypeName :
+ attributeValueNode.Descriptor.TypeName;
var error = new RazorError(
- LegacyResources.FormatTagHelpers_InlineMarkupBlocks_NotSupported_InAttributes(attributeValueNode.Descriptor.TypeName),
+ LegacyResources.FormatTagHelpers_InlineMarkupBlocks_NotSupported_InAttributes(expectedTypeName),
new SourceLocation(documentLocation.AbsoluteIndex, documentLocation.CharacterIndex, documentLocation.Length),
documentLocation.Length);
Context.Diagnostics.Add(RazorDiagnostic.Create(error));
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorIRLoweringPhase.cs b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorIRLoweringPhase.cs
index a1feb35873..f34d90d590 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorIRLoweringPhase.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorIRLoweringPhase.cs
@@ -18,8 +18,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
ThrowForMissingDependency(syntaxTree);
var builder = RazorIRBuilder.Document();
-
var document = (DocumentIRNode)builder.Current;
+
document.Options = syntaxTree.Options;
var namespaces = new HashSet();
@@ -56,7 +56,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
}
}
- var visitor = new MainSourceVisitor(document, builder, namespaces)
+ var tagHelperPrefix = codeDocument.GetTagHelperPrefix();
+ var visitor = new MainSourceVisitor(document, builder, namespaces, tagHelperPrefix)
{
FileName = syntaxTree.Source.FileName,
};
@@ -220,10 +221,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution
private class MainSourceVisitor : LoweringVisitor
{
private DeclareTagHelperFieldsIRNode _tagHelperFields;
+ private readonly string _tagHelperPrefix;
- public MainSourceVisitor(DocumentIRNode document, RazorIRBuilder builder, HashSet namespaces)
+ public MainSourceVisitor(DocumentIRNode document, RazorIRBuilder builder, HashSet namespaces, string tagHelperPrefix)
: base(document, builder, namespaces)
{
+ _tagHelperPrefix = tagHelperPrefix;
}
public override void VisitDirectiveToken(DirectiveTokenChunkGenerator chunkGenerator, Span span)
@@ -467,9 +470,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
});
var tagName = tagHelperBlock.TagName;
- if (tagHelperBlock.Descriptors.First().Prefix != null)
+ if (_tagHelperPrefix != null)
{
- tagName = tagName.Substring(tagHelperBlock.Descriptors.First().Prefix.Length);
+ tagName = tagName.Substring(_tagHelperPrefix.Length);
}
_builder.Push(new InitializeTagHelperStructureIRNode()
@@ -482,8 +485,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
_builder.Pop(); // Pop InitializeTagHelperStructureIRNode
- AddTagHelperCreation(tagHelperBlock.Descriptors);
- AddTagHelperAttributes(tagHelperBlock.Attributes, tagHelperBlock.Descriptors);
+ AddTagHelperCreation(tagHelperBlock.Binding);
+ AddTagHelperAttributes(tagHelperBlock.Attributes, tagHelperBlock.Binding);
AddExecuteTagHelpers();
_builder.Pop(); // Pop TagHelperIRNode
@@ -497,19 +500,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution
_document.Children.Add(_tagHelperFields);
}
- foreach (var descriptor in block.Descriptors)
+ foreach (var descriptor in block.Binding.Descriptors)
{
- _tagHelperFields.UsedTagHelperTypeNames.Add(descriptor.TypeName);
+ var typeName = descriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
+ _tagHelperFields.UsedTagHelperTypeNames.Add(typeName);
}
}
- private void AddTagHelperCreation(IEnumerable descriptors)
+ private void AddTagHelperCreation(TagHelperBinding tagHelperBinding)
{
+ var descriptors = tagHelperBinding.Descriptors;
foreach (var descriptor in descriptors)
{
+ var typeName = descriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
var createTagHelper = new CreateTagHelperIRNode()
{
- TagHelperTypeName = descriptor.TypeName,
+ TagHelperTypeName = typeName,
Descriptor = descriptor
};
@@ -517,14 +523,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution
}
}
- private void AddTagHelperAttributes(IList attributes, IEnumerable descriptors)
+ private void AddTagHelperAttributes(IList attributes, TagHelperBinding tagHelperBinding)
{
+ var descriptors = tagHelperBinding.Descriptors;
var renderedBoundAttributeNames = new HashSet(StringComparer.OrdinalIgnoreCase);
foreach (var attribute in attributes)
{
var attributeValueNode = attribute.Value;
var associatedDescriptors = descriptors.Where(descriptor =>
- descriptor.Attributes.Any(attributeDescriptor => attributeDescriptor.IsNameMatch(attribute.Name)));
+ descriptor.BoundAttributes.Any(attributeDescriptor => attributeDescriptor.CanMatchName(attribute.Name)));
if (associatedDescriptors.Any() && renderedBoundAttributeNames.Add(attribute.Name))
{
@@ -537,16 +544,21 @@ namespace Microsoft.AspNetCore.Razor.Evolution
foreach (var associatedDescriptor in associatedDescriptors)
{
- var associatedAttributeDescriptor = associatedDescriptor.Attributes.First(
- attributeDescriptor => attributeDescriptor.IsNameMatch(attribute.Name));
+ var associatedAttributeDescriptor = associatedDescriptor.BoundAttributes.First(
+ attributeDescriptor => attributeDescriptor.CanMatchName(attribute.Name));
+ var tagHelperTypeName = associatedDescriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
+ var attributePropertyName = associatedAttributeDescriptor.Metadata[ITagHelperBoundAttributeDescriptorBuilder.PropertyNameKey];
+
var setTagHelperProperty = new SetTagHelperPropertyIRNode()
{
- PropertyName = associatedAttributeDescriptor.PropertyName,
+ PropertyName = attributePropertyName,
AttributeName = attribute.Name,
- TagHelperTypeName = associatedDescriptor.TypeName,
+ TagHelperTypeName = tagHelperTypeName,
Descriptor = associatedAttributeDescriptor,
+ Binding = tagHelperBinding,
ValueStyle = attribute.ValueStyle,
- Source = BuildSourceSpanFromNode(attributeValueNode)
+ Source = BuildSourceSpanFromNode(attributeValueNode),
+ IsIndexerNameMatch = associatedAttributeDescriptor.IsIndexerNameMatch(attribute.Name),
};
_builder.Push(setTagHelperProperty);
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/ITagHelperBoundAttributeDescriptorBuilder.cs b/src/Microsoft.AspNetCore.Razor.Evolution/ITagHelperBoundAttributeDescriptorBuilder.cs
new file mode 100644
index 0000000000..2c3c4efc04
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/ITagHelperBoundAttributeDescriptorBuilder.cs
@@ -0,0 +1,281 @@
+// 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.Linq;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ public sealed class ITagHelperBoundAttributeDescriptorBuilder
+ {
+ public static readonly string DescriptorKind = "ITagHelper";
+ public static readonly string PropertyNameKey = "ITagHelper.PropertyName";
+
+ private static readonly IReadOnlyDictionary PrimitiveDisplayTypeNameLookups = new Dictionary(StringComparer.Ordinal)
+ {
+ [typeof(byte).FullName] = "byte",
+ [typeof(sbyte).FullName] = "sbyte",
+ [typeof(int).FullName] = "int",
+ [typeof(uint).FullName] = "uint",
+ [typeof(short).FullName] = "short",
+ [typeof(ushort).FullName] = "ushort",
+ [typeof(long).FullName] = "long",
+ [typeof(ulong).FullName] = "ulong",
+ [typeof(float).FullName] = "float",
+ [typeof(double).FullName] = "double",
+ [typeof(char).FullName] = "char",
+ [typeof(bool).FullName] = "bool",
+ [typeof(object).FullName] = "object",
+ [typeof(string).FullName] = "string",
+ [typeof(decimal).FullName] = "decimal",
+ };
+
+ private static ICollection InvalidNonWhitespaceAttributeNameCharacters { get; } = new HashSet(
+ new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*' });
+
+ private bool _isEnum;
+ private string _indexerValueTypeName;
+ private string _name;
+ private string _propertyName;
+ private string _typeName;
+ private string _documentation;
+ private string _indexerNamePrefix;
+ private readonly string _containingTypeName;
+ private readonly Dictionary _metadata;
+ private HashSet _diagnostics;
+
+ private ITagHelperBoundAttributeDescriptorBuilder(string containingTypeName)
+ {
+ _containingTypeName = containingTypeName;
+ _metadata = new Dictionary();
+ }
+
+ public static ITagHelperBoundAttributeDescriptorBuilder Create(string containingTypeName)
+ {
+ return new ITagHelperBoundAttributeDescriptorBuilder(containingTypeName);
+ }
+
+ public ITagHelperBoundAttributeDescriptorBuilder Name(string name)
+ {
+ _name = name;
+
+ return this;
+ }
+
+ public ITagHelperBoundAttributeDescriptorBuilder PropertyName(string propertyName)
+ {
+ _propertyName = propertyName;
+
+ return this;
+ }
+
+ public ITagHelperBoundAttributeDescriptorBuilder TypeName(string typeName)
+ {
+ _typeName = typeName;
+
+ return this;
+ }
+
+ public ITagHelperBoundAttributeDescriptorBuilder AsEnum()
+ {
+ _isEnum = true;
+
+ return this;
+ }
+
+ public ITagHelperBoundAttributeDescriptorBuilder AsDictionary(string attributeNamePrefix, string valueTypeName)
+ {
+ _indexerNamePrefix = attributeNamePrefix;
+ _indexerValueTypeName = valueTypeName;
+
+ return this;
+ }
+
+ public ITagHelperBoundAttributeDescriptorBuilder Documentation(string documentation)
+ {
+ _documentation = documentation;
+
+ return this;
+ }
+
+ public ITagHelperBoundAttributeDescriptorBuilder AddMetadata(string key, string value)
+ {
+ _metadata[key] = value;
+
+ return this;
+ }
+
+ public ITagHelperBoundAttributeDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic)
+ {
+ EnsureDiagnostics();
+ _diagnostics.Add(diagnostic);
+
+ return this;
+ }
+
+ public BoundAttributeDescriptor Build()
+ {
+ var validationDiagnostics = Validate();
+ var diagnostics = new HashSet(validationDiagnostics);
+ if (_diagnostics != null)
+ {
+ diagnostics.UnionWith(_diagnostics);
+ }
+
+ if (!PrimitiveDisplayTypeNameLookups.TryGetValue(_typeName, out var simpleName))
+ {
+ simpleName = _typeName;
+ }
+
+ var displayName = $"{simpleName} {_containingTypeName}.{_propertyName}";
+ var descriptor = new ITagHelperBoundAttributeDescriptor(
+ _isEnum,
+ _name,
+ _propertyName,
+ _typeName,
+ _indexerNamePrefix,
+ _indexerValueTypeName,
+ _documentation,
+ displayName,
+ _metadata,
+ diagnostics);
+
+ return descriptor;
+ }
+
+ public void Reset()
+ {
+ _name = null;
+ _propertyName = null;
+ _typeName = null;
+ _documentation = null;
+ _isEnum = false;
+ _indexerNamePrefix = null;
+ _indexerValueTypeName = null;
+ _metadata.Clear();
+ _diagnostics?.Clear();
+ }
+
+ private IEnumerable Validate()
+ {
+ // data-* attributes are explicitly not implemented by user agents and are not intended for use on
+ // the server; therefore it's invalid for TagHelpers to bind to them.
+ const string DataDashPrefix = "data-";
+
+ if (string.IsNullOrWhiteSpace(_name))
+ {
+ if (_indexerNamePrefix == null)
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNullOrWhitespace(
+ _containingTypeName,
+ _propertyName);
+
+ yield return diagnostic;
+ }
+ }
+ else
+ {
+ if (_name.StartsWith(DataDashPrefix, StringComparison.OrdinalIgnoreCase))
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNameStartsWith(
+ _containingTypeName,
+ _propertyName,
+ _name);
+
+ yield return diagnostic;
+ }
+
+ foreach (var character in _name)
+ {
+ if (char.IsWhiteSpace(character) || InvalidNonWhitespaceAttributeNameCharacters.Contains(character))
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeName(
+ _containingTypeName,
+ _propertyName,
+ _name,
+ character);
+
+ yield return diagnostic;
+ }
+ }
+ }
+
+ if (_indexerNamePrefix != null)
+ {
+ if (_indexerNamePrefix.StartsWith(DataDashPrefix, StringComparison.OrdinalIgnoreCase))
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributePrefixStartsWith(
+ _containingTypeName,
+ _propertyName,
+ _indexerNamePrefix);
+
+ yield return diagnostic;
+ }
+ else if (_indexerNamePrefix.Length > 0 && string.IsNullOrWhiteSpace(_indexerNamePrefix))
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNullOrWhitespace(
+ _containingTypeName,
+ _propertyName);
+
+ yield return diagnostic;
+ }
+ else
+ {
+ foreach (var character in _indexerNamePrefix)
+ {
+ if (char.IsWhiteSpace(character) || InvalidNonWhitespaceAttributeNameCharacters.Contains(character))
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributePrefix(
+ _containingTypeName,
+ _propertyName,
+ _indexerNamePrefix,
+ character);
+
+ yield return diagnostic;
+ }
+ }
+ }
+ }
+ }
+
+ private void EnsureDiagnostics()
+ {
+ if (_diagnostics == null)
+ {
+ _diagnostics = new HashSet();
+ }
+ }
+
+ private class ITagHelperBoundAttributeDescriptor : BoundAttributeDescriptor
+ {
+ public ITagHelperBoundAttributeDescriptor(
+ bool isEnum,
+ string name,
+ string propertyName,
+ string typeName,
+ string dictionaryAttributeNamePrefix,
+ string dictionaryValueTypeName,
+ string documentation,
+ string displayName,
+ Dictionary metadata,
+ IEnumerable diagnostics) : base(DescriptorKind)
+ {
+ IsEnum = isEnum;
+ IsIndexerStringProperty = dictionaryValueTypeName == typeof(string).FullName || dictionaryValueTypeName == "string";
+ IsStringProperty = typeName == typeof(string).FullName || typeName == "string";
+ Name = name;
+ TypeName = typeName;
+ IndexerNamePrefix = dictionaryAttributeNamePrefix;
+ IndexerTypeName = dictionaryValueTypeName;
+ Documentation = documentation;
+ DisplayName = displayName;
+ Diagnostics = new List(diagnostics);
+ Metadata = new Dictionary(metadata)
+ {
+ [PropertyNameKey] = propertyName
+ };
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/ITagHelperDescriptorBuilder.cs b/src/Microsoft.AspNetCore.Razor.Evolution/ITagHelperDescriptorBuilder.cs
new file mode 100644
index 0000000000..46fced9122
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/ITagHelperDescriptorBuilder.cs
@@ -0,0 +1,263 @@
+// 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.Linq;
+using Microsoft.AspNetCore.Razor.Evolution.Legacy;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ public sealed class ITagHelperDescriptorBuilder
+ {
+ public static readonly string DescriptorKind = "ITagHelper";
+ public static readonly string TypeNameKey = "ITagHelper.TypeName";
+
+ private static ICollection InvalidNonWhitespaceAllowedChildCharacters { get; } = new HashSet(
+ new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*' });
+
+ private string _documentation;
+ private string _tagOutputHint;
+ private HashSet _allowedChildTags;
+ private HashSet _attributeDescriptors;
+ private HashSet _tagMatchingRules;
+ private readonly string _assemblyName;
+ private readonly string _typeName;
+ private readonly Dictionary _metadata;
+ private HashSet _diagnostics;
+
+ private ITagHelperDescriptorBuilder(string typeName, string assemblyName)
+ {
+ _typeName = typeName;
+ _assemblyName = assemblyName;
+ _metadata = new Dictionary(StringComparer.Ordinal);
+ }
+
+ public static ITagHelperDescriptorBuilder Create(string typeName, string assemblyName)
+ {
+ return new ITagHelperDescriptorBuilder(typeName, assemblyName);
+ }
+
+ public ITagHelperDescriptorBuilder BindAttribute(BoundAttributeDescriptor descriptor)
+ {
+ if (descriptor == null)
+ {
+ throw new ArgumentNullException(nameof(descriptor));
+ }
+
+ EnsureAttributeDescriptors();
+ _attributeDescriptors.Add(descriptor);
+
+ return this;
+ }
+
+ public ITagHelperDescriptorBuilder BindAttribute(Action configure)
+ {
+ if (configure == null)
+ {
+ throw new ArgumentNullException(nameof(configure));
+ }
+
+ var builder = ITagHelperBoundAttributeDescriptorBuilder.Create(_typeName);
+
+ configure(builder);
+
+ var attributeDescriptor = builder.Build();
+
+ return BindAttribute(attributeDescriptor);
+ }
+
+ public ITagHelperDescriptorBuilder TagMatchingRule(TagMatchingRule rule)
+ {
+ if (rule == null)
+ {
+ throw new ArgumentNullException(nameof(rule));
+ }
+
+ EnsureTagMatchingRules();
+ _tagMatchingRules.Add(rule);
+
+ return this;
+ }
+
+ public ITagHelperDescriptorBuilder TagMatchingRule(Action configure)
+ {
+ if (configure == null)
+ {
+ throw new ArgumentNullException(nameof(configure));
+ }
+
+ var builder = TagMatchingRuleBuilder.Create();
+
+ configure(builder);
+
+ var rule = builder.Build();
+
+ return TagMatchingRule(rule);
+ }
+
+ public ITagHelperDescriptorBuilder AllowChildTag(string allowedChild)
+ {
+ EnsureAllowedChildTags();
+ _allowedChildTags.Add(allowedChild);
+
+ return this;
+ }
+
+ public ITagHelperDescriptorBuilder TagOutputHint(string hint)
+ {
+ _tagOutputHint = hint;
+
+ return this;
+ }
+
+ public ITagHelperDescriptorBuilder Documentation(string documentation)
+ {
+ _documentation = documentation;
+
+ return this;
+ }
+
+ public ITagHelperDescriptorBuilder AddMetadata(string key, string value)
+ {
+ _metadata[key] = value;
+
+ return this;
+ }
+
+ public ITagHelperDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic)
+ {
+ EnsureDiagnostics();
+ _diagnostics.Add(diagnostic);
+
+ return this;
+ }
+
+ public TagHelperDescriptor Build()
+ {
+ var validationDiagnostics = Validate();
+ var diagnostics = new HashSet(validationDiagnostics);
+ if (_diagnostics != null)
+ {
+ diagnostics.UnionWith(_diagnostics);
+ }
+
+ var descriptor = new ITagHelperDescriptor(
+ _typeName,
+ _assemblyName,
+ _typeName /* Name */,
+ _typeName /* DisplayName */,
+ _documentation,
+ _tagOutputHint,
+ _tagMatchingRules ?? Enumerable.Empty(),
+ _attributeDescriptors ?? Enumerable.Empty(),
+ _allowedChildTags ?? Enumerable.Empty(),
+ _metadata,
+ diagnostics);
+
+ return descriptor;
+ }
+
+ public void Reset()
+ {
+ _documentation = null;
+ _tagOutputHint = null;
+ _allowedChildTags?.Clear();
+ _attributeDescriptors?.Clear();
+ _tagMatchingRules?.Clear();
+ _metadata.Clear();
+ _diagnostics?.Clear();
+ }
+
+ private IEnumerable Validate()
+ {
+ if (_allowedChildTags != null)
+ {
+ foreach (var name in _allowedChildTags)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRestrictedChildNullOrWhitespace(_typeName);
+
+ yield return diagnostic;
+ }
+ else if (name != TagHelperDescriptorProvider.ElementCatchAllTarget)
+ {
+ foreach (var character in name)
+ {
+ if (char.IsWhiteSpace(character) || InvalidNonWhitespaceAllowedChildCharacters.Contains(character))
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRestrictedChild(name, _typeName, character);
+
+ yield return diagnostic;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void EnsureAttributeDescriptors()
+ {
+ if (_attributeDescriptors == null)
+ {
+ _attributeDescriptors = new HashSet(BoundAttributeDescriptorComparer.Default);
+ }
+ }
+
+ private void EnsureTagMatchingRules()
+ {
+ if (_tagMatchingRules == null)
+ {
+ _tagMatchingRules = new HashSet(TagMatchingRuleComparer.Default);
+ }
+ }
+
+ private void EnsureAllowedChildTags()
+ {
+ if (_allowedChildTags == null)
+ {
+ _allowedChildTags = new HashSet(StringComparer.OrdinalIgnoreCase);
+ }
+ }
+
+ private void EnsureDiagnostics()
+ {
+ if (_diagnostics == null)
+ {
+ _diagnostics = new HashSet();
+ }
+ }
+
+ private class ITagHelperDescriptor : TagHelperDescriptor
+ {
+ public ITagHelperDescriptor(
+ string typeName,
+ string assemblyName,
+ string name,
+ string displayName,
+ string documentation,
+ string tagOutputHint,
+ IEnumerable tagMatchingRules,
+ IEnumerable attributeDescriptors,
+ IEnumerable allowedChildTags,
+ Dictionary metadata,
+ IEnumerable diagnostics) : base(DescriptorKind)
+ {
+ Name = typeName;
+ AssemblyName = assemblyName;
+ DisplayName = displayName;
+ Documentation = documentation;
+ TagOutputHint = tagOutputHint;
+ TagMatchingRules = new List(tagMatchingRules);
+ BoundAttributes = new List(attributeDescriptors);
+ AllowedChildTags = new List(allowedChildTags);
+ Diagnostics = new List(diagnostics);
+ Metadata = new Dictionary(metadata)
+ {
+ [TypeNameKey] = typeName
+ };
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/CreateTagHelperIRNode.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/CreateTagHelperIRNode.cs
index 393ee87721..59435401fd 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/CreateTagHelperIRNode.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/CreateTagHelperIRNode.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using Microsoft.AspNetCore.Razor.Evolution.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/SetPreallocatedTagHelperPropertyIRNode.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/SetPreallocatedTagHelperPropertyIRNode.cs
index b283af0e1b..9b08e39941 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/SetPreallocatedTagHelperPropertyIRNode.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/SetPreallocatedTagHelperPropertyIRNode.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
+using Microsoft.AspNetCore.Razor.Evolution.Legacy;
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
{
@@ -21,7 +22,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
public string PropertyName { get; set; }
- public TagHelperAttributeDescriptor Descriptor { get; set; }
+ public BoundAttributeDescriptor Descriptor { get; set; }
+
+ public TagHelperBinding Binding { get; set; }
+
+ public bool IsIndexerNameMatch { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/SetTagHelperPropertyIRNode.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/SetTagHelperPropertyIRNode.cs
index 83b307d178..1bcf84d6cc 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/SetTagHelperPropertyIRNode.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/SetTagHelperPropertyIRNode.cs
@@ -23,7 +23,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
internal HtmlAttributeValueStyle ValueStyle { get; set; }
- public TagHelperAttributeDescriptor Descriptor { get; set; }
+ public BoundAttributeDescriptor Descriptor { get; set; }
+
+ public TagHelperBinding Binding { get; set; }
+
+ public bool IsIndexerNameMatch { get; set; }
public override void Accept(RazorIRNodeVisitor visitor)
{
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/RequiredAttributeDescriptorComparer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/RequiredAttributeDescriptorComparer.cs
new file mode 100644
index 0000000000..46ea8f704c
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/RequiredAttributeDescriptorComparer.cs
@@ -0,0 +1,81 @@
+// 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.Linq;
+using Microsoft.Extensions.Internal;
+
+namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
+{
+ ///
+ /// An used to check equality between
+ /// two s.
+ ///
+ internal class RequiredAttributeDescriptorComparer : IEqualityComparer
+ {
+ ///
+ /// A default instance of the .
+ ///
+ public static readonly RequiredAttributeDescriptorComparer Default =
+ new RequiredAttributeDescriptorComparer();
+
+ ///
+ /// A default instance of the that does case-sensitive comparison.
+ ///
+ internal static readonly RequiredAttributeDescriptorComparer CaseSensitive =
+ new RequiredAttributeDescriptorComparer(caseSensitive: true);
+
+ private readonly StringComparer _stringComparer;
+ private readonly StringComparison _stringComparison;
+
+ private RequiredAttributeDescriptorComparer(bool caseSensitive = false)
+ {
+ if (caseSensitive)
+ {
+ _stringComparer = StringComparer.Ordinal;
+ _stringComparison = StringComparison.Ordinal;
+ }
+ else
+ {
+ _stringComparer = StringComparer.OrdinalIgnoreCase;
+ _stringComparison = StringComparison.OrdinalIgnoreCase;
+ }
+ }
+
+ ///
+ public virtual bool Equals(
+ RequiredAttributeDescriptor descriptorX,
+ RequiredAttributeDescriptor descriptorY)
+ {
+ if (object.ReferenceEquals(descriptorX, descriptorY))
+ {
+ return true;
+ }
+
+ if (descriptorX == null ^ descriptorY == null)
+ {
+ return false;
+ }
+
+ return descriptorX != null &&
+ descriptorX.NameComparison == descriptorY.NameComparison &&
+ descriptorX.ValueComparison == descriptorY.ValueComparison &&
+ string.Equals(descriptorX.Name, descriptorY.Name, _stringComparison) &&
+ string.Equals(descriptorX.Value, descriptorY.Value, StringComparison.Ordinal) &&
+ Enumerable.SequenceEqual(descriptorX.Diagnostics, descriptorY.Diagnostics);
+ }
+
+ ///
+ public virtual int GetHashCode(RequiredAttributeDescriptor descriptor)
+ {
+ var hashCodeCombiner = HashCodeCombiner.Start();
+ hashCodeCombiner.Add(descriptor.NameComparison);
+ hashCodeCombiner.Add(descriptor.ValueComparison);
+ hashCodeCombiner.Add(descriptor.Name, _stringComparer);
+ hashCodeCombiner.Add(descriptor.Value, StringComparer.Ordinal);
+
+ return hashCodeCombiner.CombinedHash;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperBinding.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperBinding.cs
new file mode 100644
index 0000000000..7859bb4478
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperBinding.cs
@@ -0,0 +1,25 @@
+// 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.Collections.Generic;
+
+namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
+{
+ public sealed class TagHelperBinding
+ {
+ public IReadOnlyDictionary> _mappings;
+
+ internal TagHelperBinding(IReadOnlyDictionary> mappings)
+ {
+ _mappings = mappings;
+ Descriptors = _mappings.Keys;
+ }
+
+ public IEnumerable Descriptors { get; }
+
+ public IEnumerable GetBoundRules(TagHelperDescriptor descriptor)
+ {
+ return _mappings[descriptor];
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperBlock.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperBlock.cs
index 0b2b5a9585..8b48259544 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperBlock.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperBlock.cs
@@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
: base(source.Type, source.Children, source.ChunkGenerator)
{
TagName = source.TagName;
- Descriptors = source.Descriptors;
+ Binding = source.BindingResult;
Attributes = new List(source.Attributes);
_start = source.Start;
TagMode = source.TagMode;
@@ -61,9 +61,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public TagMode TagMode { get; }
///
- /// s for the HTML element.
+ /// bindings for the HTML element.
///
- public IEnumerable Descriptors { get; }
+ public TagHelperBinding Binding { get; }
///
/// The HTML attributes.
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperBlockBuilder.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperBlockBuilder.cs
index d171a0080a..8b37f2a381 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperBlockBuilder.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperBlockBuilder.cs
@@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
: base(original)
{
TagName = original.TagName;
- Descriptors = original.Descriptors;
+ BindingResult = original.Binding;
Attributes = new List(original.Attributes);
}
@@ -31,22 +31,21 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
/// HTML syntax of the element in the Razor source.
/// Starting location of the .
/// Attributes of the .
- /// The s associated with the current HTML
- /// tag.
+ ///
public TagHelperBlockBuilder(
string tagName,
TagMode tagMode,
SourceLocation start,
IList attributes,
- IEnumerable descriptors)
+ TagHelperBinding bindingResult)
{
TagName = tagName;
TagMode = tagMode;
Start = start;
- Descriptors = descriptors;
+ BindingResult = bindingResult;
Attributes = new List(attributes);
Type = BlockType.Tag;
- ChunkGenerator = new TagHelperChunkGenerator(descriptors);
+ ChunkGenerator = new TagHelperChunkGenerator();
}
// Internal for testing
@@ -60,7 +59,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
TagMode = tagMode;
Attributes = attributes;
Type = BlockType.Tag;
- ChunkGenerator = new TagHelperChunkGenerator(tagHelperDescriptors: null);
+ ChunkGenerator = new TagHelperChunkGenerator();
// Children is IList, no AddRange
foreach (var child in children)
@@ -89,7 +88,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
///
/// s for the HTML element.
///
- public IEnumerable Descriptors { get; }
+ public TagHelperBinding BindingResult { get; }
///
/// The HTML attributes.
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperBlockRewriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperBlockRewriter.cs
index 0472199bca..c8955919c5 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperBlockRewriter.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperBlockRewriter.cs
@@ -17,28 +17,24 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
string tagName,
bool validStructure,
Block tag,
- IEnumerable descriptors,
+ TagHelperBinding bindingResult,
ErrorSink errorSink)
{
// There will always be at least one child for the '<'.
var start = tag.Children.First().Start;
- var attributes = GetTagAttributes(tagName, validStructure, tag, descriptors, errorSink);
- var tagMode = GetTagMode(tagName, tag, descriptors, errorSink);
+ var attributes = GetTagAttributes(tagName, validStructure, tag, bindingResult, errorSink);
+ var tagMode = GetTagMode(tagName, tag, bindingResult, errorSink);
- return new TagHelperBlockBuilder(tagName, tagMode, start, attributes, descriptors);
+ return new TagHelperBlockBuilder(tagName, tagMode, start, attributes, bindingResult);
}
private static IList GetTagAttributes(
string tagName,
bool validStructure,
Block tagBlock,
- IEnumerable descriptors,
+ TagHelperBinding bindingResult,
ErrorSink errorSink)
{
- // Ignore all but one descriptor per type since this method uses the TagHelperDescriptors only to get the
- // contained TagHelperAttributeDescriptor's.
- descriptors = descriptors.Distinct(TypeBasedTagHelperDescriptorComparer.Default);
-
var attributes = new List();
// We skip the first child "" or "/>".
@@ -52,11 +48,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
TryParseResult result;
if (child.IsBlock)
{
- result = TryParseBlock(tagName, (Block)child, descriptors, errorSink);
+ result = TryParseBlock(tagName, (Block)child, bindingResult.Descriptors, errorSink);
}
else
{
- result = TryParseSpan((Span)child, descriptors, errorSink);
+ result = TryParseSpan((Span)child, bindingResult.Descriptors, errorSink);
}
// Only want to track the attribute if we succeeded in parsing its corresponding Block/Span.
@@ -77,7 +73,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
LegacyResources.FormatRewriterError_EmptyTagHelperBoundAttribute(
result.AttributeName,
tagName,
- GetPropertyType(result.AttributeName, descriptors)),
+ GetPropertyType(result.AttributeName, bindingResult.Descriptors)),
result.AttributeName.Length);
}
@@ -118,7 +114,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
private static TagMode GetTagMode(
string tagName,
Block beginTagBlock,
- IEnumerable descriptors,
+ TagHelperBinding bindingResult,
ErrorSink errorSink)
{
var childSpan = beginTagBlock.FindLastDescendentSpan();
@@ -129,12 +125,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
return TagMode.SelfClosing;
}
- var baseDescriptor = descriptors.FirstOrDefault(
- descriptor => descriptor.TagStructure != TagStructure.Unspecified);
- var resolvedTagStructure = baseDescriptor?.TagStructure ?? TagStructure.Unspecified;
- if (resolvedTagStructure == TagStructure.WithoutEndTag)
+ foreach (var descriptor in bindingResult.Descriptors)
{
- return TagMode.StartTagOnly;
+ var boundRules = bindingResult.GetBoundRules(descriptor);
+ var nonDefaultRule = boundRules.FirstOrDefault(rule => rule.TagStructure != TagStructure.Unspecified);
+
+ if (nonDefaultRule?.TagStructure == TagStructure.WithoutEndTag)
+ {
+ return TagMode.StartTagOnly;
+ }
}
return TagMode.StartTagAndEndTag;
@@ -656,8 +655,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
private static string GetPropertyType(string name, IEnumerable descriptors)
{
var firstBoundAttribute = FindFirstBoundAttribute(name, descriptors);
+ var isBoundToIndexer = firstBoundAttribute.IsIndexerNameMatch(name);
- return firstBoundAttribute?.TypeName;
+ if (isBoundToIndexer)
+ {
+ return firstBoundAttribute?.IndexerTypeName;
+ }
+ else
+ {
+ return firstBoundAttribute?.TypeName;
+ }
}
// Create a TryParseResult for given name, filling in binding details.
@@ -665,10 +672,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
var firstBoundAttribute = FindFirstBoundAttribute(name, descriptors);
var isBoundAttribute = firstBoundAttribute != null;
- var isBoundNonStringAttribute = isBoundAttribute && !firstBoundAttribute.IsStringProperty;
+ var isBoundNonStringAttribute = isBoundAttribute &&
+ !(firstBoundAttribute.IsStringProperty ||
+ (firstBoundAttribute.IsIndexerNameMatch(name) && firstBoundAttribute.IsIndexerStringProperty));
var isMissingDictionaryKey = isBoundAttribute &&
- firstBoundAttribute.IsIndexer &&
- name.Length == firstBoundAttribute.Name.Length;
+ firstBoundAttribute.IndexerNamePrefix != null &&
+ name.Length == firstBoundAttribute.IndexerNamePrefix.Length;
return new TryParseResult
{
@@ -680,15 +689,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
}
// Finds first TagHelperAttributeDescriptor matching given name.
- private static TagHelperAttributeDescriptor FindFirstBoundAttribute(
+ private static BoundAttributeDescriptor FindFirstBoundAttribute(
string name,
IEnumerable descriptors)
{
- // Non-indexers (exact HTML attribute name matches) have higher precedence than indexers (prefix matches).
- // Attributes already sorted to ensure this precedence.
var firstBoundAttribute = descriptors
- .SelectMany(descriptor => descriptor.Attributes)
- .FirstOrDefault(attributeDescriptor => attributeDescriptor.IsNameMatch(name));
+ .SelectMany(descriptor => descriptor.BoundAttributes)
+ .FirstOrDefault(attributeDescriptor => attributeDescriptor.CanMatchName(name));
return firstBoundAttribute;
}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperChunkGenerator.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperChunkGenerator.cs
index b9c42fb6fc..c6977108f1 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperChunkGenerator.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperChunkGenerator.cs
@@ -1,25 +1,10 @@
// 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.Collections.Generic;
-
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
internal class TagHelperChunkGenerator : ParentChunkGenerator
{
- private IEnumerable _tagHelperDescriptors;
-
- ///
- /// Instantiates a new .
- ///
- ///
- /// s associated with the current HTML tag.
- ///
- public TagHelperChunkGenerator(IEnumerable tagHelperDescriptors)
- {
- _tagHelperDescriptors = tagHelperDescriptors;
- }
-
public override void GenerateStartParentChunk(Block target, ChunkGeneratorContext context)
{
//var tagHelperBlock = target as TagHelperBlock;
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorComparer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorComparer.cs
index 5c4e4ca403..7e429d5dad 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorComparer.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorComparer.cs
@@ -8,10 +8,6 @@ using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
- ///
- /// An used to check equality between
- /// two s.
- ///
internal class TagHelperDescriptorComparer : IEqualityComparer
{
///
@@ -20,27 +16,34 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public static readonly TagHelperDescriptorComparer Default = new TagHelperDescriptorComparer();
///
- /// An instance of that only compares
- /// .
+ /// A default instance of the that does case-sensitive comparison.
///
- public static readonly TagHelperDescriptorComparer TypeName = new TypeNameTagHelperDescriptorComparer();
+ internal static readonly TagHelperDescriptorComparer CaseSensitive =
+ new TagHelperDescriptorComparer(caseSensitive: true);
- ///
- /// Initializes a new instance.
- ///
- protected TagHelperDescriptorComparer()
+ private readonly StringComparer _stringComparer;
+ private readonly StringComparison _stringComparison;
+ private readonly BoundAttributeDescriptorComparer _boundAttributeComparer;
+ private readonly TagMatchingRuleComparer _tagMatchingRuleComparer;
+
+ private TagHelperDescriptorComparer(bool caseSensitive = false)
{
+ if (caseSensitive)
+ {
+ _stringComparer = StringComparer.Ordinal;
+ _stringComparison = StringComparison.Ordinal;
+ _boundAttributeComparer = BoundAttributeDescriptorComparer.CaseSensitive;
+ _tagMatchingRuleComparer = TagMatchingRuleComparer.CaseSensitive;
+ }
+ else
+ {
+ _stringComparer = StringComparer.OrdinalIgnoreCase;
+ _stringComparison = StringComparison.OrdinalIgnoreCase;
+ _boundAttributeComparer = BoundAttributeDescriptorComparer.Default;
+ _tagMatchingRuleComparer = TagMatchingRuleComparer.Default;
+ }
}
- ///
- ///
- /// Determines equality based on ,
- /// , ,
- /// , ,
- /// and .
- /// Ignores because it can be inferred directly from
- /// and .
- ///
public virtual bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
{
if (descriptorX == descriptorY)
@@ -49,28 +52,30 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
}
return descriptorX != null &&
- string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal) &&
- string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.OrdinalIgnoreCase) &&
+ string.Equals(descriptorX.Kind, descriptorY.Kind, StringComparison.Ordinal) &&
string.Equals(descriptorX.AssemblyName, descriptorY.AssemblyName, StringComparison.Ordinal) &&
- string.Equals(
- descriptorX.RequiredParent,
- descriptorY.RequiredParent,
- StringComparison.OrdinalIgnoreCase) &&
Enumerable.SequenceEqual(
- descriptorX.RequiredAttributes.OrderBy(attribute => attribute.Name, StringComparer.OrdinalIgnoreCase),
- descriptorY.RequiredAttributes.OrderBy(attribute => attribute.Name, StringComparer.OrdinalIgnoreCase),
- TagHelperRequiredAttributeDescriptorComparer.Default) &&
- (descriptorX.AllowedChildren == descriptorY.AllowedChildren ||
- (descriptorX.AllowedChildren != null &&
- descriptorY.AllowedChildren != null &&
+ descriptorX.BoundAttributes.OrderBy(attribute => attribute.Name, _stringComparer),
+ descriptorY.BoundAttributes.OrderBy(attribute => attribute.Name, _stringComparer),
+ _boundAttributeComparer) &&
Enumerable.SequenceEqual(
- descriptorX.AllowedChildren.OrderBy(child => child, StringComparer.OrdinalIgnoreCase),
- descriptorY.AllowedChildren.OrderBy(child => child, StringComparer.OrdinalIgnoreCase),
- StringComparer.OrdinalIgnoreCase))) &&
- descriptorX.TagStructure == descriptorY.TagStructure &&
+ descriptorX.TagMatchingRules.OrderBy(rule => rule.TagName, _stringComparer),
+ descriptorY.TagMatchingRules.OrderBy(rule => rule.TagName, _stringComparer),
+ _tagMatchingRuleComparer) &&
+ (descriptorX.AllowedChildTags == descriptorY.AllowedChildTags ||
+ (descriptorX.AllowedChildTags != null &&
+ descriptorY.AllowedChildTags != null &&
Enumerable.SequenceEqual(
- descriptorX.PropertyBag.OrderBy(propertyX => propertyX.Key, StringComparer.Ordinal),
- descriptorY.PropertyBag.OrderBy(propertyY => propertyY.Key, StringComparer.Ordinal));
+ descriptorX.AllowedChildTags.OrderBy(child => child, _stringComparer),
+ descriptorY.AllowedChildTags.OrderBy(child => child, _stringComparer),
+ _stringComparer))) &&
+ string.Equals(descriptorX.Documentation, descriptorY.Documentation, StringComparison.Ordinal) &&
+ string.Equals(descriptorX.DisplayName, descriptorY.DisplayName, StringComparison.Ordinal) &&
+ string.Equals(descriptorX.TagOutputHint, descriptorY.TagOutputHint, _stringComparison) &&
+ Enumerable.SequenceEqual(descriptorX.Diagnostics, descriptorY.Diagnostics) &&
+ Enumerable.SequenceEqual(
+ descriptorX.Metadata.OrderBy(metadataX => metadataX.Key, StringComparer.Ordinal),
+ descriptorY.Metadata.OrderBy(metadataY => metadataY.Key, StringComparer.Ordinal));
}
///
@@ -82,59 +87,35 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
}
var hashCodeCombiner = HashCodeCombiner.Start();
- hashCodeCombiner.Add(descriptor.TypeName, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.TagName, StringComparer.OrdinalIgnoreCase);
+ hashCodeCombiner.Add(descriptor.Kind);
hashCodeCombiner.Add(descriptor.AssemblyName, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.RequiredParent, StringComparer.OrdinalIgnoreCase);
- hashCodeCombiner.Add(descriptor.TagStructure);
- var attributes = descriptor.RequiredAttributes.OrderBy(
- attribute => attribute.Name,
- StringComparer.OrdinalIgnoreCase);
- foreach (var attribute in attributes)
+ var boundAttributes = descriptor.BoundAttributes.OrderBy(attribute => attribute.Name, _stringComparer);
+ foreach (var attribute in boundAttributes)
{
- hashCodeCombiner.Add(TagHelperRequiredAttributeDescriptorComparer.Default.GetHashCode(attribute));
+ hashCodeCombiner.Add(_boundAttributeComparer.GetHashCode(attribute));
}
- if (descriptor.AllowedChildren != null)
+ var rules = descriptor.TagMatchingRules.OrderBy(rule => rule.TagName, _stringComparer);
+ foreach (var rule in rules)
{
- var allowedChildren = descriptor.AllowedChildren.OrderBy(child => child, StringComparer.OrdinalIgnoreCase);
+ hashCodeCombiner.Add(_tagMatchingRuleComparer.GetHashCode(rule));
+ }
+
+ hashCodeCombiner.Add(descriptor.Documentation, StringComparer.Ordinal);
+ hashCodeCombiner.Add(descriptor.DisplayName, StringComparer.Ordinal);
+ hashCodeCombiner.Add(descriptor.TagOutputHint, _stringComparer);
+
+ if (descriptor.AllowedChildTags != null)
+ {
+ var allowedChildren = descriptor.AllowedChildTags.OrderBy(child => child, _stringComparer);
foreach (var child in allowedChildren)
{
- hashCodeCombiner.Add(child, StringComparer.OrdinalIgnoreCase);
+ hashCodeCombiner.Add(child, _stringComparer);
}
}
return hashCodeCombiner.CombinedHash;
}
-
- private class TypeNameTagHelperDescriptorComparer : TagHelperDescriptorComparer
- {
- public override bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
- {
- if (object.ReferenceEquals(descriptorX, descriptorY))
- {
- return true;
- }
- else if (descriptorX == null ^ descriptorY == null)
- {
- return false;
- }
- else
- {
- return string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal);
- }
- }
-
- public override int GetHashCode(TagHelperDescriptor descriptor)
- {
- if (descriptor == null)
- {
- throw new ArgumentNullException(nameof(descriptor));
- }
-
- return descriptor.TypeName == null ? 0 : StringComparer.Ordinal.GetHashCode(descriptor.TypeName);
- }
- }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorProvider.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorProvider.cs
index 744e400fb1..2853c3a16b 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorProvider.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperDescriptorProvider.cs
@@ -15,14 +15,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public const string ElementCatchAllTarget = "*";
private IDictionary> _registrations;
- private string _tagHelperPrefix;
+ private readonly string _tagHelperPrefix;
///
/// Instantiates a new instance of the .
///
+ /// The tag helper prefix being used by the document.
/// The descriptors that the will pull from.
- public TagHelperDescriptorProvider(IEnumerable descriptors)
+ public TagHelperDescriptorProvider(string tagHelperPrefix, IEnumerable descriptors)
{
+ _tagHelperPrefix = tagHelperPrefix;
_registrations = new Dictionary>(StringComparer.OrdinalIgnoreCase);
// Populate our registrations
@@ -42,7 +44,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
/// s that apply to the given .
/// Will return an empty if no s are
/// found.
- public IEnumerable GetDescriptors(
+ public TagHelperBinding GetTagHelperBinding(
string tagName,
IEnumerable> attributes,
string parentTagName)
@@ -52,14 +54,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
!tagName.StartsWith(_tagHelperPrefix, StringComparison.OrdinalIgnoreCase)))
{
// The tagName doesn't have the tag helper prefix, we can short circuit.
- return Enumerable.Empty();
+ return null;
}
- HashSet catchAllDescriptors;
IEnumerable descriptors;
// Ensure there's a HashSet to use.
- if (!_registrations.TryGetValue(ElementCatchAllTarget, out catchAllDescriptors))
+ if (!_registrations.TryGetValue(ElementCatchAllTarget, out HashSet catchAllDescriptors))
{
descriptors = new HashSet(TagHelperDescriptorComparer.Default);
}
@@ -70,64 +71,87 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
// If we have a tag name associated with the requested name, we need to combine matchingDescriptors
// with all the catch-all descriptors.
- HashSet matchingDescriptors;
- if (_registrations.TryGetValue(tagName, out matchingDescriptors))
+ if (_registrations.TryGetValue(tagName, out HashSet matchingDescriptors))
{
descriptors = matchingDescriptors.Concat(descriptors);
}
- var applicableDescriptors = new List();
+ var tagNameWithoutPrefix = _tagHelperPrefix != null ? tagName.Substring(_tagHelperPrefix.Length) : tagName;
+ Dictionary> applicableDescriptorMappings = null;
foreach (var descriptor in descriptors)
{
- if (HasRequiredAttributes(descriptor, attributes) &&
- HasRequiredParentTag(descriptor, parentTagName))
+ var applicableRules = descriptor.TagMatchingRules.Where(
+ rule => MatchesRule(rule, attributes, tagNameWithoutPrefix, parentTagName));
+
+ if (applicableRules.Any())
{
- applicableDescriptors.Add(descriptor);
+ if (applicableDescriptorMappings == null)
+ {
+ applicableDescriptorMappings = new Dictionary>();
+ }
+
+ applicableDescriptorMappings[descriptor] = applicableRules;
}
}
- return applicableDescriptors.Distinct(TagHelperDescriptorComparer.TypeName);
+ if (applicableDescriptorMappings == null)
+ {
+ return null;
+ }
+
+ var tagMappingResult = new TagHelperBinding(applicableDescriptorMappings);
+
+ return tagMappingResult;
}
- private bool HasRequiredParentTag(
- TagHelperDescriptor descriptor,
+ private bool MatchesRule(
+ TagMatchingRule rule,
+ IEnumerable> tagAttributes,
+ string tagNameWithoutPrefix,
string parentTagName)
{
- return descriptor.RequiredParent == null ||
- string.Equals(parentTagName, descriptor.RequiredParent, StringComparison.OrdinalIgnoreCase);
- }
+ // Verify tag name
+ if (rule.TagName != ElementCatchAllTarget &&
+ rule.TagName != null &&
+ !string.Equals(tagNameWithoutPrefix, rule.TagName, StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
- private bool HasRequiredAttributes(
- TagHelperDescriptor descriptor,
- IEnumerable> attributes)
- {
- return descriptor.RequiredAttributes.All(
- requiredAttribute => attributes.Any(
- attribute => requiredAttribute.IsMatch(attribute.Key, attribute.Value)));
+ // 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)
{
- HashSet descriptorSet;
-
- if (_tagHelperPrefix == null)
+ foreach (var rule in descriptor.TagMatchingRules)
{
- _tagHelperPrefix = descriptor.Prefix;
+ var registrationKey =
+ string.Equals(rule.TagName, ElementCatchAllTarget, StringComparison.Ordinal) ?
+ ElementCatchAllTarget :
+ _tagHelperPrefix + rule.TagName;
+
+ // Ensure there's a HashSet to add the descriptor to.
+ if (!_registrations.TryGetValue(registrationKey, out HashSet descriptorSet))
+ {
+ descriptorSet = new HashSet(TagHelperDescriptorComparer.Default);
+ _registrations[registrationKey] = descriptorSet;
+ }
+
+ descriptorSet.Add(descriptor);
}
-
- var registrationKey =
- string.Equals(descriptor.TagName, ElementCatchAllTarget, StringComparison.Ordinal) ?
- ElementCatchAllTarget :
- descriptor.FullTagName;
-
- // Ensure there's a HashSet to add the descriptor to.
- if (!_registrations.TryGetValue(registrationKey, out descriptorSet))
- {
- descriptorSet = new HashSet(TagHelperDescriptorComparer.Default);
- _registrations[registrationKey] = descriptorSet;
- }
-
- descriptorSet.Add(descriptor);
}
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperParseTreeRewriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperParseTreeRewriter.cs
index 14f4ea972b..bee30a3eeb 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperParseTreeRewriter.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperParseTreeRewriter.cs
@@ -36,6 +36,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
"wbr"
};
+ private readonly string _tagHelperPrefix;
private readonly List> _htmlAttributeTracker;
private readonly StringBuilder _attributeValueBuilder;
private readonly TagHelperDescriptorProvider _provider;
@@ -45,8 +46,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
private BlockBuilder _currentBlock;
private string _currentParentTagName;
- public TagHelperParseTreeRewriter(TagHelperDescriptorProvider provider)
+ public TagHelperParseTreeRewriter(string tagHelperPrefix, TagHelperDescriptorProvider provider)
{
+ _tagHelperPrefix = tagHelperPrefix;
_provider = provider;
_trackerStack = new Stack();
_blockStack = new Stack();
@@ -172,7 +174,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
return false;
}
- var descriptors = Enumerable.Empty();
+ TagHelperBinding tagHelperBinding;
if (!IsPotentialTagHelper(tagName, tagBlock))
{
@@ -187,10 +189,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
// We're now in a start tag block, we first need to see if the tag block is a tag helper.
var providedAttributes = GetAttributeNameValuePairs(tagBlock);
- descriptors = _provider.GetDescriptors(tagName, providedAttributes, _currentParentTagName);
+ tagHelperBinding = _provider.GetTagHelperBinding(tagName, providedAttributes, _currentParentTagName);
// If there aren't any TagHelperDescriptors registered then we aren't a TagHelper
- if (!descriptors.Any())
+ if (tagHelperBinding == null)
{
// If the current tag matches the current TagHelper scope it means the parent TagHelper matched
// all the required attributes but the current one did not; therefore, we need to increment the
@@ -206,7 +208,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
}
ValidateParentAllowsTagHelper(tagName, tagBlock, errorSink);
- ValidateDescriptors(descriptors, tagName, tagBlock, errorSink);
+ ValidateBinding(tagHelperBinding, tagName, tagBlock, errorSink);
// We're in a start TagHelper block.
var validTagStructure = ValidateTagSyntax(tagName, tagBlock, errorSink);
@@ -215,7 +217,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
tagName,
validTagStructure,
tagBlock,
- descriptors,
+ tagHelperBinding,
errorSink);
// Track the original start tag so the editor knows where each piece of the TagHelperBlock lies
@@ -252,32 +254,38 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
}
else
{
- descriptors = _provider.GetDescriptors(
+ tagHelperBinding = _provider.GetTagHelperBinding(
tagName,
attributes: Enumerable.Empty>(),
parentTagName: _currentParentTagName);
// If there are not TagHelperDescriptors associated with the end tag block that also have no
// required attributes then it means we can't be a TagHelper, bail out.
- if (!descriptors.Any())
+ if (tagHelperBinding == null)
{
return false;
}
- var invalidDescriptor = descriptors.FirstOrDefault(
- descriptor => descriptor.TagStructure == TagStructure.WithoutEndTag);
- if (invalidDescriptor != null)
+ foreach (var descriptor in tagHelperBinding.Descriptors)
{
- // End tag TagHelper that states it shouldn't have an end tag.
- errorSink.OnError(
- SourceLocationTracker.Advance(tagBlock.Start, ""),
- LegacyResources.FormatTagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag(
- tagName,
- invalidDescriptor.TypeName,
- invalidDescriptor.TagStructure),
- tagName.Length);
+ var boundRules = tagHelperBinding.GetBoundRules(descriptor);
+ var invalidRule = boundRules.FirstOrDefault(rule => rule.TagStructure == TagStructure.WithoutEndTag);
- return false;
+ if (invalidRule != null)
+ {
+ var typeName = descriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
+
+ // End tag TagHelper that states it shouldn't have an end tag.
+ errorSink.OnError(
+ SourceLocationTracker.Advance(tagBlock.Start, ""),
+ LegacyResources.FormatTagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag(
+ tagName,
+ typeName,
+ invalidRule.TagStructure),
+ tagName.Length);
+
+ return false;
+ }
}
// Current tag helper scope does not match the end tag. Attempt to recover the tag
@@ -463,7 +471,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
return false;
}
- return _currentTagHelperTracker.AllowedChildren != null;
+ return _currentTagHelperTracker.AllowedChildren != null && _currentTagHelperTracker.AllowedChildren.Count > 0;
}
private void ValidateParentAllowsContent(Span child, ErrorSink errorSink)
@@ -536,8 +544,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
errorSink.OnError(errorStart, errorMessage, tagName.Length);
}
- private static void ValidateDescriptors(
- IEnumerable descriptors,
+ private static void ValidateBinding(
+ TagHelperBinding bindingResult,
string tagName,
Block tagBlock,
ErrorSink errorSink)
@@ -545,24 +553,32 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
// Ensure that all descriptors associated with this tag have appropriate TagStructures. Cannot have
// multiple descriptors that expect different TagStructures (other than TagStructure.Unspecified).
TagHelperDescriptor baseDescriptor = null;
- foreach (var descriptor in descriptors)
+ TagStructure? baseStructure = null;
+ foreach (var descriptor in bindingResult.Descriptors)
{
- if (descriptor.TagStructure != TagStructure.Unspecified)
+ var boundRules = bindingResult.GetBoundRules(descriptor);
+ foreach (var rule in boundRules)
{
- // Can't have a set of TagHelpers that expect different structures.
- if (baseDescriptor != null && baseDescriptor.TagStructure != descriptor.TagStructure)
+ if (rule.TagStructure != TagStructure.Unspecified)
{
- errorSink.OnError(
- tagBlock.Start,
- LegacyResources.FormatTagHelperParseTreeRewriter_InconsistentTagStructure(
- baseDescriptor.TypeName,
- descriptor.TypeName,
- tagName,
- nameof(TagHelperDescriptor.TagStructure)),
- tagBlock.Length);
- }
+ // Can't have a set of TagHelpers that expect different structures.
+ if (baseStructure.HasValue && baseStructure != rule.TagStructure)
+ {
+ var baseDescriptorTypeName = baseDescriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
+ var descriptorTypeName = descriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
+ errorSink.OnError(
+ tagBlock.Start,
+ LegacyResources.FormatTagHelperParseTreeRewriter_InconsistentTagStructure(
+ baseDescriptorTypeName,
+ descriptorTypeName,
+ tagName,
+ nameof(TagMatchingRule.TagStructure)),
+ tagBlock.Length);
+ }
- baseDescriptor = descriptor;
+ baseDescriptor = descriptor;
+ baseStructure = rule.TagStructure;
+ }
}
}
}
@@ -680,7 +696,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
private void TrackTagHelperBlock(TagHelperBlockBuilder builder)
{
- _currentTagHelperTracker = new TagHelperBlockTracker(builder);
+ _currentTagHelperTracker = new TagHelperBlockTracker(_tagHelperPrefix, builder);
PushTrackerStack(_currentTagHelperTracker);
TrackBlock(builder);
@@ -830,19 +846,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
private class TagHelperBlockTracker : TagBlockTracker
{
- private IEnumerable _prefixedAllowedChildren;
+ private IReadOnlyList _prefixedAllowedChildren;
+ private readonly string _tagHelperPrefix;
- public TagHelperBlockTracker(TagHelperBlockBuilder builder)
+ public TagHelperBlockTracker(string tagHelperPrefix, TagHelperBlockBuilder builder)
: base(builder.TagName, isTagHelper: true, depth: 0)
{
+ _tagHelperPrefix = tagHelperPrefix;
Builder = builder;
- if (Builder.Descriptors.Any(descriptor => descriptor.AllowedChildren != null))
+ if (Builder.BindingResult.Descriptors.Any(descriptor => descriptor.AllowedChildTags != null))
{
- AllowedChildren = Builder.Descriptors
- .Where(descriptor => descriptor.AllowedChildren != null)
- .SelectMany(descriptor => descriptor.AllowedChildren)
- .Distinct(StringComparer.OrdinalIgnoreCase);
+ AllowedChildren = Builder.BindingResult.Descriptors
+ .Where(descriptor => descriptor.AllowedChildTags != null)
+ .SelectMany(descriptor => descriptor.AllowedChildTags)
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToList();
}
}
@@ -850,18 +869,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public uint OpenMatchingTags { get; set; }
- public IEnumerable AllowedChildren { get; }
+ public IReadOnlyList AllowedChildren { get; }
- public IEnumerable PrefixedAllowedChildren
+ public IReadOnlyList PrefixedAllowedChildren
{
get
{
if (AllowedChildren != null && _prefixedAllowedChildren == null)
{
- Debug.Assert(Builder.Descriptors.Count() >= 1);
+ Debug.Assert(Builder.BindingResult.Descriptors.Count() >= 1);
- var prefix = Builder.Descriptors.First().Prefix;
- _prefixedAllowedChildren = AllowedChildren.Select(allowedChild => prefix + allowedChild);
+ _prefixedAllowedChildren = AllowedChildren.Select(allowedChild => _tagHelperPrefix + allowedChild).ToList();
}
return _prefixedAllowedChildren;
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperRequiredAttributeDescriptorComparer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperRequiredAttributeDescriptorComparer.cs
deleted file mode 100644
index aeffbf27d2..0000000000
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperRequiredAttributeDescriptorComparer.cs
+++ /dev/null
@@ -1,58 +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.Collections.Generic;
-using Microsoft.Extensions.Internal;
-
-namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
-{
- ///
- /// An used to check equality between
- /// two s.
- ///
- internal class TagHelperRequiredAttributeDescriptorComparer : IEqualityComparer
- {
- ///
- /// A default instance of the .
- ///
- public static readonly TagHelperRequiredAttributeDescriptorComparer Default =
- new TagHelperRequiredAttributeDescriptorComparer();
-
- ///
- /// Initializes a new instance.
- ///
- protected TagHelperRequiredAttributeDescriptorComparer()
- {
- }
-
- ///
- public virtual bool Equals(
- TagHelperRequiredAttributeDescriptor descriptorX,
- TagHelperRequiredAttributeDescriptor descriptorY)
- {
- if (descriptorX == descriptorY)
- {
- return true;
- }
-
- return descriptorX != null &&
- descriptorX.NameComparison == descriptorY.NameComparison &&
- descriptorX.ValueComparison == descriptorY.ValueComparison &&
- string.Equals(descriptorX.Name, descriptorY.Name, StringComparison.OrdinalIgnoreCase) &&
- string.Equals(descriptorX.Value, descriptorY.Value, StringComparison.Ordinal);
- }
-
- ///
- public virtual int GetHashCode(TagHelperRequiredAttributeDescriptor descriptor)
- {
- var hashCodeCombiner = HashCodeCombiner.Start();
- hashCodeCombiner.Add(descriptor.NameComparison);
- hashCodeCombiner.Add(descriptor.ValueComparison);
- hashCodeCombiner.Add(descriptor.Name, StringComparer.OrdinalIgnoreCase);
- hashCodeCombiner.Add(descriptor.Value, StringComparer.Ordinal);
-
- return hashCodeCombiner.CombinedHash;
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TypeBasedTagHelperDescriptorComparer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TypeBasedTagHelperDescriptorComparer.cs
deleted file mode 100644
index 82ab897d63..0000000000
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TypeBasedTagHelperDescriptorComparer.cs
+++ /dev/null
@@ -1,64 +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.Collections.Generic;
-using Microsoft.Extensions.Internal;
-
-namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
-{
- ///
- /// An that checks equality between two
- /// s using only their s and
- /// s.
- ///
- ///
- /// This class is intended for scenarios where Reflection-based information is all important i.e.
- /// , , and related
- /// properties are not relevant.
- ///
- internal class TypeBasedTagHelperDescriptorComparer : IEqualityComparer
- {
- ///
- /// A default instance of the .
- ///
- public static readonly TypeBasedTagHelperDescriptorComparer Default =
- new TypeBasedTagHelperDescriptorComparer();
-
- private TypeBasedTagHelperDescriptorComparer()
- {
- }
-
- ///
- ///
- /// Determines equality based on and
- /// .
- ///
- public bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
- {
- if (descriptorX == descriptorY)
- {
- return true;
- }
-
- return descriptorX != null &&
- string.Equals(descriptorX.AssemblyName, descriptorY.AssemblyName, StringComparison.Ordinal) &&
- string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal);
- }
-
- ///
- public int GetHashCode(TagHelperDescriptor descriptor)
- {
- if (descriptor == null)
- {
- throw new ArgumentNullException(nameof(descriptor));
- }
-
- var hashCodeCombiner = HashCodeCombiner.Start();
- hashCodeCombiner.Add(descriptor.AssemblyName, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.TypeName, StringComparer.Ordinal);
-
- return hashCodeCombiner;
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Properties/AssemblyInfo.cs
index 2043ddeaab..5ab099a91d 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Properties/AssemblyInfo.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Properties/AssemblyInfo.cs
@@ -9,5 +9,6 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor.Workspaces, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Remote.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LanguageServices.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
+[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LanguageServices.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.RazorExtension, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Properties/LegacyResources.Designer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Properties/LegacyResources.Designer.cs
index 3fa9fc3ed1..49827c79eb 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Properties/LegacyResources.Designer.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Properties/LegacyResources.Designer.cs
@@ -15,512 +15,448 @@ namespace Microsoft.AspNetCore.Razor.Evolution
///
internal static string Argument_Cannot_Be_Null_Or_Empty
{
- get { return GetString("Argument_Cannot_Be_Null_Or_Empty"); }
+ get => GetString("Argument_Cannot_Be_Null_Or_Empty");
}
///
/// Value cannot be null or an empty string.
///
internal static string FormatArgument_Cannot_Be_Null_Or_Empty()
- {
- return GetString("Argument_Cannot_Be_Null_Or_Empty");
- }
+ => GetString("Argument_Cannot_Be_Null_Or_Empty");
///
/// Value must be between {0} and {1}.
///
internal static string Argument_Must_Be_Between
{
- get { return GetString("Argument_Must_Be_Between"); }
+ get => GetString("Argument_Must_Be_Between");
}
///
/// Value must be between {0} and {1}.
///
internal static string FormatArgument_Must_Be_Between(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("Argument_Must_Be_Between"), p0, p1);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("Argument_Must_Be_Between"), p0, p1);
///
/// Value must be a value from the "{0}" enumeration.
///
internal static string Argument_Must_Be_Enum_Member
{
- get { return GetString("Argument_Must_Be_Enum_Member"); }
+ get => GetString("Argument_Must_Be_Enum_Member");
}
///
/// Value must be a value from the "{0}" enumeration.
///
internal static string FormatArgument_Must_Be_Enum_Member(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("Argument_Must_Be_Enum_Member"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("Argument_Must_Be_Enum_Member"), p0);
///
/// Value must be greater than {0}.
///
internal static string Argument_Must_Be_GreaterThan
{
- get { return GetString("Argument_Must_Be_GreaterThan"); }
+ get => GetString("Argument_Must_Be_GreaterThan");
}
///
/// Value must be greater than {0}.
///
internal static string FormatArgument_Must_Be_GreaterThan(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("Argument_Must_Be_GreaterThan"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("Argument_Must_Be_GreaterThan"), p0);
///
/// Value must be greater than or equal to {0}.
///
internal static string Argument_Must_Be_GreaterThanOrEqualTo
{
- get { return GetString("Argument_Must_Be_GreaterThanOrEqualTo"); }
+ get => GetString("Argument_Must_Be_GreaterThanOrEqualTo");
}
///
/// Value must be greater than or equal to {0}.
///
internal static string FormatArgument_Must_Be_GreaterThanOrEqualTo(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("Argument_Must_Be_GreaterThanOrEqualTo"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("Argument_Must_Be_GreaterThanOrEqualTo"), p0);
///
/// Value must be less than {0}.
///
internal static string Argument_Must_Be_LessThan
{
- get { return GetString("Argument_Must_Be_LessThan"); }
+ get => GetString("Argument_Must_Be_LessThan");
}
///
/// Value must be less than {0}.
///
internal static string FormatArgument_Must_Be_LessThan(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("Argument_Must_Be_LessThan"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("Argument_Must_Be_LessThan"), p0);
///
/// Value must be less than or equal to {0}.
///
internal static string Argument_Must_Be_LessThanOrEqualTo
{
- get { return GetString("Argument_Must_Be_LessThanOrEqualTo"); }
+ get => GetString("Argument_Must_Be_LessThanOrEqualTo");
}
///
/// Value must be less than or equal to {0}.
///
internal static string FormatArgument_Must_Be_LessThanOrEqualTo(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("Argument_Must_Be_LessThanOrEqualTo"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("Argument_Must_Be_LessThanOrEqualTo"), p0);
///
/// Value cannot be an empty string. It must either be null or a non-empty string.
///
internal static string Argument_Must_Be_Null_Or_Non_Empty
{
- get { return GetString("Argument_Must_Be_Null_Or_Non_Empty"); }
+ get => GetString("Argument_Must_Be_Null_Or_Non_Empty");
}
///
/// Value cannot be an empty string. It must either be null or a non-empty string.
///
internal static string FormatArgument_Must_Be_Null_Or_Non_Empty()
- {
- return GetString("Argument_Must_Be_Null_Or_Non_Empty");
- }
+ => GetString("Argument_Must_Be_Null_Or_Non_Empty");
///
/// code
///
internal static string BlockName_Code
{
- get { return GetString("BlockName_Code"); }
+ get => GetString("BlockName_Code");
}
///
/// code
///
internal static string FormatBlockName_Code()
- {
- return GetString("BlockName_Code");
- }
+ => GetString("BlockName_Code");
///
/// explicit expression
///
internal static string BlockName_ExplicitExpression
{
- get { return GetString("BlockName_ExplicitExpression"); }
+ get => GetString("BlockName_ExplicitExpression");
}
///
/// explicit expression
///
internal static string FormatBlockName_ExplicitExpression()
- {
- return GetString("BlockName_ExplicitExpression");
- }
+ => GetString("BlockName_ExplicitExpression");
///
/// Block cannot be built because a Type has not been specified in the BlockBuilder
///
internal static string Block_Type_Not_Specified
{
- get { return GetString("Block_Type_Not_Specified"); }
+ get => GetString("Block_Type_Not_Specified");
}
///
/// Block cannot be built because a Type has not been specified in the BlockBuilder
///
internal static string FormatBlock_Type_Not_Specified()
- {
- return GetString("Block_Type_Not_Specified");
- }
+ => GetString("Block_Type_Not_Specified");
///
/// <<character literal>>
///
internal static string CSharpSymbol_CharacterLiteral
{
- get { return GetString("CSharpSymbol_CharacterLiteral"); }
+ get => GetString("CSharpSymbol_CharacterLiteral");
}
///
/// <<character literal>>
///
internal static string FormatCSharpSymbol_CharacterLiteral()
- {
- return GetString("CSharpSymbol_CharacterLiteral");
- }
+ => GetString("CSharpSymbol_CharacterLiteral");
///
/// <<comment>>
///
internal static string CSharpSymbol_Comment
{
- get { return GetString("CSharpSymbol_Comment"); }
+ get => GetString("CSharpSymbol_Comment");
}
///
/// <<comment>>
///
internal static string FormatCSharpSymbol_Comment()
- {
- return GetString("CSharpSymbol_Comment");
- }
+ => GetString("CSharpSymbol_Comment");
///
/// <<identifier>>
///
internal static string CSharpSymbol_Identifier
{
- get { return GetString("CSharpSymbol_Identifier"); }
+ get => GetString("CSharpSymbol_Identifier");
}
///
/// <<identifier>>
///
internal static string FormatCSharpSymbol_Identifier()
- {
- return GetString("CSharpSymbol_Identifier");
- }
+ => GetString("CSharpSymbol_Identifier");
///
/// <<integer literal>>
///
internal static string CSharpSymbol_IntegerLiteral
{
- get { return GetString("CSharpSymbol_IntegerLiteral"); }
+ get => GetString("CSharpSymbol_IntegerLiteral");
}
///
/// <<integer literal>>
///
internal static string FormatCSharpSymbol_IntegerLiteral()
- {
- return GetString("CSharpSymbol_IntegerLiteral");
- }
+ => GetString("CSharpSymbol_IntegerLiteral");
///
/// <<keyword>>
///
internal static string CSharpSymbol_Keyword
{
- get { return GetString("CSharpSymbol_Keyword"); }
+ get => GetString("CSharpSymbol_Keyword");
}
///
/// <<keyword>>
///
internal static string FormatCSharpSymbol_Keyword()
- {
- return GetString("CSharpSymbol_Keyword");
- }
+ => GetString("CSharpSymbol_Keyword");
///
/// <<newline sequence>>
///
internal static string CSharpSymbol_Newline
{
- get { return GetString("CSharpSymbol_Newline"); }
+ get => GetString("CSharpSymbol_Newline");
}
///
/// <<newline sequence>>
///
internal static string FormatCSharpSymbol_Newline()
- {
- return GetString("CSharpSymbol_Newline");
- }
+ => GetString("CSharpSymbol_Newline");
///
/// <<real literal>>
///
internal static string CSharpSymbol_RealLiteral
{
- get { return GetString("CSharpSymbol_RealLiteral"); }
+ get => GetString("CSharpSymbol_RealLiteral");
}
///
/// <<real literal>>
///
internal static string FormatCSharpSymbol_RealLiteral()
- {
- return GetString("CSharpSymbol_RealLiteral");
- }
+ => GetString("CSharpSymbol_RealLiteral");
///
/// <<string literal>>
///
internal static string CSharpSymbol_StringLiteral
{
- get { return GetString("CSharpSymbol_StringLiteral"); }
+ get => GetString("CSharpSymbol_StringLiteral");
}
///
/// <<string literal>>
///
internal static string FormatCSharpSymbol_StringLiteral()
- {
- return GetString("CSharpSymbol_StringLiteral");
- }
+ => GetString("CSharpSymbol_StringLiteral");
///
/// <<white space>>
///
internal static string CSharpSymbol_Whitespace
{
- get { return GetString("CSharpSymbol_Whitespace"); }
+ get => GetString("CSharpSymbol_Whitespace");
}
///
/// <<white space>>
///
internal static string FormatCSharpSymbol_Whitespace()
- {
- return GetString("CSharpSymbol_Whitespace");
- }
+ => GetString("CSharpSymbol_Whitespace");
///
/// The '{0}' directive expects an identifier.
///
internal static string DirectiveExpectsIdentifier
{
- get { return GetString("DirectiveExpectsIdentifier"); }
+ get => GetString("DirectiveExpectsIdentifier");
}
///
/// The '{0}' directive expects an identifier.
///
internal static string FormatDirectiveExpectsIdentifier(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("DirectiveExpectsIdentifier"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("DirectiveExpectsIdentifier"), p0);
///
/// The '{0}' directive expects a type name.
///
internal static string DirectiveExpectsTypeName
{
- get { return GetString("DirectiveExpectsTypeName"); }
+ get => GetString("DirectiveExpectsTypeName");
}
///
/// The '{0}' directive expects a type name.
///
internal static string FormatDirectiveExpectsTypeName(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("DirectiveExpectsTypeName"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("DirectiveExpectsTypeName"), p0);
///
/// "EndBlock" was called without a matching call to "StartBlock".
///
internal static string EndBlock_Called_Without_Matching_StartBlock
{
- get { return GetString("EndBlock_Called_Without_Matching_StartBlock"); }
+ get => GetString("EndBlock_Called_Without_Matching_StartBlock");
}
///
/// "EndBlock" was called without a matching call to "StartBlock".
///
internal static string FormatEndBlock_Called_Without_Matching_StartBlock()
- {
- return GetString("EndBlock_Called_Without_Matching_StartBlock");
- }
+ => GetString("EndBlock_Called_Without_Matching_StartBlock");
///
/// "{0}" character
///
internal static string ErrorComponent_Character
{
- get { return GetString("ErrorComponent_Character"); }
+ get => GetString("ErrorComponent_Character");
}
///
/// "{0}" character
///
internal static string FormatErrorComponent_Character(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ErrorComponent_Character"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ErrorComponent_Character"), p0);
///
/// end of file
///
internal static string ErrorComponent_EndOfFile
{
- get { return GetString("ErrorComponent_EndOfFile"); }
+ get => GetString("ErrorComponent_EndOfFile");
}
///
/// end of file
///
internal static string FormatErrorComponent_EndOfFile()
- {
- return GetString("ErrorComponent_EndOfFile");
- }
+ => GetString("ErrorComponent_EndOfFile");
///
/// line break
///
internal static string ErrorComponent_Newline
{
- get { return GetString("ErrorComponent_Newline"); }
+ get => GetString("ErrorComponent_Newline");
}
///
/// line break
///
internal static string FormatErrorComponent_Newline()
- {
- return GetString("ErrorComponent_Newline");
- }
+ => GetString("ErrorComponent_Newline");
///
/// space or line break
///
internal static string ErrorComponent_Whitespace
{
- get { return GetString("ErrorComponent_Whitespace"); }
+ get => GetString("ErrorComponent_Whitespace");
}
///
/// space or line break
///
internal static string FormatErrorComponent_Whitespace()
- {
- return GetString("ErrorComponent_Whitespace");
- }
+ => GetString("ErrorComponent_Whitespace");
///
/// <<newline sequence>>
///
internal static string HtmlSymbol_NewLine
{
- get { return GetString("HtmlSymbol_NewLine"); }
+ get => GetString("HtmlSymbol_NewLine");
}
///
/// <<newline sequence>>
///
internal static string FormatHtmlSymbol_NewLine()
- {
- return GetString("HtmlSymbol_NewLine");
- }
+ => GetString("HtmlSymbol_NewLine");
///
/// <<razor comment>>
///
internal static string HtmlSymbol_RazorComment
{
- get { return GetString("HtmlSymbol_RazorComment"); }
+ get => GetString("HtmlSymbol_RazorComment");
}
///
/// <<razor comment>>
///
internal static string FormatHtmlSymbol_RazorComment()
- {
- return GetString("HtmlSymbol_RazorComment");
- }
+ => GetString("HtmlSymbol_RazorComment");
///
/// <<text>>
///
internal static string HtmlSymbol_Text
{
- get { return GetString("HtmlSymbol_Text"); }
+ get => GetString("HtmlSymbol_Text");
}
///
/// <<text>>
///
internal static string FormatHtmlSymbol_Text()
- {
- return GetString("HtmlSymbol_Text");
- }
+ => GetString("HtmlSymbol_Text");
///
/// <<white space>>
///
internal static string HtmlSymbol_WhiteSpace
{
- get { return GetString("HtmlSymbol_WhiteSpace"); }
+ get => GetString("HtmlSymbol_WhiteSpace");
}
///
/// <<white space>>
///
internal static string FormatHtmlSymbol_WhiteSpace()
- {
- return GetString("HtmlSymbol_WhiteSpace");
- }
+ => GetString("HtmlSymbol_WhiteSpace");
///
/// Cannot use built-in RazorComment handler, language characteristics does not define the CommentStart, CommentStar and CommentBody known symbol types or parser does not override TokenizerBackedParser.OutputSpanBeforeRazorComment
///
internal static string Language_Does_Not_Support_RazorComment
{
- get { return GetString("Language_Does_Not_Support_RazorComment"); }
+ get => GetString("Language_Does_Not_Support_RazorComment");
}
///
/// Cannot use built-in RazorComment handler, language characteristics does not define the CommentStart, CommentStar and CommentBody known symbol types or parser does not override TokenizerBackedParser.OutputSpanBeforeRazorComment
///
internal static string FormatLanguage_Does_Not_Support_RazorComment()
- {
- return GetString("Language_Does_Not_Support_RazorComment");
- }
+ => GetString("Language_Does_Not_Support_RazorComment");
///
/// The "@" character must be followed by a ":", "(", or a C# identifier. If you intended to switch to markup, use an HTML start tag, for example:
@@ -531,7 +467,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
///
internal static string ParseError_AtInCode_Must_Be_Followed_By_Colon_Paren_Or_Identifier_Start
{
- get { return GetString("ParseError_AtInCode_Must_Be_Followed_By_Colon_Paren_Or_Identifier_Start"); }
+ get => GetString("ParseError_AtInCode_Must_Be_Followed_By_Colon_Paren_Or_Identifier_Start");
}
///
@@ -542,185 +478,161 @@ namespace Microsoft.AspNetCore.Razor.Evolution
/// }
///
internal static string FormatParseError_AtInCode_Must_Be_Followed_By_Colon_Paren_Or_Identifier_Start()
- {
- return GetString("ParseError_AtInCode_Must_Be_Followed_By_Colon_Paren_Or_Identifier_Start");
- }
+ => GetString("ParseError_AtInCode_Must_Be_Followed_By_Colon_Paren_Or_Identifier_Start");
///
/// End of file was reached before the end of the block comment. All comments started with "/*" sequence must be terminated with a matching "*/" sequence.
///
internal static string ParseError_BlockComment_Not_Terminated
{
- get { return GetString("ParseError_BlockComment_Not_Terminated"); }
+ get => GetString("ParseError_BlockComment_Not_Terminated");
}
///
/// End of file was reached before the end of the block comment. All comments started with "/*" sequence must be terminated with a matching "*/" sequence.
///
internal static string FormatParseError_BlockComment_Not_Terminated()
- {
- return GetString("ParseError_BlockComment_Not_Terminated");
- }
+ => GetString("ParseError_BlockComment_Not_Terminated");
///
/// Directive '{0}' must have a value.
///
internal static string ParseError_DirectiveMustHaveValue
{
- get { return GetString("ParseError_DirectiveMustHaveValue"); }
+ get => GetString("ParseError_DirectiveMustHaveValue");
}
///
/// Directive '{0}' must have a value.
///
internal static string FormatParseError_DirectiveMustHaveValue(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_DirectiveMustHaveValue"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ParseError_DirectiveMustHaveValue"), p0);
///
/// An opening "{0}" is missing the corresponding closing "{1}".
///
internal static string ParseError_Expected_CloseBracket_Before_EOF
{
- get { return GetString("ParseError_Expected_CloseBracket_Before_EOF"); }
+ get => GetString("ParseError_Expected_CloseBracket_Before_EOF");
}
///
/// An opening "{0}" is missing the corresponding closing "{1}".
///
internal static string FormatParseError_Expected_CloseBracket_Before_EOF(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_Expected_CloseBracket_Before_EOF"), p0, p1);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ParseError_Expected_CloseBracket_Before_EOF"), p0, p1);
///
/// The {0} block is missing a closing "{1}" character. Make sure you have a matching "{1}" character for all the "{2}" characters within this block, and that none of the "{1}" characters are being interpreted as markup.
///
internal static string ParseError_Expected_EndOfBlock_Before_EOF
{
- get { return GetString("ParseError_Expected_EndOfBlock_Before_EOF"); }
+ get => GetString("ParseError_Expected_EndOfBlock_Before_EOF");
}
///
/// The {0} block is missing a closing "{1}" character. Make sure you have a matching "{1}" character for all the "{2}" characters within this block, and that none of the "{1}" characters are being interpreted as markup.
///
internal static string FormatParseError_Expected_EndOfBlock_Before_EOF(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_Expected_EndOfBlock_Before_EOF"), p0, p1, p2);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ParseError_Expected_EndOfBlock_Before_EOF"), p0, p1, p2);
///
/// Expected "{0}".
///
internal static string ParseError_Expected_X
{
- get { return GetString("ParseError_Expected_X"); }
+ get => GetString("ParseError_Expected_X");
}
///
/// Expected "{0}".
///
internal static string FormatParseError_Expected_X(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_Expected_X"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ParseError_Expected_X"), p0);
///
/// The {0} directive is not supported.
///
internal static string ParseError_HelperDirectiveNotAvailable
{
- get { return GetString("ParseError_HelperDirectiveNotAvailable"); }
+ get => GetString("ParseError_HelperDirectiveNotAvailable");
}
///
/// The {0} directive is not supported.
///
internal static string FormatParseError_HelperDirectiveNotAvailable(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_HelperDirectiveNotAvailable"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ParseError_HelperDirectiveNotAvailable"), p0);
///
/// Optional quote around the directive '{0}' is missing the corresponding opening or closing quote.
///
internal static string ParseError_IncompleteQuotesAroundDirective
{
- get { return GetString("ParseError_IncompleteQuotesAroundDirective"); }
+ get => GetString("ParseError_IncompleteQuotesAroundDirective");
}
///
/// Optional quote around the directive '{0}' is missing the corresponding opening or closing quote.
///
internal static string FormatParseError_IncompleteQuotesAroundDirective(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_IncompleteQuotesAroundDirective"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ParseError_IncompleteQuotesAroundDirective"), p0);
///
/// The 'inherits' keyword must be followed by a type name on the same line.
///
internal static string ParseError_InheritsKeyword_Must_Be_Followed_By_TypeName
{
- get { return GetString("ParseError_InheritsKeyword_Must_Be_Followed_By_TypeName"); }
+ get => GetString("ParseError_InheritsKeyword_Must_Be_Followed_By_TypeName");
}
///
/// The 'inherits' keyword must be followed by a type name on the same line.
///
internal static string FormatParseError_InheritsKeyword_Must_Be_Followed_By_TypeName()
- {
- return GetString("ParseError_InheritsKeyword_Must_Be_Followed_By_TypeName");
- }
+ => GetString("ParseError_InheritsKeyword_Must_Be_Followed_By_TypeName");
///
/// Inline markup blocks (@<p>Content</p>) cannot be nested. Only one level of inline markup is allowed.
///
internal static string ParseError_InlineMarkup_Blocks_Cannot_Be_Nested
{
- get { return GetString("ParseError_InlineMarkup_Blocks_Cannot_Be_Nested"); }
+ get => GetString("ParseError_InlineMarkup_Blocks_Cannot_Be_Nested");
}
///
/// Inline markup blocks (@<p>Content</p>) cannot be nested. Only one level of inline markup is allowed.
///
internal static string FormatParseError_InlineMarkup_Blocks_Cannot_Be_Nested()
- {
- return GetString("ParseError_InlineMarkup_Blocks_Cannot_Be_Nested");
- }
+ => GetString("ParseError_InlineMarkup_Blocks_Cannot_Be_Nested");
///
/// Markup in a code block must start with a tag and all start tags must be matched with end tags. Do not use unclosed tags like "<br>". Instead use self-closing tags like "<br/>".
///
internal static string ParseError_MarkupBlock_Must_Start_With_Tag
{
- get { return GetString("ParseError_MarkupBlock_Must_Start_With_Tag"); }
+ get => GetString("ParseError_MarkupBlock_Must_Start_With_Tag");
}
///
/// Markup in a code block must start with a tag and all start tags must be matched with end tags. Do not use unclosed tags like "<br>". Instead use self-closing tags like "<br/>".
///
internal static string FormatParseError_MarkupBlock_Must_Start_With_Tag()
- {
- return GetString("ParseError_MarkupBlock_Must_Start_With_Tag");
- }
+ => GetString("ParseError_MarkupBlock_Must_Start_With_Tag");
///
/// The "{0}" element was not closed. All elements must be either self-closing or have a matching end tag.
///
internal static string ParseError_MissingEndTag
{
- get { return GetString("ParseError_MissingEndTag"); }
+ get => GetString("ParseError_MissingEndTag");
}
///
/// The "{0}" element was not closed. All elements must be either self-closing or have a matching end tag.
///
internal static string FormatParseError_MissingEndTag(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_MissingEndTag"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ParseError_MissingEndTag"), p0);
///
/// Sections cannot be empty. The "@section" keyword must be followed by a block of markup surrounded by "{}". For example:
@@ -731,7 +643,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
///
internal static string ParseError_MissingOpenBraceAfterSection
{
- get { return GetString("ParseError_MissingOpenBraceAfterSection"); }
+ get => GetString("ParseError_MissingOpenBraceAfterSection");
}
///
@@ -742,9 +654,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
/// }
///
internal static string FormatParseError_MissingOpenBraceAfterSection()
- {
- return GetString("ParseError_MissingOpenBraceAfterSection");
- }
+ => GetString("ParseError_MissingOpenBraceAfterSection");
///
/// Namespace imports and type aliases cannot be placed within code blocks. They must immediately follow an "@" character in markup. It is recommended that you put them at the top of the page, as in the following example:
@@ -756,7 +666,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
///
internal static string ParseError_NamespaceImportAndTypeAlias_Cannot_Exist_Within_CodeBlock
{
- get { return GetString("ParseError_NamespaceImportAndTypeAlias_Cannot_Exist_Within_CodeBlock"); }
+ get => GetString("ParseError_NamespaceImportAndTypeAlias_Cannot_Exist_Within_CodeBlock");
}
///
@@ -768,73 +678,63 @@ namespace Microsoft.AspNetCore.Razor.Evolution
/// }
///
internal static string FormatParseError_NamespaceImportAndTypeAlias_Cannot_Exist_Within_CodeBlock()
- {
- return GetString("ParseError_NamespaceImportAndTypeAlias_Cannot_Exist_Within_CodeBlock");
- }
+ => GetString("ParseError_NamespaceImportAndTypeAlias_Cannot_Exist_Within_CodeBlock");
///
/// Outer tag is missing a name. The first character of a markup block must be an HTML tag with a valid name.
///
internal static string ParseError_OuterTagMissingName
{
- get { return GetString("ParseError_OuterTagMissingName"); }
+ get => GetString("ParseError_OuterTagMissingName");
}
///
/// Outer tag is missing a name. The first character of a markup block must be an HTML tag with a valid name.
///
internal static string FormatParseError_OuterTagMissingName()
- {
- return GetString("ParseError_OuterTagMissingName");
- }
+ => GetString("ParseError_OuterTagMissingName");
///
/// End of file was reached before the end of the block comment. All comments that start with the "@*" sequence must be terminated with a matching "*@" sequence.
///
internal static string ParseError_RazorComment_Not_Terminated
{
- get { return GetString("ParseError_RazorComment_Not_Terminated"); }
+ get => GetString("ParseError_RazorComment_Not_Terminated");
}
///
/// End of file was reached before the end of the block comment. All comments that start with the "@*" sequence must be terminated with a matching "*@" sequence.
///
internal static string FormatParseError_RazorComment_Not_Terminated()
- {
- return GetString("ParseError_RazorComment_Not_Terminated");
- }
+ => GetString("ParseError_RazorComment_Not_Terminated");
///
/// "{0}" is a reserved word and cannot be used in implicit expressions. An explicit expression ("@()") must be used.
///
internal static string ParseError_ReservedWord
{
- get { return GetString("ParseError_ReservedWord"); }
+ get => GetString("ParseError_ReservedWord");
}
///
/// "{0}" is a reserved word and cannot be used in implicit expressions. An explicit expression ("@()") must be used.
///
internal static string FormatParseError_ReservedWord(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_ReservedWord"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ParseError_ReservedWord"), p0);
///
/// Section blocks ("{0}") cannot be nested. Only one level of section blocks are allowed.
///
internal static string ParseError_Sections_Cannot_Be_Nested
{
- get { return GetString("ParseError_Sections_Cannot_Be_Nested"); }
+ get => GetString("ParseError_Sections_Cannot_Be_Nested");
}
///
/// Section blocks ("{0}") cannot be nested. Only one level of section blocks are allowed.
///
internal static string FormatParseError_Sections_Cannot_Be_Nested(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_Sections_Cannot_Be_Nested"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ParseError_Sections_Cannot_Be_Nested"), p0);
///
/// Expected a "{0}" but found a "{1}". Block statements must be enclosed in "{{" and "}}". You cannot use single-statement control-flow statements in CSHTML pages. For example, the following is not allowed:
@@ -850,7 +750,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
///
internal static string ParseError_SingleLine_ControlFlowStatements_Not_Allowed
{
- get { return GetString("ParseError_SingleLine_ControlFlowStatements_Not_Allowed"); }
+ get => GetString("ParseError_SingleLine_ControlFlowStatements_Not_Allowed");
}
///
@@ -866,473 +766,413 @@ namespace Microsoft.AspNetCore.Razor.Evolution
/// }}
///
internal static string FormatParseError_SingleLine_ControlFlowStatements_Not_Allowed(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_SingleLine_ControlFlowStatements_Not_Allowed"), p0, p1);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ParseError_SingleLine_ControlFlowStatements_Not_Allowed"), p0, p1);
///
/// "<text>" and "</text>" tags cannot contain attributes.
///
internal static string ParseError_TextTagCannotContainAttributes
{
- get { return GetString("ParseError_TextTagCannotContainAttributes"); }
+ get => GetString("ParseError_TextTagCannotContainAttributes");
}
///
/// "<text>" and "</text>" tags cannot contain attributes.
///
internal static string FormatParseError_TextTagCannotContainAttributes()
- {
- return GetString("ParseError_TextTagCannotContainAttributes");
- }
+ => GetString("ParseError_TextTagCannotContainAttributes");
///
/// Encountered end tag "{0}" with no matching start tag. Are your start/end tags properly balanced?
///
internal static string ParseError_UnexpectedEndTag
{
- get { return GetString("ParseError_UnexpectedEndTag"); }
+ get => GetString("ParseError_UnexpectedEndTag");
}
///
/// Encountered end tag "{0}" with no matching start tag. Are your start/end tags properly balanced?
///
internal static string FormatParseError_UnexpectedEndTag(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_UnexpectedEndTag"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ParseError_UnexpectedEndTag"), p0);
///
/// Unexpected {0} after section keyword. Section names must start with an "_" or alphabetic character, and the remaining characters must be either "_" or alphanumeric.
///
internal static string ParseError_Unexpected_Character_At_Section_Name_Start
{
- get { return GetString("ParseError_Unexpected_Character_At_Section_Name_Start"); }
+ get => GetString("ParseError_Unexpected_Character_At_Section_Name_Start");
}
///
/// Unexpected {0} after section keyword. Section names must start with an "_" or alphabetic character, and the remaining characters must be either "_" or alphanumeric.
///
internal static string FormatParseError_Unexpected_Character_At_Section_Name_Start(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_Unexpected_Character_At_Section_Name_Start"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ParseError_Unexpected_Character_At_Section_Name_Start"), p0);
///
/// "{0}" is not valid at the start of a code block. Only identifiers, keywords, comments, "(" and "{{" are valid.
///
internal static string ParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS
{
- get { return GetString("ParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS"); }
+ get => GetString("ParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS");
}
///
/// "{0}" is not valid at the start of a code block. Only identifiers, keywords, comments, "(" and "{{" are valid.
///
internal static string FormatParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS"), p0);
///
/// End-of-file was found after the "@" character. "@" must be followed by a valid code block. If you want to output an "@", escape it using the sequence: "@@"
///
internal static string ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock
{
- get { return GetString("ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock"); }
+ get => GetString("ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock");
}
///
/// End-of-file was found after the "@" character. "@" must be followed by a valid code block. If you want to output an "@", escape it using the sequence: "@@"
///
internal static string FormatParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock()
- {
- return GetString("ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock");
- }
+ => GetString("ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock");
///
/// End-of-file was found after the "@" character. "@" must be followed by a valid code block. If you want to output an "@", escape it using the sequence: "@@"
///
internal static string ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock1
{
- get { return GetString("ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock1"); }
+ get => GetString("ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock1");
}
///
/// End-of-file was found after the "@" character. "@" must be followed by a valid code block. If you want to output an "@", escape it using the sequence: "@@"
///
internal static string FormatParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock1()
- {
- return GetString("ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock1");
- }
+ => GetString("ParseError_Unexpected_EndOfFile_At_Start_Of_CodeBlock1");
///
/// Unexpected "{" after "@" character. Once inside the body of a code block (@if {}, @{}, etc.) you do not need to use "@{" to switch to code.
///
internal static string ParseError_Unexpected_Nested_CodeBlock
{
- get { return GetString("ParseError_Unexpected_Nested_CodeBlock"); }
+ get => GetString("ParseError_Unexpected_Nested_CodeBlock");
}
///
/// Unexpected "{" after "@" character. Once inside the body of a code block (@if {}, @{}, etc.) you do not need to use "@{" to switch to code.
///
internal static string FormatParseError_Unexpected_Nested_CodeBlock()
- {
- return GetString("ParseError_Unexpected_Nested_CodeBlock");
- }
+ => GetString("ParseError_Unexpected_Nested_CodeBlock");
///
/// A space or line break was encountered after the "@" character. Only valid identifiers, keywords, comments, "(" and "{" are valid at the start of a code block and they must occur immediately following "@" with no space in between.
///
internal static string ParseError_Unexpected_WhiteSpace_At_Start_Of_CodeBlock_CS
{
- get { return GetString("ParseError_Unexpected_WhiteSpace_At_Start_Of_CodeBlock_CS"); }
+ get => GetString("ParseError_Unexpected_WhiteSpace_At_Start_Of_CodeBlock_CS");
}
///
/// A space or line break was encountered after the "@" character. Only valid identifiers, keywords, comments, "(" and "{" are valid at the start of a code block and they must occur immediately following "@" with no space in between.
///
internal static string FormatParseError_Unexpected_WhiteSpace_At_Start_Of_CodeBlock_CS()
- {
- return GetString("ParseError_Unexpected_WhiteSpace_At_Start_Of_CodeBlock_CS");
- }
+ => GetString("ParseError_Unexpected_WhiteSpace_At_Start_Of_CodeBlock_CS");
///
/// End of file or an unexpected character was reached before the "{0}" tag could be parsed. Elements inside markup blocks must be complete. They must either be self-closing ("<br />") or have matching end tags ("<p>Hello</p>"). If you intended to display a "<" character, use the "<" HTML entity.
///
internal static string ParseError_UnfinishedTag
{
- get { return GetString("ParseError_UnfinishedTag"); }
+ get => GetString("ParseError_UnfinishedTag");
}
///
/// End of file or an unexpected character was reached before the "{0}" tag could be parsed. Elements inside markup blocks must be complete. They must either be self-closing ("<br />") or have matching end tags ("<p>Hello</p>"). If you intended to display a "<" character, use the "<" HTML entity.
///
internal static string FormatParseError_UnfinishedTag(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_UnfinishedTag"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ParseError_UnfinishedTag"), p0);
///
/// Unterminated string literal. Strings that start with a quotation mark (") must be terminated before the end of the line. However, strings that start with @ and a quotation mark (@") can span multiple lines.
///
internal static string ParseError_Unterminated_String_Literal
{
- get { return GetString("ParseError_Unterminated_String_Literal"); }
+ get => GetString("ParseError_Unterminated_String_Literal");
}
///
/// Unterminated string literal. Strings that start with a quotation mark (") must be terminated before the end of the line. However, strings that start with @ and a quotation mark (@") can span multiple lines.
///
internal static string FormatParseError_Unterminated_String_Literal()
- {
- return GetString("ParseError_Unterminated_String_Literal");
- }
+ => GetString("ParseError_Unterminated_String_Literal");
///
/// Cannot complete the tree, StartBlock must be called at least once.
///
internal static string ParserContext_CannotCompleteTree_NoRootBlock
{
- get { return GetString("ParserContext_CannotCompleteTree_NoRootBlock"); }
+ get => GetString("ParserContext_CannotCompleteTree_NoRootBlock");
}
///
/// Cannot complete the tree, StartBlock must be called at least once.
///
internal static string FormatParserContext_CannotCompleteTree_NoRootBlock()
- {
- return GetString("ParserContext_CannotCompleteTree_NoRootBlock");
- }
+ => GetString("ParserContext_CannotCompleteTree_NoRootBlock");
///
/// Cannot complete the tree, there are still open blocks.
///
internal static string ParserContext_CannotCompleteTree_OutstandingBlocks
{
- get { return GetString("ParserContext_CannotCompleteTree_OutstandingBlocks"); }
+ get => GetString("ParserContext_CannotCompleteTree_OutstandingBlocks");
}
///
/// Cannot complete the tree, there are still open blocks.
///
internal static string FormatParserContext_CannotCompleteTree_OutstandingBlocks()
- {
- return GetString("ParserContext_CannotCompleteTree_OutstandingBlocks");
- }
+ => GetString("ParserContext_CannotCompleteTree_OutstandingBlocks");
///
/// Cannot finish span, there is no current block. Call StartBlock at least once before finishing a span
///
internal static string ParserContext_NoCurrentBlock
{
- get { return GetString("ParserContext_NoCurrentBlock"); }
+ get => GetString("ParserContext_NoCurrentBlock");
}
///
/// Cannot finish span, there is no current block. Call StartBlock at least once before finishing a span
///
internal static string FormatParserContext_NoCurrentBlock()
- {
- return GetString("ParserContext_NoCurrentBlock");
- }
+ => GetString("ParserContext_NoCurrentBlock");
///
/// Cannot complete action, the parser has finished. Only CompleteParse can be called to extract the final parser results after the parser has finished
///
internal static string ParserContext_ParseComplete
{
- get { return GetString("ParserContext_ParseComplete"); }
+ get => GetString("ParserContext_ParseComplete");
}
///
/// Cannot complete action, the parser has finished. Only CompleteParse can be called to extract the final parser results after the parser has finished
///
internal static string FormatParserContext_ParseComplete()
- {
- return GetString("ParserContext_ParseComplete");
- }
+ => GetString("ParserContext_ParseComplete");
///
/// Parser was started with a null Context property. The Context property must be set BEFORE calling any methods on the parser.
///
internal static string Parser_Context_Not_Set
{
- get { return GetString("Parser_Context_Not_Set"); }
+ get => GetString("Parser_Context_Not_Set");
}
///
/// Parser was started with a null Context property. The Context property must be set BEFORE calling any methods on the parser.
///
internal static string FormatParser_Context_Not_Set()
- {
- return GetString("Parser_Context_Not_Set");
- }
+ => GetString("Parser_Context_Not_Set");
///
/// Attribute '{0}' on tag helper element '{1}' requires a value. Tag helper bound attributes of type '{2}' cannot be empty or contain only whitespace.
///
internal static string RewriterError_EmptyTagHelperBoundAttribute
{
- get { return GetString("RewriterError_EmptyTagHelperBoundAttribute"); }
+ get => GetString("RewriterError_EmptyTagHelperBoundAttribute");
}
///
/// Attribute '{0}' on tag helper element '{1}' requires a value. Tag helper bound attributes of type '{2}' cannot be empty or contain only whitespace.
///
internal static string FormatRewriterError_EmptyTagHelperBoundAttribute(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("RewriterError_EmptyTagHelperBoundAttribute"), p0, p1, p2);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("RewriterError_EmptyTagHelperBoundAttribute"), p0, p1, p2);
///
/// @section Header { ... }
///
internal static string SectionExample_CS
{
- get { return GetString("SectionExample_CS"); }
+ get => GetString("SectionExample_CS");
}
///
/// @section Header { ... }
///
internal static string FormatSectionExample_CS()
- {
- return GetString("SectionExample_CS");
- }
+ => GetString("SectionExample_CS");
///
/// Cannot perform '{1}' operations on '{0}' instances with different file paths.
///
internal static string SourceLocationFilePathDoesNotMatch
{
- get { return GetString("SourceLocationFilePathDoesNotMatch"); }
+ get => GetString("SourceLocationFilePathDoesNotMatch");
}
///
/// Cannot perform '{1}' operations on '{0}' instances with different file paths.
///
internal static string FormatSourceLocationFilePathDoesNotMatch(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("SourceLocationFilePathDoesNotMatch"), p0, p1);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("SourceLocationFilePathDoesNotMatch"), p0, p1);
///
/// <<unknown>>
///
internal static string Symbol_Unknown
{
- get { return GetString("Symbol_Unknown"); }
+ get => GetString("Symbol_Unknown");
}
///
/// <<unknown>>
///
internal static string FormatSymbol_Unknown()
- {
- return GetString("Symbol_Unknown");
- }
+ => GetString("Symbol_Unknown");
///
/// The tag helper attribute '{0}' in element '{1}' is missing a key. The syntax is '<{1} {0}{{ key }}="value">'.
///
internal static string TagHelperBlockRewriter_IndexerAttributeNameMustIncludeKey
{
- get { return GetString("TagHelperBlockRewriter_IndexerAttributeNameMustIncludeKey"); }
+ get => GetString("TagHelperBlockRewriter_IndexerAttributeNameMustIncludeKey");
}
///
/// The tag helper attribute '{0}' in element '{1}' is missing a key. The syntax is '<{1} {0}{{ key }}="value">'.
///
internal static string FormatTagHelperBlockRewriter_IndexerAttributeNameMustIncludeKey(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperBlockRewriter_IndexerAttributeNameMustIncludeKey"), p0, p1);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperBlockRewriter_IndexerAttributeNameMustIncludeKey"), p0, p1);
///
/// TagHelper attributes must be well-formed.
///
internal static string TagHelperBlockRewriter_TagHelperAttributeListMustBeWellFormed
{
- get { return GetString("TagHelperBlockRewriter_TagHelperAttributeListMustBeWellFormed"); }
+ get => GetString("TagHelperBlockRewriter_TagHelperAttributeListMustBeWellFormed");
}
///
/// TagHelper attributes must be well-formed.
///
internal static string FormatTagHelperBlockRewriter_TagHelperAttributeListMustBeWellFormed()
- {
- return GetString("TagHelperBlockRewriter_TagHelperAttributeListMustBeWellFormed");
- }
+ => GetString("TagHelperBlockRewriter_TagHelperAttributeListMustBeWellFormed");
///
/// The parent <{0}> tag helper does not allow non-tag content. Only child tag helper(s) targeting tag name(s) '{1}' are allowed.
///
internal static string TagHelperParseTreeRewriter_CannotHaveNonTagContent
{
- get { return GetString("TagHelperParseTreeRewriter_CannotHaveNonTagContent"); }
+ get => GetString("TagHelperParseTreeRewriter_CannotHaveNonTagContent");
}
///
/// The parent <{0}> tag helper does not allow non-tag content. Only child tag helper(s) targeting tag name(s) '{1}' are allowed.
///
internal static string FormatTagHelperParseTreeRewriter_CannotHaveNonTagContent(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperParseTreeRewriter_CannotHaveNonTagContent"), p0, p1);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperParseTreeRewriter_CannotHaveNonTagContent"), p0, p1);
///
/// Found an end tag (</{0}>) for tag helper '{1}' with tag structure that disallows an end tag ('{2}').
///
internal static string TagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag
{
- get { return GetString("TagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag"); }
+ get => GetString("TagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag");
}
///
/// Found an end tag (</{0}>) for tag helper '{1}' with tag structure that disallows an end tag ('{2}').
///
internal static string FormatTagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag"), p0, p1, p2);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag"), p0, p1, p2);
///
/// Tag helpers '{0}' and '{1}' targeting element '{2}' must not expect different {3} values.
///
internal static string TagHelperParseTreeRewriter_InconsistentTagStructure
{
- get { return GetString("TagHelperParseTreeRewriter_InconsistentTagStructure"); }
+ get => GetString("TagHelperParseTreeRewriter_InconsistentTagStructure");
}
///
/// Tag helpers '{0}' and '{1}' targeting element '{2}' must not expect different {3} values.
///
internal static string FormatTagHelperParseTreeRewriter_InconsistentTagStructure(object p0, object p1, object p2, object p3)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperParseTreeRewriter_InconsistentTagStructure"), p0, p1, p2, p3);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperParseTreeRewriter_InconsistentTagStructure"), p0, p1, p2, p3);
///
/// The <{0}> tag is not allowed by parent <{1}> tag helper. Only child tags with name(s) '{2}' are allowed.
///
internal static string TagHelperParseTreeRewriter_InvalidNestedTag
{
- get { return GetString("TagHelperParseTreeRewriter_InvalidNestedTag"); }
+ get => GetString("TagHelperParseTreeRewriter_InvalidNestedTag");
}
///
/// The <{0}> tag is not allowed by parent <{1}> tag helper. Only child tags with name(s) '{2}' are allowed.
///
internal static string FormatTagHelperParseTreeRewriter_InvalidNestedTag(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperParseTreeRewriter_InvalidNestedTag"), p0, p1, p2);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperParseTreeRewriter_InvalidNestedTag"), p0, p1, p2);
///
/// Found a malformed '{0}' tag helper. Tag helpers must have a start and end tag or be self closing.
///
internal static string TagHelpersParseTreeRewriter_FoundMalformedTagHelper
{
- get { return GetString("TagHelpersParseTreeRewriter_FoundMalformedTagHelper"); }
+ get => GetString("TagHelpersParseTreeRewriter_FoundMalformedTagHelper");
}
///
/// Found a malformed '{0}' tag helper. Tag helpers must have a start and end tag or be self closing.
///
internal static string FormatTagHelpersParseTreeRewriter_FoundMalformedTagHelper(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpersParseTreeRewriter_FoundMalformedTagHelper"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelpersParseTreeRewriter_FoundMalformedTagHelper"), p0);
///
/// Missing close angle for tag helper '{0}'.
///
internal static string TagHelpersParseTreeRewriter_MissingCloseAngle
{
- get { return GetString("TagHelpersParseTreeRewriter_MissingCloseAngle"); }
+ get => GetString("TagHelpersParseTreeRewriter_MissingCloseAngle");
}
///
/// Missing close angle for tag helper '{0}'.
///
internal static string FormatTagHelpersParseTreeRewriter_MissingCloseAngle(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpersParseTreeRewriter_MissingCloseAngle"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelpersParseTreeRewriter_MissingCloseAngle"), p0);
///
/// Tag Helper '{0}'s attributes must have names.
///
internal static string TagHelpers_AttributesMustHaveAName
{
- get { return GetString("TagHelpers_AttributesMustHaveAName"); }
+ get => GetString("TagHelpers_AttributesMustHaveAName");
}
///
/// Tag Helper '{0}'s attributes must have names.
///
internal static string FormatTagHelpers_AttributesMustHaveAName(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_AttributesMustHaveAName"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_AttributesMustHaveAName"), p0);
///
/// The tag helper '{0}' must not have C# in the element's attribute declaration area.
///
internal static string TagHelpers_CannotHaveCSharpInTagDeclaration
{
- get { return GetString("TagHelpers_CannotHaveCSharpInTagDeclaration"); }
+ get => GetString("TagHelpers_CannotHaveCSharpInTagDeclaration");
}
///
/// The tag helper '{0}' must not have C# in the element's attribute declaration area.
///
internal static string FormatTagHelpers_CannotHaveCSharpInTagDeclaration(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_CannotHaveCSharpInTagDeclaration"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_CannotHaveCSharpInTagDeclaration"), p0);
///
/// Code blocks (e.g. @{{var variable = 23;}}) must not appear in non-string tag helper attribute values.
@@ -1340,7 +1180,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
///
internal static string TagHelpers_CodeBlocks_NotSupported_InAttributes
{
- get { return GetString("TagHelpers_CodeBlocks_NotSupported_InAttributes"); }
+ get => GetString("TagHelpers_CodeBlocks_NotSupported_InAttributes");
}
///
@@ -1348,9 +1188,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
/// Already in an expression (code) context. If necessary an explicit expression (e.g. @(@readonly)) may be used.
///
internal static string FormatTagHelpers_CodeBlocks_NotSupported_InAttributes()
- {
- return GetString("TagHelpers_CodeBlocks_NotSupported_InAttributes");
- }
+ => GetString("TagHelpers_CodeBlocks_NotSupported_InAttributes");
///
/// Inline markup blocks (e.g. @<p>content</p>) must not appear in non-string tag helper attribute values.
@@ -1358,7 +1196,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
///
internal static string TagHelpers_InlineMarkupBlocks_NotSupported_InAttributes
{
- get { return GetString("TagHelpers_InlineMarkupBlocks_NotSupported_InAttributes"); }
+ get => GetString("TagHelpers_InlineMarkupBlocks_NotSupported_InAttributes");
}
///
@@ -1366,57 +1204,49 @@ namespace Microsoft.AspNetCore.Razor.Evolution
/// Expected a '{0}' attribute value, not a string.
///
internal static string FormatTagHelpers_InlineMarkupBlocks_NotSupported_InAttributes(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_InlineMarkupBlocks_NotSupported_InAttributes"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_InlineMarkupBlocks_NotSupported_InAttributes"), p0);
///
/// In order to put a symbol back, it must have been the symbol which ended at the current position. The specified symbol ends at {0}, but the current position is {1}
///
internal static string TokenizerView_CannotPutBack
{
- get { return GetString("TokenizerView_CannotPutBack"); }
+ get => GetString("TokenizerView_CannotPutBack");
}
///
/// In order to put a symbol back, it must have been the symbol which ended at the current position. The specified symbol ends at {0}, but the current position is {1}
///
internal static string FormatTokenizerView_CannotPutBack(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TokenizerView_CannotPutBack"), p0, p1);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TokenizerView_CannotPutBack"), p0, p1);
///
/// Unexpected literal following the '{0}' directive. Expected '{1}'.
///
internal static string UnexpectedDirectiveLiteral
{
- get { return GetString("UnexpectedDirectiveLiteral"); }
+ get => GetString("UnexpectedDirectiveLiteral");
}
///
/// Unexpected literal following the '{0}' directive. Expected '{1}'.
///
internal static string FormatUnexpectedDirectiveLiteral(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("UnexpectedDirectiveLiteral"), p0, p1);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("UnexpectedDirectiveLiteral"), p0, p1);
///
/// Unexpected end of file following the '{0}' directive. Expected '{1}'.
///
internal static string UnexpectedEOFAfterDirective
{
- get { return GetString("UnexpectedEOFAfterDirective"); }
+ get => GetString("UnexpectedEOFAfterDirective");
}
///
/// Unexpected end of file following the '{0}' directive. Expected '{1}'.
///
internal static string FormatUnexpectedEOFAfterDirective(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("UnexpectedEOFAfterDirective"), p0, p1);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("UnexpectedEOFAfterDirective"), p0, p1);
private static string GetString(string name, params string[] formatterNames)
{
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Properties/Resources.Designer.cs
index 9ec06f2f43..dc78770483 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Properties/Resources.Designer.cs
@@ -15,225 +15,379 @@ namespace Microsoft.AspNetCore.Razor.Evolution
///
internal static string ArgumentCannotBeNullOrEmpty
{
- get { return GetString("ArgumentCannotBeNullOrEmpty"); }
+ get => GetString("ArgumentCannotBeNullOrEmpty");
}
///
/// Value cannot be null or an empty string.
///
internal static string FormatArgumentCannotBeNullOrEmpty()
- {
- return GetString("ArgumentCannotBeNullOrEmpty");
- }
+ => GetString("ArgumentCannotBeNullOrEmpty");
///
/// The '{0}' feature requires a '{1}' provided by the '{2}'.
///
internal static string FeatureDependencyMissing
{
- get { return GetString("FeatureDependencyMissing"); }
+ get => GetString("FeatureDependencyMissing");
}
///
/// The '{0}' feature requires a '{1}' provided by the '{2}'.
///
internal static string FormatFeatureDependencyMissing(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("FeatureDependencyMissing"), p0, p1, p2);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("FeatureDependencyMissing"), p0, p1, p2);
///
/// Invalid tag helper directive look up text '{0}'. The correct look up text format is: "typeName, assemblyName".
///
internal static string InvalidTagHelperLookupText
{
- get { return GetString("InvalidTagHelperLookupText"); }
+ get => GetString("InvalidTagHelperLookupText");
}
///
/// Invalid tag helper directive look up text '{0}'. The correct look up text format is: "typeName, assemblyName".
///
internal static string FormatInvalidTagHelperLookupText(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("InvalidTagHelperLookupText"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidTagHelperLookupText"), p0);
///
/// Invalid tag helper directive '{0}' value. '{1}' is not allowed in prefix '{2}'.
///
internal static string InvalidTagHelperPrefixValue
{
- get { return GetString("InvalidTagHelperPrefixValue"); }
+ get => GetString("InvalidTagHelperPrefixValue");
}
///
/// Invalid tag helper directive '{0}' value. '{1}' is not allowed in prefix '{2}'.
///
internal static string FormatInvalidTagHelperPrefixValue(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("InvalidTagHelperPrefixValue"), p0, p1, p2);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidTagHelperPrefixValue"), p0, p1, p2);
///
/// The '{0}' operation is not valid when the builder is empty.
///
internal static string IRBuilder_PopInvalid
{
- get { return GetString("IRBuilder_PopInvalid"); }
+ get => GetString("IRBuilder_PopInvalid");
}
///
/// The '{0}' operation is not valid when the builder is empty.
///
internal static string FormatIRBuilder_PopInvalid(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("IRBuilder_PopInvalid"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("IRBuilder_PopInvalid"), p0);
///
/// The specified encoding '{0}' does not match the content's encoding '{1}'.
///
internal static string MismatchedContentEncoding
{
- get { return GetString("MismatchedContentEncoding"); }
+ get => GetString("MismatchedContentEncoding");
}
///
/// The specified encoding '{0}' does not match the content's encoding '{1}'.
///
internal static string FormatMismatchedContentEncoding(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("MismatchedContentEncoding"), p0, p1);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("MismatchedContentEncoding"), p0, p1);
///
/// The '{0}' phase requires a '{1}' provided by the '{2}'.
///
internal static string PhaseDependencyMissing
{
- get { return GetString("PhaseDependencyMissing"); }
+ get => GetString("PhaseDependencyMissing");
}
///
/// The '{0}' phase requires a '{1}' provided by the '{2}'.
///
internal static string FormatPhaseDependencyMissing(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("PhaseDependencyMissing"), p0, p1, p2);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("PhaseDependencyMissing"), p0, p1, p2);
///
/// The phase must be initialized by setting the '{0}' property.
///
internal static string PhaseMustBeInitialized
{
- get { return GetString("PhaseMustBeInitialized"); }
+ get => GetString("PhaseMustBeInitialized");
}
///
/// The phase must be initialized by setting the '{0}' property.
///
internal static string FormatPhaseMustBeInitialized(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("PhaseMustBeInitialized"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("PhaseMustBeInitialized"), p0);
///
/// Tag helper directive assembly name cannot be null or empty.
///
internal static string TagHelperAssemblyNameCannotBeEmptyOrNull
{
- get { return GetString("TagHelperAssemblyNameCannotBeEmptyOrNull"); }
+ get => GetString("TagHelperAssemblyNameCannotBeEmptyOrNull");
}
///
/// Tag helper directive assembly name cannot be null or empty.
///
internal static string FormatTagHelperAssemblyNameCannotBeEmptyOrNull()
- {
- return GetString("TagHelperAssemblyNameCannotBeEmptyOrNull");
- }
+ => GetString("TagHelperAssemblyNameCannotBeEmptyOrNull");
///
/// The assembly '{0}' could not be resolved or contains no tag helpers.
///
internal static string TagHelperAssemblyCouldNotBeResolved
{
- get { return GetString("TagHelperAssemblyCouldNotBeResolved"); }
+ get => GetString("TagHelperAssemblyCouldNotBeResolved");
}
///
/// The assembly '{0}' could not be resolved or contains no tag helpers.
///
internal static string FormatTagHelperAssemblyCouldNotBeResolved(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperAssemblyCouldNotBeResolved"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperAssemblyCouldNotBeResolved"), p0);
///
/// Path must begin with a forward slash '/'.
///
internal static string RazorProject_PathMustStartWithForwardSlash
{
- get { return GetString("RazorProject_PathMustStartWithForwardSlash"); }
+ get => GetString("RazorProject_PathMustStartWithForwardSlash");
}
///
/// Path must begin with a forward slash '/'.
///
internal static string FormatRazorProject_PathMustStartWithForwardSlash()
- {
- return GetString("RazorProject_PathMustStartWithForwardSlash");
- }
+ => GetString("RazorProject_PathMustStartWithForwardSlash");
///
/// The method '{0}' has already been invoked.
///
internal static string DirectiveDescriptor_BeginOptionalsAlreadyInvoked
{
- get { return GetString("DirectiveDescriptor_BeginOptionalsAlreadyInvoked"); }
+ get => GetString("DirectiveDescriptor_BeginOptionalsAlreadyInvoked");
}
///
/// The method '{0}' has already been invoked.
///
internal static string FormatDirectiveDescriptor_BeginOptionalsAlreadyInvoked(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("DirectiveDescriptor_BeginOptionalsAlreadyInvoked"), p0);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("DirectiveDescriptor_BeginOptionalsAlreadyInvoked"), p0);
///
/// The document of kind '{0}' does not have a '{1}'. The document classifier must set a value for '{2}'.
///
internal static string DocumentMissingTarget
{
- get { return GetString("DocumentMissingTarget"); }
+ get => GetString("DocumentMissingTarget");
}
///
/// The document of kind '{0}' does not have a '{1}'. The document classifier must set a value for '{2}'.
///
internal static string FormatDocumentMissingTarget(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("DocumentMissingTarget"), p0, p1, p2);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("DocumentMissingTarget"), p0, p1, p2);
///
/// The item '{0}' could not be found.
///
internal static string RazorTemplateEngine_ItemCouldNotBeFound
{
- get { return GetString("RazorTemplateEngine_ItemCouldNotBeFound"); }
+ get => GetString("RazorTemplateEngine_ItemCouldNotBeFound");
}
///
/// The item '{0}' could not be found.
///
internal static string FormatRazorTemplateEngine_ItemCouldNotBeFound(object p0)
+ => string.Format(CultureInfo.CurrentCulture, GetString("RazorTemplateEngine_ItemCouldNotBeFound"), p0);
+
+ ///
+ /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name contains a '{3}' character.
+ ///
+ internal static string InvalidBoundAttributeName
{
- return string.Format(CultureInfo.CurrentCulture, GetString("RazorTemplateEngine_ItemCouldNotBeFound"), p0);
+ get => GetString("InvalidBoundAttributeName");
}
+ ///
+ /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name contains a '{3}' character.
+ ///
+ internal static string FormatInvalidBoundAttributeName(object p0, object p1, object p2, object p3)
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidBoundAttributeName"), p0, p1, p2, p3);
+
+ ///
+ /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name starts with '{3}'.
+ ///
+ internal static string InvalidBoundAttributeNameStartsWith
+ {
+ get => GetString("InvalidBoundAttributeNameStartsWith");
+ }
+
+ ///
+ /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name starts with '{3}'.
+ ///
+ internal static string FormatInvalidBoundAttributeNameStartsWith(object p0, object p1, object p2, object p3)
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidBoundAttributeNameStartsWith"), p0, p1, p2, p3);
+
+ ///
+ /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.
+ ///
+ internal static string InvalidBoundAttributeNullOrWhitespace
+ {
+ get => GetString("InvalidBoundAttributeNullOrWhitespace");
+ }
+
+ ///
+ /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.
+ ///
+ internal static string FormatInvalidBoundAttributeNullOrWhitespace(object p0, object p1)
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidBoundAttributeNullOrWhitespace"), p0, p1);
+
+ ///
+ /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix contains a '{3}' character.
+ ///
+ internal static string InvalidBoundAttributePrefix
+ {
+ get => GetString("InvalidBoundAttributePrefix");
+ }
+
+ ///
+ /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix contains a '{3}' character.
+ ///
+ internal static string FormatInvalidBoundAttributePrefix(object p0, object p1, object p2, object p3)
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidBoundAttributePrefix"), p0, p1, p2, p3);
+
+ ///
+ /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix starts with '{3}'.
+ ///
+ internal static string InvalidBoundAttributePrefixStartsWith
+ {
+ get => GetString("InvalidBoundAttributePrefixStartsWith");
+ }
+
+ ///
+ /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix starts with '{3}'.
+ ///
+ internal static string FormatInvalidBoundAttributePrefixStartsWith(object p0, object p1, object p2, object p3)
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidBoundAttributePrefixStartsWith"), p0, p1, p2, p3);
+
+ ///
+ /// Invalid restricted child '{0}' for tag helper '{1}'. Tag helpers cannot restrict child elements that contain a '{2}' character.
+ ///
+ internal static string InvalidRestrictedChild
+ {
+ get => GetString("InvalidRestrictedChild");
+ }
+
+ ///
+ /// Invalid restricted child '{0}' for tag helper '{1}'. Tag helpers cannot restrict child elements that contain a '{2}' character.
+ ///
+ internal static string FormatInvalidRestrictedChild(object p0, object p1, object p2)
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidRestrictedChild"), p0, p1, p2);
+
+ ///
+ /// Invalid restricted child for tag helper '{0}'. Name cannot be null or whitespace.
+ ///
+ internal static string InvalidRestrictedChildNullOrWhitespace
+ {
+ get => GetString("InvalidRestrictedChildNullOrWhitespace");
+ }
+
+ ///
+ /// Invalid restricted child for tag helper '{0}'. Name cannot be null or whitespace.
+ ///
+ internal static string FormatInvalidRestrictedChildNullOrWhitespace(object p0)
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidRestrictedChildNullOrWhitespace"), p0);
+
+ ///
+ /// Tag helpers cannot target attribute name '{0}' because it contains a '{1}' character.
+ ///
+ internal static string InvalidTargetedAttributeName
+ {
+ get => GetString("InvalidTargetedAttributeName");
+ }
+
+ ///
+ /// Tag helpers cannot target attribute name '{0}' because it contains a '{1}' character.
+ ///
+ internal static string FormatInvalidTargetedAttributeName(object p0, object p1)
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidTargetedAttributeName"), p0, p1);
+
+ ///
+ /// Targeted attribute name cannot be null or whitespace.
+ ///
+ internal static string InvalidTargetedAttributeNameNullOrWhitespace
+ {
+ get => GetString("InvalidTargetedAttributeNameNullOrWhitespace");
+ }
+
+ ///
+ /// Targeted attribute name cannot be null or whitespace.
+ ///
+ internal static string FormatInvalidTargetedAttributeNameNullOrWhitespace()
+ => GetString("InvalidTargetedAttributeNameNullOrWhitespace");
+
+ ///
+ /// Tag helpers cannot target parent tag name '{0}' because it contains a '{1}' character.
+ ///
+ internal static string InvalidTargetedParentTagName
+ {
+ get => GetString("InvalidTargetedParentTagName");
+ }
+
+ ///
+ /// Tag helpers cannot target parent tag name '{0}' because it contains a '{1}' character.
+ ///
+ internal static string FormatInvalidTargetedParentTagName(object p0, object p1)
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidTargetedParentTagName"), p0, p1);
+
+ ///
+ /// Targeted parent tag name cannot be null or whitespace.
+ ///
+ internal static string InvalidTargetedParentTagNameNullOrWhitespace
+ {
+ get => GetString("InvalidTargetedParentTagNameNullOrWhitespace");
+ }
+
+ ///
+ /// Targeted parent tag name cannot be null or whitespace.
+ ///
+ internal static string FormatInvalidTargetedParentTagNameNullOrWhitespace()
+ => GetString("InvalidTargetedParentTagNameNullOrWhitespace");
+
+ ///
+ /// Tag helpers cannot target tag name '{0}' because it contains a '{1}' character.
+ ///
+ internal static string InvalidTargetedTagName
+ {
+ get => GetString("InvalidTargetedTagName");
+ }
+
+ ///
+ /// Tag helpers cannot target tag name '{0}' because it contains a '{1}' character.
+ ///
+ internal static string FormatInvalidTargetedTagName(object p0, object p1)
+ => string.Format(CultureInfo.CurrentCulture, GetString("InvalidTargetedTagName"), p0, p1);
+
+ ///
+ /// Targeted tag name cannot be null or whitespace.
+ ///
+ internal static string InvalidTargetedTagNameNullOrWhitespace
+ {
+ get => GetString("InvalidTargetedTagNameNullOrWhitespace");
+ }
+
+ ///
+ /// Targeted tag name cannot be null or whitespace.
+ ///
+ internal static string FormatInvalidTargetedTagNameNullOrWhitespace()
+ => GetString("InvalidTargetedTagNameNullOrWhitespace");
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/RazorCodeDocumentExtensions.cs b/src/Microsoft.AspNetCore.Razor.Evolution/RazorCodeDocumentExtensions.cs
index 6e6416289c..ea2e4dce74 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/RazorCodeDocumentExtensions.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/RazorCodeDocumentExtensions.cs
@@ -9,6 +9,28 @@ namespace Microsoft.AspNetCore.Razor.Evolution
{
public static class RazorCodeDocumentExtensions
{
+ private static object TagHelperPrefixKey = new object();
+
+ public static string GetTagHelperPrefix(this RazorCodeDocument document)
+ {
+ if (document == null)
+ {
+ throw new ArgumentNullException(nameof(document));
+ }
+
+ return document.Items[TagHelperPrefixKey] as string;
+ }
+
+ public static void SetTagHelperPrefix(this RazorCodeDocument document, string tagHelperPrefix)
+ {
+ if (document == null)
+ {
+ throw new ArgumentNullException(nameof(document));
+ }
+
+ document.Items[TagHelperPrefixKey] = tagHelperPrefix;
+ }
+
public static RazorSyntaxTree GetSyntaxTree(this RazorCodeDocument document)
{
if (document == null)
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnosticDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnosticDescriptor.cs
index e29e48cea9..8749b74f1e 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnosticDescriptor.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnosticDescriptor.cs
@@ -6,7 +6,7 @@ using System.Diagnostics;
namespace Microsoft.AspNetCore.Razor.Evolution
{
- [DebuggerDisplay("{Error} {Id}: {GetMessageFormat()}")]
+ [DebuggerDisplay("Error {Id}: {GetMessageFormat()}")]
public sealed class RazorDiagnosticDescriptor : IEquatable
{
private readonly Func _messageFormat;
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnosticFactory.cs b/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnosticFactory.cs
new file mode 100644
index 0000000000..23ad044a5a
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/RazorDiagnosticFactory.cs
@@ -0,0 +1,268 @@
+// 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;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ internal static class RazorDiagnosticFactory
+ {
+ private const string DiagnosticPrefix = "RZ";
+
+ #region General Errors
+
+ /*
+ * General Errors ID Offset = 0
+ */
+
+ #endregion
+
+ #region Language Errors
+
+ /*
+ * Language Errors ID Offset = 1000
+ */
+
+ #endregion
+
+ #region Semantic Errors
+
+ /*
+ * Semantic Errors ID Offset = 2000
+ */
+
+ #endregion
+
+ #region TagHelper Errors
+
+ /*
+ * TagHelper Errors ID Offset = 3000
+ */
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidRestrictedChildNullOrWhitespace =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3000",
+ () => Resources.InvalidRestrictedChildNullOrWhitespace,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidRestrictedChildNullOrWhitespace(string tagHelperType)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidRestrictedChildNullOrWhitespace,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ tagHelperType);
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidRestrictedChild =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3001",
+ () => Resources.InvalidRestrictedChild,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidRestrictedChild(string restrictedChild, string tagHelperType, char invalidCharacter)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidRestrictedChild,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ restrictedChild,
+ tagHelperType,
+ invalidCharacter);
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributeNullOrWhitespace =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3002",
+ () => Resources.InvalidBoundAttributeNullOrWhitespace,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributeNullOrWhitespace(string containingTypeName, string propertyName)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidBoundAttributeNullOrWhitespace,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ containingTypeName,
+ propertyName);
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributeName =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3003",
+ () => Resources.InvalidBoundAttributeName,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributeName(
+ string containingTypeName,
+ string propertyName,
+ string invalidName,
+ char invalidCharacter)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidBoundAttributeName,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ containingTypeName,
+ propertyName,
+ invalidName,
+ invalidCharacter);
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributeNameStartsWith =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3004",
+ () => Resources.InvalidBoundAttributeNameStartsWith,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributeNameStartsWith(
+ string containingTypeName,
+ string propertyName,
+ string invalidName)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidBoundAttributeNameStartsWith,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ containingTypeName,
+ propertyName,
+ invalidName,
+ "data-");
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributePrefix =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3005",
+ () => Resources.InvalidBoundAttributePrefix,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributePrefix(
+ string containingTypeName,
+ string propertyName,
+ string invalidName,
+ char invalidCharacter)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidBoundAttributePrefix,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ containingTypeName,
+ propertyName,
+ invalidName,
+ invalidCharacter);
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributePrefixStartsWith =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3006",
+ () => Resources.InvalidBoundAttributePrefixStartsWith,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributePrefixStartsWith(
+ string containingTypeName,
+ string propertyName,
+ string invalidName)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidBoundAttributePrefixStartsWith,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ containingTypeName,
+ propertyName,
+ invalidName,
+ "data-");
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedTagNameNullOrWhitespace =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3007",
+ () => Resources.InvalidTargetedTagNameNullOrWhitespace,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidTargetedTagNameNullOrWhitespace()
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidTargetedTagNameNullOrWhitespace,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0));
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedTagName =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3008",
+ () => Resources.InvalidTargetedTagName,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidTargetedTagName(string invalidTagName, char invalidCharacter)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidTargetedTagName,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ invalidTagName,
+ invalidCharacter);
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedParentTagNameNullOrWhitespace =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3009",
+ () => Resources.InvalidTargetedParentTagNameNullOrWhitespace,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidTargetedParentTagNameNullOrWhitespace()
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidTargetedParentTagNameNullOrWhitespace,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0));
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedParentTagName =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3010",
+ () => Resources.InvalidTargetedParentTagName,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidTargetedParentTagName(string invalidTagName, char invalidCharacter)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidTargetedParentTagName,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ invalidTagName,
+ invalidCharacter);
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedAttributeNameNullOrWhitespace =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3009",
+ () => Resources.InvalidTargetedAttributeNameNullOrWhitespace,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidTargetedAttributeNameNullOrWhitespace()
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidTargetedAttributeNameNullOrWhitespace,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0));
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedAttributeName =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3010",
+ () => Resources.InvalidTargetedAttributeName,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidTargetedAttributeName(string invalidAttributeName, char invalidCharacter)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidTargetedAttributeName,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ invalidAttributeName,
+ invalidCharacter);
+
+ return diagnostic;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/RazorPreallocatedTagHelperAttributeOptimizationPass.cs b/src/Microsoft.AspNetCore.Razor.Evolution/RazorPreallocatedTagHelperAttributeOptimizationPass.cs
index 211923a91b..044397af78 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/RazorPreallocatedTagHelperAttributeOptimizationPass.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/RazorPreallocatedTagHelperAttributeOptimizationPass.cs
@@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
public override void VisitSetTagHelperProperty(SetTagHelperPropertyIRNode node)
{
- if (!node.Descriptor.IsStringProperty ||
+ if (!(node.Descriptor.IsStringProperty || (node.IsIndexerNameMatch && node.Descriptor.IsIndexerStringProperty)) ||
node.Children.Count != 1 ||
!(node.Children.First() is HtmlContentIRNode))
{
@@ -139,7 +139,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
TagHelperTypeName = node.TagHelperTypeName,
PropertyName = node.PropertyName,
Descriptor = node.Descriptor,
- Parent = node.Parent
+ Binding = node.Binding,
+ Parent = node.Parent,
+ IsIndexerNameMatch = node.IsIndexerNameMatch,
};
var nodeIndex = node.Parent.Children.IndexOf(node);
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/RequiredAttributeDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Evolution/RequiredAttributeDescriptor.cs
new file mode 100644
index 0000000000..e26443e6ba
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/RequiredAttributeDescriptor.cs
@@ -0,0 +1,75 @@
+// 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
+{
+ public abstract class RequiredAttributeDescriptor
+ {
+ public string Name { get; protected set; }
+
+ public NameComparisonMode NameComparison { get; protected set; }
+
+ public string Value { get; protected set; }
+
+ public ValueComparisonMode ValueComparison { get; protected set; }
+
+ public IReadOnlyList Diagnostics { get; protected set; }
+
+ public bool HasAnyErrors
+ {
+ get
+ {
+ var anyErrors = Diagnostics.Any(diagnostic => diagnostic.Severity == RazorDiagnosticSeverity.Error);
+
+ return anyErrors;
+ }
+ }
+
+ ///
+ /// Acceptable comparison modes.
+ ///
+ public enum NameComparisonMode
+ {
+ ///
+ /// HTML attribute name case insensitively matches .
+ ///
+ FullMatch,
+
+ ///
+ /// HTML attribute name case insensitively starts with .
+ ///
+ PrefixMatch,
+ }
+
+ ///
+ /// Acceptable comparison modes.
+ ///
+ public enum ValueComparisonMode
+ {
+ ///
+ /// HTML attribute value always matches .
+ ///
+ None,
+
+ ///
+ /// HTML attribute value case sensitively matches .
+ ///
+ FullMatch,
+
+ ///
+ /// HTML attribute value case sensitively starts with .
+ ///
+ PrefixMatch,
+
+ ///
+ /// HTML attribute value case sensitively ends with .
+ ///
+ SuffixMatch,
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/RequiredAttributeDescriptorBuilder.cs b/src/Microsoft.AspNetCore.Razor.Evolution/RequiredAttributeDescriptorBuilder.cs
new file mode 100644
index 0000000000..1b0ea7968a
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/RequiredAttributeDescriptorBuilder.cs
@@ -0,0 +1,141 @@
+// 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.Linq;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ public sealed class RequiredAttributeDescriptorBuilder
+ {
+ private static ICollection InvalidNonWhitespaceAttributeNameCharacters { get; } = new HashSet(
+ new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*' });
+
+ private string _name;
+ private RequiredAttributeDescriptor.NameComparisonMode _nameComparison;
+ private string _value;
+ private RequiredAttributeDescriptor.ValueComparisonMode _valueComparison;
+ private HashSet _diagnostics;
+
+ private RequiredAttributeDescriptorBuilder()
+ {
+ }
+
+ public static RequiredAttributeDescriptorBuilder Create()
+ {
+ return new RequiredAttributeDescriptorBuilder();
+ }
+
+ public RequiredAttributeDescriptorBuilder Name(string name)
+ {
+ _name = name;
+
+ return this;
+ }
+
+ public RequiredAttributeDescriptorBuilder NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode nameComparison)
+ {
+ _nameComparison = nameComparison;
+
+ return this;
+ }
+
+ public RequiredAttributeDescriptorBuilder Value(string value)
+ {
+ _value = value;
+
+ return this;
+ }
+
+ public RequiredAttributeDescriptorBuilder ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode valueComparison)
+ {
+ _valueComparison = valueComparison;
+
+ return this;
+ }
+
+ public RequiredAttributeDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic)
+ {
+ EnsureDiagnostics();
+ _diagnostics.Add(diagnostic);
+
+ return this;
+ }
+
+ public RequiredAttributeDescriptor Build()
+ {
+ var validationDiagnostics = Validate();
+ var diagnostics = new HashSet(validationDiagnostics);
+ if (_diagnostics != null)
+ {
+ diagnostics.UnionWith(_diagnostics);
+ }
+
+ var rule = new DefaultTagHelperRequiredAttributeDescriptor(
+ _name,
+ _nameComparison,
+ _value,
+ _valueComparison,
+ diagnostics);
+
+ return rule;
+ }
+
+ public void Reset()
+ {
+ _name = null;
+ _value = null;
+ _nameComparison = default(RequiredAttributeDescriptor.NameComparisonMode);
+ _valueComparison = default(RequiredAttributeDescriptor.ValueComparisonMode);
+ _diagnostics?.Clear();
+ }
+
+ private IEnumerable Validate()
+ {
+ if (string.IsNullOrWhiteSpace(_name))
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeNameNullOrWhitespace();
+
+ yield return diagnostic;
+ }
+ else
+ {
+ foreach (var character in _name)
+ {
+ if (char.IsWhiteSpace(character) || InvalidNonWhitespaceAttributeNameCharacters.Contains(character))
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeName(_name, character);
+
+ yield return diagnostic;
+ }
+ }
+ }
+ }
+
+ private void EnsureDiagnostics()
+ {
+ if (_diagnostics == null)
+ {
+ _diagnostics = new HashSet();
+ }
+ }
+
+ private class DefaultTagHelperRequiredAttributeDescriptor : RequiredAttributeDescriptor
+ {
+ public DefaultTagHelperRequiredAttributeDescriptor(
+ string name,
+ NameComparisonMode nameComparison,
+ string value,
+ ValueComparisonMode valueComparison,
+ IEnumerable diagnostics)
+ {
+ Name = name;
+ NameComparison = nameComparison;
+ Value = value;
+ ValueComparison = valueComparison;
+ Diagnostics = new List(diagnostics);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Resources.resx b/src/Microsoft.AspNetCore.Razor.Evolution/Resources.resx
index 2701fa6d07..6db3b53ac4 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/Resources.resx
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/Resources.resx
@@ -159,4 +159,43 @@
The item '{0}' could not be found.
+
+ Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name contains a '{3}' character.
+
+
+ Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name starts with '{3}'.
+
+
+ Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.
+
+
+ Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix contains a '{3}' character.
+
+
+ Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix starts with '{3}'.
+
+
+ Invalid restricted child '{0}' for tag helper '{1}'. Tag helpers cannot restrict child elements that contain a '{2}' character.
+
+
+ Invalid restricted child for tag helper '{0}'. Name cannot be null or whitespace.
+
+
+ Tag helpers cannot target attribute name '{0}' because it contains a '{1}' character.
+
+
+ Targeted attribute name cannot be null or whitespace.
+
+
+ Tag helpers cannot target parent tag name '{0}' because it contains a '{1}' character.
+
+
+ Targeted parent tag name cannot be null or whitespace.
+
+
+ Tag helpers cannot target tag name '{0}' because it contains a '{1}' character.
+
+
+ Targeted tag name cannot be null or whitespace.
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperAttributeDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperAttributeDescriptor.cs
deleted file mode 100644
index 3f9a8aef88..0000000000
--- a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperAttributeDescriptor.cs
+++ /dev/null
@@ -1,156 +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.Reflection;
-
-namespace Microsoft.AspNetCore.Razor.Evolution
-{
- ///
- /// A metadata class describing a tag helper attribute.
- ///
- public class TagHelperAttributeDescriptor
- {
- private string _typeName;
- private string _name;
- private string _propertyName;
-
- ///
- /// Instantiates a new instance of the class.
- ///
- public TagHelperAttributeDescriptor()
- {
- }
-
- // Internal for testing i.e. for easy TagHelperAttributeDescriptor creation when PropertyInfo is available.
- internal TagHelperAttributeDescriptor(string name, PropertyInfo propertyInfo)
- {
- Name = name;
- PropertyName = propertyInfo.Name;
- TypeName = propertyInfo.PropertyType.FullName;
- IsEnum = propertyInfo.PropertyType.GetTypeInfo().IsEnum;
- }
-
- ///
- /// Gets an indication whether this is used for dictionary indexer
- /// assignments.
- ///
- ///
- /// If true this should be associated with all HTML
- /// attributes that have names starting with . Otherwise this
- /// is used for property assignment and is only associated with an
- /// HTML attribute that has the exact .
- ///
- ///
- /// HTML attribute names are matched case-insensitively, regardless of .
- ///
- public bool IsIndexer { get; set; }
-
- ///
- /// Gets or sets an indication whether this property is an .
- ///
- public bool IsEnum { get; set; }
-
- ///
- /// Gets or sets an indication whether this property is of type or, if
- /// is true, whether the indexer's value is of type .
- ///
- ///
- /// If true the is for . This causes the Razor parser
- /// to allow empty values for HTML attributes matching this . If
- /// false empty values for such matching attributes lead to errors.
- ///
- public bool IsStringProperty { get; set; }
-
- ///
- /// The HTML attribute name or, if is true, the prefix for matching attribute
- /// names.
- ///
- public string Name
- {
- get
- {
- return _name;
- }
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
- _name = value;
- }
- }
-
-
- ///
- /// The name of the CLR property that corresponds to the HTML attribute.
- ///
- public string PropertyName
- {
- get
- {
- return _propertyName;
- }
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
- _propertyName = value;
- }
- }
-
- ///
- /// The full name of the named (see ) property's or, if
- /// is true, the full name of the indexer's value .
- ///
- public string TypeName
- {
- get
- {
- return _typeName;
- }
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
- _typeName = value;
- IsStringProperty = string.Equals(TypeName, typeof(string).FullName, StringComparison.Ordinal);
- }
- }
-
- ///
- /// The that contains design time information about
- /// this attribute.
- ///
- public TagHelperAttributeDesignTimeDescriptor DesignTimeDescriptor { get; set; }
-
- ///
- /// Determines whether HTML attribute matches this
- /// .
- ///
- /// Name of the HTML attribute to check.
- ///
- /// true if this matches .
- /// false otherwise.
- ///
- public bool IsNameMatch(string name)
- {
- if (IsIndexer)
- {
- return name.StartsWith(Name, StringComparison.OrdinalIgnoreCase);
- }
- else
- {
- return string.Equals(name, Name, StringComparison.OrdinalIgnoreCase);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperAttributeDesignTimeDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperAttributeDesignTimeDescriptor.cs
deleted file mode 100644
index dfadeda0b0..0000000000
--- a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperAttributeDesignTimeDescriptor.cs
+++ /dev/null
@@ -1,21 +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.AspNetCore.Razor.Evolution
-{
- ///
- /// A metadata class containing information about tag helper use.
- ///
- public class TagHelperAttributeDesignTimeDescriptor
- {
- ///
- /// A summary of how to use a tag helper.
- ///
- public string Summary { get; set; }
-
- ///
- /// Remarks about how to use a tag helper.
- ///
- public string Remarks { get; set; }
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperBinderSyntaxTreePass.cs b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperBinderSyntaxTreePass.cs
index 0b229ddeaa..be0c732154 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperBinderSyntaxTreePass.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperBinderSyntaxTreePass.cs
@@ -42,7 +42,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
visitor.VisitBlock(import.Root);
}
}
-
+
visitor.VisitBlock(syntaxTree.Root);
var errorList = new List();
@@ -52,6 +52,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
var directives = visitor.Directives;
descriptors = ProcessDirectives(directives, descriptors, errorSink);
+ var tagHelperPrefix = ProcessTagHelperPrefix(directives, codeDocument, errorSink);
var root = syntaxTree.Root;
if (descriptors.Count == 0)
@@ -64,20 +65,51 @@ namespace Microsoft.AspNetCore.Razor.Evolution
}
else
{
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
- var rewriter = new TagHelperParseTreeRewriter(descriptorProvider);
+ var descriptorProvider = new TagHelperDescriptorProvider(tagHelperPrefix, descriptors);
+ var rewriter = new TagHelperParseTreeRewriter(tagHelperPrefix, descriptorProvider);
root = rewriter.Rewrite(root, errorSink);
}
// Temporary code while we're still using legacy diagnostics in the SyntaxTree.
errorList.AddRange(errorSink.Errors.Select(error => RazorDiagnostic.Create(error)));
+ errorList.AddRange(descriptors.SelectMany(d => d.GetAllDiagnostics()));
+
var diagnostics = CombineErrors(syntaxTree.Diagnostics, errorList);
var newSyntaxTree = RazorSyntaxTree.Create(root, syntaxTree.Source, diagnostics, syntaxTree.Options);
return newSyntaxTree;
}
+ // Internal for testing
+ internal string ProcessTagHelperPrefix(List directives, RazorCodeDocument codeDocument, ErrorSink errorSink)
+ {
+ // We only support a single prefix directive.
+ TagHelperDirectiveDescriptor prefixDirective = null;
+ for (var i = 0; i < directives.Count; i++)
+ {
+ if (directives[i].DirectiveType == TagHelperDirectiveType.TagHelperPrefix)
+ {
+ // We only expect to see a single one of these per file, but that's enforced at another level.
+ prefixDirective = directives[i];
+ }
+ }
+
+ var prefix = prefixDirective?.DirectiveText;
+ if (prefix != null && !IsValidTagHelperPrefix(prefix, prefixDirective.Location, errorSink))
+ {
+ prefix = null;
+ }
+
+ if (!string.IsNullOrEmpty(prefix))
+ {
+ codeDocument.SetTagHelperPrefix(prefixDirective.DirectiveText);
+ return prefixDirective.DirectiveText;
+ }
+
+ return null;
+ }
+
internal IReadOnlyList ProcessDirectives(
IReadOnlyList directives,
IReadOnlyList tagHelpers,
@@ -85,9 +117,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution
{
var matches = new HashSet(TagHelperDescriptorComparer.Default);
- // We only support a single prefix directive.
- TagHelperDirectiveDescriptor prefixDirective = null;
-
for (var i = 0; i < directives.Count; i++)
{
var directive = directives[i];
@@ -156,24 +185,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution
}
}
- break;
-
- case TagHelperDirectiveType.TagHelperPrefix:
-
- // We only expect to see a single one of these per file, but that's enforced at another level.
- prefixDirective = directive;
-
break;
}
}
- var prefix = prefixDirective?.DirectiveText;
- if (prefix != null && !IsValidTagHelperPrefix(prefix, prefixDirective.Location, errorSink))
- {
- prefix = null;
- }
-
- return PrefixDescriptors(prefix, matches);
+ return matches.ToArray();
}
private bool AssemblyContainsTagHelpers(string assemblyName, IReadOnlyList tagHelpers)
@@ -254,21 +270,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution
return true;
}
- private static IReadOnlyList PrefixDescriptors(
- string prefix,
- IEnumerable descriptors)
- {
- if (!string.IsNullOrEmpty(prefix))
- {
- return descriptors.Select(descriptor => new TagHelperDescriptor(descriptor)
- {
- Prefix = prefix
- }).ToList();
- }
-
- return descriptors.ToList();
- }
-
private static bool MatchesDirective(TagHelperDescriptor descriptor, ParsedDirective lookupInfo)
{
if (!string.Equals(descriptor.AssemblyName, lookupInfo.AssemblyName, StringComparison.Ordinal))
@@ -276,6 +277,14 @@ namespace Microsoft.AspNetCore.Razor.Evolution
return false;
}
+ if (descriptor.Kind != ITagHelperDescriptorBuilder.DescriptorKind)
+ {
+ // We only understand TagHelperDescriptors generated from ITagHelpers.
+ return false;
+ }
+
+ var descriptorTypeName = descriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
+
if (lookupInfo.TypePattern.EndsWith("*", StringComparison.Ordinal))
{
if (lookupInfo.TypePattern.Length == 1)
@@ -286,10 +295,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution
var lookupTypeName = lookupInfo.TypePattern.Substring(0, lookupInfo.TypePattern.Length - 1);
- return descriptor.TypeName.StartsWith(lookupTypeName, StringComparison.Ordinal);
+ return descriptorTypeName.StartsWith(lookupTypeName, StringComparison.Ordinal);
}
- return string.Equals(descriptor.TypeName, lookupInfo.TypePattern, StringComparison.Ordinal);
+ return string.Equals(descriptorTypeName, lookupInfo.TypePattern, StringComparison.Ordinal);
}
private static int GetErrorLength(string directiveText)
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperDescriptor.cs
index 0f6fb23e08..0babe18bc2 100644
--- a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperDescriptor.cs
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperDescriptor.cs
@@ -1,251 +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 System.Linq;
namespace Microsoft.AspNetCore.Razor.Evolution
{
- ///
- /// A metadata class describing a tag helper.
- ///
- public class TagHelperDescriptor
+ public abstract class TagHelperDescriptor
{
- private string _prefix = string.Empty;
- private string _tagName;
- private string _typeName;
- private string _assemblyName;
- private IDictionary _propertyBag;
- private IEnumerable _attributes =
- Enumerable.Empty();
- private IEnumerable _requiredAttributes =
- Enumerable.Empty();
+ private IEnumerable _allDiagnostics;
- ///
- /// Creates a new .
- ///
- public TagHelperDescriptor()
+ protected TagHelperDescriptor(string kind)
{
+ Kind = kind;
}
- ///
- /// Creates a shallow copy of the given .
- ///
- /// The to copy.
- public TagHelperDescriptor(TagHelperDescriptor descriptor)
- {
- Prefix = descriptor.Prefix;
- TagName = descriptor.TagName;
- TypeName = descriptor.TypeName;
- AssemblyName = descriptor.AssemblyName;
- Attributes = descriptor.Attributes;
- RequiredAttributes = descriptor.RequiredAttributes;
- AllowedChildren = descriptor.AllowedChildren;
- RequiredParent = descriptor.RequiredParent;
- TagStructure = descriptor.TagStructure;
- DesignTimeDescriptor = descriptor.DesignTimeDescriptor;
+ public string Kind { get; }
- foreach (var property in descriptor.PropertyBag)
- {
- PropertyBag.Add(property.Key, property.Value);
- }
- }
+ public string Name { get; protected set; }
- ///
- /// Text used as a required prefix when matching HTML start and end tags in the Razor source to available
- /// tag helpers.
- ///
- public string Prefix
+ public IEnumerable TagMatchingRules { get; protected set; }
+
+ public string AssemblyName { get; protected set; }
+
+ public IEnumerable BoundAttributes { get; protected set; }
+
+ public IEnumerable AllowedChildTags { get; protected set; }
+
+ public string Documentation { get; protected set; }
+
+ public string DisplayName { get; protected set; }
+
+ public string TagOutputHint { get; protected set; }
+
+ public IReadOnlyList Diagnostics { get; protected set; }
+
+ public IReadOnlyDictionary Metadata { get; protected set; }
+
+ public bool HasAnyErrors
{
get
{
- return _prefix;
- }
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
+ var allDiagnostics = GetAllDiagnostics();
+ var anyErrors = allDiagnostics.Any(diagnostic => diagnostic.Severity == RazorDiagnosticSeverity.Error);
- _prefix = value;
+ return anyErrors;
}
}
- ///
- /// The tag name that the tag helper should target.
- ///
- public string TagName
+ public virtual IEnumerable GetAllDiagnostics()
{
- get
+ if (_allDiagnostics == null)
{
- return _tagName;
+ var attributeDiagnostics = BoundAttributes.SelectMany(attribute => attribute.Diagnostics);
+ var ruleDiagnostics = TagMatchingRules.SelectMany(rule => rule.GetAllDiagnostics());
+ var combinedDiagnostics = attributeDiagnostics.Concat(ruleDiagnostics).Concat(Diagnostics);
+ _allDiagnostics = combinedDiagnostics.ToArray();
}
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
- _tagName = value;
- }
- }
-
- ///
- /// The full tag name that is required for the tag helper to target an HTML element.
- ///
- /// This is equivalent to and concatenated.
- public string FullTagName
- {
- get
- {
- return Prefix + TagName;
- }
- }
-
- ///
- /// The full name of the tag helper class.
- ///
- public string TypeName
- {
- get
- {
- return _typeName;
- }
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
- _typeName = value;
- }
- }
-
- ///
- /// The name of the assembly containing the tag helper class.
- ///
- public string AssemblyName
- {
- get
- {
- return _assemblyName;
- }
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
- _assemblyName = value;
- }
- }
-
- ///
- /// The list of attributes the tag helper expects.
- ///
- public IEnumerable Attributes
- {
- get
- {
- return _attributes;
- }
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
- _attributes = value;
- }
- }
-
- ///
- /// The list of required attribute names the tag helper expects to target an element.
- ///
- ///
- /// * at the end of an attribute name acts as a prefix match.
- ///
- public IEnumerable RequiredAttributes
- {
- get
- {
- return _requiredAttributes;
- }
- set
- {
- if (value == null)
- {
- throw new ArgumentNullException(nameof(value));
- }
-
- _requiredAttributes = value;
- }
- }
-
- ///
- /// Get the names of elements allowed as children.
- ///
- /// null indicates all children are allowed.
- public IEnumerable AllowedChildren { get; set; }
-
- ///
- /// Get the name of the HTML element required as the immediate parent.
- ///
- /// null indicates no restriction on parent tag.
- public string RequiredParent { get; set; }
-
- ///
- /// The expected tag structure.
- ///
- ///
- /// If and no other tag helpers applying to the same element specify
- /// their the behavior is used:
- ///
- ///
- /// <my-tag-helper></my-tag-helper>
- /// <!-- OR -->
- /// <my-tag-helper />
- ///
- /// Otherwise, if another tag helper applying to the same element does specify their behavior, that behavior
- /// is used.
- ///
- ///
- /// If HTML elements can be written in the following formats:
- ///
- /// <my-tag-helper>
- /// <!-- OR -->
- /// <my-tag-helper />
- ///
- ///
- ///
- public TagStructure TagStructure { get; set; }
-
- ///
- /// The that contains design time information about this
- /// tag helper.
- ///
- public TagHelperDesignTimeDescriptor DesignTimeDescriptor { get; set; }
-
- ///
- /// A dictionary containing additional information about the .
- ///
- public IDictionary PropertyBag
- {
- get
- {
- if (_propertyBag == null)
- {
- _propertyBag = new Dictionary();
- }
-
- return _propertyBag;
- }
+ return _allDiagnostics;
}
}
}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperDescriptorMatchingConventions.cs b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperDescriptorMatchingConventions.cs
new file mode 100644
index 0000000000..b57dc141ce
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperDescriptorMatchingConventions.cs
@@ -0,0 +1,67 @@
+// 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;
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperDesignTimeDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperDesignTimeDescriptor.cs
deleted file mode 100644
index 49d781d5bc..0000000000
--- a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperDesignTimeDescriptor.cs
+++ /dev/null
@@ -1,29 +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.AspNetCore.Razor.Evolution
-{
- ///
- /// A metadata class containing design time information about a tag helper.
- ///
- public class TagHelperDesignTimeDescriptor
- {
- ///
- /// A summary of how to use a tag helper.
- ///
- public string Summary { get; set; }
-
- ///
- /// Remarks about how to use a tag helper.
- ///
- public string Remarks { get; set; }
-
- ///
- /// The HTML element a tag helper may output.
- ///
- ///
- /// In IDEs supporting IntelliSense, may override the HTML information provided at design time.
- ///
- public string OutputElementHint { get; set; }
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperRequiredAttributeDescriptor.cs b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperRequiredAttributeDescriptor.cs
deleted file mode 100644
index 9b4b144cd6..0000000000
--- a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperRequiredAttributeDescriptor.cs
+++ /dev/null
@@ -1,82 +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;
-using System.Linq;
-
-namespace Microsoft.AspNetCore.Razor.Evolution
-{
- ///
- /// A metadata class describing a required tag helper attribute.
- ///
- public class TagHelperRequiredAttributeDescriptor
- {
- ///
- /// The HTML attribute name.
- ///
- public string Name { get; set; }
-
- ///
- /// The comparison method to use for when determining if an HTML attribute name matches.
- ///
- public TagHelperRequiredAttributeNameComparison NameComparison { get; set; }
-
- ///
- /// The HTML attribute value.
- ///
- public string Value { get; set; }
-
- ///
- /// The comparison method to use for when determining if an HTML attribute value matches.
- ///
- public TagHelperRequiredAttributeValueComparison ValueComparison { get; set; }
-
- ///
- /// Determines if the current matches the given
- /// and .
- ///
- /// An HTML attribute name.
- /// An HTML attribute value.
- /// true if the current matches
- /// and ; false otherwise.
- public bool IsMatch(string attributeName, string attributeValue)
- {
- var nameMatches = false;
- if (NameComparison == TagHelperRequiredAttributeNameComparison.FullMatch)
- {
- nameMatches = string.Equals(Name, attributeName, StringComparison.OrdinalIgnoreCase);
- }
- else if (NameComparison == TagHelperRequiredAttributeNameComparison.PrefixMatch)
- {
- // attributeName cannot equal the Name if comparing as a PrefixMatch.
- nameMatches = attributeName.Length != Name.Length &&
- attributeName.StartsWith(Name, StringComparison.OrdinalIgnoreCase);
- }
- else
- {
- Debug.Assert(false, "Unknown name comparison.");
- }
-
- if (!nameMatches)
- {
- return false;
- }
-
- switch (ValueComparison)
- {
- case TagHelperRequiredAttributeValueComparison.None:
- return true;
- case TagHelperRequiredAttributeValueComparison.PrefixMatch: // Value starts with
- return attributeValue.StartsWith(Value, StringComparison.Ordinal);
- case TagHelperRequiredAttributeValueComparison.SuffixMatch: // Value ends with
- return attributeValue.EndsWith(Value, StringComparison.Ordinal);
- case TagHelperRequiredAttributeValueComparison.FullMatch: // Value equals
- return string.Equals(attributeValue, Value, StringComparison.Ordinal);
- default:
- Debug.Assert(false, "Unknown value comparison.");
- return false;
- }
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperRequiredAttributeNameComparison.cs b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperRequiredAttributeNameComparison.cs
deleted file mode 100644
index 7e413e1381..0000000000
--- a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperRequiredAttributeNameComparison.cs
+++ /dev/null
@@ -1,21 +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.AspNetCore.Razor.Evolution
-{
- ///
- /// Acceptable comparison modes.
- ///
- public enum TagHelperRequiredAttributeNameComparison
- {
- ///
- /// HTML attribute name case insensitively matches .
- ///
- FullMatch,
-
- ///
- /// HTML attribute name case insensitively starts with .
- ///
- PrefixMatch,
- }
-}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperRequiredAttributeValueComparison.cs b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperRequiredAttributeValueComparison.cs
deleted file mode 100644
index 2d4bc41289..0000000000
--- a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperRequiredAttributeValueComparison.cs
+++ /dev/null
@@ -1,31 +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.AspNetCore.Razor.Evolution
-{
- ///
- /// Acceptable comparison modes.
- ///
- public enum TagHelperRequiredAttributeValueComparison
- {
- ///
- /// HTML attribute value always matches .
- ///
- None,
-
- ///
- /// HTML attribute value case sensitively matches .
- ///
- FullMatch,
-
- ///
- /// HTML attribute value case sensitively starts with .
- ///
- PrefixMatch,
-
- ///
- /// HTML attribute value case sensitively ends with .
- ///
- SuffixMatch,
- }
-}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/TagMatchingRule.cs b/src/Microsoft.AspNetCore.Razor.Evolution/TagMatchingRule.cs
new file mode 100644
index 0000000000..3de80d4e2c
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/TagMatchingRule.cs
@@ -0,0 +1,46 @@
+// 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.Collections.Generic;
+using System.Linq;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ public abstract class TagMatchingRule
+ {
+ private IEnumerable _allDiagnostics;
+
+ public string TagName { get; protected set; }
+
+ public IEnumerable Attributes { get; protected set; }
+
+ public string ParentTag { get; protected set; }
+
+ public TagStructure TagStructure { get; protected set; }
+
+ public IReadOnlyList Diagnostics { get; protected set; }
+
+ public bool HasAnyErrors
+ {
+ get
+ {
+ var allDiagnostics = GetAllDiagnostics();
+ var anyErrors = allDiagnostics.Any(diagnostic => diagnostic.Severity == RazorDiagnosticSeverity.Error);
+
+ return anyErrors;
+ }
+ }
+
+ public virtual IEnumerable GetAllDiagnostics()
+ {
+ if (_allDiagnostics == null)
+ {
+ var attributeDiagnostics = Attributes.SelectMany(attribute => attribute.Diagnostics);
+ var combinedDiagnostics = Diagnostics.Concat(attributeDiagnostics);
+ _allDiagnostics = combinedDiagnostics.ToArray();
+ }
+
+ return _allDiagnostics;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/TagMatchingRuleBuilder.cs b/src/Microsoft.AspNetCore.Razor.Evolution/TagMatchingRuleBuilder.cs
new file mode 100644
index 0000000000..3d914717c6
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/TagMatchingRuleBuilder.cs
@@ -0,0 +1,194 @@
+// 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.Linq;
+using Microsoft.AspNetCore.Razor.Evolution.Legacy;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ public sealed class TagMatchingRuleBuilder
+ {
+ private static ICollection InvalidNonWhitespaceTagNameCharacters { get; } = new HashSet(
+ new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*' });
+
+ private string _tagName;
+ private string _parentTag;
+ private TagStructure _tagStructure;
+ private HashSet _requiredAttributeDescriptors;
+ private HashSet _diagnostics;
+
+ private TagMatchingRuleBuilder()
+ {
+ }
+
+ public static TagMatchingRuleBuilder Create()
+ {
+ return new TagMatchingRuleBuilder();
+ }
+
+ public TagMatchingRuleBuilder RequireTagName(string tagName)
+ {
+ _tagName = tagName;
+
+ return this;
+ }
+
+ public TagMatchingRuleBuilder RequireParentTag(string parentTag)
+ {
+ _parentTag = parentTag;
+
+ return this;
+ }
+
+ public TagMatchingRuleBuilder RequireTagStructure(TagStructure tagStructure)
+ {
+ _tagStructure = tagStructure;
+
+ return this;
+ }
+
+ public TagMatchingRuleBuilder RequireAttribute(RequiredAttributeDescriptor requiredAttributeDescriptor)
+ {
+ if (requiredAttributeDescriptor == null)
+ {
+ throw new ArgumentNullException(nameof(requiredAttributeDescriptor));
+ }
+
+ EnsureRequiredAttributeDescriptors();
+ _requiredAttributeDescriptors.Add(requiredAttributeDescriptor);
+
+ return this;
+ }
+
+ public TagMatchingRuleBuilder RequireAttribute(Action configure)
+ {
+ if (configure == null)
+ {
+ throw new ArgumentNullException(nameof(configure));
+ }
+
+ var builder = RequiredAttributeDescriptorBuilder.Create();
+
+ configure(builder);
+
+ var requiredAttributeDescriptor = builder.Build();
+
+ return RequireAttribute(requiredAttributeDescriptor);
+ }
+
+ public TagMatchingRuleBuilder AddDiagnostic(RazorDiagnostic diagnostic)
+ {
+ EnsureDiagnostics();
+ _diagnostics.Add(diagnostic);
+
+ return this;
+ }
+
+ public TagMatchingRule Build()
+ {
+ var validationDiagnostics = Validate();
+ var diagnostics = new HashSet(validationDiagnostics);
+ if (_diagnostics != null)
+ {
+ diagnostics.UnionWith(_diagnostics);
+ }
+
+ var rule = new DefaultTagMatchingRule(
+ _tagName,
+ _parentTag,
+ _tagStructure,
+ _requiredAttributeDescriptors ?? Enumerable.Empty(),
+ diagnostics);
+
+ return rule;
+ }
+
+ public void Reset()
+ {
+ _tagName = null;
+ _parentTag = null;
+ _tagStructure = default(TagStructure);
+ _requiredAttributeDescriptors?.Clear();
+ _diagnostics?.Clear();
+ }
+
+ private IEnumerable Validate()
+ {
+ if (string.IsNullOrWhiteSpace(_tagName))
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedTagNameNullOrWhitespace();
+
+ yield return diagnostic;
+ }
+ else if (_tagName != TagHelperDescriptorProvider.ElementCatchAllTarget)
+ {
+ foreach (var character in _tagName)
+ {
+ if (char.IsWhiteSpace(character) || InvalidNonWhitespaceTagNameCharacters.Contains(character))
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedTagName(_tagName, character);
+
+ yield return diagnostic;
+ }
+ }
+ }
+
+ if (_parentTag != null)
+ {
+ if (string.IsNullOrWhiteSpace(_parentTag))
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedParentTagNameNullOrWhitespace();
+
+ AddDiagnostic(diagnostic);
+ }
+ else
+ {
+ foreach (var character in _parentTag)
+ {
+ if (char.IsWhiteSpace(character) || InvalidNonWhitespaceTagNameCharacters.Contains(character))
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedParentTagName(_parentTag, character);
+
+ AddDiagnostic(diagnostic);
+ }
+ }
+ }
+ }
+ }
+
+ private void EnsureRequiredAttributeDescriptors()
+ {
+ if (_requiredAttributeDescriptors == null)
+ {
+ _requiredAttributeDescriptors = new HashSet(RequiredAttributeDescriptorComparer.Default);
+ }
+ }
+
+ private void EnsureDiagnostics()
+ {
+ if (_diagnostics == null)
+ {
+ _diagnostics = new HashSet();
+ }
+ }
+
+ private class DefaultTagMatchingRule : TagMatchingRule
+ {
+ public DefaultTagMatchingRule(
+ string tagName,
+ string parentTag,
+ TagStructure tagStructure,
+ IEnumerable requiredAttributeDescriptors,
+ IEnumerable diagnostics)
+ {
+ TagName = tagName;
+ ParentTag = parentTag;
+ TagStructure = tagStructure;
+ Attributes = new List(requiredAttributeDescriptors);
+ Diagnostics = new List(diagnostics);
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/TagMatchingRuleComparer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/TagMatchingRuleComparer.cs
new file mode 100644
index 0000000000..2de76358d6
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Razor.Evolution/TagMatchingRuleComparer.cs
@@ -0,0 +1,86 @@
+// 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.Linq;
+using Microsoft.AspNetCore.Razor.Evolution.Legacy;
+using Microsoft.Extensions.Internal;
+
+namespace Microsoft.AspNetCore.Razor.Evolution
+{
+ internal class TagMatchingRuleComparer : IEqualityComparer
+ {
+ ///
+ /// A default instance of the .
+ ///
+ public static readonly TagMatchingRuleComparer Default = new TagMatchingRuleComparer();
+
+ ///
+ /// A default instance of the that does case-sensitive comparison.
+ ///
+ internal static readonly TagMatchingRuleComparer CaseSensitive =
+ new TagMatchingRuleComparer(caseSensitive: true);
+
+ private readonly StringComparer _stringComparer;
+ private readonly StringComparison _stringComparison;
+ private readonly RequiredAttributeDescriptorComparer _requiredAttributeComparer;
+
+ private TagMatchingRuleComparer(bool caseSensitive = false)
+ {
+ if (caseSensitive)
+ {
+ _stringComparer = StringComparer.Ordinal;
+ _stringComparison = StringComparison.Ordinal;
+ _requiredAttributeComparer = RequiredAttributeDescriptorComparer.CaseSensitive;
+ }
+ else
+ {
+ _stringComparer = StringComparer.OrdinalIgnoreCase;
+ _stringComparison = StringComparison.OrdinalIgnoreCase;
+ _requiredAttributeComparer = RequiredAttributeDescriptorComparer.Default;
+ }
+ }
+
+ public virtual bool Equals(TagMatchingRule ruleX, TagMatchingRule ruleY)
+ {
+ if (object.ReferenceEquals(ruleX, ruleY))
+ {
+ return true;
+ }
+
+ if (ruleX == null ^ ruleY == null)
+ {
+ return false;
+ }
+
+ return ruleX != null &&
+ string.Equals(ruleX.TagName, ruleY.TagName, _stringComparison) &&
+ string.Equals(ruleX.ParentTag, ruleY.ParentTag, _stringComparison) &&
+ ruleX.TagStructure == ruleY.TagStructure &&
+ Enumerable.SequenceEqual(ruleX.Attributes, ruleY.Attributes, _requiredAttributeComparer) &&
+ Enumerable.SequenceEqual(ruleX.Diagnostics, ruleY.Diagnostics);
+ }
+
+ public virtual int GetHashCode(TagMatchingRule rule)
+ {
+ if (rule == null)
+ {
+ throw new ArgumentNullException(nameof(rule));
+ }
+
+ var hashCodeCombiner = HashCodeCombiner.Start();
+ hashCodeCombiner.Add(rule.TagName, _stringComparer);
+ hashCodeCombiner.Add(rule.ParentTag, _stringComparer);
+ hashCodeCombiner.Add(rule.TagStructure);
+
+ var attributes = rule.Attributes.OrderBy(attribute => attribute.Name, _stringComparer);
+ foreach (var attribute in attributes)
+ {
+ hashCodeCombiner.Add(_requiredAttributeComparer.GetHashCode(attribute));
+ }
+
+ return hashCodeCombiner.CombinedHash;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Razor/Compilation/TagHelpers/TagHelperDescriptorComparer.cs b/src/Microsoft.AspNetCore.Razor/Compilation/TagHelpers/TagHelperDescriptorComparer.cs
index 5bc2238387..cde2d5ba31 100644
--- a/src/Microsoft.AspNetCore.Razor/Compilation/TagHelpers/TagHelperDescriptorComparer.cs
+++ b/src/Microsoft.AspNetCore.Razor/Compilation/TagHelpers/TagHelperDescriptorComparer.cs
@@ -37,11 +37,16 @@ namespace Microsoft.AspNetCore.Razor.Compilation.TagHelpers
///
public virtual bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
{
- if (descriptorX == descriptorY)
+ if (object.ReferenceEquals(descriptorX, descriptorY))
{
return true;
}
+ if (descriptorX == null ^ descriptorY == null)
+ {
+ return false;
+ }
+
return descriptorX != null &&
string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal) &&
string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.OrdinalIgnoreCase) &&
diff --git a/src/Microsoft.AspNetCore.Razor/Parser/TagHelpers/TagHelperBlockBuilder.cs b/src/Microsoft.AspNetCore.Razor/Parser/TagHelpers/TagHelperBlockBuilder.cs
index 3063c315ae..d1990de5c9 100644
--- a/src/Microsoft.AspNetCore.Razor/Parser/TagHelpers/TagHelperBlockBuilder.cs
+++ b/src/Microsoft.AspNetCore.Razor/Parser/TagHelpers/TagHelperBlockBuilder.cs
@@ -91,7 +91,7 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers
public TagMode TagMode { get; }
///
- /// s for the HTML element.
+ /// bindings for the HTML element.
///
public IEnumerable Descriptors { get; }
diff --git a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperResolver.cs b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperResolver.cs
index f9a6d66a23..961638b907 100644
--- a/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperResolver.cs
+++ b/src/Microsoft.CodeAnalysis.Razor.Workspaces/DefaultTagHelperResolver.cs
@@ -21,9 +21,10 @@ namespace Microsoft.CodeAnalysis.Razor
public override TagHelperResolutionResult GetTagHelpers(Compilation compilation, IEnumerable assemblyNameFilters)
{
var descriptors = new List();
- var errors = new ErrorSink();
- VisitTagHelpers(compilation, assemblyNameFilters, descriptors, errors);
+ VisitTagHelpers(compilation, assemblyNameFilters, descriptors);
+
+ var errors = new ErrorSink();
VisitViewComponents(compilation, assemblyNameFilters, descriptors, errors);
var diagnostics = new List();
@@ -37,7 +38,7 @@ namespace Microsoft.CodeAnalysis.Razor
return resolutionResult;
}
- private void VisitTagHelpers(Compilation compilation, IEnumerable assemblyNameFilters, List results, ErrorSink errors)
+ private void VisitTagHelpers(Compilation compilation, IEnumerable assemblyNameFilters, List results)
{
var types = new List();
var visitor = TagHelperTypeVisitor.Create(compilation, types);
@@ -50,8 +51,12 @@ namespace Microsoft.CodeAnalysis.Razor
{
if (assemblyNameFilters.Contains(type.ContainingAssembly.Identity.Name))
{
- var descriptors = factory.CreateDescriptors(type, errors);
- results.AddRange(descriptors);
+ var descriptor = factory.CreateDescriptor(type);
+
+ if (descriptor != null)
+ {
+ results.Add(descriptor);
+ }
}
}
}
diff --git a/src/Microsoft.CodeAnalysis.Razor/DefaultTagHelperDescriptorFactory.cs b/src/Microsoft.CodeAnalysis.Razor/DefaultTagHelperDescriptorFactory.cs
index 203f27ea5d..7804f051a0 100644
--- a/src/Microsoft.CodeAnalysis.Razor/DefaultTagHelperDescriptorFactory.cs
+++ b/src/Microsoft.CodeAnalysis.Razor/DefaultTagHelperDescriptorFactory.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
-using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Razor.Evolution;
@@ -18,7 +17,6 @@ namespace Microsoft.CodeAnalysis.Razor
private const string DataDashPrefix = "data-";
private const string TagHelperNameEnding = "TagHelper";
private const string HtmlCaseRegexReplacement = "-$1$2";
- private const char RequiredAttributeWildcardSuffix = '*';
// This matches the following AFTER the start of the input string (MATCH).
// Any letter/number followed by an uppercase letter then lowercase letter: 1(Aa), a(Aa), A(Aa)
@@ -61,87 +59,38 @@ namespace Microsoft.CodeAnalysis.Razor
protected bool DesignTime { get; }
///
- public virtual IEnumerable CreateDescriptors(
- INamedTypeSymbol type,
- ErrorSink errorSink)
+ public virtual TagHelperDescriptor CreateDescriptor(INamedTypeSymbol type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
- if (errorSink == null)
- {
- throw new ArgumentNullException(nameof(errorSink));
- }
-
if (ShouldSkipDescriptorCreation(type))
{
- return Enumerable.Empty();
- }
-
- var attributeDescriptors = GetAttributeDescriptors(type, errorSink);
- var targetElementAttributes = GetValidHtmlTargetElementAttributes(type, errorSink);
- var allowedChildren = GetAllowedChildren(type, errorSink);
-
- var tagHelperDescriptors =
- BuildTagHelperDescriptors(
- type,
- type.ContainingAssembly.Identity.Name,
- attributeDescriptors,
- targetElementAttributes,
- allowedChildren);
-
- return tagHelperDescriptors.Distinct(TagHelperDescriptorComparer.Default);
- }
-
- private IEnumerable GetValidHtmlTargetElementAttributes(
- INamedTypeSymbol typeSymbol,
- ErrorSink errorSink)
- {
- var targetElementAttributes = typeSymbol.GetAttributes().Where(a => a.AttributeClass == _htmlTargetElementAttributeSymbol);
- return targetElementAttributes.Where(a => ValidHtmlTargetElementAttributeNames(a, errorSink));
- }
-
- private IEnumerable BuildTagHelperDescriptors(
- INamedTypeSymbol type,
- string assemblyName,
- IEnumerable attributeDescriptors,
- IEnumerable targetElementAttributes,
- IEnumerable allowedChildren)
- {
- TagHelperDesignTimeDescriptor designTimeDescriptor = null;
- if (DesignTime)
- {
- XmlMemberDocumentation documentation = null;
- var xml = type.GetDocumentationCommentXml();
- if (!string.IsNullOrEmpty(xml))
- {
- documentation = new XmlMemberDocumentation(xml);
- }
-
- string outputElementHint = null;
- var outputElementHintAttribute = type.GetAttributes().Where(a => a.AttributeClass == _outputElementHintAttributeSymbol).FirstOrDefault();
- if (outputElementHintAttribute != null)
- {
- outputElementHint = (string)(outputElementHintAttribute.ConstructorArguments[0]).Value;
- }
-
- var remarks = documentation?.GetRemarks();
- var summary = documentation?.GetSummary();
-
- if (outputElementHint != null || summary != null || remarks != null)
- {
- designTimeDescriptor = new TagHelperDesignTimeDescriptor()
- {
- OutputElementHint = outputElementHint,
- Remarks = remarks,
- Summary = summary,
- };
- }
+ return null;
}
var typeName = GetFullName(type);
+ var assemblyName = type.ContainingAssembly.Identity.Name;
+ var descriptorBuilder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
+
+ AddBoundAttributes(type, descriptorBuilder);
+ AddTagMatchingRules(type, descriptorBuilder);
+ AddAllowedChildren(type, descriptorBuilder);
+ AddDocumentation(type, descriptorBuilder);
+ AddTagOutputHint(type, descriptorBuilder);
+
+ var descriptor = descriptorBuilder.Build();
+
+ return descriptor;
+ }
+
+ private void AddTagMatchingRules(INamedTypeSymbol type, ITagHelperDescriptorBuilder descriptorBuilder)
+ {
+ var targetElementAttributes = type
+ .GetAttributes()
+ .Where(attribute => attribute.AttributeClass == _htmlTargetElementAttributeSymbol);
// If there isn't an attribute specifying the tag name derive it from the name
if (!targetElementAttributes.Any())
@@ -153,121 +102,243 @@ namespace Microsoft.CodeAnalysis.Razor
name = name.Substring(0, name.Length - TagHelperNameEnding.Length);
}
- return new[]
+ descriptorBuilder.TagMatchingRule(ruleBuilder =>
{
- BuildTagHelperDescriptor(
- ToHtmlCase(name),
- typeName,
- assemblyName,
- attributeDescriptors,
- requiredAttributeDescriptors: Enumerable.Empty(),
- allowedChildren: allowedChildren,
- tagStructure: default(TagStructure),
- parentTag: null,
- designTimeDescriptor: designTimeDescriptor)
- };
+ var htmlCasedName = ToHtmlCase(name);
+ ruleBuilder.RequireTagName(htmlCasedName);
+ });
+
+ return;
}
- return targetElementAttributes.Select(
- attribute =>
- BuildTagHelperDescriptor(
- typeName,
- assemblyName,
- attributeDescriptors,
- attribute,
- allowedChildren,
- designTimeDescriptor));
+ foreach (var targetElementAttribute in targetElementAttributes)
+ {
+ descriptorBuilder.TagMatchingRule(ruleBuilder =>
+ {
+ var tagName = HtmlTargetElementAttribute_Tag(targetElementAttribute);
+ ruleBuilder.RequireTagName(tagName);
+
+ var parentTag = HtmlTargetElementAttribute_ParentTag(targetElementAttribute);
+ ruleBuilder.RequireParentTag(parentTag);
+
+ var tagStructure = HtmlTargetElementAttribute_TagStructure(targetElementAttribute);
+ ruleBuilder.RequireTagStructure(tagStructure);
+
+ var requiredAttributeString = HtmlTargetElementAttribute_Attributes(targetElementAttribute);
+ RequiredAttributeParser.AddRequiredAttributes(requiredAttributeString, ruleBuilder);
+ });
+ }
}
- private IEnumerable GetAllowedChildren(INamedTypeSymbol type, ErrorSink errorSink)
+ private void AddBoundAttributes(INamedTypeSymbol type, ITagHelperDescriptorBuilder builder)
+ {
+ var accessibleProperties = GetAccessibleProperties(type);
+ foreach (var property in accessibleProperties)
+ {
+ if (ShouldSkipDescriptorCreation(property))
+ {
+ continue;
+ }
+
+ builder.BindAttribute(attributeBuilder =>
+ {
+ ConfigureBoundAttribute(attributeBuilder, property, type);
+ });
+ }
+ }
+
+ private void AddAllowedChildren(INamedTypeSymbol type, ITagHelperDescriptorBuilder builder)
{
var restrictChildrenAttribute = type.GetAttributes().Where(a => a.AttributeClass == _restrictChildrenAttributeSymbol).FirstOrDefault();
if (restrictChildrenAttribute == null)
{
- return null;
+ return;
}
- var allowedChildren = new List();
- allowedChildren.Add((string)restrictChildrenAttribute.ConstructorArguments[0].Value);
+ builder.AllowChildTag((string)restrictChildrenAttribute.ConstructorArguments[0].Value);
if (restrictChildrenAttribute.ConstructorArguments.Length == 2)
{
foreach (var value in restrictChildrenAttribute.ConstructorArguments[1].Values)
{
- allowedChildren.Add((string)value.Value);
+ builder.AllowChildTag((string)value.Value);
}
}
+ }
- var validAllowedChildren = GetValidAllowedChildren(allowedChildren, GetFullName(type), errorSink);
-
- if (validAllowedChildren.Any())
+ private void AddDocumentation(INamedTypeSymbol type, ITagHelperDescriptorBuilder builder)
+ {
+ if (!DesignTime)
{
- return validAllowedChildren;
+ return;
+ }
+
+ var xml = type.GetDocumentationCommentXml();
+
+ if (!string.IsNullOrEmpty(xml))
+ {
+ builder.Documentation(xml);
+ }
+ }
+
+ private void AddTagOutputHint(INamedTypeSymbol type, ITagHelperDescriptorBuilder builder)
+ {
+ if (!DesignTime)
+ {
+ return;
+ }
+ string outputElementHint = null;
+ var outputElementHintAttribute = type.GetAttributes().Where(a => a.AttributeClass == _outputElementHintAttributeSymbol).FirstOrDefault();
+ if (outputElementHintAttribute != null)
+ {
+ outputElementHint = (string)(outputElementHintAttribute.ConstructorArguments[0]).Value;
+ builder.TagOutputHint(outputElementHint);
+ }
+ }
+
+ private void ConfigureBoundAttribute(
+ ITagHelperBoundAttributeDescriptorBuilder builder,
+ IPropertySymbol property,
+ INamedTypeSymbol containingType)
+ {
+ var attributeNameAttribute = property
+ .GetAttributes()
+ .Where(a => a.AttributeClass == _htmlAttributeNameAttributeSymbol)
+ .FirstOrDefault();
+
+ bool hasExplicitName;
+ string attributeName;
+ if (attributeNameAttribute == null ||
+ attributeNameAttribute.ConstructorArguments.Length == 0 ||
+ string.IsNullOrEmpty((string)attributeNameAttribute.ConstructorArguments[0].Value))
+ {
+ hasExplicitName = false;
+ attributeName = ToHtmlCase(property.Name);
}
else
{
- // All allowed children were invalid, return null to indicate that any child is acceptable.
- return null;
+ hasExplicitName = true;
+ attributeName = (string)attributeNameAttribute.ConstructorArguments[0].Value;
}
- }
- // Internal for unit testing
- internal static IEnumerable GetValidAllowedChildren(
- IEnumerable allowedChildren,
- string tagHelperName,
- ErrorSink errorSink)
- {
- var validAllowedChildren = new List();
+ var hasPublicSetter = property.SetMethod != null && property.SetMethod.DeclaredAccessibility == Accessibility.Public;
+ var typeName = GetFullName(property.Type);
+ builder
+ .TypeName(typeName)
+ .PropertyName(property.Name);
- foreach (var name in allowedChildren)
+ if (hasPublicSetter)
{
- if (string.IsNullOrWhiteSpace(name))
+ builder.Name(attributeName);
+
+ if (property.Type.TypeKind == TypeKind.Enum)
{
- var whitespaceError = Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace(
- TagHelperTypes.RestrictChildrenAttribute,
- tagHelperName);
- errorSink.OnError(SourceLocation.Zero, whitespaceError, length: 0);
+ builder.AsEnum();
}
- else if (TryValidateName(
- name,
- invalidCharacter => Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName(
- TagHelperTypes.RestrictChildrenAttribute,
- name,
- tagHelperName,
- invalidCharacter),
- errorSink))
+
+ if (DesignTime)
{
- validAllowedChildren.Add(name);
+ var xml = property.GetDocumentationCommentXml();
+
+ if (!string.IsNullOrEmpty(xml))
+ {
+ builder.Documentation(xml);
+ }
+ }
+ }
+ else if (hasExplicitName && !IsPotentialDictionaryProperty(property))
+ {
+ // Specified HtmlAttributeNameAttribute.Name though property has no public setter.
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidAttributeNameNullOrEmpty(GetFullName(containingType), property.Name);
+ builder.AddDiagnostic(diagnostic);
+ }
+
+ ConfigureDictionaryBoundAttribute(builder, property, containingType, attributeNameAttribute, attributeName, hasPublicSetter);
+ }
+
+ private void ConfigureDictionaryBoundAttribute(
+ ITagHelperBoundAttributeDescriptorBuilder builder,
+ IPropertySymbol property,
+ INamedTypeSymbol containingType,
+ AttributeData attributeNameAttribute,
+ string attributeName,
+ bool hasPublicSetter)
+ {
+ string dictionaryAttributePrefix = null;
+ var dictionaryAttributePrefixSet = false;
+
+ if (attributeNameAttribute != null)
+ {
+ foreach (var argument in attributeNameAttribute.NamedArguments)
+ {
+ if (argument.Key == TagHelperTypes.HtmlAttributeName.DictionaryAttributePrefix)
+ {
+ dictionaryAttributePrefix = (string)argument.Value.Value;
+ dictionaryAttributePrefixSet = true;
+ break;
+ }
}
}
- return validAllowedChildren;
+ var dictionaryArgumentTypes = GetDictionaryArgumentTypes(property);
+ if (dictionaryArgumentTypes != null)
+ {
+ var prefix = dictionaryAttributePrefix;
+ if (attributeNameAttribute == null || !dictionaryAttributePrefixSet)
+ {
+ prefix = attributeName + "-";
+ }
+
+ if (prefix != null)
+ {
+ var dictionaryValueType = dictionaryArgumentTypes[1];
+ var dictionaryValueTypeName = GetFullName(dictionaryValueType);
+ builder.AsDictionary(prefix, dictionaryValueTypeName);
+ }
+ }
+
+ var dictionaryKeyType = dictionaryArgumentTypes?[0];
+
+ if (dictionaryKeyType?.SpecialType != SpecialType.System_String)
+ {
+ if (dictionaryAttributePrefix != null)
+ {
+ // DictionaryAttributePrefix is not supported unless associated with an
+ // IDictionary property.
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidAttributePrefixNotNull(GetFullName(containingType), property.Name);
+ builder.AddDiagnostic(diagnostic);
+ }
+
+ return;
+ }
+ else if (!hasPublicSetter && attributeNameAttribute != null && !dictionaryAttributePrefixSet)
+ {
+ // Must set DictionaryAttributePrefix when using HtmlAttributeNameAttribute with a dictionary property
+ // that lacks a public setter.
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidAttributePrefixNull(GetFullName(containingType), property.Name);
+ builder.AddDiagnostic(diagnostic);
+
+ return;
+ }
}
- private static TagHelperDescriptor BuildTagHelperDescriptor(
- string typeName,
- string assemblyName,
- IEnumerable attributeDescriptors,
- AttributeData targetElementAttribute,
- IEnumerable allowedChildren,
- TagHelperDesignTimeDescriptor designTimeDescriptor)
+ private IReadOnlyList GetDictionaryArgumentTypes(IPropertySymbol property)
{
- IEnumerable requiredAttributeDescriptors;
- TryGetRequiredAttributeDescriptors(
- HtmlTargetElementAttribute_Attributes(targetElementAttribute),
- errorSink: null,
- descriptors: out requiredAttributeDescriptors);
+ INamedTypeSymbol dictionaryType;
+ if ((property.Type as INamedTypeSymbol)?.ConstructedFrom == _iDictionarySymbol)
+ {
+ dictionaryType = (INamedTypeSymbol)property.Type;
+ }
+ else if (property.Type.AllInterfaces.Any(s => s.ConstructedFrom == _iDictionarySymbol))
+ {
+ dictionaryType = property.Type.AllInterfaces.First(s => s.ConstructedFrom == _iDictionarySymbol);
+ }
+ else
+ {
+ dictionaryType = null;
+ }
- return BuildTagHelperDescriptor(
- HtmlTargetElementAttribute_Tag(targetElementAttribute),
- typeName,
- assemblyName,
- attributeDescriptors,
- requiredAttributeDescriptors,
- allowedChildren,
- HtmlTargetElementAttribute_ParentTag(targetElementAttribute),
- HtmlTargetElementAttribute_TagStructure(targetElementAttribute),
- designTimeDescriptor);
+ return dictionaryType?.TypeArguments;
}
private static string HtmlTargetElementAttribute_Attributes(AttributeData attibute)
@@ -321,238 +392,11 @@ namespace Microsoft.CodeAnalysis.Razor
return TagStructure.Unspecified;
}
- private static TagHelperDescriptor BuildTagHelperDescriptor(
- string tagName,
- string typeName,
- string assemblyName,
- IEnumerable attributeDescriptors,
- IEnumerable requiredAttributeDescriptors,
- IEnumerable allowedChildren,
- string parentTag,
- TagStructure tagStructure,
- TagHelperDesignTimeDescriptor designTimeDescriptor)
+ private bool IsPotentialDictionaryProperty(IPropertySymbol property)
{
- return new TagHelperDescriptor
- {
- TagName = tagName,
- TypeName = typeName,
- AssemblyName = assemblyName,
- Attributes = attributeDescriptors,
- RequiredAttributes = requiredAttributeDescriptors,
- AllowedChildren = allowedChildren,
- RequiredParent = parentTag,
- TagStructure = tagStructure,
- DesignTimeDescriptor = designTimeDescriptor
- };
- }
-
- ///
- /// Internal for testing.
- ///
- internal static bool ValidHtmlTargetElementAttributeNames(
- AttributeData attribute,
- ErrorSink errorSink)
- {
- var validTagName = ValidateName(HtmlTargetElementAttribute_Tag(attribute), targetingAttributes: false, errorSink: errorSink);
- IEnumerable requiredAttributeDescriptors;
- var validRequiredAttributes = TryGetRequiredAttributeDescriptors(HtmlTargetElementAttribute_Attributes(attribute), errorSink, out requiredAttributeDescriptors);
- var validParentTagName = ValidateParentTagName(HtmlTargetElementAttribute_ParentTag(attribute), errorSink);
-
- return validTagName && validRequiredAttributes && validParentTagName;
- }
-
- ///
- /// Internal for unit testing.
- ///
- internal static bool ValidateParentTagName(string parentTag, ErrorSink errorSink)
- {
- if (parentTag == null)
- {
- return true;
- }
- else if (string.IsNullOrWhiteSpace(parentTag))
- {
- var error = Resources.FormatHtmlTargetElementAttribute_NameCannotBeNullOrWhitespace(
- Resources.TagHelperDescriptorFactory_ParentTag);
- errorSink.OnError(SourceLocation.Zero, error, length: 0);
- return false;
- }
- else if (!TryValidateName(
- parentTag,
- invalidCharacter => Resources.FormatHtmlTargetElementAttribute_InvalidName(
- Resources.TagHelperDescriptorFactory_ParentTag.ToLower(),
- parentTag,
- invalidCharacter),
- errorSink))
- {
- return false;
- }
-
- return true;
- }
-
- private static bool TryGetRequiredAttributeDescriptors(
- string requiredAttributes,
- ErrorSink errorSink,
- out IEnumerable descriptors)
- {
- var parser = new RequiredAttributeParser(requiredAttributes);
-
- return parser.TryParse(errorSink, out descriptors);
- }
-
- private static bool ValidateName(string name, bool targetingAttributes, ErrorSink errorSink)
- {
- if (!targetingAttributes &&
- string.Equals(
- name,
- TagHelperDescriptorProvider.ElementCatchAllTarget,
- StringComparison.OrdinalIgnoreCase))
- {
- // '*' as the entire name is OK in the HtmlTargetElement catch-all case.
- return true;
- }
-
- var targetName = targetingAttributes ?
- Resources.TagHelperDescriptorFactory_Attribute :
- Resources.TagHelperDescriptorFactory_Tag;
-
- if (string.IsNullOrWhiteSpace(name))
- {
- var error = Resources.FormatHtmlTargetElementAttribute_NameCannotBeNullOrWhitespace(targetName);
- errorSink.OnError(SourceLocation.Zero, error, length: 0);
- return false;
- }
- else if (!TryValidateName(
- name,
- invalidCharacter => Resources.FormatHtmlTargetElementAttribute_InvalidName(
- targetName.ToLower(),
- name,
- invalidCharacter),
- errorSink))
- {
- return false;
- }
-
- return true;
- }
-
- private static bool TryValidateName(
- string name,
- Func characterErrorBuilder,
- ErrorSink errorSink)
- {
- var validName = true;
-
- foreach (var character in name)
- {
- if (char.IsWhiteSpace(character) ||
- InvalidNonWhitespaceNameCharacters.Contains(character))
- {
- var error = characterErrorBuilder(character);
- errorSink.OnError(SourceLocation.Zero, error, length: 0);
-
- validName = false;
- }
- }
-
- return validName;
- }
-
- private IEnumerable GetAttributeDescriptors(INamedTypeSymbol type, ErrorSink errorSink)
- {
- var attributeDescriptors = new List();
-
- // Keep indexer descriptors separate to avoid sorting the combined list later.
- var indexerDescriptors = new List();
-
- var accessibleProperties = GetAccessibleProperties(type);
- foreach (var property in accessibleProperties)
- {
- if (ShouldSkipDescriptorCreation(property))
- {
- continue;
- }
-
- var attributeNameAttribute = property
- .GetAttributes()
- .Where(a => a.AttributeClass == _htmlAttributeNameAttributeSymbol)
- .FirstOrDefault();
-
- bool hasExplicitName;
- string attributeName;
- if (attributeNameAttribute == null ||
- attributeNameAttribute.ConstructorArguments.Length == 0 ||
- string.IsNullOrEmpty((string)attributeNameAttribute.ConstructorArguments[0].Value))
- {
- hasExplicitName = false;
- attributeName = ToHtmlCase(property.Name);
- }
- else
- {
- hasExplicitName = true;
- attributeName = (string)attributeNameAttribute.ConstructorArguments[0].Value;
- }
-
- TagHelperAttributeDescriptor mainDescriptor = null;
- if (property.SetMethod != null && property.SetMethod.DeclaredAccessibility == Accessibility.Public)
- {
- mainDescriptor = ToAttributeDescriptor(property, attributeName);
- if (!ValidateTagHelperAttributeDescriptor(mainDescriptor, type, errorSink))
- {
- // HtmlAttributeNameAttribute.Name is invalid. Ignore this property completely.
- continue;
- }
- }
- else if (hasExplicitName)
- {
- // Specified HtmlAttributeNameAttribute.Name though property has no public setter.
- errorSink.OnError(
- SourceLocation.Zero,
- Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty(
- GetFullName(type),
- property.Name,
- TagHelperTypes.HtmlAttributeNameAttribute,
- TagHelperTypes.HtmlAttributeName.Name),
- length: 0);
- continue;
- }
-
- bool isInvalid;
- var indexerDescriptor = ToIndexerAttributeDescriptor(
- property,
- attributeNameAttribute,
- parentType: type,
- errorSink: errorSink,
- defaultPrefix: attributeName + "-",
- isInvalid: out isInvalid);
- if (indexerDescriptor != null &&
- !ValidateTagHelperAttributeDescriptor(indexerDescriptor, type, errorSink))
- {
- isInvalid = true;
- }
-
- if (isInvalid)
- {
- // The property type or HtmlAttributeNameAttribute.DictionaryAttributePrefix (or perhaps the
- // HTML-casing of the property name) is invalid. Ignore this property completely.
- continue;
- }
-
- if (mainDescriptor != null)
- {
- attributeDescriptors.Add(mainDescriptor);
- }
-
- if (indexerDescriptor != null)
- {
- indexerDescriptors.Add(indexerDescriptor);
- }
- }
-
- attributeDescriptors.AddRange(indexerDescriptors);
-
- return attributeDescriptors;
+ return
+ ((property.Type as INamedTypeSymbol)?.ConstructedFrom == _iDictionarySymbol || property.Type.AllInterfaces.Any(s => s.ConstructedFrom == _iDictionarySymbol)) &&
+ GetDictionaryArgumentTypes(property)?[0].SpecialType == SpecialType.System_String;
}
private IEnumerable GetAccessibleProperties(INamedTypeSymbol typeSymbol)
@@ -569,6 +413,9 @@ namespace Microsoft.CodeAnalysis.Razor
property.GetMethod != null &&
property.GetMethod.DeclaredAccessibility == Accessibility.Public &&
property.GetAttributes().Where(a => a.AttributeClass == _htmlAttributeNotBoundAttributeSymbol).FirstOrDefault() == null &&
+ (property.GetAttributes().Any(a => a.AttributeClass == _htmlAttributeNameAttributeSymbol) ||
+ property.SetMethod != null && property.SetMethod.DeclaredAccessibility == Accessibility.Public ||
+ IsPotentialDictionaryProperty(property)) &&
!accessibleProperties.ContainsKey(property.Name))
{
accessibleProperties.Add(property.Name, property);
@@ -582,278 +429,6 @@ namespace Microsoft.CodeAnalysis.Razor
return accessibleProperties.Values;
}
- // Internal for testing.
- internal static bool ValidateTagHelperAttributeDescriptor(
- TagHelperAttributeDescriptor attributeDescriptor,
- INamedTypeSymbol parentType,
- ErrorSink errorSink)
- {
- string nameOrPrefix;
- if (attributeDescriptor.IsIndexer)
- {
- nameOrPrefix = Resources.TagHelperDescriptorFactory_Prefix;
- }
- else if (string.IsNullOrEmpty(attributeDescriptor.Name))
- {
- errorSink.OnError(
- SourceLocation.Zero,
- Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty(
- GetFullName(parentType),
- attributeDescriptor.PropertyName),
- length: 0);
-
- return false;
- }
- else
- {
- nameOrPrefix = Resources.TagHelperDescriptorFactory_Name;
- }
-
- return ValidateTagHelperAttributeNameOrPrefix(
- attributeDescriptor.Name,
- parentType,
- attributeDescriptor.PropertyName,
- errorSink,
- nameOrPrefix);
- }
-
- private static bool ValidateTagHelperAttributeNameOrPrefix(
- string attributeNameOrPrefix,
- INamedTypeSymbol parentType,
- string propertyName,
- ErrorSink errorSink,
- string nameOrPrefix)
- {
- if (string.IsNullOrEmpty(attributeNameOrPrefix))
- {
- // ValidateTagHelperAttributeDescriptor validates Name is non-null and non-empty. The empty string is
- // valid for DictionaryAttributePrefix and null is impossible at this point because it means "don't
- // create a descriptor". (Empty DictionaryAttributePrefix is a corner case which would bind every
- // attribute of a target element. Likely not particularly useful but unclear what minimum length
- // should be required and what scenarios a minimum length would break.)
- return true;
- }
-
- if (string.IsNullOrWhiteSpace(attributeNameOrPrefix))
- {
- // Provide a single error if the entire name is whitespace, not an error per character.
- errorSink.OnError(
- SourceLocation.Zero,
- Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace(
- GetFullName(parentType),
- propertyName,
- nameOrPrefix),
- length: 0);
-
- return false;
- }
-
- // data-* attributes are explicitly not implemented by user agents and are not intended for use on
- // the server; therefore it's invalid for TagHelpers to bind to them.
- if (attributeNameOrPrefix.StartsWith(DataDashPrefix, StringComparison.OrdinalIgnoreCase))
- {
- errorSink.OnError(
- SourceLocation.Zero,
- Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart(
- GetFullName(parentType),
- propertyName,
- nameOrPrefix,
- attributeNameOrPrefix,
- DataDashPrefix),
- length: 0);
-
- return false;
- }
-
- var isValid = true;
- foreach (var character in attributeNameOrPrefix)
- {
- if (char.IsWhiteSpace(character) || InvalidNonWhitespaceNameCharacters.Contains(character))
- {
- errorSink.OnError(
- SourceLocation.Zero,
- Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter(
- GetFullName(parentType),
- propertyName,
- nameOrPrefix,
- attributeNameOrPrefix,
- character),
- length: 0);
-
- isValid = false;
- }
- }
-
- return isValid;
- }
-
- private TagHelperAttributeDescriptor ToAttributeDescriptor(IPropertySymbol property, string attributeName)
- {
- return ToAttributeDescriptor(
- property,
- attributeName,
- GetFullName(property.Type),
- isIndexer: false,
- isStringProperty: property.Type.SpecialType == SpecialType.System_String);
- }
-
- private TagHelperAttributeDescriptor ToIndexerAttributeDescriptor(
- IPropertySymbol property,
- AttributeData attributeNameAttribute,
- INamedTypeSymbol parentType,
- ErrorSink errorSink,
- string defaultPrefix,
- out bool isInvalid)
- {
- isInvalid = false;
- var hasPublicSetter = property.SetMethod != null && property.SetMethod.DeclaredAccessibility == Accessibility.Public;
-
-
- string dictionaryAttributePrefix = null;
- bool dictionaryAttributePrefixSet = false;
-
- if (attributeNameAttribute != null)
- {
- foreach (var argument in attributeNameAttribute.NamedArguments)
- {
- if (argument.Key == TagHelperTypes.HtmlAttributeName.DictionaryAttributePrefix)
- {
- dictionaryAttributePrefix = (string)argument.Value.Value;
- dictionaryAttributePrefixSet = true;
- break;
- }
- }
- }
-
- INamedTypeSymbol dictionaryType;
- if ((property.Type as INamedTypeSymbol)?.ConstructedFrom == _iDictionarySymbol)
- {
- dictionaryType = (INamedTypeSymbol)property.Type;
- }
- else if (property.Type.AllInterfaces.Any(s => s.ConstructedFrom == _iDictionarySymbol))
- {
- dictionaryType = property.Type.AllInterfaces.First(s => s.ConstructedFrom == _iDictionarySymbol);
- }
- else
- {
- dictionaryType = null;
- }
-
- if (dictionaryType == null ||
- dictionaryType.TypeArguments[0].SpecialType != SpecialType.System_String)
- {
- if (dictionaryAttributePrefix != null)
- {
- // DictionaryAttributePrefix is not supported unless associated with an
- // IDictionary property.
- isInvalid = true;
- errorSink.OnError(
- SourceLocation.Zero,
- Resources.FormatTagHelperDescriptorFactory_InvalidAttributePrefixNotNull(
- GetFullName(parentType),
- property.Name,
- TagHelperTypes.HtmlAttributeNameAttribute,
- TagHelperTypes.HtmlAttributeName.DictionaryAttributePrefix,
- "IDictionary"),
- length: 0);
- }
- else if (attributeNameAttribute != null && !hasPublicSetter)
- {
- // Associated an HtmlAttributeNameAttribute with a non-dictionary property that lacks a public
- // setter.
- isInvalid = true;
- errorSink.OnError(
- SourceLocation.Zero,
- Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameAttribute(
- GetFullName(parentType),
- property.Name,
- TagHelperTypes.HtmlAttributeNameAttribute,
- "IDictionary"),
- length: 0);
- }
-
- return null;
- }
- else if (
- !hasPublicSetter &&
- attributeNameAttribute != null &&
- !dictionaryAttributePrefixSet)
- {
- // Must set DictionaryAttributePrefix when using HtmlAttributeNameAttribute with a dictionary property
- // that lacks a public setter.
- isInvalid = true;
- errorSink.OnError(
- SourceLocation.Zero,
- Resources.FormatTagHelperDescriptorFactory_InvalidAttributePrefixNull(
- GetFullName(parentType),
- property.Name,
- TagHelperTypes.HtmlAttributeNameAttribute,
- TagHelperTypes.HtmlAttributeName.DictionaryAttributePrefix,
- "IDictionary"),
- length: 0);
-
- return null;
- }
-
- // Potential prefix case. Use default prefix (based on name)?
- var useDefault = attributeNameAttribute == null || !dictionaryAttributePrefixSet;
-
- var prefix = useDefault ? defaultPrefix : dictionaryAttributePrefix;
- if (prefix == null)
- {
- // DictionaryAttributePrefix explicitly set to null. Ignore.
- return null;
- }
-
- return ToAttributeDescriptor(
- property,
- attributeName: prefix,
- typeName: dictionaryType == null ? null : GetFullName(dictionaryType.TypeArguments[1]),
- isIndexer: true,
- isStringProperty: dictionaryType == null ? false : dictionaryType.TypeArguments[1].SpecialType == SpecialType.System_String);
- }
-
- private TagHelperAttributeDescriptor ToAttributeDescriptor(
- IPropertySymbol property,
- string attributeName,
- string typeName,
- bool isIndexer,
- bool isStringProperty)
- {
- TagHelperAttributeDesignTimeDescriptor designTimeDescriptor = null;
- if (DesignTime)
- {
- XmlMemberDocumentation documentation = null;
- var xml = property.GetDocumentationCommentXml();
- if (!string.IsNullOrEmpty(xml))
- {
- documentation = new XmlMemberDocumentation(xml);
- }
-
- var remarks = documentation?.GetRemarks();
- var summary = documentation?.GetSummary();
- if (summary != null || remarks != null)
- {
- designTimeDescriptor = new TagHelperAttributeDesignTimeDescriptor()
- {
- Remarks = remarks,
- Summary = summary,
- };
- }
- }
-
- return new TagHelperAttributeDescriptor
- {
- Name = attributeName,
- PropertyName = property.Name,
- IsEnum = property.Type.TypeKind == TypeKind.Enum,
- TypeName = typeName,
- IsStringProperty = isStringProperty,
- IsIndexer = isIndexer,
- DesignTimeDescriptor = designTimeDescriptor,
- };
- }
-
private bool ShouldSkipDescriptorCreation(ISymbol symbol)
{
if (DesignTime)
@@ -892,327 +467,5 @@ namespace Microsoft.CodeAnalysis.Razor
}
private static string GetFullName(ITypeSymbol type) => type.ToDisplayString(FullNameTypeDisplayFormat);
-
- // Internal for testing
- internal class RequiredAttributeParser
- {
- private static readonly IReadOnlyDictionary CssValueComparisons =
- new Dictionary
- {
- { '=', TagHelperRequiredAttributeValueComparison.FullMatch },
- { '^', TagHelperRequiredAttributeValueComparison.PrefixMatch },
- { '$', TagHelperRequiredAttributeValueComparison.SuffixMatch }
- };
- private static readonly char[] InvalidPlainAttributeNameCharacters = { ' ', '\t', ',', RequiredAttributeWildcardSuffix };
- private static readonly char[] InvalidCssAttributeNameCharacters = (new[] { ' ', '\t', ',', ']' })
- .Concat(CssValueComparisons.Keys)
- .ToArray();
- private static readonly char[] InvalidCssQuotelessValueCharacters = { ' ', '\t', ']' };
-
- private int _index;
- private string _requiredAttributes;
-
- public RequiredAttributeParser(string requiredAttributes)
- {
- _requiredAttributes = requiredAttributes;
- }
-
- private char Current => _requiredAttributes[_index];
-
- private bool AtEnd => _index >= _requiredAttributes.Length;
-
- public bool TryParse(
- ErrorSink errorSink,
- out IEnumerable requiredAttributeDescriptors)
- {
- if (string.IsNullOrEmpty(_requiredAttributes))
- {
- requiredAttributeDescriptors = Enumerable.Empty();
- return true;
- }
-
- requiredAttributeDescriptors = null;
- var descriptors = new List();
-
- PassOptionalWhitespace();
-
- do
- {
- TagHelperRequiredAttributeDescriptor descriptor;
- if (At('['))
- {
- descriptor = ParseCssSelector(errorSink);
- }
- else
- {
- descriptor = ParsePlainSelector(errorSink);
- }
-
- if (descriptor == null)
- {
- // Failed to create the descriptor due to an invalid required attribute.
- return false;
- }
- else
- {
- descriptors.Add(descriptor);
- }
-
- PassOptionalWhitespace();
-
- if (At(','))
- {
- _index++;
-
- if (!EnsureNotAtEnd(errorSink))
- {
- return false;
- }
- }
- else if (!AtEnd)
- {
- errorSink.OnError(
- SourceLocation.Zero,
- Resources.FormatTagHelperDescriptorFactory_InvalidRequiredAttributeCharacter(Current, _requiredAttributes),
- length: 0);
- return false;
- }
-
- PassOptionalWhitespace();
- }
- while (!AtEnd);
-
- requiredAttributeDescriptors = descriptors;
- return true;
- }
-
- private TagHelperRequiredAttributeDescriptor ParsePlainSelector(ErrorSink errorSink)
- {
- var nameEndIndex = _requiredAttributes.IndexOfAny(InvalidPlainAttributeNameCharacters, _index);
- string attributeName;
-
- var nameComparison = TagHelperRequiredAttributeNameComparison.FullMatch;
- if (nameEndIndex == -1)
- {
- attributeName = _requiredAttributes.Substring(_index);
- _index = _requiredAttributes.Length;
- }
- else
- {
- attributeName = _requiredAttributes.Substring(_index, nameEndIndex - _index);
- _index = nameEndIndex;
-
- if (_requiredAttributes[nameEndIndex] == RequiredAttributeWildcardSuffix)
- {
- nameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch;
-
- // Move past wild card
- _index++;
- }
- }
-
- TagHelperRequiredAttributeDescriptor descriptor = null;
- if (ValidateName(attributeName, targetingAttributes: true, errorSink: errorSink))
- {
- descriptor = new TagHelperRequiredAttributeDescriptor
- {
- Name = attributeName,
- NameComparison = nameComparison
- };
- }
-
- return descriptor;
- }
-
- private string ParseCssAttributeName(ErrorSink errorSink)
- {
- var nameStartIndex = _index;
- var nameEndIndex = _requiredAttributes.IndexOfAny(InvalidCssAttributeNameCharacters, _index);
- nameEndIndex = nameEndIndex == -1 ? _requiredAttributes.Length : nameEndIndex;
- _index = nameEndIndex;
-
- var attributeName = _requiredAttributes.Substring(nameStartIndex, nameEndIndex - nameStartIndex);
-
- return attributeName;
- }
-
- private TagHelperRequiredAttributeValueComparison? ParseCssValueComparison(ErrorSink errorSink)
- {
- Debug.Assert(!AtEnd);
- TagHelperRequiredAttributeValueComparison valueComparison;
-
- if (CssValueComparisons.TryGetValue(Current, out valueComparison))
- {
- var op = Current;
- _index++;
-
- if (op != '=' && At('='))
- {
- // Two length operator (ex: ^=). Move past the second piece
- _index++;
- }
- else if (op != '=') // We're at an incomplete operator (ex: [foo^]
- {
- errorSink.OnError(
- SourceLocation.Zero,
- Resources.FormatTagHelperDescriptorFactory_PartialRequiredAttributeOperator(_requiredAttributes, op),
- length: 0);
- return null;
- }
- }
- else if (!At(']'))
- {
- errorSink.OnError(
- SourceLocation.Zero,
- Resources.FormatTagHelperDescriptorFactory_InvalidRequiredAttributeOperator(Current, _requiredAttributes),
- length: 0);
- return null;
- }
-
- return valueComparison;
- }
-
- private string ParseCssValue(ErrorSink errorSink)
- {
- int valueStart;
- int valueEnd;
- if (At('\'') || At('"'))
- {
- var quote = Current;
-
- // Move past the quote
- _index++;
-
- valueStart = _index;
- valueEnd = _requiredAttributes.IndexOf(quote, _index);
- if (valueEnd == -1)
- {
- errorSink.OnError(
- SourceLocation.Zero,
- Resources.FormatTagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes(_requiredAttributes, quote),
- length: 0);
- return null;
- }
- _index = valueEnd + 1;
- }
- else
- {
- valueStart = _index;
- var valueEndIndex = _requiredAttributes.IndexOfAny(InvalidCssQuotelessValueCharacters, _index);
- valueEnd = valueEndIndex == -1 ? _requiredAttributes.Length : valueEndIndex;
- _index = valueEnd;
- }
-
- var value = _requiredAttributes.Substring(valueStart, valueEnd - valueStart);
-
- return value;
- }
-
- private TagHelperRequiredAttributeDescriptor ParseCssSelector(ErrorSink errorSink)
- {
- Debug.Assert(At('['));
-
- // Move past '['.
- _index++;
- PassOptionalWhitespace();
-
- var attributeName = ParseCssAttributeName(errorSink);
-
- PassOptionalWhitespace();
-
- if (!EnsureNotAtEnd(errorSink))
- {
- return null;
- }
-
- if (!ValidateName(attributeName, targetingAttributes: true, errorSink: errorSink))
- {
- // Couldn't parse a valid attribute name.
- return null;
- }
-
- var valueComparison = ParseCssValueComparison(errorSink);
-
- if (!valueComparison.HasValue)
- {
- return null;
- }
-
- PassOptionalWhitespace();
-
- if (!EnsureNotAtEnd(errorSink))
- {
- return null;
- }
-
- var value = ParseCssValue(errorSink);
-
- if (value == null)
- {
- // Couldn't parse value
- return null;
- }
-
- PassOptionalWhitespace();
-
- if (At(']'))
- {
- // Move past the ending bracket.
- _index++;
- }
- else if (AtEnd)
- {
- errorSink.OnError(
- SourceLocation.Zero,
- Resources.FormatTagHelperDescriptorFactory_CouldNotFindMatchingEndBrace(_requiredAttributes),
- length: 0);
- return null;
- }
- else
- {
- errorSink.OnError(
- SourceLocation.Zero,
- Resources.FormatTagHelperDescriptorFactory_InvalidRequiredAttributeCharacter(Current, _requiredAttributes),
- length: 0);
- return null;
- }
-
- return new TagHelperRequiredAttributeDescriptor
- {
- Name = attributeName,
- NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
- Value = value,
- ValueComparison = valueComparison.Value,
- };
- }
-
- private bool EnsureNotAtEnd(ErrorSink errorSink)
- {
- if (AtEnd)
- {
- errorSink.OnError(
- SourceLocation.Zero,
- Resources.FormatTagHelperDescriptorFactory_CouldNotFindMatchingEndBrace(_requiredAttributes),
- length: 0);
-
- return false;
- }
-
- return true;
- }
-
- private bool At(char c)
- {
- return !AtEnd && Current == c;
- }
-
- private void PassOptionalWhitespace()
- {
- while (!AtEnd && (Current == ' ' || Current == '\t'))
- {
- _index++;
- }
- }
- }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.CodeAnalysis.Razor/Properties/Resources.Designer.cs b/src/Microsoft.CodeAnalysis.Razor/Properties/Resources.Designer.cs
index 3b1139091e..4c8094d092 100644
--- a/src/Microsoft.CodeAnalysis.Razor/Properties/Resources.Designer.cs
+++ b/src/Microsoft.CodeAnalysis.Razor/Properties/Resources.Designer.cs
@@ -10,373 +10,117 @@ namespace Microsoft.CodeAnalysis.Razor
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.CodeAnalysis.Razor.Resources", typeof(Resources).GetTypeInfo().Assembly);
- ///
- /// {0} cannot be null or an empty string.
- ///
- internal static string Argument_Cannot_Be_Null_Or_Empty
- {
- get { return GetString("Argument_Cannot_Be_Null_Or_Empty"); }
- }
-
- ///
- /// {0} cannot be null or an empty string.
- ///
- internal static string FormatArgument_Cannot_Be_Null_Or_Empty(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("Argument_Cannot_Be_Null_Or_Empty"), p0);
- }
-
- ///
- /// Tag helpers cannot target {0} name '{1}' because it contains a '{2}' character.
- ///
- internal static string HtmlTargetElementAttribute_InvalidName
- {
- get { return GetString("HtmlTargetElementAttribute_InvalidName"); }
- }
-
- ///
- /// Tag helpers cannot target {0} name '{1}' because it contains a '{2}' character.
- ///
- internal static string FormatHtmlTargetElementAttribute_InvalidName(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("HtmlTargetElementAttribute_InvalidName"), p0, p1, p2);
- }
-
- ///
- /// {0} name cannot be null or whitespace.
- ///
- internal static string HtmlTargetElementAttribute_NameCannotBeNullOrWhitespace
- {
- get { return GetString("HtmlTargetElementAttribute_NameCannotBeNullOrWhitespace"); }
- }
-
- ///
- /// {0} name cannot be null or whitespace.
- ///
- internal static string FormatHtmlTargetElementAttribute_NameCannotBeNullOrWhitespace(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("HtmlTargetElementAttribute_NameCannotBeNullOrWhitespace"), p0);
- }
-
- ///
- /// Attribute
- ///
- internal static string TagHelperDescriptorFactory_Attribute
- {
- get { return GetString("TagHelperDescriptorFactory_Attribute"); }
- }
-
- ///
- /// Attribute
- ///
- internal static string FormatTagHelperDescriptorFactory_Attribute()
- {
- return GetString("TagHelperDescriptorFactory_Attribute");
- }
-
///
/// Could not find matching ']' for required attribute '{0}'.
///
internal static string TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace
{
- get { return GetString("TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace"); }
+ get => GetString("TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace");
}
///
/// Could not find matching ']' for required attribute '{0}'.
///
internal static string FormatTagHelperDescriptorFactory_CouldNotFindMatchingEndBrace(object p0)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace"), p0);
- }
-
- ///
- /// Invalid tag helper bound property '{0}.{1}'. An '{2}' must not be associated with a property with no public setter unless its type implements '{3}'.
- ///
- internal static string TagHelperDescriptorFactory_InvalidAttributeNameAttribute
- {
- get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameAttribute"); }
- }
-
- ///
- /// Invalid tag helper bound property '{0}.{1}'. An '{2}' must not be associated with a property with no public setter unless its type implements '{3}'.
- ///
- internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameAttribute(object p0, object p1, object p2, object p3)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameAttribute"), p0, p1, p2, p3);
- }
-
- ///
- /// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.
- ///
- internal static string TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty
- {
- get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty"); }
- }
-
- ///
- /// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.
- ///
- internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty(object p0, object p1, object p2, object p3)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty"), p0, p1, p2, p3);
- }
-
- ///
- /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.
- ///
- internal static string TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty
- {
- get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty"); }
- }
-
- ///
- /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.
- ///
- internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty"), p0, p1);
- }
-
- ///
- /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} contains a '{4}' character.
- ///
- internal static string TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter
- {
- get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter"); }
- }
-
- ///
- /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} contains a '{4}' character.
- ///
- internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter(object p0, object p1, object p2, object p3, object p4)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter"), p0, p1, p2, p3, p4);
- }
-
- ///
- /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} starts with '{4}'.
- ///
- internal static string TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart
- {
- get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart"); }
- }
-
- ///
- /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} starts with '{4}'.
- ///
- internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart(object p0, object p1, object p2, object p3, object p4)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart"), p0, p1, p2, p3, p4);
- }
-
- ///
- /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a whitespace {2}.
- ///
- internal static string TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace
- {
- get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace"); }
- }
-
- ///
- /// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a whitespace {2}.
- ///
- internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace"), p0, p1, p2);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace"), p0);
///
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
///
internal static string TagHelperDescriptorFactory_InvalidAttributePrefixNotNull
{
- get { return GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull"); }
+ get => GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull");
}
///
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
///
internal static string FormatTagHelperDescriptorFactory_InvalidAttributePrefixNotNull(object p0, object p1, object p2, object p3, object p4)
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull"), p0, p1, p2, p3, p4);
+
+ ///
+ /// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.
+ ///
+ internal static string TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty
{
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull"), p0, p1, p2, p3, p4);
+ get => GetString("TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty");
}
+ ///
+ /// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.
+ ///
+ internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty(object p0, object p1, object p2, object p3)
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty"), p0, p1, p2, p3);
+
///
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.
///
internal static string TagHelperDescriptorFactory_InvalidAttributePrefixNull
{
- get { return GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull"); }
+ get => GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull");
}
///
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.
///
internal static string FormatTagHelperDescriptorFactory_InvalidAttributePrefixNull(object p0, object p1, object p2, object p3, object p4)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull"), p0, p1, p2, p3, p4);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull"), p0, p1, p2, p3, p4);
///
/// Invalid required attribute character '{0}' in required attribute '{1}'. Separate required attributes with commas.
///
internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter
{
- get { return GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter"); }
+ get => GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter");
}
///
/// Invalid required attribute character '{0}' in required attribute '{1}'. Separate required attributes with commas.
///
internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeCharacter(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter"), p0, p1);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter"), p0, p1);
///
/// Required attribute '{0}' has mismatched quotes '{1}' around value.
///
internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes
{
- get { return GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes"); }
+ get => GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes");
}
///
/// Required attribute '{0}' has mismatched quotes '{1}' around value.
///
internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes"), p0, p1);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes"), p0, p1);
///
/// Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.
///
internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeOperator
{
- get { return GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeOperator"); }
+ get => GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeOperator");
}
///
/// Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.
///
internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeOperator(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeOperator"), p0, p1);
- }
-
- ///
- /// Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.
- ///
- internal static string TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName
- {
- get { return GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName"); }
- }
-
- ///
- /// Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.
- ///
- internal static string FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName(object p0, object p1, object p2, object p3)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName"), p0, p1, p2, p3);
- }
-
- ///
- /// Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.
- ///
- internal static string TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace
- {
- get { return GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace"); }
- }
-
- ///
- /// Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.
- ///
- internal static string FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace"), p0, p1);
- }
-
- ///
- /// name
- ///
- internal static string TagHelperDescriptorFactory_Name
- {
- get { return GetString("TagHelperDescriptorFactory_Name"); }
- }
-
- ///
- /// name
- ///
- internal static string FormatTagHelperDescriptorFactory_Name()
- {
- return GetString("TagHelperDescriptorFactory_Name");
- }
-
- ///
- /// Parent Tag
- ///
- internal static string TagHelperDescriptorFactory_ParentTag
- {
- get { return GetString("TagHelperDescriptorFactory_ParentTag"); }
- }
-
- ///
- /// Parent Tag
- ///
- internal static string FormatTagHelperDescriptorFactory_ParentTag()
- {
- return GetString("TagHelperDescriptorFactory_ParentTag");
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeOperator"), p0, p1);
///
/// Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.
///
internal static string TagHelperDescriptorFactory_PartialRequiredAttributeOperator
{
- get { return GetString("TagHelperDescriptorFactory_PartialRequiredAttributeOperator"); }
+ get => GetString("TagHelperDescriptorFactory_PartialRequiredAttributeOperator");
}
///
/// Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.
///
internal static string FormatTagHelperDescriptorFactory_PartialRequiredAttributeOperator(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_PartialRequiredAttributeOperator"), p0, p1);
- }
-
- ///
- /// prefix
- ///
- internal static string TagHelperDescriptorFactory_Prefix
- {
- get { return GetString("TagHelperDescriptorFactory_Prefix"); }
- }
-
- ///
- /// prefix
- ///
- internal static string FormatTagHelperDescriptorFactory_Prefix()
- {
- return GetString("TagHelperDescriptorFactory_Prefix");
- }
-
- ///
- /// Tag
- ///
- internal static string TagHelperDescriptorFactory_Tag
- {
- get { return GetString("TagHelperDescriptorFactory_Tag"); }
- }
-
- ///
- /// Tag
- ///
- internal static string FormatTagHelperDescriptorFactory_Tag()
- {
- return GetString("TagHelperDescriptorFactory_Tag");
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_PartialRequiredAttributeOperator"), p0, p1);
private static string GetString(string name, params string[] formatterNames)
{
diff --git a/src/Microsoft.CodeAnalysis.Razor/Properties/ViewComponentResources.Designer.cs b/src/Microsoft.CodeAnalysis.Razor/Properties/ViewComponentResources.Designer.cs
index d2f1b85b09..343bd246be 100644
--- a/src/Microsoft.CodeAnalysis.Razor/Properties/ViewComponentResources.Designer.cs
+++ b/src/Microsoft.CodeAnalysis.Razor/Properties/ViewComponentResources.Designer.cs
@@ -15,80 +15,70 @@ namespace Microsoft.CodeAnalysis.Razor
///
internal static string ViewComponent_AmbiguousMethods
{
- get { return GetString("ViewComponent_AmbiguousMethods"); }
+ get => GetString("ViewComponent_AmbiguousMethods");
}
///
/// View component '{0}' must have exactly one public method named '{1}' or '{2}'.
///
internal static string FormatViewComponent_AmbiguousMethods(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AmbiguousMethods"), p0, p1, p2);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AmbiguousMethods"), p0, p1, p2);
///
/// Method '{0}' of view component '{1}' should be declared to return {2}<T>.
///
internal static string ViewComponent_AsyncMethod_ShouldReturnTask
{
- get { return GetString("ViewComponent_AsyncMethod_ShouldReturnTask"); }
+ get => GetString("ViewComponent_AsyncMethod_ShouldReturnTask");
}
///
/// Method '{0}' of view component '{1}' should be declared to return {2}<T>.
///
internal static string FormatViewComponent_AsyncMethod_ShouldReturnTask(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AsyncMethod_ShouldReturnTask"), p0, p1, p2);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AsyncMethod_ShouldReturnTask"), p0, p1, p2);
///
/// Could not find an '{0}' or '{1}' method for the view component '{2}'.
///
internal static string ViewComponent_CannotFindMethod
{
- get { return GetString("ViewComponent_CannotFindMethod"); }
+ get => GetString("ViewComponent_CannotFindMethod");
}
///
/// Could not find an '{0}' or '{1}' method for the view component '{2}'.
///
internal static string FormatViewComponent_CannotFindMethod(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_CannotFindMethod"), p0, p1, p2);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_CannotFindMethod"), p0, p1, p2);
///
/// Method '{0}' of view component '{1}' cannot return a {2}.
///
internal static string ViewComponent_SyncMethod_CannotReturnTask
{
- get { return GetString("ViewComponent_SyncMethod_CannotReturnTask"); }
+ get => GetString("ViewComponent_SyncMethod_CannotReturnTask");
}
///
/// Method '{0}' of view component '{1}' cannot return a {2}.
///
internal static string FormatViewComponent_SyncMethod_CannotReturnTask(object p0, object p1, object p2)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_CannotReturnTask"), p0, p1, p2);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_CannotReturnTask"), p0, p1, p2);
///
/// Method '{0}' of view component '{1}' should be declared to return a value.
///
internal static string ViewComponent_SyncMethod_ShouldReturnValue
{
- get { return GetString("ViewComponent_SyncMethod_ShouldReturnValue"); }
+ get => GetString("ViewComponent_SyncMethod_ShouldReturnValue");
}
///
/// Method '{0}' of view component '{1}' should be declared to return a value.
///
internal static string FormatViewComponent_SyncMethod_ShouldReturnValue(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_ShouldReturnValue"), p0, p1);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_ShouldReturnValue"), p0, p1);
private static string GetString(string name, params string[] formatterNames)
{
diff --git a/src/Microsoft.CodeAnalysis.Razor/RazorDiagnosticFactory.cs b/src/Microsoft.CodeAnalysis.Razor/RazorDiagnosticFactory.cs
new file mode 100644
index 0000000000..ae28bf4106
--- /dev/null
+++ b/src/Microsoft.CodeAnalysis.Razor/RazorDiagnosticFactory.cs
@@ -0,0 +1,185 @@
+// 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;
+
+namespace Microsoft.CodeAnalysis.Razor
+{
+ internal static class RazorDiagnosticFactory
+ {
+ private const string DiagnosticPrefix = "RZ";
+
+ /*
+ * Razor.Evolution starts at 0, 1000, 2000, 3000. Therefore, we should offset by 500 to ensure we can easily
+ * maintain this list of diagnostic descriptors in conjunction with the one in Razor.Evolution.
+ */
+
+ #region General Errors
+
+ /*
+ * General Errors ID Offset = 500
+ */
+
+ #endregion
+
+ #region Language Errors
+
+ /*
+ * Language Errors ID Offset = 1500
+ */
+
+ #endregion
+
+ #region Semantic Errors
+
+ /*
+ * Semantic Errors ID Offset = 2500
+ */
+
+ #endregion
+
+ #region TagHelper Errors
+
+ /*
+ * TagHelper Errors ID Offset = 3500
+ */
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidAttributeNameNullOrEmpty =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3500",
+ () => Resources.TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidAttributeNameNullOrEmpty(string containingTypeName, string boundPropertyName)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidAttributeNameNullOrEmpty,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ containingTypeName,
+ boundPropertyName,
+ TagHelperTypes.HtmlAttributeNameAttribute,
+ TagHelperTypes.HtmlAttributeName.Name);
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidAttributePrefixNotNull =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3501",
+ () => Resources.TagHelperDescriptorFactory_InvalidAttributePrefixNotNull,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidAttributePrefixNotNull(string containingTypeName, string boundPropertyName)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidAttributePrefixNotNull,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ containingTypeName,
+ boundPropertyName,
+ TagHelperTypes.HtmlAttributeNameAttribute,
+ TagHelperTypes.HtmlAttributeName.DictionaryAttributePrefix,
+ "IDictionary");
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidAttributePrefixNull =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3502",
+ () => Resources.TagHelperDescriptorFactory_InvalidAttributePrefixNull,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidAttributePrefixNull(string containingTypeName, string boundPropertyName)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidAttributePrefixNull,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ containingTypeName,
+ boundPropertyName,
+ TagHelperTypes.HtmlAttributeNameAttribute,
+ TagHelperTypes.HtmlAttributeName.DictionaryAttributePrefix,
+ "IDictionary");
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidRequiredAttributeCharacter =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3503",
+ () => Resources.TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidRequiredAttributeCharacter(char invalidCharacter, string requiredAttributes)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidRequiredAttributeCharacter,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ invalidCharacter,
+ requiredAttributes);
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_PartialRequiredAttributeOperator =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3504",
+ () => Resources.TagHelperDescriptorFactory_PartialRequiredAttributeOperator,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_PartialRequiredAttributeOperator(char partialOperator, string requiredAttributes)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_PartialRequiredAttributeOperator,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ requiredAttributes,
+ partialOperator);
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidRequiredAttributeOperator =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3505",
+ () => Resources.TagHelperDescriptorFactory_InvalidRequiredAttributeOperator,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidRequiredAttributeOperator(char invalidOperator, string requiredAttributes)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidRequiredAttributeOperator,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ invalidOperator,
+ requiredAttributes);
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_InvalidRequiredAttributeMismatchedQuotes =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3506",
+ () => Resources.TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_InvalidRequiredAttributeMismatchedQuotes(char quote, string requiredAttributes)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_InvalidRequiredAttributeMismatchedQuotes,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ requiredAttributes,
+ quote);
+
+ return diagnostic;
+ }
+
+ private static readonly RazorDiagnosticDescriptor TagHelper_CouldNotFindMatchingEndBrace =
+ new RazorDiagnosticDescriptor(
+ $"{DiagnosticPrefix}3507",
+ () => Resources.TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace,
+ RazorDiagnosticSeverity.Error);
+ public static RazorDiagnostic CreateTagHelper_CouldNotFindMatchingEndBrace(string requiredAttributes)
+ {
+ var diagnostic = RazorDiagnostic.Create(
+ TagHelper_CouldNotFindMatchingEndBrace,
+ new SourceSpan(SourceLocation.Undefined, contentLength: 0),
+ requiredAttributes);
+
+ return diagnostic;
+ }
+
+
+ #endregion
+ }
+}
diff --git a/src/Microsoft.CodeAnalysis.Razor/RequiredAttributeParser.cs b/src/Microsoft.CodeAnalysis.Razor/RequiredAttributeParser.cs
new file mode 100644
index 0000000000..c6dbfb9c87
--- /dev/null
+++ b/src/Microsoft.CodeAnalysis.Razor/RequiredAttributeParser.cs
@@ -0,0 +1,304 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using Microsoft.AspNetCore.Razor.Evolution;
+
+namespace Microsoft.CodeAnalysis.Razor
+{
+ // Internal for testing
+ internal static class RequiredAttributeParser
+ {
+ public static void AddRequiredAttributes(string requiredAttributes, TagMatchingRuleBuilder ruleBuilder)
+ {
+ var requiredAttributeParser = new DefaultRequiredAttributeParser(requiredAttributes);
+ requiredAttributeParser.AddRequiredAttributes(ruleBuilder);
+ }
+
+ private class DefaultRequiredAttributeParser
+ {
+ private const char RequiredAttributeWildcardSuffix = '*';
+
+ private static readonly IReadOnlyDictionary CssValueComparisons =
+ new Dictionary
+ {
+ { '=', RequiredAttributeDescriptor.ValueComparisonMode.FullMatch },
+ { '^', RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch },
+ { '$', RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch }
+ };
+ private static readonly char[] InvalidPlainAttributeNameCharacters = { ' ', '\t', ',', RequiredAttributeWildcardSuffix };
+ private static readonly char[] InvalidCssAttributeNameCharacters = (new[] { ' ', '\t', ',', ']' })
+ .Concat(CssValueComparisons.Keys)
+ .ToArray();
+ private static readonly char[] InvalidCssQuotelessValueCharacters = { ' ', '\t', ']' };
+
+ private int _index;
+ private string _requiredAttributes;
+
+ public DefaultRequiredAttributeParser(string requiredAttributes)
+ {
+ _requiredAttributes = requiredAttributes;
+ }
+
+ private char Current => _requiredAttributes[_index];
+
+ private bool AtEnd => _index >= _requiredAttributes.Length;
+
+ public void AddRequiredAttributes(TagMatchingRuleBuilder ruleBuilder)
+ {
+ if (string.IsNullOrEmpty(_requiredAttributes))
+ {
+ return;
+ }
+ var descriptors = new List();
+
+ PassOptionalWhitespace();
+
+ do
+ {
+ var successfulParse = true;
+ ruleBuilder.RequireAttribute(attributeBuilder =>
+ {
+ if (At('['))
+ {
+ if (!TryParseCssSelector(attributeBuilder))
+ {
+ successfulParse = false;
+ return;
+ }
+ }
+ else
+ {
+ ParsePlainSelector(attributeBuilder);
+ }
+
+ PassOptionalWhitespace();
+
+ if (At(','))
+ {
+ _index++;
+
+ if (!EnsureNotAtEnd(attributeBuilder))
+ {
+ successfulParse = false;
+ return;
+ }
+ }
+ else if (!AtEnd)
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeCharacter(Current, _requiredAttributes);
+ attributeBuilder.AddDiagnostic(diagnostic);
+ successfulParse = false;
+ return;
+ }
+
+ PassOptionalWhitespace();
+ });
+
+ if (!successfulParse)
+ {
+ break;
+ }
+ }
+ while (!AtEnd);
+ }
+
+ private void ParsePlainSelector(RequiredAttributeDescriptorBuilder attributeBuilder)
+ {
+ var nameEndIndex = _requiredAttributes.IndexOfAny(InvalidPlainAttributeNameCharacters, _index);
+ string attributeName;
+
+ var nameComparison = RequiredAttributeDescriptor.NameComparisonMode.FullMatch;
+ if (nameEndIndex == -1)
+ {
+ attributeName = _requiredAttributes.Substring(_index);
+ _index = _requiredAttributes.Length;
+ }
+ else
+ {
+ attributeName = _requiredAttributes.Substring(_index, nameEndIndex - _index);
+ _index = nameEndIndex;
+
+ if (_requiredAttributes[nameEndIndex] == RequiredAttributeWildcardSuffix)
+ {
+ nameComparison = RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch;
+
+ // Move past wild card
+ _index++;
+ }
+ }
+
+ attributeBuilder.Name(attributeName);
+ attributeBuilder.NameComparisonMode(nameComparison);
+ }
+
+ private void ParseCssAttributeName(RequiredAttributeDescriptorBuilder builder)
+ {
+ var nameStartIndex = _index;
+ var nameEndIndex = _requiredAttributes.IndexOfAny(InvalidCssAttributeNameCharacters, _index);
+ nameEndIndex = nameEndIndex == -1 ? _requiredAttributes.Length : nameEndIndex;
+ _index = nameEndIndex;
+
+ var attributeName = _requiredAttributes.Substring(nameStartIndex, nameEndIndex - nameStartIndex);
+
+ builder.Name(attributeName);
+ }
+
+ private bool TryParseCssValueComparison(RequiredAttributeDescriptorBuilder builder, out RequiredAttributeDescriptor.ValueComparisonMode valueComparison)
+ {
+ Debug.Assert(!AtEnd);
+
+ if (CssValueComparisons.TryGetValue(Current, out valueComparison))
+ {
+ var op = Current;
+ _index++;
+
+ if (op != '=' && At('='))
+ {
+ // Two length operator (ex: ^=). Move past the second piece
+ _index++;
+ }
+ else if (op != '=') // We're at an incomplete operator (ex: [foo^]
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_PartialRequiredAttributeOperator(op, _requiredAttributes);
+ builder.AddDiagnostic(diagnostic);
+
+ return false;
+ }
+ }
+ else if (!At(']'))
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeOperator(Current, _requiredAttributes);
+ builder.AddDiagnostic(diagnostic);
+
+ return false;
+ }
+
+ builder.ValueComparisonMode(valueComparison);
+
+ return true;
+ }
+
+ private bool TryParseCssValue(RequiredAttributeDescriptorBuilder builder)
+ {
+ int valueStart;
+ int valueEnd;
+ if (At('\'') || At('"'))
+ {
+ var quote = Current;
+
+ // Move past the quote
+ _index++;
+
+ valueStart = _index;
+ valueEnd = _requiredAttributes.IndexOf(quote, _index);
+ if (valueEnd == -1)
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeMismatchedQuotes(quote, _requiredAttributes);
+ builder.AddDiagnostic(diagnostic);
+
+ return false;
+ }
+ _index = valueEnd + 1;
+ }
+ else
+ {
+ valueStart = _index;
+ var valueEndIndex = _requiredAttributes.IndexOfAny(InvalidCssQuotelessValueCharacters, _index);
+ valueEnd = valueEndIndex == -1 ? _requiredAttributes.Length : valueEndIndex;
+ _index = valueEnd;
+ }
+
+ var value = _requiredAttributes.Substring(valueStart, valueEnd - valueStart);
+
+ builder.Value(value);
+
+ return true;
+ }
+
+ private bool TryParseCssSelector(RequiredAttributeDescriptorBuilder attributeBuilder)
+ {
+ Debug.Assert(At('['));
+
+ // Move past '['.
+ _index++;
+ PassOptionalWhitespace();
+
+ ParseCssAttributeName(attributeBuilder);
+
+ PassOptionalWhitespace();
+
+ if (!EnsureNotAtEnd(attributeBuilder))
+ {
+ return false;
+ }
+
+ if (!TryParseCssValueComparison(attributeBuilder, out RequiredAttributeDescriptor.ValueComparisonMode valueComparison))
+ {
+ return false;
+ }
+
+ PassOptionalWhitespace();
+
+ if (!EnsureNotAtEnd(attributeBuilder))
+ {
+ return false;
+ }
+
+ if (valueComparison != RequiredAttributeDescriptor.ValueComparisonMode.None && !TryParseCssValue(attributeBuilder))
+ {
+ return false;
+ }
+
+ PassOptionalWhitespace();
+
+ if (At(']'))
+ {
+ // Move past the ending bracket.
+ _index++;
+ return true;
+ }
+ else if (AtEnd)
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace(_requiredAttributes);
+ attributeBuilder.AddDiagnostic(diagnostic);
+ }
+ else
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeCharacter(Current, _requiredAttributes);
+ attributeBuilder.AddDiagnostic(diagnostic);
+ }
+
+ return false;
+ }
+
+ private bool EnsureNotAtEnd(RequiredAttributeDescriptorBuilder builder)
+ {
+ if (AtEnd)
+ {
+ var diagnostic = RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace(_requiredAttributes);
+ builder.AddDiagnostic(diagnostic);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ private bool At(char c)
+ {
+ return !AtEnd && Current == c;
+ }
+
+ private void PassOptionalWhitespace()
+ {
+ while (!AtEnd && (Current == ' ' || Current == '\t'))
+ {
+ _index++;
+ }
+ }
+ }
+ }
+}
diff --git a/src/Microsoft.CodeAnalysis.Razor/Resources.resx b/src/Microsoft.CodeAnalysis.Razor/Resources.resx
index ee2c26d650..edfa6f2970 100644
--- a/src/Microsoft.CodeAnalysis.Razor/Resources.resx
+++ b/src/Microsoft.CodeAnalysis.Razor/Resources.resx
@@ -117,42 +117,15 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
- {0} cannot be null or an empty string.
-
-
- Tag helpers cannot target {0} name '{1}' because it contains a '{2}' character.
-
-
- {0} name cannot be null or whitespace.
-
-
- Attribute
-
Could not find matching ']' for required attribute '{0}'.
-
- Invalid tag helper bound property '{0}.{1}'. An '{2}' must not be associated with a property with no public setter unless its type implements '{3}'.
+
+ Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.
-
- Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.
-
-
- Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} contains a '{4}' character.
-
-
- Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} starts with '{4}'.
-
-
- Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a whitespace {2}.
-
-
- Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
-
Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.
@@ -165,25 +138,7 @@
Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.
-
- Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.
-
-
- Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.
-
-
- name
-
-
- Parent Tag
-
Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.
-
- prefix
-
-
- Tag
-
\ No newline at end of file
diff --git a/src/Microsoft.CodeAnalysis.Razor/TagHelpers.cs b/src/Microsoft.CodeAnalysis.Razor/TagHelpers.cs
index 32e6de8055..5f9944e7f1 100644
--- a/src/Microsoft.CodeAnalysis.Razor/TagHelpers.cs
+++ b/src/Microsoft.CodeAnalysis.Razor/TagHelpers.cs
@@ -32,8 +32,11 @@ namespace Microsoft.CodeAnalysis.Razor
foreach (var type in types)
{
- var descriptors = factory.CreateDescriptors(type, errors);
- results.AddRange(descriptors);
+ var descriptor = factory.CreateDescriptor(type);
+ if (descriptor != null)
+ {
+ results.Add(descriptor);
+ }
}
}
@@ -52,7 +55,10 @@ namespace Microsoft.CodeAnalysis.Razor
{
var descriptor = factory.CreateDescriptor(type);
- results.Add(descriptor);
+ if (descriptor != null)
+ {
+ results.Add(descriptor);
+ }
}
catch (Exception ex)
{
diff --git a/src/Microsoft.CodeAnalysis.Razor/ViewComponentTagHelperDescriptorFactory.cs b/src/Microsoft.CodeAnalysis.Razor/ViewComponentTagHelperDescriptorFactory.cs
index 966ac5865b..3489a2fd39 100644
--- a/src/Microsoft.CodeAnalysis.Razor/ViewComponentTagHelperDescriptorFactory.cs
+++ b/src/Microsoft.CodeAnalysis.Razor/ViewComponentTagHelperDescriptorFactory.cs
@@ -2,7 +2,6 @@
// 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.Immutable;
using System.Linq;
using System.Threading.Tasks;
@@ -36,66 +35,69 @@ namespace Microsoft.CodeAnalysis.Razor
var shortName = GetShortName(type);
var tagName = $"vc:{DefaultTagHelperDescriptorFactory.ToHtmlCase(shortName)}";
var typeName = $"__Generated__{shortName}ViewComponentTagHelper";
-
- var descriptor = new TagHelperDescriptor
+ var descriptorBuilder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
+ var methodParameters = GetInvokeMethodParameters(type);
+ descriptorBuilder.TagMatchingRule(ruleBuilder =>
{
- TagName = tagName,
- TypeName = typeName,
- AssemblyName = assemblyName
- };
+ ruleBuilder.RequireTagName(tagName);
+ AddRequiredAttributes(methodParameters, ruleBuilder);
+ });
- SetAttributeDescriptors(type, descriptor);
+ AddBoundAttributes(methodParameters, descriptorBuilder);
- descriptor.PropertyBag.Add(ViewComponentTypes.ViewComponentNameKey, shortName);
+ descriptorBuilder.AddMetadata(ViewComponentTypes.ViewComponentNameKey, shortName);
+ var descriptor = descriptorBuilder.Build();
return descriptor;
}
- private void SetAttributeDescriptors(INamedTypeSymbol type, TagHelperDescriptor descriptor)
+ private void AddRequiredAttributes(ImmutableArray methodParameters, TagMatchingRuleBuilder builder)
{
- var methodParameters = GetInvokeMethodParameters(type);
- var attributeDescriptors = new List();
- var indexerDescriptors = new List();
- var requiredAttributeDescriptors = new List();
+ foreach (var parameter in methodParameters)
+ {
+ if (GetIndexerValueTypeName(parameter) == null)
+ {
+ // Set required attributes only for non-indexer attributes. Indexer attributes can't be required attributes
+ // because there are two ways of setting values for the attribute.
+ builder.RequireAttribute(attributeBuilder =>
+ {
+ var lowerKebabName = DefaultTagHelperDescriptorFactory.ToHtmlCase(parameter.Name);
+ attributeBuilder.Name(lowerKebabName);
+ });
+ }
+ }
+ }
+ private void AddBoundAttributes(ImmutableArray methodParameters, ITagHelperDescriptorBuilder builder)
+ {
foreach (var parameter in methodParameters)
{
var lowerKebabName = DefaultTagHelperDescriptorFactory.ToHtmlCase(parameter.Name);
var typeName = parameter.Type.ToDisplayString(FullNameTypeDisplayFormat);
- var attributeDescriptor = new TagHelperAttributeDescriptor
+ builder.BindAttribute(attributeBuilder =>
{
- Name = lowerKebabName,
- PropertyName = parameter.Name,
- TypeName = typeName
- };
+ attributeBuilder
+ .Name(lowerKebabName)
+ .PropertyName(parameter.Name)
+ .TypeName(typeName);
- attributeDescriptor.IsEnum = parameter.Type.TypeKind == TypeKind.Enum;
- attributeDescriptor.IsIndexer = false;
-
- attributeDescriptors.Add(attributeDescriptor);
-
- var indexerDescriptor = GetIndexerAttributeDescriptor(parameter, lowerKebabName);
- if (indexerDescriptor != null)
- {
- indexerDescriptors.Add(indexerDescriptor);
- }
- else
- {
- // Set required attributes only for non-indexer attributes. Indexer attributes can't be required attributes
- // because there are two ways of setting values for the attribute.
- requiredAttributeDescriptors.Add(new TagHelperRequiredAttributeDescriptor
+ if (parameter.Type.TypeKind == TypeKind.Enum)
{
- Name = lowerKebabName
- });
- }
+ attributeBuilder.AsEnum();
+ }
+ else
+ {
+ var dictionaryValueType = GetIndexerValueTypeName(parameter);
+ if (dictionaryValueType != null)
+ {
+ attributeBuilder.AsDictionary(lowerKebabName + "-", dictionaryValueType);
+ }
+ }
+ });
}
-
- attributeDescriptors.AddRange(indexerDescriptors);
- descriptor.Attributes = attributeDescriptors;
- descriptor.RequiredAttributes = requiredAttributeDescriptors;
}
- private TagHelperAttributeDescriptor GetIndexerAttributeDescriptor(IParameterSymbol parameter, string name)
+ private string GetIndexerValueTypeName(IParameterSymbol parameter)
{
INamedTypeSymbol dictionaryType;
if ((parameter.Type as INamedTypeSymbol)?.ConstructedFrom == _iDictionarySymbol)
@@ -117,16 +119,9 @@ namespace Microsoft.CodeAnalysis.Razor
}
var type = dictionaryType.TypeArguments[1];
- var descriptor = new TagHelperAttributeDescriptor
- {
- Name = name + "-",
- PropertyName = parameter.Name,
- TypeName = type.ToDisplayString(FullNameTypeDisplayFormat),
- IsEnum = type.TypeKind == TypeKind.Enum,
- IsIndexer = true
- };
+ var typeName = type.ToDisplayString(FullNameTypeDisplayFormat);
- return descriptor;
+ return typeName;
}
private ImmutableArray GetInvokeMethodParameters(INamedTypeSymbol componentType)
diff --git a/src/Microsoft.CodeAnalysis.Razor/XmlMemberDocumentation.cs b/src/Microsoft.CodeAnalysis.Razor/XmlMemberDocumentation.cs
deleted file mode 100644
index cec3ebed7f..0000000000
--- a/src/Microsoft.CodeAnalysis.Razor/XmlMemberDocumentation.cs
+++ /dev/null
@@ -1,80 +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;
-using System.Text;
-using System.Xml.Linq;
-
-namespace Microsoft.CodeAnalysis.Razor
-{
- ///
- /// Extracts summary and remarks XML documentation from XML member documentation.
- ///
- internal class XmlMemberDocumentation
- {
- private readonly XElement _element;
-
- public XmlMemberDocumentation(string content)
- {
- if (string.IsNullOrEmpty(content))
- {
- throw new ArgumentException(Resources.FormatArgument_Cannot_Be_Null_Or_Empty(nameof(content)));
- }
-
- // the structure of the XML is defined by: https://msdn.microsoft.com/en-us/library/fsbx0t7x.aspx
- // we expect the root node of the content we are passed to always be 'member' or 'doc'.
- _element = XElement.Parse(content);
- }
-
- ///
- /// Retrieves the <summary> documentation.
- ///
- /// <summary> documentation.
- public string GetSummary()
- {
- var summaryElement = _element.Element("summary");
- if (summaryElement != null)
- {
- var summaryValue = GetElementValue(summaryElement);
-
- return summaryValue;
- }
-
- return null;
- }
-
- ///
- /// Retrieves the <remarks> documentation.
- ///
- /// <remarks> documentation.
- public string GetRemarks()
- {
- var remarksElement = _element.Element("remarks");
-
- if (remarksElement != null)
- {
- var remarksValue = GetElementValue(remarksElement);
-
- return remarksValue;
- }
-
- return null;
- }
-
- private static string GetElementValue(XElement element)
- {
- var stringBuilder = new StringBuilder();
- var node = element.FirstNode;
-
- while (node != null)
- {
- stringBuilder.Append(node.ToString(SaveOptions.DisableFormatting));
-
- node = node.NextNode;
- }
-
- return stringBuilder.ToString().Trim();
- }
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTagHelperResolver.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTagHelperResolver.cs
index c67a99756f..59eeb40485 100644
--- a/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTagHelperResolver.cs
+++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/DefaultTagHelperResolver.cs
@@ -50,11 +50,12 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
// Per https://github.com/dotnet/roslyn/issues/12770 - there's currently no support for documentation in the OOP host
// until that's available we add the documentation on the VS side by looking up each symbol again.
var compilation = await project.GetCompilationAsync().ConfigureAwait(false);
- AddXmlDocumentation(compilation, result.Descriptors);
- return result;
+ var documentedTagHelpers = GetDocumentedTagHelpers(compilation, result.Descriptors);
+ var documentedResult = new TagHelperResolutionResult(documentedTagHelpers, result.Diagnostics);
+
+ return documentedResult;
}
}
-
}
}
@@ -82,42 +83,123 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
}
}
- private void AddXmlDocumentation(Compilation compilation, IReadOnlyList tagHelpers)
+ private IReadOnlyList GetDocumentedTagHelpers(Compilation compilation, IReadOnlyList tagHelpers)
{
+ var documentedTagHelpers = new List();
for (var i = 0; i < tagHelpers.Count; i++)
{
var tagHelper = tagHelpers[i];
- tagHelper.DesignTimeDescriptor = tagHelper.DesignTimeDescriptor ?? new TagHelperDesignTimeDescriptor();
- var symbol = compilation.GetTypeByMetadataName(tagHelper.TypeName);
+ if (tagHelper.Documentation != null)
+ {
+ documentedTagHelpers.Add(tagHelper);
+ continue;
+ }
+
+ var typeName = tagHelper.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
+ var symbol = compilation.GetTypeByMetadataName(typeName);
if (symbol != null)
{
+ var tagHelperBuilder = ShallowCopy(typeName, tagHelper);
var xml = symbol.GetDocumentationCommentXml();
+
if (!string.IsNullOrEmpty(xml))
{
- var documentation = new XmlMemberDocumentation(xml);
- tagHelper.DesignTimeDescriptor.Summary = documentation.GetSummary();
- tagHelper.DesignTimeDescriptor.Remarks = documentation.GetRemarks();
+ tagHelperBuilder.Documentation(xml);
}
- foreach (var attribute in tagHelper.Attributes)
+ foreach (var attribute in tagHelper.BoundAttributes)
{
- attribute.DesignTimeDescriptor = attribute.DesignTimeDescriptor ?? new TagHelperAttributeDesignTimeDescriptor();
+ var propertyName = attribute.Metadata[ITagHelperBoundAttributeDescriptorBuilder.PropertyNameKey];
- var attributeSymbol = symbol.GetMembers(attribute.PropertyName).FirstOrDefault();
+ var resolvedAttribute = attribute;
+ var attributeSymbol = symbol.GetMembers(propertyName).FirstOrDefault();
if (attributeSymbol != null)
{
xml = attributeSymbol.GetDocumentationCommentXml();
if (!string.IsNullOrEmpty(xml))
{
- var documentation = new XmlMemberDocumentation(xml);
- attribute.DesignTimeDescriptor.Summary = documentation.GetSummary();
- attribute.DesignTimeDescriptor.Remarks = documentation.GetRemarks();
+ var attributeBuilder = ShallowCopy(typeName, resolvedAttribute);
+ attributeBuilder.Documentation(xml);
+ resolvedAttribute = attributeBuilder.Build();
}
}
+
+ tagHelperBuilder.BindAttribute(resolvedAttribute);
}
+
+ tagHelper = tagHelperBuilder.Build();
+ }
+
+ documentedTagHelpers.Add(tagHelper);
+ }
+
+ return documentedTagHelpers;
+ }
+
+ private ITagHelperBoundAttributeDescriptorBuilder ShallowCopy(string tagHelperTypeName, BoundAttributeDescriptor attribute)
+ {
+ var builder = ITagHelperBoundAttributeDescriptorBuilder.Create(tagHelperTypeName);
+
+ if (attribute.IsEnum)
+ {
+ builder.AsEnum();
+ }
+
+ if (attribute.IndexerNamePrefix != null)
+ {
+ builder.AsDictionary(attribute.IndexerNamePrefix, attribute.IndexerTypeName);
+ }
+
+ builder.Name(attribute.Name);
+ builder.TypeName(attribute.TypeName);
+
+ var propertyName = attribute.Metadata[ITagHelperBoundAttributeDescriptorBuilder.PropertyNameKey];
+ builder.PropertyName(propertyName);
+
+ foreach (var metadata in attribute.Metadata)
+ {
+ builder.AddMetadata(metadata.Key, metadata.Value);
+ }
+
+ foreach (var diagnostic in attribute.Diagnostics)
+ {
+ builder.AddDiagnostic(diagnostic);
+ }
+
+ return builder;
+ }
+
+ private ITagHelperDescriptorBuilder ShallowCopy(string tagHelperTypeName, TagHelperDescriptor tagHelper)
+ {
+ var builder = ITagHelperDescriptorBuilder.Create(tagHelperTypeName, tagHelper.AssemblyName);
+
+ foreach (var rule in tagHelper.TagMatchingRules)
+ {
+ builder.TagMatchingRule(rule);
+ }
+
+ if (tagHelper.AllowedChildTags != null)
+ {
+ foreach (var allowedChild in tagHelper.AllowedChildTags)
+ {
+ builder.AllowChildTag(allowedChild);
}
}
+
+ builder.TagOutputHint(tagHelper.TagOutputHint);
+
+ foreach (var metadata in tagHelper.Metadata)
+ {
+ builder.AddMetadata(metadata.Key, metadata.Value);
+ }
+
+ foreach (var diagnostic in tagHelper.Diagnostics)
+ {
+ builder.AddDiagnostic(diagnostic);
+ }
+
+ return builder;
}
private IVsActivityLog GetActivityLog()
diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Microsoft.VisualStudio.LanguageServices.Razor.csproj b/src/Microsoft.VisualStudio.LanguageServices.Razor/Microsoft.VisualStudio.LanguageServices.Razor.csproj
index 9846325f70..40f0a7d387 100644
--- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Microsoft.VisualStudio.LanguageServices.Razor.csproj
+++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Microsoft.VisualStudio.LanguageServices.Razor.csproj
@@ -37,6 +37,7 @@
+
diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Properties/Resources.Designer.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/Properties/Resources.Designer.cs
index 597444de46..562ff5a7ab 100644
--- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Properties/Resources.Designer.cs
+++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Properties/Resources.Designer.cs
@@ -10,21 +10,33 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.VisualStudio.LanguageServices.Razor.Resources", typeof(Resources).GetTypeInfo().Assembly);
+ ///
+ /// Deserialization of {0} type '{1}' is not supported.
+ ///
+ internal static string RazorDiagnosticJsonConverter_UnsupportedRazorDiagnosticType
+ {
+ get => GetString("RazorDiagnosticJsonConverter_UnsupportedRazorDiagnosticType");
+ }
+
+ ///
+ /// Deserialization of {0} type '{1}' is not supported.
+ ///
+ internal static string FormatRazorDiagnosticJsonConverter_UnsupportedRazorDiagnosticType(object p0, object p1)
+ => string.Format(CultureInfo.CurrentCulture, GetString("RazorDiagnosticJsonConverter_UnsupportedRazorDiagnosticType"), p0, p1);
+
///
/// An unexpected exception occurred when invoking '{0}.{1}' on the Razor language service.
///
internal static string UnexpectedException
{
- get { return GetString("UnexpectedException"); }
+ get => GetString("UnexpectedException");
}
///
/// An unexpected exception occurred when invoking '{0}.{1}' on the Razor language service.
///
internal static string FormatUnexpectedException(object p0, object p1)
- {
- return string.Format(CultureInfo.CurrentCulture, GetString("UnexpectedException"), p0, p1);
- }
+ => string.Format(CultureInfo.CurrentCulture, GetString("UnexpectedException"), p0, p1);
private static string GetString(string name, params string[] formatterNames)
{
diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorDiagnosticJsonConverter.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorDiagnosticJsonConverter.cs
new file mode 100644
index 0000000000..204a84ab56
--- /dev/null
+++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/RazorDiagnosticJsonConverter.cs
@@ -0,0 +1,83 @@
+// 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 Microsoft.AspNetCore.Razor.Evolution;
+using Microsoft.AspNetCore.Razor.Evolution.Legacy;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Microsoft.VisualStudio.LanguageServices.Razor
+{
+ public class RazorDiagnosticJsonConverter : JsonConverter
+ {
+ public static readonly RazorDiagnosticJsonConverter Instance = new RazorDiagnosticJsonConverter();
+ private const string RazorDiagnosticMessageKey = "Message";
+ private const string RazorDiagnosticTypeNameKey = "TypeName";
+
+ public override bool CanConvert(Type objectType)
+ {
+ return typeof(RazorDiagnostic).IsAssignableFrom(objectType);
+ }
+
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ if (reader.TokenType != JsonToken.StartObject)
+ {
+ return null;
+ }
+
+ var diagnostic = JObject.Load(reader);
+ var span = diagnostic[nameof(RazorDiagnostic.Span)].Value();
+ var absoluteIndex = span[nameof(SourceSpan.AbsoluteIndex)].Value();
+ var lineIndex = span[nameof(SourceSpan.LineIndex)].Value();
+ var characterIndex = span[nameof(SourceSpan.CharacterIndex)].Value();
+ var length = span[nameof(SourceSpan.Length)].Value();
+ var filePath = span[nameof(SourceSpan.FilePath)].Value();
+ var message = diagnostic[RazorDiagnosticMessageKey].Value();
+ var typeName = diagnostic[RazorDiagnosticTypeNameKey].Value();
+
+ if (string.Equals(typeName, typeof(DefaultRazorDiagnostic).FullName, StringComparison.Ordinal))
+ {
+ var id = diagnostic[nameof(RazorDiagnostic.Id)].Value();
+ var severity = diagnostic[nameof(RazorDiagnostic.Severity)].Value();
+
+ var descriptor = new RazorDiagnosticDescriptor(id, () => message, (RazorDiagnosticSeverity)severity);
+ var sourceSpan = new SourceSpan(filePath, absoluteIndex, lineIndex, characterIndex, length);
+
+ return RazorDiagnostic.Create(descriptor, sourceSpan);
+ }
+ else if (string.Equals(typeName, typeof(LegacyRazorDiagnostic).FullName, StringComparison.Ordinal))
+ {
+ var error = new RazorError(message, absoluteIndex, lineIndex, characterIndex, length);
+
+ return RazorDiagnostic.Create(error);
+ }
+
+ throw new NotSupportedException(
+ Resources.FormatRazorDiagnosticJsonConverter_UnsupportedRazorDiagnosticType(typeof(RazorDiagnostic).Name, typeName));
+ }
+
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ var diagnostic = (RazorDiagnostic)value;
+
+ writer.WriteStartObject();
+ WriteProperty(writer, nameof(RazorDiagnostic.Id), diagnostic.Id);
+ WriteProperty(writer, nameof(RazorDiagnostic.Severity), (int)diagnostic.Severity);
+ WriteProperty(writer, RazorDiagnosticMessageKey, diagnostic.GetMessage());
+ WriteProperty(writer, RazorDiagnosticTypeNameKey, diagnostic.GetType().FullName);
+
+ writer.WritePropertyName(nameof(RazorDiagnostic.Span));
+ serializer.Serialize(writer, diagnostic.Span);
+
+ writer.WriteEndObject();
+ }
+
+ private void WriteProperty(JsonWriter writer, string key, T value)
+ {
+ writer.WritePropertyName(key);
+ writer.WriteValue(value);
+ }
+ }
+}
diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/Resources.resx b/src/Microsoft.VisualStudio.LanguageServices.Razor/Resources.resx
index 6783beb144..b4189aac42 100644
--- a/src/Microsoft.VisualStudio.LanguageServices.Razor/Resources.resx
+++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/Resources.resx
@@ -117,6 +117,9 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ Deserialization of {0} type '{1}' is not supported.
+
An unexpected exception occurred when invoking '{0}.{1}' on the Razor language service.
diff --git a/src/Microsoft.VisualStudio.LanguageServices.Razor/TagHelperDescriptorJsonConverter.cs b/src/Microsoft.VisualStudio.LanguageServices.Razor/TagHelperDescriptorJsonConverter.cs
new file mode 100644
index 0000000000..1163a6ec3d
--- /dev/null
+++ b/src/Microsoft.VisualStudio.LanguageServices.Razor/TagHelperDescriptorJsonConverter.cs
@@ -0,0 +1,189 @@
+// 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.AspNetCore.Razor.Evolution;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace Microsoft.VisualStudio.LanguageServices.Razor
+{
+ internal class TagHelperDescriptorJsonConverter : JsonConverter
+ {
+ public static readonly TagHelperDescriptorJsonConverter Instance = new TagHelperDescriptorJsonConverter();
+
+ public override bool CanWrite => false;
+
+ public override bool CanConvert(Type objectType)
+ {
+ return typeof(TagHelperDescriptor).IsAssignableFrom(objectType);
+ }
+
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ if (reader.TokenType != JsonToken.StartObject)
+ {
+ return null;
+ }
+
+ var descriptor = JObject.Load(reader);
+ var descriptorKind = descriptor[nameof(TagHelperDescriptor.Kind)].Value();
+ var typeName = descriptor[nameof(TagHelperDescriptor.Name)].Value();
+ var assemblyName = descriptor[nameof(TagHelperDescriptor.AssemblyName)].Value();
+ var tagMatchingRules = descriptor[nameof(TagHelperDescriptor.TagMatchingRules)].Value();
+ var boundAttributes = descriptor[nameof(TagHelperDescriptor.BoundAttributes)].Value();
+ var childTags = descriptor[nameof(TagHelperDescriptor.AllowedChildTags)].Value();
+ var documentation = descriptor[nameof(TagHelperDescriptor.Documentation)].Value();
+ var tagOutputHint = descriptor[nameof(TagHelperDescriptor.TagOutputHint)].Value();
+ var diagnostics = descriptor[nameof(TagHelperDescriptor.Diagnostics)].Value();
+ var metadata = descriptor[nameof(TagHelperDescriptor.Metadata)].Value();
+
+ var builder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
+
+ builder
+ .Documentation(documentation)
+ .TagOutputHint(tagOutputHint);
+
+ foreach (var tagMatchingRule in tagMatchingRules)
+ {
+ var rule = tagMatchingRule.Value();
+ builder.TagMatchingRule(b => ReadTagMatchingRule(b, rule, serializer));
+ }
+
+ foreach (var boundAttribute in boundAttributes)
+ {
+ var attribute = boundAttribute.Value();
+ builder.BindAttribute(b => ReadBoundAttribute(b, attribute, serializer));
+ }
+
+ foreach (var childTag in childTags)
+ {
+ var tagValue = childTag.Value();
+ builder.AllowChildTag(tagValue);
+ }
+
+ foreach (var diagnostic in diagnostics)
+ {
+ var diagnosticReader = diagnostic.CreateReader();
+ var diagnosticObject = serializer.Deserialize(diagnosticReader);
+ builder.AddDiagnostic(diagnosticObject);
+ }
+
+ var metadataReader = metadata.CreateReader();
+ var metadataValue = serializer.Deserialize>(metadataReader);
+ foreach (var item in metadataValue)
+ {
+ builder.AddMetadata(item.Key, item.Value);
+ }
+
+ return builder.Build();
+ }
+
+ public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
+ {
+ // We should never get here because CanWrite returns false.
+ // We want the default serializer to handle TagHelperDescriptor serialization.
+ throw new NotImplementedException();
+ }
+
+ private void ReadTagMatchingRule(TagMatchingRuleBuilder builder, JObject rule, JsonSerializer serializer)
+ {
+ var tagName = rule[nameof(TagMatchingRule.TagName)].Value();
+ var attributes = rule[nameof(TagMatchingRule.Attributes)].Value();
+ var parentTag = rule[nameof(TagMatchingRule.ParentTag)].Value();
+ var tagStructure = rule[nameof(TagMatchingRule.TagStructure)].Value();
+ var diagnostics = rule[nameof(TagMatchingRule.Diagnostics)].Value();
+
+ builder
+ .RequireTagName(tagName)
+ .RequireParentTag(parentTag)
+ .RequireTagStructure((TagStructure)tagStructure);
+
+ foreach (var attribute in attributes)
+ {
+ var attibuteValue = attribute.Value();
+ builder.RequireAttribute(b => ReadRequiredAttribute(b, attibuteValue, serializer));
+ }
+
+ foreach (var diagnostic in diagnostics)
+ {
+ var diagnosticReader = diagnostic.CreateReader();
+ var diagnosticObject = serializer.Deserialize(diagnosticReader);
+ builder.AddDiagnostic(diagnosticObject);
+ }
+ }
+
+ private void ReadRequiredAttribute(RequiredAttributeDescriptorBuilder builder, JObject attribute, JsonSerializer serializer)
+ {
+ var name = attribute[nameof(RequiredAttributeDescriptor.Name)].Value();
+ var nameComparison = attribute[nameof(RequiredAttributeDescriptor.NameComparison)].Value();
+ var value = attribute[nameof(RequiredAttributeDescriptor.Value)].Value();
+ var valueComparison = attribute[nameof(RequiredAttributeDescriptor.ValueComparison)].Value();
+ var diagnostics = attribute[nameof(RequiredAttributeDescriptor.Diagnostics)].Value();
+
+ builder
+ .Name(name)
+ .NameComparisonMode((RequiredAttributeDescriptor.NameComparisonMode)nameComparison)
+ .Value(value)
+ .ValueComparisonMode((RequiredAttributeDescriptor.ValueComparisonMode)valueComparison);
+
+ foreach (var diagnostic in diagnostics)
+ {
+ var diagnosticReader = diagnostic.CreateReader();
+ var diagnosticObject = serializer.Deserialize(diagnosticReader);
+ builder.AddDiagnostic(diagnosticObject);
+ }
+ }
+
+ private void ReadBoundAttribute(ITagHelperBoundAttributeDescriptorBuilder builder, JObject attribute, JsonSerializer serializer)
+ {
+ var descriptorKind = attribute[nameof(BoundAttributeDescriptor.Kind)].Value();
+ if (descriptorKind != ITagHelperBoundAttributeDescriptorBuilder.DescriptorKind)
+ {
+ throw new NotSupportedException();
+ }
+
+ var name = attribute[nameof(BoundAttributeDescriptor.Name)].Value();
+ var typeName = attribute[nameof(BoundAttributeDescriptor.TypeName)].Value();
+ var isEnum = attribute[nameof(BoundAttributeDescriptor.IsEnum)].Value();
+ var indexerNamePrefix = attribute[nameof(BoundAttributeDescriptor.IndexerNamePrefix)].Value();
+ var indexerTypeName = attribute[nameof(BoundAttributeDescriptor.IndexerTypeName)].Value();
+ var documentation = attribute[nameof(BoundAttributeDescriptor.Documentation)].Value();
+ var diagnostics = attribute[nameof(BoundAttributeDescriptor.Diagnostics)].Value();
+ var metadata = attribute[nameof(BoundAttributeDescriptor.Metadata)].Value();
+
+ builder
+ .Name(name)
+ .TypeName(typeName)
+ .Documentation(documentation);
+
+ if (indexerNamePrefix != null)
+ {
+ builder.AsDictionary(indexerNamePrefix, indexerTypeName);
+ }
+
+ if (isEnum)
+ {
+ builder.AsEnum();
+ }
+
+ foreach (var diagnostic in diagnostics)
+ {
+ var diagnosticReader = diagnostic.CreateReader();
+ var diagnosticObject = serializer.Deserialize(diagnosticReader);
+ builder.AddDiagnostic(diagnosticObject);
+ }
+
+ var metadataReader = metadata.CreateReader();
+ var metadataValue = serializer.Deserialize>(metadataReader);
+ foreach (var item in metadataValue)
+ {
+ builder.AddMetadata(item.Key, item.Value);
+ }
+
+ var propertyName = metadataValue[ITagHelperBoundAttributeDescriptorBuilder.PropertyNameKey];
+ builder.PropertyName(propertyName);
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultDirectiveIRPassTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultDirectiveIRPassTest.cs
index 2469b7754d..56a1101022 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultDirectiveIRPassTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultDirectiveIRPassTest.cs
@@ -155,7 +155,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
{
var phase = engine.Phases[i];
phase.Execute(codeDocument);
-
+
if (phase is IRazorDocumentClassifierPhase)
{
break;
@@ -168,4 +168,4 @@ namespace Microsoft.AspNetCore.Razor.Evolution
return irDocument;
}
}
-}
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultRazorIRLoweringPhaseIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultRazorIRLoweringPhaseIntegrationTest.cs
index c6f2d70d60..9d6fff7a3e 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultRazorIRLoweringPhaseIntegrationTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultRazorIRLoweringPhaseIntegrationTest.cs
@@ -174,12 +174,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution
");
var tagHelpers = new[]
{
- new TagHelperDescriptor
- {
- TagName = "span",
- TypeName = "SpanTagHelper",
- AssemblyName = "TestAssembly",
- }
+ CreateTagHelperDescriptor(
+ tagName: "span",
+ typeName: "SpanTagHelper",
+ assemblyName: "TestAssembly")
};
// Act
@@ -221,12 +219,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution
");
var tagHelpers = new[]
{
- new TagHelperDescriptor
- {
- TagName = "span",
- TypeName = "SpanTagHelper",
- AssemblyName = "TestAssembly",
- }
+ CreateTagHelperDescriptor(
+ tagName: "span",
+ typeName: "SpanTagHelper",
+ assemblyName: "TestAssembly")
};
// Act
@@ -273,12 +269,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution
}");
var tagHelpers = new[]
{
- new TagHelperDescriptor
- {
- TagName = "span",
- TypeName = "SpanTagHelper",
- AssemblyName = "TestAssembly",
- }
+ CreateTagHelperDescriptor(
+ tagName: "span",
+ typeName: "SpanTagHelper",
+ assemblyName: "TestAssembly")
};
// Act
@@ -299,7 +293,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
n,
c1 => DirectiveToken(DirectiveTokenKind.Member, "test", c1),
c1 => Html(Environment.NewLine, c1),
- c1 =>
+ c1 =>
{
var tagHelperNode = Assert.IsType(c1);
Children(
@@ -324,24 +318,23 @@ namespace Microsoft.AspNetCore.Razor.Evolution
// Arrange
var codeDocument = TestRazorCodeDocument.Create(@"@addTagHelper *, TestAssembly
");
- var descriptor = new TagHelperDescriptor
+ var tagHelpers = new[]
{
- TagName = "input",
- TypeName = "InputTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new[]
- {
- new TagHelperAttributeDescriptor
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "InputTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- Name = "bound",
- PropertyName = "FooProp",
- TypeName = "System.String"
- }
- }
+ builder => builder
+ .Name("bound")
+ .PropertyName("FooProp")
+ .TypeName("System.String"),
+ })
};
// Act
- var irDocument = Lower(codeDocument, tagHelpers: new[] { descriptor });
+ var irDocument = Lower(codeDocument, tagHelpers: tagHelpers);
// Assert
Children(
@@ -484,5 +477,28 @@ namespace Microsoft.AspNetCore.Razor.Evolution
return irDocument;
}
+
+ private static TagHelperDescriptor CreateTagHelperDescriptor(
+ string tagName,
+ string typeName,
+ string assemblyName,
+ IEnumerable> attributes = null)
+ {
+ var builder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
+
+ if (attributes != null)
+ {
+ foreach (var attributeBuilder in attributes)
+ {
+ builder.BindAttribute(attributeBuilder);
+ }
+ }
+
+ builder.TagMatchingRule(ruleBuilder => ruleBuilder.RequireTagName(tagName));
+
+ var descriptor = builder.Build();
+
+ return descriptor;
+ }
}
}
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/CodeGenerationIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/CodeGenerationIntegrationTest.cs
index d1f13cf38b..9604e3a959 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/CodeGenerationIntegrationTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/CodeGenerationIntegrationTest.cs
@@ -666,7 +666,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
public void BasicTagHelpers_Prefixed_Runtime()
{
// Arrange, Act & Assert
- RunRuntimeTagHelpersTest(TestTagHelperDescriptors.PrefixedPAndInputTagHelperDescriptors);
+ RunRuntimeTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors, tagHelperPrefix: "THS");
}
[Fact]
@@ -690,13 +690,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
RunRuntimeTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors);
}
- [Fact]
- public void DuplicateTargetTagHelper_Runtime()
- {
- // Arrange, Act & Assert
- RunRuntimeTagHelpersTest(TestTagHelperDescriptors.DuplicateTargetTagHelperDescriptors);
- }
-
[Fact]
public void EmptyAttributeTagHelpers_Runtime()
{
@@ -1480,7 +1473,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
public void BasicTagHelpers_Prefixed_DesignTime()
{
// Arrange, Act & Assert
- RunDesignTimeTagHelpersTest(TestTagHelperDescriptors.PrefixedPAndInputTagHelperDescriptors);
+ RunDesignTimeTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors, tagHelperPrefix: "THS");
}
[Fact]
@@ -1490,13 +1483,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
RunDesignTimeTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors);
}
- [Fact]
- public void DuplicateTargetTagHelper_DesignTime()
- {
- // Arrange, Act & Assert
- RunDesignTimeTagHelpersTest(TestTagHelperDescriptors.DuplicateTargetTagHelperDescriptors);
- }
-
[Fact]
public void EmptyAttributeTagHelpers_DesignTime()
{
@@ -1616,7 +1602,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
return codeDocument;
}
- private void RunRuntimeTagHelpersTest(IEnumerable descriptors)
+ private void RunRuntimeTagHelpersTest(IEnumerable descriptors, string tagHelperPrefix = null)
{
// Arrange
var engine = RazorEngine.Create(
@@ -1626,6 +1612,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
builder.AddTagHelpers(descriptors);
});
var document = CreateCodeDocument();
+ if (tagHelperPrefix != null)
+ {
+ document.SetTagHelperPrefix(tagHelperPrefix);
+ }
// Act
engine.Process(document);
@@ -1635,7 +1625,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
AssertCSharpDocumentMatchesBaseline(document.GetCSharpDocument());
}
- private void RunDesignTimeTagHelpersTest(IEnumerable descriptors)
+ private void RunDesignTimeTagHelpersTest(IEnumerable descriptors, string tagHelperPrefix = null)
{
// Arrange
var engine = RazorEngine.CreateDesignTime(
@@ -1645,6 +1635,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
builder.AddTagHelpers(descriptors);
});
var document = CreateCodeDocument();
+ if (tagHelperPrefix != null)
+ {
+ document.SetTagHelperPrefix(tagHelperPrefix);
+ }
// Act
engine.Process(document);
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/InstrumentationPassIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/InstrumentationPassIntegrationTest.cs
index ef96bead23..1b3510d521 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/InstrumentationPassIntegrationTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/InstrumentationPassIntegrationTest.cs
@@ -1,6 +1,8 @@
// 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 Xunit;
namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
@@ -13,39 +15,29 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
// Arrange
var descriptors = new[]
{
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper",
- AssemblyName = "TestAssembly",
- },
- new TagHelperDescriptor
- {
- TagName = "form",
- TypeName = "FormTagHelper",
- AssemblyName = "TestAssembly",
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new[]
+ CreateTagHelperDescriptor(
+ tagName: "p",
+ typeName: "PTagHelper",
+ assemblyName: "TestAssembly"),
+ CreateTagHelperDescriptor(
+ tagName: "form",
+ typeName: "FormTagHelper",
+ assemblyName: "TestAssembly"),
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "InputTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- new TagHelperAttributeDescriptor
- {
- Name = "value",
- PropertyName = "FooProp",
- TypeName = "System.String" // Gets preallocated
- },
- new TagHelperAttributeDescriptor
- {
- Name = "date",
- PropertyName = "BarProp",
- TypeName = "System.DateTime" // Doesn't get preallocated
- }
- }
- }
+ builder => builder
+ .Name("value")
+ .PropertyName("FooProp")
+ .TypeName("System.String"), // Gets preallocated
+ builder => builder
+ .Name("date")
+ .PropertyName("BarProp")
+ .TypeName("System.DateTime"), // Doesn't get preallocated
+ })
};
var engine = RazorEngine.Create(b =>
@@ -53,7 +45,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
b.AddTagHelpers(descriptors);
b.Features.Add(new DefaultInstrumentationPass());
});
-
+
var document = CreateCodeDocument();
// Act
@@ -66,5 +58,28 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
AssertCSharpDocumentMatchesBaseline(csharpDocument);
Assert.Empty(csharpDocument.Diagnostics);
}
+
+ private static TagHelperDescriptor CreateTagHelperDescriptor(
+ string tagName,
+ string typeName,
+ string assemblyName,
+ IEnumerable> attributes = null)
+ {
+ var builder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
+
+ if (attributes != null)
+ {
+ foreach (var attributeBuilder in attributes)
+ {
+ builder.BindAttribute(attributeBuilder);
+ }
+ }
+
+ builder.TagMatchingRule(ruleBuilder => ruleBuilder.RequireTagName(tagName));
+
+ var descriptor = builder.Build();
+
+ return descriptor;
+ }
}
}
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/TagHelpersIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/TagHelpersIntegrationTest.cs
index b3292094fd..27b0934228 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/TagHelpersIntegrationTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/TagHelpersIntegrationTest.cs
@@ -1,6 +1,8 @@
// 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 Xunit;
namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
@@ -13,12 +15,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
// Arrange
var descriptors = new[]
{
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper",
- AssemblyName = "TestAssembly",
- }
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "InputTagHelper",
+ assemblyName: "TestAssembly")
};
var engine = RazorEngine.Create(builder => builder.AddTagHelpers(descriptors));
@@ -37,18 +37,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
// Arrange
var descriptors = new[]
{
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new[] { new TagHelperAttributeDescriptor
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "InputTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- Name = "bound",
- PropertyName = "FooProp",
- TypeName = "System.String"
- } }
- }
+ builder => builder
+ .Name("bound")
+ .PropertyName("FooProp")
+ .TypeName("System.String"),
+ })
};
var engine = RazorEngine.Create(builder => builder.AddTagHelpers(descriptors));
@@ -67,30 +66,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
// Arrange
var descriptors = new[]
{
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper",
- AssemblyName = "TestAssembly",
- },
- new TagHelperDescriptor
- {
- TagName = "form",
- TypeName = "FormTagHelper",
- AssemblyName = "TestAssembly",
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new[] { new TagHelperAttributeDescriptor
+ CreateTagHelperDescriptor(
+ tagName: "p",
+ typeName: "PTagHelper",
+ assemblyName: "TestAssembly"),
+ CreateTagHelperDescriptor(
+ tagName: "form",
+ typeName: "FormTagHelper",
+ assemblyName: "TestAssembly"),
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "InputTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- Name = "value",
- PropertyName = "FooProp",
- TypeName = "System.String"
- } }
- }
+ builder => builder
+ .Name("value")
+ .PropertyName("FooProp")
+ .TypeName("System.String"),
+ })
};
var engine = RazorEngine.Create(builder => builder.AddTagHelpers(descriptors));
@@ -102,5 +96,28 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
// Assert
AssertIRMatchesBaseline(document.GetIRDocument());
}
+
+ private static TagHelperDescriptor CreateTagHelperDescriptor(
+ string tagName,
+ string typeName,
+ string assemblyName,
+ IEnumerable> attributes = null)
+ {
+ var builder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
+
+ if (attributes != null)
+ {
+ foreach (var attributeBuilder in attributes)
+ {
+ builder.BindAttribute(attributeBuilder);
+ }
+ }
+
+ builder.TagMatchingRule(ruleBuilder => ruleBuilder.RequireTagName(tagName));
+
+ var descriptor = builder.Build();
+
+ return descriptor;
+ }
}
}
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/TestTagHelperDescriptors.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/TestTagHelperDescriptors.cs
index d4b8d017c9..a849f094b7 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/TestTagHelperDescriptors.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/TestTagHelperDescriptors.cs
@@ -9,50 +9,35 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
{
public class TestTagHelperDescriptors
{
- internal static IEnumerable DefaultPAndInputTagHelperDescriptors { get; }
- = BuildPAndInputTagHelperDescriptors(prefix: string.Empty);
- internal static IEnumerable PrefixedPAndInputTagHelperDescriptors { get; }
- = BuildPAndInputTagHelperDescriptors(prefix: "THS");
-
internal static IEnumerable SimpleTagHelperDescriptors
{
get
{
return new[]
{
- new TagHelperDescriptor
- {
- TagName = "span",
- TypeName = "SpanTagHelper",
- AssemblyName = "TestAssembly",
- },
- new TagHelperDescriptor
- {
- TagName = "div",
- TypeName = "DivTagHelper",
- AssemblyName = "TestAssembly",
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new[]
+ CreateTagHelperDescriptor(
+ tagName: "span",
+ typeName: "SpanTagHelper",
+ assemblyName: "TestAssembly"),
+ CreateTagHelperDescriptor(
+ tagName: "div",
+ typeName: "DivTagHelper",
+ assemblyName: "TestAssembly"),
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "InputTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- new TagHelperAttributeDescriptor
- {
- Name = "value",
- PropertyName = "FooProp",
- TypeName = "System.String"
- },
- new TagHelperAttributeDescriptor
- {
- Name = "bound",
- PropertyName = "BoundProp",
- TypeName = "System.String"
- }
- }
- }
+ builder => builder
+ .Name("value")
+ .PropertyName("FooProp")
+ .TypeName("System.String"),
+ builder => builder
+ .Name("bound")
+ .PropertyName("BoundProp")
+ .TypeName("System.String"),
+ })
};
}
}
@@ -66,113 +51,93 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
return new[]
{
- new TagHelperDescriptor
- {
- TagName = "a",
- TypeName = "TestNamespace.ATagHelper",
- AssemblyName = "TestAssembly",
- RequiredAttributes = new[]
+ CreateTagHelperDescriptor(
+ tagName: "a",
+ typeName: "TestNamespace.ATagHelper",
+ assemblyName: "TestAssembly",
+ ruleBuilders: new Action[]
{
- new TagHelperRequiredAttributeDescriptor
- {
- Name = "href",
- NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
- Value = "~/",
- ValueComparison = TagHelperRequiredAttributeValueComparison.FullMatch,
- }
- },
- },
- new TagHelperDescriptor
- {
- TagName = "a",
- TypeName = "TestNamespace.ATagHelperMultipleSelectors",
- AssemblyName = "TestAssembly",
- RequiredAttributes = new[]
+ builder => builder
+ .RequireAttribute(attribute => attribute
+ .Name("href")
+ .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
+ .Value("~/")
+ .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch)),
+ }),
+ CreateTagHelperDescriptor(
+ tagName: "a",
+ typeName: "TestNamespace.ATagHelperMultipleSelectors",
+ assemblyName: "TestAssembly",
+ ruleBuilders: new Action[]
{
- new TagHelperRequiredAttributeDescriptor
- {
- Name = "href",
- NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
- Value = "~/",
- ValueComparison = TagHelperRequiredAttributeValueComparison.PrefixMatch,
- },
- new TagHelperRequiredAttributeDescriptor
- {
- Name = "href",
- NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
- Value = "?hello=world",
- ValueComparison = TagHelperRequiredAttributeValueComparison.SuffixMatch,
- }
- },
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "TestNamespace.InputTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new TagHelperAttributeDescriptor[]
+ builder => builder
+ .RequireAttribute(attribute => attribute
+ .Name("href")
+ .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
+ .Value("~/")
+ .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch))
+ .RequireAttribute(attribute => attribute
+ .Name("href")
+ .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
+ .Value("?hello=world")
+ .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch)),
+ }),
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "TestNamespace.InputTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
+ builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
},
- RequiredAttributes = new[]
+ ruleBuilders: new Action[]
{
- new TagHelperRequiredAttributeDescriptor
- {
- Name = "type",
- NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
- Value = "text",
- ValueComparison = TagHelperRequiredAttributeValueComparison.FullMatch,
- }
- },
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "TestNamespace.InputTagHelper2",
- AssemblyName = "TestAssembly",
- Attributes = new TagHelperAttributeDescriptor[]
+ builder => builder
+ .RequireAttribute(attribute => attribute
+ .Name("type")
+ .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
+ .Value("text")
+ .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch)),
+ }),
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "TestNamespace.InputTagHelper2",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
+ builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
},
- RequiredAttributes = new[]
+ ruleBuilders: new Action[]
{
- new TagHelperRequiredAttributeDescriptor
- {
- Name = "ty",
- NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch,
- }
- },
- },
- new TagHelperDescriptor
- {
- TagName = "*",
- TypeName = "TestNamespace.CatchAllTagHelper",
- AssemblyName = "TestAssembly",
- RequiredAttributes = new[]
+ builder => builder
+ .RequireAttribute(attribute => attribute
+ .Name("ty")
+ .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)),
+ }),
+ CreateTagHelperDescriptor(
+ tagName: "*",
+ typeName: "TestNamespace.CatchAllTagHelper",
+ assemblyName: "TestAssembly",
+ ruleBuilders: new Action[]
{
- new TagHelperRequiredAttributeDescriptor
- {
- Name = "href",
- NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
- Value = "~/",
- ValueComparison = TagHelperRequiredAttributeValueComparison.PrefixMatch,
- }
- },
- },
- new TagHelperDescriptor
- {
- TagName = "*",
- TypeName = "TestNamespace.CatchAllTagHelper2",
- AssemblyName = "TestAssembly",
- RequiredAttributes = new[]
+ builder => builder
+ .RequireAttribute(attribute => attribute
+ .Name("href")
+ .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
+ .Value("~/")
+ .ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch)),
+ }),
+ CreateTagHelperDescriptor(
+ tagName: "*",
+ typeName: "TestNamespace.CatchAllTagHelper2",
+ assemblyName: "TestAssembly",
+ ruleBuilders: new Action[]
{
- new TagHelperRequiredAttributeDescriptor
- {
- Name = "type",
- NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
- }
- },
- }
+ builder => builder
+ .RequireAttribute(attribute => attribute
+ .Name("type")
+ .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)),
+ }),
};
}
}
@@ -183,38 +148,30 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
{
return new[]
{
- new TagHelperDescriptor
- {
- TagName = "*",
- TypeName = "TestNamespace.CatchAllTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new[]
+ CreateTagHelperDescriptor(
+ tagName: "*",
+ typeName: "TestNamespace.CatchAllTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- new TagHelperAttributeDescriptor
- {
- Name = "catch-all",
- PropertyName = "CatchAll",
- IsEnum = true,
- TypeName = typeof(MyEnum).FullName
- },
- }
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "TestNamespace.InputTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new[]
+ builder => builder
+ .Name("catch-all")
+ .PropertyName("CatchAll")
+ .AsEnum()
+ .TypeName(typeof(MyEnum).FullName),
+ }),
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "TestNamespace.InputTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- new TagHelperAttributeDescriptor
- {
- Name = "value",
- PropertyName = "Value",
- IsEnum = true,
- TypeName = typeof(MyEnum).FullName
- },
- }
- },
+ builder => builder
+ .Name("value")
+ .PropertyName("Value")
+ .AsEnum()
+ .TypeName(typeof(MyEnum).FullName),
+ }),
};
}
}
@@ -225,52 +182,41 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
{
return new[]
{
- new TagHelperDescriptor
- {
- TagName = "*",
- TypeName = "TestNamespace.CatchAllTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new[]
+ CreateTagHelperDescriptor(
+ tagName: "*",
+ typeName: "TestNamespace.CatchAllTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- new TagHelperAttributeDescriptor
- {
- Name = "[item]",
- PropertyName = "ListItems",
- TypeName = typeof(List).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "[(item)]",
- PropertyName = "ArrayItems",
- TypeName = typeof(string[]).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "(click)",
- PropertyName = "Event1",
- TypeName = typeof(Action).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "(^click)",
- PropertyName = "Event2",
- TypeName = typeof(Action).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "*something",
- PropertyName = "StringProperty1",
- TypeName = typeof(string).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "#local",
- PropertyName = "StringProperty2",
- TypeName = typeof(string).FullName
- },
+ builder => builder
+ .Name("[item]")
+ .PropertyName("ListItems")
+ .TypeName(typeof(List).FullName),
+ builder => builder
+ .Name("[(item)]")
+ .PropertyName("ArrayItems")
+ .TypeName(typeof(string[]).FullName),
+ builder => builder
+ .Name("(click)")
+ .PropertyName("Event1")
+ .TypeName(typeof(Action).FullName),
+ builder => builder
+ .Name("(^click)")
+ .PropertyName("Event2")
+ .TypeName(typeof(Action).FullName),
+ builder => builder
+ .Name("*something")
+ .PropertyName("StringProperty1")
+ .TypeName(typeof(string).FullName),
+ builder => builder
+ .Name("#local")
+ .PropertyName("StringProperty2")
+ .TypeName(typeof(string).FullName),
},
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "bound" } },
- },
+ ruleBuilders: new Action[]
+ {
+ builder => builder.RequireAttribute(attribute => attribute.Name("bound")),
+ }),
};
}
}
@@ -281,54 +227,42 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
{
return new[]
{
- new TagHelperDescriptor
- {
- TagName = "*",
- TypeName = "TestNamespace.CatchAllTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new[]
+ CreateTagHelperDescriptor(
+ tagName: "*",
+ typeName: "TestNamespace.CatchAllTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- new TagHelperAttributeDescriptor
- {
- Name = "catchall-bound-string",
- PropertyName = "BoundRequiredString",
- TypeName = typeof(string).FullName,
- IsStringProperty = true
- }
+ builder => builder
+ .Name("catchall-bound-string")
+ .PropertyName("BoundRequiredString")
+ .TypeName(typeof(string).FullName),
},
- RequiredAttributes = new[]
+ ruleBuilders: new Action[]
{
- new TagHelperRequiredAttributeDescriptor { Name = "catchall-unbound-required" }
- },
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "TestNamespace.InputTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new[]
+ builder => builder.RequireAttribute(attribute => attribute.Name("catchall-unbound-required")),
+ }),
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "TestNamespace.InputTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- new TagHelperAttributeDescriptor
- {
- Name = "input-bound-required-string",
- PropertyName = "BoundRequiredString",
- TypeName = typeof(string).FullName,
- IsStringProperty = true
- },
- new TagHelperAttributeDescriptor
- {
- Name = "input-bound-string",
- PropertyName = "BoundString",
- TypeName = typeof(string).FullName,
- IsStringProperty = true
- }
+ builder => builder
+ .Name("input-bound-required-string")
+ .PropertyName("BoundRequiredString")
+ .TypeName(typeof(string).FullName),
+ builder => builder
+ .Name("input-bound-string")
+ .PropertyName("BoundString")
+ .TypeName(typeof(string).FullName),
},
- RequiredAttributes = new[]
+ ruleBuilders: new Action[]
{
- new TagHelperRequiredAttributeDescriptor { Name = "input-bound-required-string" },
- new TagHelperRequiredAttributeDescriptor { Name = "input-unbound-required" }
- },
- }
+ builder => builder
+ .RequireAttribute(attribute => attribute.Name("input-bound-required-string"))
+ .RequireAttribute(attribute => attribute.Name("input-unbound-required")),
+ }),
};
}
}
@@ -339,82 +273,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
{
return new[]
{
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "TestNamespace.InputTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new[]
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "TestNamespace.InputTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- new TagHelperAttributeDescriptor
- {
- Name = "bound",
- PropertyName = "Bound",
- TypeName = typeof(string).FullName,
- IsStringProperty = true
- }
- }
- }
- };
- }
- }
-
- internal static IEnumerable DuplicateTargetTagHelperDescriptors
- {
- get
- {
- var inputTypePropertyInfo = typeof(TestType).GetProperty("Type");
- var inputCheckedPropertyInfo = typeof(TestType).GetProperty("Checked");
- return new[]
- {
- new TagHelperDescriptor
- {
- TagName = "*",
- TypeName = "TestNamespace.CatchAllTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new TagHelperAttributeDescriptor[]
- {
- new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
- new TagHelperAttributeDescriptor("checked", inputCheckedPropertyInfo)
- },
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "type" } },
- },
- new TagHelperDescriptor
- {
- TagName = "*",
- TypeName = "TestNamespace.CatchAllTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new TagHelperAttributeDescriptor[]
- {
- new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
- new TagHelperAttributeDescriptor("checked", inputCheckedPropertyInfo)
- },
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "checked" } },
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "TestNamespace.InputTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new TagHelperAttributeDescriptor[]
- {
- new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
- new TagHelperAttributeDescriptor("checked", inputCheckedPropertyInfo)
- },
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "type" } },
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "TestNamespace.InputTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new TagHelperAttributeDescriptor[]
- {
- new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
- new TagHelperAttributeDescriptor("checked", inputCheckedPropertyInfo)
- },
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "checked" } },
- }
+ builder => builder
+ .Name("bound")
+ .PropertyName("Bound")
+ .TypeName(typeof(string).FullName)
+ }),
};
}
}
@@ -427,47 +296,49 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
var inputCheckedPropertyInfo = typeof(TestType).GetProperty("Checked");
return new[]
{
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "TestNamespace.PTagHelper",
- AssemblyName = "TestAssembly",
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "class" } },
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "TestNamespace.InputTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new TagHelperAttributeDescriptor[]
+ CreateTagHelperDescriptor(
+ tagName: "p",
+ typeName: "TestNamespace.PTagHelper",
+ assemblyName: "TestAssembly",
+ ruleBuilders: new Action[]
{
- new TagHelperAttributeDescriptor("type", inputTypePropertyInfo)
- },
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "type" } },
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "TestNamespace.InputTagHelper2",
- AssemblyName = "TestAssembly",
- Attributes = new TagHelperAttributeDescriptor[]
+ builder => builder.RequireAttribute(attribute => attribute.Name("class")),
+ }),
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "TestNamespace.InputTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
- new TagHelperAttributeDescriptor("checked", inputCheckedPropertyInfo)
+ builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
},
- RequiredAttributes = new[]
+ ruleBuilders: new Action[]
{
- new TagHelperRequiredAttributeDescriptor { Name = "type" },
- new TagHelperRequiredAttributeDescriptor { Name = "checked" }
+ builder => builder.RequireAttribute(attribute => attribute.Name("type")),
+ }),
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "TestNamespace.InputTagHelper2",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
+ {
+ builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
+ builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", inputCheckedPropertyInfo),
},
- },
- new TagHelperDescriptor
- {
- TagName = "*",
- TypeName = "TestNamespace.CatchAllTagHelper",
- AssemblyName = "TestAssembly",
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "catchAll" } },
- }
+ ruleBuilders: new Action[]
+ {
+ builder => builder
+ .RequireAttribute(attribute => attribute.Name("type"))
+ .RequireAttribute(attribute => attribute.Name("checked")),
+ }),
+ CreateTagHelperDescriptor(
+ tagName: "*",
+ typeName: "TestNamespace.CatchAllTagHelper",
+ assemblyName: "TestAssembly",
+ ruleBuilders: new Action[]
+ {
+ builder => builder.RequireAttribute(attribute => attribute.Name("catchAll")),
+ }),
};
}
}
@@ -478,91 +349,48 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
{
return new[]
{
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "TestNamespace.InputTagHelper1",
- AssemblyName = "TestAssembly",
- Attributes = new[]
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "TestNamespace.InputTagHelper1",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- new TagHelperAttributeDescriptor
- {
- Name = "int-prefix-grabber",
- PropertyName = "IntProperty",
- TypeName = typeof(int).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "int-dictionary",
- PropertyName = "IntDictionaryProperty",
- TypeName = typeof(IDictionary).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "string-dictionary",
- PropertyName = "StringDictionaryProperty",
- TypeName = "Namespace.DictionaryWithoutParameterlessConstructor"
- },
- new TagHelperAttributeDescriptor
- {
- Name = "string-prefix-grabber",
- PropertyName = "StringProperty",
- TypeName = typeof(string).FullName,
- IsStringProperty = true
- },
- new TagHelperAttributeDescriptor
- {
- Name = "int-prefix-",
- PropertyName = "IntDictionaryProperty",
- TypeName = typeof(int).FullName,
- IsIndexer = true
- },
- new TagHelperAttributeDescriptor
- {
- Name = "string-prefix-",
- PropertyName = "StringDictionaryProperty",
- TypeName = typeof(string).FullName,
- IsIndexer = true,
- IsStringProperty = true
- }
- }
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "TestNamespace.InputTagHelper2",
- AssemblyName = "TestAssembly",
- Attributes = new[]
+ builder => builder
+ .Name("int-prefix-grabber")
+ .PropertyName("IntProperty")
+ .TypeName(typeof(int).FullName),
+ builder => builder
+ .Name("int-dictionary")
+ .PropertyName("IntDictionaryProperty")
+ .TypeName(typeof(IDictionary).FullName)
+ .AsDictionary("int-prefix-", typeof(int).FullName),
+ builder => builder
+ .Name("string-prefix-grabber")
+ .PropertyName("StringProperty")
+ .TypeName(typeof(string).FullName),
+ builder => builder
+ .Name("string-dictionary")
+ .PropertyName("StringDictionaryProperty")
+ .TypeName("Namespace.DictionaryWithoutParameterlessConstructor")
+ .AsDictionary("string-prefix-", typeof(string).FullName),
+ }),
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "TestNamespace.InputTagHelper2",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- new TagHelperAttributeDescriptor
- {
- Name = "int-dictionary",
- PropertyName = "IntDictionaryProperty",
- TypeName = typeof(int).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "string-dictionary",
- PropertyName = "StringDictionaryProperty",
- TypeName = "Namespace.DictionaryWithoutParameterlessConstructor"
- },
- new TagHelperAttributeDescriptor
- {
- Name = "int-prefix-",
- PropertyName = "IntDictionaryProperty",
- TypeName = typeof(int).FullName,
- IsIndexer = true
- },
- new TagHelperAttributeDescriptor
- {
- Name = "string-prefix-",
- PropertyName = "StringDictionaryProperty",
- TypeName = typeof(string).FullName,
- IsIndexer = true,
- IsStringProperty = true
- }
- }
- }
+ builder => builder
+ .Name("int-dictionary")
+ .PropertyName("IntDictionaryProperty")
+ .TypeName(typeof(int).FullName)
+ .AsDictionary("int-prefix-", typeof(int).FullName),
+ builder => builder
+ .Name("string-dictionary")
+ .PropertyName("StringDictionaryProperty")
+ .TypeName("Namespace.DictionaryWithoutParameterlessConstructor")
+ .AsDictionary("string-prefix-", typeof(string).FullName),
+ }),
};
}
}
@@ -574,71 +402,120 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
var propertyInfo = typeof(TestType).GetProperty("BoundProperty");
return new[]
{
- new TagHelperDescriptor
- {
- TagName = "MyTagHelper",
- TypeName = "TestNamespace.MyTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new []
+ CreateTagHelperDescriptor(
+ tagName: "MyTagHelper",
+ typeName: "TestNamespace.MyTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
{
- new TagHelperAttributeDescriptor("BoundProperty", propertyInfo)
- }
- },
- new TagHelperDescriptor
- {
- TagName = "NestedTagHelper",
- TypeName = "TestNamespace.NestedTagHelper",
- AssemblyName = "TestAssembly"
- }
+ builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "BoundProperty", propertyInfo),
+ }),
+ CreateTagHelperDescriptor(
+ tagName: "NestedTagHelper",
+ typeName: "TestNamespace.NestedTagHelper",
+ assemblyName: "TestAssembly"),
};
}
}
- private static IEnumerable BuildPAndInputTagHelperDescriptors(string prefix)
+ internal static IEnumerable DefaultPAndInputTagHelperDescriptors
{
- var pAgePropertyInfo = typeof(TestType).GetProperty("Age");
- var inputTypePropertyInfo = typeof(TestType).GetProperty("Type");
- var checkedPropertyInfo = typeof(TestType).GetProperty("Checked");
-
- return new[]
+ get
{
- new TagHelperDescriptor
+ var pAgePropertyInfo = typeof(TestType).GetProperty("Age");
+ var inputTypePropertyInfo = typeof(TestType).GetProperty("Type");
+ var checkedPropertyInfo = typeof(TestType).GetProperty("Checked");
+
+ return new[]
{
- Prefix = prefix,
- TagName = "p",
- TypeName = "TestNamespace.PTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new TagHelperAttributeDescriptor[]
- {
- new TagHelperAttributeDescriptor("age", pAgePropertyInfo)
- },
- TagStructure = TagStructure.NormalOrSelfClosing
- },
- new TagHelperDescriptor
+ CreateTagHelperDescriptor(
+ tagName: "p",
+ typeName: "TestNamespace.PTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
+ {
+ builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "age", pAgePropertyInfo),
+ },
+ ruleBuilders: new Action[]
+ {
+ builder => builder.RequireTagStructure(TagStructure.NormalOrSelfClosing)
+ }),
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "TestNamespace.InputTagHelper",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
+ {
+ builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
+ },
+ ruleBuilders: new Action[]
+ {
+ builder => builder.RequireTagStructure(TagStructure.WithoutEndTag)
+ }),
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: "TestNamespace.InputTagHelper2",
+ assemblyName: "TestAssembly",
+ attributes: new Action[]
+ {
+ builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
+ builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", checkedPropertyInfo),
+ }),
+ };
+ }
+ }
+
+ private static TagHelperDescriptor CreateTagHelperDescriptor(
+ string tagName,
+ string typeName,
+ string assemblyName,
+ IEnumerable> attributes = null,
+ IEnumerable> ruleBuilders = null)
+ {
+ var builder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
+
+ if (attributes != null)
+ {
+ foreach (var attributeBuilder in attributes)
{
- Prefix = prefix,
- TagName = "input",
- TypeName = "TestNamespace.InputTagHelper",
- AssemblyName = "TestAssembly",
- Attributes = new TagHelperAttributeDescriptor[]
- {
- new TagHelperAttributeDescriptor("type", inputTypePropertyInfo)
- },
- TagStructure = TagStructure.WithoutEndTag
- },
- new TagHelperDescriptor
- {
- Prefix = prefix,
- TagName = "input",
- TypeName = "TestNamespace.InputTagHelper2",
- AssemblyName = "TestAssembly",
- Attributes = new TagHelperAttributeDescriptor[]
- {
- new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
- new TagHelperAttributeDescriptor("checked", checkedPropertyInfo)
- },
+ builder.BindAttribute(attributeBuilder);
}
- };
+ }
+
+ if (ruleBuilders != null)
+ {
+ foreach (var ruleBuilder in ruleBuilders)
+ {
+ builder.TagMatchingRule(innerRuleBuilder => {
+ innerRuleBuilder.RequireTagName(tagName);
+ ruleBuilder(innerRuleBuilder);
+ });
+ }
+ }
+ else
+ {
+ builder.TagMatchingRule(ruleBuilder => ruleBuilder.RequireTagName(tagName));
+ }
+
+ var descriptor = builder.Build();
+
+ return descriptor;
+ }
+
+ private static void BuildBoundAttributeDescriptorFromPropertyInfo(
+ ITagHelperBoundAttributeDescriptorBuilder builder,
+ string name,
+ PropertyInfo propertyInfo)
+ {
+ builder
+ .Name(name)
+ .PropertyName(propertyInfo.Name)
+ .TypeName(propertyInfo.PropertyType.FullName);
+
+ if (propertyInfo.PropertyType.GetTypeInfo().IsEnum)
+ {
+ builder.AsEnum();
+ }
}
private class TestType
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CaseSensitiveTagHelperDescriptorComparer.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CaseSensitiveTagHelperDescriptorComparer.cs
deleted file mode 100644
index 263ff352b5..0000000000
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CaseSensitiveTagHelperDescriptorComparer.cs
+++ /dev/null
@@ -1,95 +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.Linq;
-using Microsoft.Extensions.Internal;
-using Xunit;
-
-namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
-{
- internal class CaseSensitiveTagHelperDescriptorComparer : TagHelperDescriptorComparer
- {
- public new static readonly CaseSensitiveTagHelperDescriptorComparer Default =
- new CaseSensitiveTagHelperDescriptorComparer();
-
- private CaseSensitiveTagHelperDescriptorComparer()
- : base()
- {
- }
-
- public override bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
- {
- if (descriptorX == descriptorY)
- {
- return true;
- }
-
- Assert.True(base.Equals(descriptorX, descriptorY));
-
- // Normal comparer doesn't care about the case, required attribute order, allowed children order,
- // attributes or prefixes. In tests we do.
- Assert.Equal(descriptorX.TagName, descriptorY.TagName, StringComparer.Ordinal);
- Assert.Equal(descriptorX.Prefix, descriptorY.Prefix, StringComparer.Ordinal);
- Assert.Equal(
- descriptorX.RequiredAttributes,
- descriptorY.RequiredAttributes,
- CaseSensitiveTagHelperRequiredAttributeDescriptorComparer.Default);
- Assert.Equal(descriptorX.RequiredParent, descriptorY.RequiredParent, StringComparer.Ordinal);
-
- if (descriptorX.AllowedChildren != descriptorY.AllowedChildren)
- {
- Assert.Equal(descriptorX.AllowedChildren, descriptorY.AllowedChildren, StringComparer.Ordinal);
- }
-
- Assert.Equal(
- descriptorX.Attributes,
- descriptorY.Attributes,
- TagHelperAttributeDescriptorComparer.Default);
- Assert.Equal(
- descriptorX.DesignTimeDescriptor,
- descriptorY.DesignTimeDescriptor,
- TagHelperDesignTimeDescriptorComparer.Default);
-
- return true;
- }
-
- public override int GetHashCode(TagHelperDescriptor descriptor)
- {
- var hashCodeCombiner = HashCodeCombiner.Start();
- hashCodeCombiner.Add(base.GetHashCode(descriptor));
- hashCodeCombiner.Add(descriptor.TagName, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.Prefix, StringComparer.Ordinal);
-
- if (descriptor.DesignTimeDescriptor != null)
- {
- hashCodeCombiner.Add(
- TagHelperDesignTimeDescriptorComparer.Default.GetHashCode(descriptor.DesignTimeDescriptor));
- }
-
- foreach (var requiredAttribute in descriptor.RequiredAttributes.OrderBy(attribute => attribute.Name))
- {
- hashCodeCombiner.Add(
- CaseSensitiveTagHelperRequiredAttributeDescriptorComparer.Default.GetHashCode(requiredAttribute));
- }
-
- if (descriptor.AllowedChildren != null)
- {
- foreach (var child in descriptor.AllowedChildren.OrderBy(child => child))
- {
- hashCodeCombiner.Add(child, StringComparer.Ordinal);
- }
- }
-
- var orderedAttributeHashCodes = descriptor.Attributes
- .Select(attribute => TagHelperAttributeDescriptorComparer.Default.GetHashCode(attribute))
- .OrderBy(hashcode => hashcode);
- foreach (var attributeHashCode in orderedAttributeHashCodes)
- {
- hashCodeCombiner.Add(attributeHashCode);
- }
-
- return hashCodeCombiner.CombinedHash;
- }
- }
-}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CaseSensitiveTagHelperRequiredAttributeDescriptorComparer.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CaseSensitiveTagHelperRequiredAttributeDescriptorComparer.cs
deleted file mode 100644
index 6fed1a2944..0000000000
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CaseSensitiveTagHelperRequiredAttributeDescriptorComparer.cs
+++ /dev/null
@@ -1,42 +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 Microsoft.Extensions.Internal;
-using Xunit;
-
-namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
-{
- internal class CaseSensitiveTagHelperRequiredAttributeDescriptorComparer : TagHelperRequiredAttributeDescriptorComparer
- {
- public new static readonly CaseSensitiveTagHelperRequiredAttributeDescriptorComparer Default =
- new CaseSensitiveTagHelperRequiredAttributeDescriptorComparer();
-
- private CaseSensitiveTagHelperRequiredAttributeDescriptorComparer()
- : base()
- {
- }
-
- public override bool Equals(TagHelperRequiredAttributeDescriptor descriptorX, TagHelperRequiredAttributeDescriptor descriptorY)
- {
- if (descriptorX == descriptorY)
- {
- return true;
- }
-
- Assert.True(base.Equals(descriptorX, descriptorY));
- Assert.Equal(descriptorX.Name, descriptorY.Name, StringComparer.Ordinal);
-
- return true;
- }
-
- public override int GetHashCode(TagHelperRequiredAttributeDescriptor descriptor)
- {
- var hashCodeCombiner = HashCodeCombiner.Start();
- hashCodeCombiner.Add(base.GetHashCode(descriptor));
- hashCodeCombiner.Add(descriptor.Name, StringComparer.Ordinal);
-
- return hashCodeCombiner.CombinedHash;
- }
- }
-}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/RazorEditorParserTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/RazorEditorParserTest.cs
index ade7ce6e82..61837b4f9a 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/RazorEditorParserTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/RazorEditorParserTest.cs
@@ -86,11 +86,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
// Arrange
var descriptors = new[]
{
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper"
- },
+ ITagHelperDescriptorBuilder.Create("PTagHelper", "TestAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("p"))
+ .Build()
};
var parser = new RazorEditorParser(CreateTemplateEngine(@"C:\This\Is\A\Test\Path"), @"C:\This\Is\A\Test\Path");
@@ -233,27 +231,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
// Arrange
var descriptors = new[]
{
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper",
- AssemblyName = "Test",
- Attributes = new[]
- {
- new TagHelperAttributeDescriptor
- {
- Name = "obj-attr",
- TypeName = typeof(object).FullName,
- PropertyName = "ObjectAttribute",
- },
- new TagHelperAttributeDescriptor
- {
- Name = "str-attr",
- TypeName = typeof(string).FullName,
- PropertyName = "StringAttribute",
- },
- }
- },
+ ITagHelperDescriptorBuilder.Create("PTagHelper", "Test")
+ .TagMatchingRule(rule => rule.RequireTagName("p"))
+ .BindAttribute(attribute => attribute
+ .Name("obj-attr")
+ .TypeName(typeof(object).FullName)
+ .PropertyName("ObjectAttribute"))
+ .BindAttribute(attribute => attribute
+ .Name("str-attr")
+ .TypeName(typeof(string).FullName)
+ .PropertyName("StringAttribute"))
+ .Build()
};
var parser = new RazorEditorParser(CreateTemplateEngine(@"C:\This\Is\A\Test\Path", descriptors), @"C:\This\Is\A\Test\Path");
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperAttributeDescriptorComparer.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperAttributeDescriptorComparer.cs
deleted file mode 100644
index c50eb84dd1..0000000000
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperAttributeDescriptorComparer.cs
+++ /dev/null
@@ -1,56 +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.Collections.Generic;
-using Microsoft.Extensions.Internal;
-using Xunit;
-
-namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
-{
- internal class TagHelperAttributeDescriptorComparer : IEqualityComparer
- {
- public static readonly TagHelperAttributeDescriptorComparer Default =
- new TagHelperAttributeDescriptorComparer();
-
- private TagHelperAttributeDescriptorComparer()
- {
- }
-
- public bool Equals(TagHelperAttributeDescriptor descriptorX, TagHelperAttributeDescriptor descriptorY)
- {
- if (descriptorX == descriptorY)
- {
- return true;
- }
-
- Assert.NotNull(descriptorX);
- Assert.NotNull(descriptorY);
- Assert.Equal(descriptorX.IsIndexer, descriptorY.IsIndexer);
- Assert.Equal(descriptorX.Name, descriptorY.Name, StringComparer.Ordinal);
- Assert.Equal(descriptorX.PropertyName, descriptorY.PropertyName, StringComparer.Ordinal);
- Assert.Equal(descriptorX.TypeName, descriptorY.TypeName, StringComparer.Ordinal);
- Assert.Equal(descriptorX.IsEnum, descriptorY.IsEnum);
- Assert.Equal(descriptorX.IsStringProperty, descriptorY.IsStringProperty);
-
- return TagHelperAttributeDesignTimeDescriptorComparer.Default.Equals(
- descriptorX.DesignTimeDescriptor,
- descriptorY.DesignTimeDescriptor);
- }
-
- public int GetHashCode(TagHelperAttributeDescriptor descriptor)
- {
- var hashCodeCombiner = HashCodeCombiner.Start();
- hashCodeCombiner.Add(descriptor.IsIndexer);
- hashCodeCombiner.Add(descriptor.Name, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.PropertyName, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.TypeName, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.IsEnum);
- hashCodeCombiner.Add(descriptor.IsStringProperty);
- hashCodeCombiner.Add(TagHelperAttributeDesignTimeDescriptorComparer.Default.GetHashCode(
- descriptor.DesignTimeDescriptor));
-
- return hashCodeCombiner;
- }
- }
-}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperAttributeDesignTimeDescriptorComparer.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperAttributeDesignTimeDescriptorComparer.cs
deleted file mode 100644
index c54668476c..0000000000
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperAttributeDesignTimeDescriptorComparer.cs
+++ /dev/null
@@ -1,47 +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.Collections.Generic;
-using Microsoft.Extensions.Internal;
-using Xunit;
-
-namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
-{
- internal class TagHelperAttributeDesignTimeDescriptorComparer :
- IEqualityComparer
- {
- public static readonly TagHelperAttributeDesignTimeDescriptorComparer Default =
- new TagHelperAttributeDesignTimeDescriptorComparer();
-
- private TagHelperAttributeDesignTimeDescriptorComparer()
- {
- }
-
- public bool Equals(
- TagHelperAttributeDesignTimeDescriptor descriptorX,
- TagHelperAttributeDesignTimeDescriptor descriptorY)
- {
- if (descriptorX == descriptorY)
- {
- return true;
- }
-
- Assert.NotNull(descriptorX);
- Assert.NotNull(descriptorY);
- Assert.Equal(descriptorX.Summary, descriptorY.Summary, StringComparer.Ordinal);
- Assert.Equal(descriptorX.Remarks, descriptorY.Remarks, StringComparer.Ordinal);
-
- return true;
- }
-
- public int GetHashCode(TagHelperAttributeDesignTimeDescriptor descriptor)
- {
- var hashCodeCombiner = HashCodeCombiner.Start();
- hashCodeCombiner.Add(descriptor.Summary, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.Remarks, StringComparer.Ordinal);
-
- return hashCodeCombiner;
- }
- }
-}
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperBlockRewriterTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperBlockRewriterTest.cs
index 02d7d88ab5..fadd35cf33 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperBlockRewriterTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperBlockRewriterTest.cs
@@ -111,54 +111,44 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
// Arrange
var descriptors = new[]
{
- new TagHelperDescriptor
- {
- TagName = "*",
- TypeName = "CatchAllTagHelper",
- AssemblyName = "SomeAssembly",
- Attributes = new[]
- {
- new TagHelperAttributeDescriptor
- {
- Name = "[item]",
- PropertyName = "ListItems",
- TypeName = typeof(List).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "[(item)]",
- PropertyName = "ArrayItems",
- TypeName = typeof(string[]).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "(click)",
- PropertyName = "Event1",
- TypeName = typeof(Action).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "(^click)",
- PropertyName = "Event2",
- TypeName = typeof(Action).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "*something",
- PropertyName = "StringProperty1",
- TypeName = typeof(string).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "#local",
- PropertyName = "StringProperty2",
- TypeName = typeof(string).FullName
- },
- },
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "bound" } },
- },
+ ITagHelperDescriptorBuilder.Create("CatchAllTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("*")
+ .RequireAttribute(attribute => attribute.Name("bound")))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("[item]")
+ .PropertyName("ListItems")
+ .TypeName(typeof(List).Namespace + "List"))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("[(item)]")
+ .PropertyName("ArrayItems")
+ .TypeName(typeof(string[]).Namespace + "System.String[]"))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("(click)")
+ .PropertyName("Event1")
+ .TypeName(typeof(Action).FullName))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("(^click)")
+ .PropertyName("Event2")
+ .TypeName(typeof(Action).FullName))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("*something")
+ .PropertyName("StringProperty1")
+ .TypeName(typeof(string).FullName))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("#local")
+ .PropertyName("StringProperty2")
+ .TypeName(typeof(string).FullName))
+ .Build()
};
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
@@ -225,16 +215,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
// Arrange
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper",
- AssemblyName = "SomeAssembly",
- TagStructure = TagStructure.WithoutEndTag,
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ ITagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("input")
+ .RequireTagStructure(TagStructure.WithoutEndTag))
+ .Build()
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
@@ -317,31 +306,29 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
[MemberData(nameof(TagStructureCompatibilityData))]
public void Rewrite_AllowsCompatibleTagStructures(
string documentContent,
- int structure1,
- int structure2,
+ TagStructure structure1,
+ TagStructure structure2,
object expectedOutput)
{
// Arrange
var factory = new SpanFactory();
var blockFactory = new BlockFactory(factory);
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper1",
- AssemblyName = "SomeAssembly",
- TagStructure = (TagStructure)structure1
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper2",
- AssemblyName = "SomeAssembly",
- TagStructure = (TagStructure)structure2
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ ITagHelperDescriptorBuilder.Create("InputTagHelper1", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("input")
+ .RequireTagStructure(structure1))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("InputTagHelper2", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("input")
+ .RequireTagStructure(structure2))
+ .Build()
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
@@ -1199,43 +1186,33 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
[Theory]
[MemberData(nameof(CodeTagHelperAttributesData))]
- public void TagHelperParseTreeRewriter_CreatesMarkupCodeSpansForNonStringTagHelperAttributes(
+ public void Rewrite_CreatesMarkupCodeSpansForNonStringTagHelperAttributes(
string documentContent,
object expectedOutput)
{
// Arrange
var descriptors = new TagHelperDescriptor[]
{
- new TagHelperDescriptor
- {
- TagName = "person",
- TypeName = "PersonTagHelper",
- AssemblyName = "personAssembly",
- Attributes = new[]
- {
- new TagHelperAttributeDescriptor
- {
- Name = "age",
- PropertyName = "Age",
- TypeName = typeof(int).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "birthday",
- PropertyName = "BirthDay",
- TypeName = typeof(DateTime).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "name",
- PropertyName = "Name",
- TypeName = typeof(string).FullName,
- IsStringProperty = true
- }
- }
- }
+ ITagHelperDescriptorBuilder.Create("PersonTagHelper", "personAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("person"))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("age")
+ .PropertyName("Age")
+ .TypeName(typeof(int).FullName))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("birthday")
+ .PropertyName("BirthDay")
+ .TypeName(typeof(DateTime).FullName))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("name")
+ .PropertyName("Name")
+ .TypeName(typeof(string).FullName))
+ .Build()
};
- var providerContext = new TagHelperDescriptorProvider(descriptors);
+ var providerContext = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(providerContext,
@@ -2253,31 +2230,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
// Arrange
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "myth",
- TypeName = "mythTagHelper",
- AssemblyName = "SomeAssembly",
- Attributes = new[]
- {
- new TagHelperAttributeDescriptor
- {
- Name = "bound",
- PropertyName = "Bound",
- TypeName = typeof(bool).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "name",
- PropertyName = "Name",
- TypeName = typeof(string).FullName,
- IsStringProperty = true
- }
- }
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ ITagHelperDescriptorBuilder.Create("mythTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("myth"))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("bound")
+ .PropertyName("Bound")
+ .TypeName(typeof(bool).FullName))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("name")
+ .PropertyName("Name")
+ .TypeName(typeof(string).FullName))
+ .Build()
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, (RazorError[])expectedErrors);
@@ -3002,7 +2970,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
new[]
{
new RazorError(
- string.Format(errorFormat, "int-dictionary", "input", typeof(IDictionary).FullName),
+ string.Format(errorFormat, "int-dictionary", "input", typeof(IDictionary).Namespace + ".IDictionary"),
absoluteIndex: 7,
lineIndex: 0,
columnIndex: 7,
@@ -3022,7 +2990,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
new[]
{
new RazorError(
- string.Format(errorFormat, "string-dictionary", "input", typeof(IDictionary).FullName),
+ string.Format(errorFormat, "string-dictionary", "input", typeof(IDictionary).Namespace + ".IDictionary"),
absoluteIndex: 7,
lineIndex: 0,
columnIndex: 7,
@@ -3911,126 +3879,63 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
// Arrange
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper1",
- AssemblyName = "SomeAssembly",
- Attributes = new[]
- {
- new TagHelperAttributeDescriptor
- {
- Name = "bound-required-string",
- PropertyName = "BoundRequiredString",
- TypeName = typeof(string).FullName,
- IsStringProperty = true
- }
- },
- RequiredAttributes = new[]
- {
- new TagHelperRequiredAttributeDescriptor { Name = "unbound-required" }
- }
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper1",
- AssemblyName = "SomeAssembly",
- Attributes = new[]
- {
- new TagHelperAttributeDescriptor
- {
- Name = "bound-required-string",
- PropertyName = "BoundRequiredString",
- TypeName = typeof(string).FullName,
- IsStringProperty = true
- }
- },
- RequiredAttributes = new[]
- {
- new TagHelperRequiredAttributeDescriptor { Name = "bound-required-string" }
- }
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper2",
- AssemblyName = "SomeAssembly",
- Attributes = new[]
- {
- new TagHelperAttributeDescriptor
- {
- Name = "bound-required-int",
- PropertyName = "BoundRequiredInt",
- TypeName = typeof(int).FullName
- }
- },
- RequiredAttributes = new[]
- {
- new TagHelperRequiredAttributeDescriptor { Name = "bound-required-int" }
- }
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper3",
- AssemblyName = "SomeAssembly",
- Attributes = new[]
- {
- new TagHelperAttributeDescriptor
- {
- Name = "int-dictionary",
- PropertyName ="DictionaryOfIntProperty",
- TypeName = typeof(IDictionary).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "string-dictionary",
- PropertyName = "DictionaryOfStringProperty",
- TypeName = typeof(IDictionary).FullName
- },
- new TagHelperAttributeDescriptor
- {
- Name = "int-prefix-",
- PropertyName = "DictionaryOfIntProperty",
- TypeName = typeof(int).FullName,
- IsIndexer = true
- },
- new TagHelperAttributeDescriptor
- {
- Name = "string-prefix-",
- PropertyName = "DictionaryOfStringProperty",
- TypeName = typeof(string).FullName,
- IsIndexer = true,
- IsStringProperty = true
- }
- }
- },
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper",
- AssemblyName = "SomeAssembly",
- Attributes = new[]
- {
- new TagHelperAttributeDescriptor
- {
- Name = "bound-string",
- PropertyName = "BoundRequiredString",
- TypeName = typeof(string).FullName,
- IsStringProperty = true
- },
- new TagHelperAttributeDescriptor
- {
- Name = "bound-int",
- PropertyName = "BoundRequiredString",
- TypeName = typeof(int).FullName
- }
- }
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ ITagHelperDescriptorBuilder.Create("InputTagHelper1", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("input")
+ .RequireAttribute(attribute => attribute.Name("unbound-required")))
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("input")
+ .RequireAttribute(attribute => attribute.Name("bound-required-string")))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("bound-required-string")
+ .PropertyName("BoundRequiredString")
+ .TypeName(typeof(string).FullName))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("InputTagHelper2", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("input")
+ .RequireAttribute(attribute => attribute.Name("bound-required-int")))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("bound-required-int")
+ .PropertyName("BoundRequiredInt")
+ .TypeName(typeof(int).FullName))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("InputTagHelper3", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("input"))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("int-dictionary")
+ .PropertyName("DictionaryOfIntProperty")
+ .TypeName(typeof(IDictionary).Namespace + ".IDictionary")
+ .AsDictionary("int-prefix-", typeof(int).FullName))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("string-dictionary")
+ .PropertyName("DictionaryOfStringProperty")
+ .TypeName(typeof(IDictionary).Namespace + ".IDictionary")
+ .AsDictionary("string-prefix-", typeof(string).FullName))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("PTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("p"))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("bound-string")
+ .PropertyName("BoundRequiredString")
+ .TypeName(typeof(string).FullName))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("bound-int")
+ .PropertyName("BoundRequiredString")
+ .TypeName(typeof(int).FullName))
+ .Build(),
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, (RazorError[])expectedErrors);
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperDescriptorProviderTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperDescriptorProviderTest.cs
index bd0ac4c64c..840c7d02f0 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperDescriptorProviderTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperDescriptorProviderTest.cs
@@ -14,27 +14,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
get
{
- var strongPParent = new TagHelperDescriptor
- {
- TagName = "strong",
- TypeName = "StrongTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredParent = "p",
- };
- var strongDivParent = new TagHelperDescriptor
- {
- TagName = "strong",
- TypeName = "StrongTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredParent = "div",
- };
- var catchAllPParent = new TagHelperDescriptor
- {
- TagName = "*",
- TypeName = "CatchAllTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredParent = "p",
- };
+ var strongPDivParent = ITagHelperDescriptorBuilder.Create("StrongTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("strong")
+ .RequireParentTag("p"))
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("strong")
+ .RequireParentTag("div"))
+ .Build();
+ var catchAllPParent = ITagHelperDescriptorBuilder.Create("CatchAllTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("*")
+ .RequireParentTag("p"))
+ .Build();
return new TheoryData<
string, // tagName
@@ -45,25 +40,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
"strong",
"p",
- new[] { strongPParent, strongDivParent },
- new[] { strongPParent }
+ new[] { strongPDivParent },
+ new[] { strongPDivParent }
},
{
"strong",
"div",
- new[] { strongPParent, strongDivParent, catchAllPParent },
- new[] { strongDivParent }
+ new[] { strongPDivParent, catchAllPParent },
+ new[] { strongPDivParent }
},
{
"strong",
"p",
- new[] { strongPParent, strongDivParent, catchAllPParent },
- new[] { strongPParent, catchAllPParent }
+ new[] { strongPDivParent, catchAllPParent },
+ new[] { strongPDivParent, catchAllPParent }
},
{
"custom",
"p",
- new[] { strongPParent, strongDivParent, catchAllPParent },
+ new[] { strongPDivParent, catchAllPParent },
new[] { catchAllPParent }
},
};
@@ -72,93 +67,73 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
[Theory]
[MemberData(nameof(RequiredParentData))]
- public void GetDescriptors_ReturnsDescriptorsParentTags(
+ public void GetTagHelperBinding_ReturnsBindingResultWithDescriptorsParentTags(
string tagName,
string parentTagName,
object availableDescriptors,
object expectedDescriptors)
{
// Arrange
- var provider = new TagHelperDescriptorProvider((IEnumerable)availableDescriptors);
+ var provider = new TagHelperDescriptorProvider(null, (IEnumerable)availableDescriptors);
// Act
- var resolvedDescriptors = provider.GetDescriptors(
+ var bindingResult = provider.GetTagHelperBinding(
tagName,
attributes: Enumerable.Empty>(),
parentTagName: parentTagName);
// Assert
- Assert.Equal((IEnumerable)expectedDescriptors, resolvedDescriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
+ Assert.Equal((IEnumerable)expectedDescriptors, bindingResult.Descriptors, TagHelperDescriptorComparer.CaseSensitive);
}
public static TheoryData RequiredAttributeData
{
get
{
- var divDescriptor = new TagHelperDescriptor
- {
- TagName = "div",
- TypeName = "DivTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "style" } }
- };
- var inputDescriptor = new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredAttributes = new[]
- {
- new TagHelperRequiredAttributeDescriptor { Name = "class" },
- new TagHelperRequiredAttributeDescriptor { Name = "style" }
- }
- };
- var inputWildcardPrefixDescriptor = new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputWildCardAttribute",
- AssemblyName = "SomeAssembly",
- RequiredAttributes = new[]
- {
- new TagHelperRequiredAttributeDescriptor
- {
- Name = "nodashprefix",
- NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch,
- }
- }
- };
- var catchAllDescriptor = new TagHelperDescriptor
- {
- TagName = TagHelperDescriptorProvider.ElementCatchAllTarget,
- TypeName = "CatchAllTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "class" } }
- };
- var catchAllDescriptor2 = new TagHelperDescriptor
- {
- TagName = TagHelperDescriptorProvider.ElementCatchAllTarget,
- TypeName = "CatchAllTagHelper2",
- AssemblyName = "SomeAssembly",
- RequiredAttributes = new[]
- {
- new TagHelperRequiredAttributeDescriptor { Name = "custom" },
- new TagHelperRequiredAttributeDescriptor { Name = "class" }
- }
- };
- var catchAllWildcardPrefixDescriptor = new TagHelperDescriptor
- {
- TagName = TagHelperDescriptorProvider.ElementCatchAllTarget,
- TypeName = "CatchAllWildCardAttribute",
- AssemblyName = "SomeAssembly",
- RequiredAttributes = new[]
- {
- new TagHelperRequiredAttributeDescriptor
- {
- Name = "prefix-",
- NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch,
- }
- }
- };
+ var divDescriptor = ITagHelperDescriptorBuilder.Create("DivTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("div")
+ .RequireAttribute(attribute => attribute.Name("style")))
+ .Build();
+ var inputDescriptor = ITagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("input")
+ .RequireAttribute(attribute => attribute.Name("class"))
+ .RequireAttribute(attribute => attribute.Name("style")))
+ .Build();
+ var inputWildcardPrefixDescriptor = ITagHelperDescriptorBuilder.Create("InputWildCardAttribute", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("input")
+ .RequireAttribute(attribute =>
+ attribute
+ .Name("nodashprefix")
+ .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)))
+ .Build();
+ var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("CatchAllTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
+ .RequireAttribute(attribute => attribute.Name("class")))
+ .Build();
+ var catchAllDescriptor2 = ITagHelperDescriptorBuilder.Create("CatchAllTagHelper2", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
+ .RequireAttribute(attribute => attribute.Name("custom"))
+ .RequireAttribute(attribute => attribute.Name("class")))
+ .Build();
+ var catchAllWildcardPrefixDescriptor = ITagHelperDescriptorBuilder.Create("CatchAllWildCardAttribute", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
+ .RequireAttribute(attribute =>
+ attribute
+ .Name("prefix-")
+ .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)))
+ .Build();
var defaultAvailableDescriptors =
new[] { divDescriptor, inputDescriptor, catchAllDescriptor, catchAllDescriptor2 };
var defaultWildcardDescriptors =
@@ -176,7 +151,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
"div",
new[] { kvp("custom") },
defaultAvailableDescriptors,
- Enumerable.Empty()
+ null
},
{ "div", new[] { kvp("style") }, defaultAvailableDescriptors, new[] { divDescriptor } },
{ "div", new[] { kvp("class") }, defaultAvailableDescriptors, new[] { catchAllDescriptor } },
@@ -214,19 +189,19 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
"input",
new[] { kvp("prefixABCnodashprefix") },
defaultWildcardDescriptors,
- Enumerable.Empty()
+ null
},
{
"input",
new[] { kvp("prefix-") },
defaultWildcardDescriptors,
- Enumerable.Empty()
+ null
},
{
"input",
new[] { kvp("nodashprefix") },
defaultWildcardDescriptors,
- Enumerable.Empty()
+ null
},
{
"input",
@@ -258,292 +233,222 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
[Theory]
[MemberData(nameof(RequiredAttributeData))]
- public void GetDescriptors_ReturnsDescriptorsWithRequiredAttributes(
+ public void GetTagHelperBinding_ReturnsBindingResultDescriptorsWithRequiredAttributes(
string tagName,
IEnumerable> providedAttributes,
object availableDescriptors,
object expectedDescriptors)
{
// Arrange
- var provider = new TagHelperDescriptorProvider((IEnumerable)availableDescriptors);
+ var provider = new TagHelperDescriptorProvider(null, (IEnumerable)availableDescriptors);
// Act
- var resolvedDescriptors = provider.GetDescriptors(tagName, providedAttributes, parentTagName: "p").ToArray();
+ var bindingResult = provider.GetTagHelperBinding(tagName, providedAttributes, parentTagName: "p");
// Assert
- Assert.Equal((IEnumerable)expectedDescriptors, resolvedDescriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
+ Assert.Equal((IEnumerable)expectedDescriptors, bindingResult?.Descriptors, TagHelperDescriptorComparer.CaseSensitive);
}
[Fact]
- public void GetDescriptors_ReturnsEmptyDescriptorsWithPrefixAsTagName()
+ public void GetTagHelperBinding_ReturnsNullBindingResultPrefixAsTagName()
{
// Arrange
- var catchAllDescriptor = CreatePrefixedDescriptor(
- "th",
- TagHelperDescriptorProvider.ElementCatchAllTarget,
- "foo1");
+ var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget))
+ .Build();
var descriptors = new[] { catchAllDescriptor };
- var provider = new TagHelperDescriptorProvider(descriptors);
+ var provider = new TagHelperDescriptorProvider("th", descriptors);
// Act
- var resolvedDescriptors = provider.GetDescriptors(
+ var bindingResult = provider.GetTagHelperBinding(
tagName: "th",
attributes: Enumerable.Empty>(),
parentTagName: "p");
// Assert
- Assert.Empty(resolvedDescriptors);
+ Assert.Null(bindingResult);
}
[Fact]
- public void GetDescriptors_DeduplicatesTagHelpersByTypeName()
+ public void GetTagHelperBinding_ReturnsBindingResultCatchAllDescriptorsForPrefixedTags()
{
// Arrange
- var descriptors = new[]
- {
- new TagHelperDescriptor
- {
- AssemblyName = "TestAssembly",
- TagName = "form",
- TypeName = "TestFormTagHelper",
- RequiredAttributes = new List()
- {
- new TagHelperRequiredAttributeDescriptor()
- {
- Name = "a",
- NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch
- }
- },
- },
- new TagHelperDescriptor
- {
- AssemblyName = "TestAssembly",
- TagName = "form",
- TypeName = "TestFormTagHelper",
- RequiredAttributes = new List()
- {
- new TagHelperRequiredAttributeDescriptor()
- {
- Name = "b",
- NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch
- }
- },
- },
- };
- var provider = new TagHelperDescriptorProvider(descriptors);
-
- // Act
- var resolvedDescriptors = provider.GetDescriptors(
- tagName: "form",
- attributes: new List>()
- {
- new KeyValuePair("a", "hi" ),
- new KeyValuePair("b", "there"),
- },
- parentTagName: "p");
-
- // Assert
- Assert.Same(descriptors[0], Assert.Single(resolvedDescriptors));
- }
-
- [Fact]
- public void GetDescriptors_OnlyUnderstandsSinglePrefix()
- {
- // Arrange
- var divDescriptor = CreatePrefixedDescriptor("th:", "div", "foo1");
- var spanDescriptor = CreatePrefixedDescriptor("th2:", "span", "foo2");
- var descriptors = new[] { divDescriptor, spanDescriptor };
- var provider = new TagHelperDescriptorProvider(descriptors);
-
- // Act
- var retrievedDescriptorsDiv = provider.GetDescriptors(
- tagName: "th:div",
- attributes: Enumerable.Empty>(),
- parentTagName: "p");
- var retrievedDescriptorsSpan = provider.GetDescriptors(
- tagName: "th2:span",
- attributes: Enumerable.Empty>(),
- parentTagName: "p");
-
- // Assert
- var descriptor = Assert.Single(retrievedDescriptorsDiv);
- Assert.Same(divDescriptor, descriptor);
- Assert.Empty(retrievedDescriptorsSpan);
- }
-
- [Fact]
- public void GetDescriptors_ReturnsCatchAllDescriptorsForPrefixedTags()
- {
- // Arrange
- var catchAllDescriptor = CreatePrefixedDescriptor("th:", TagHelperDescriptorProvider.ElementCatchAllTarget, "foo1");
+ var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget))
+ .Build();
var descriptors = new[] { catchAllDescriptor };
- var provider = new TagHelperDescriptorProvider(descriptors);
+ var provider = new TagHelperDescriptorProvider("th:", descriptors);
// Act
- var retrievedDescriptorsDiv = provider.GetDescriptors(
+ var bindingResultDiv = provider.GetTagHelperBinding(
tagName: "th:div",
attributes: Enumerable.Empty>(),
parentTagName: "p");
- var retrievedDescriptorsSpan = provider.GetDescriptors(
+ var bindingResultSpan = provider.GetTagHelperBinding(
tagName: "th:span",
attributes: Enumerable.Empty>(),
parentTagName: "p");
// Assert
- var descriptor = Assert.Single(retrievedDescriptorsDiv);
+ var descriptor = Assert.Single(bindingResultDiv.Descriptors);
Assert.Same(catchAllDescriptor, descriptor);
- descriptor = Assert.Single(retrievedDescriptorsSpan);
+ descriptor = Assert.Single(bindingResultSpan.Descriptors);
Assert.Same(catchAllDescriptor, descriptor);
}
[Fact]
- public void GetDescriptors_ReturnsDescriptorsForPrefixedTags()
+ public void GetTagHelperBinding_ReturnsBindingResultDescriptorsForPrefixedTags()
{
// Arrange
- var divDescriptor = CreatePrefixedDescriptor("th:", "div", "foo1");
+ var divDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("div"))
+ .Build();
var descriptors = new[] { divDescriptor };
- var provider = new TagHelperDescriptorProvider(descriptors);
+ var provider = new TagHelperDescriptorProvider("th:", descriptors);
// Act
- var retrievedDescriptors = provider.GetDescriptors(
+ var bindingResult = provider.GetTagHelperBinding(
tagName: "th:div",
attributes: Enumerable.Empty>(),
parentTagName: "p");
// Assert
- var descriptor = Assert.Single(retrievedDescriptors);
+ var descriptor = Assert.Single(bindingResult.Descriptors);
Assert.Same(divDescriptor, descriptor);
}
[Theory]
[InlineData("*")]
[InlineData("div")]
- public void GetDescriptors_ReturnsNothingForUnprefixedTags(string tagName)
+ public void GetTagHelperBinding_ReturnsNullForUnprefixedTags(string tagName)
{
// Arrange
- var divDescriptor = CreatePrefixedDescriptor("th:", tagName, "foo1");
+ var divDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName(tagName))
+ .Build();
var descriptors = new[] { divDescriptor };
- var provider = new TagHelperDescriptorProvider(descriptors);
+ var provider = new TagHelperDescriptorProvider("th:", descriptors);
// Act
- var retrievedDescriptorsDiv = provider.GetDescriptors(
+ var bindingResult = provider.GetTagHelperBinding(
tagName: "div",
attributes: Enumerable.Empty>(),
parentTagName: "p");
// Assert
- Assert.Empty(retrievedDescriptorsDiv);
+ Assert.Null(bindingResult);
}
[Fact]
public void GetDescriptors_ReturnsNothingForUnregisteredTags()
{
// Arrange
- var divDescriptor = new TagHelperDescriptor
- {
- TagName = "div",
- TypeName = "foo1",
- AssemblyName = "SomeAssembly",
- };
- var spanDescriptor = new TagHelperDescriptor
- {
- TagName = "span",
- TypeName = "foo2",
- AssemblyName = "SomeAssembly",
- };
+ var divDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("div"))
+ .Build();
+ var spanDescriptor = ITagHelperDescriptorBuilder.Create("foo2", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("span"))
+ .Build();
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor };
- var provider = new TagHelperDescriptorProvider(descriptors);
+ var provider = new TagHelperDescriptorProvider(null, descriptors);
// Act
- var retrievedDescriptors = provider.GetDescriptors(
+ var tagHelperBinding = provider.GetTagHelperBinding(
tagName: "foo",
attributes: Enumerable.Empty>(),
parentTagName: "p");
// Assert
- Assert.Empty(retrievedDescriptors);
+ Assert.Null(tagHelperBinding);
}
[Fact]
public void GetDescriptors_ReturnsCatchAllsWithEveryTagName()
{
// Arrange
- var divDescriptor = new TagHelperDescriptor
- {
- TagName = "div",
- TypeName = "foo1",
- AssemblyName = "SomeAssembly",
- };
- var spanDescriptor = new TagHelperDescriptor
- {
- TagName = "span",
- TypeName = "foo2",
- AssemblyName = "SomeAssembly",
- };
- var catchAllDescriptor = new TagHelperDescriptor
- {
- TagName = TagHelperDescriptorProvider.ElementCatchAllTarget,
- TypeName = "foo3",
- AssemblyName = "SomeAssembly",
- };
+ var divDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("div"))
+ .Build();
+ var spanDescriptor = ITagHelperDescriptorBuilder.Create("foo2", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("span"))
+ .Build();
+ var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("foo3", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget))
+ .Build();
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor };
- var provider = new TagHelperDescriptorProvider(descriptors);
+ var provider = new TagHelperDescriptorProvider(null, descriptors);
// Act
- var divDescriptors = provider.GetDescriptors(
+ var divBinding = provider.GetTagHelperBinding(
tagName: "div",
attributes: Enumerable.Empty>(),
parentTagName: "p");
- var spanDescriptors = provider.GetDescriptors(
+ var spanBinding = provider.GetTagHelperBinding(
tagName: "span",
attributes: Enumerable.Empty>(),
parentTagName: "p");
// Assert
// For divs
- Assert.Equal(2, divDescriptors.Count());
- Assert.Contains(divDescriptor, divDescriptors);
- Assert.Contains(catchAllDescriptor, divDescriptors);
+ Assert.Equal(2, divBinding.Descriptors.Count());
+ Assert.Contains(divDescriptor, divBinding.Descriptors);
+ Assert.Contains(catchAllDescriptor, divBinding.Descriptors);
// For spans
- Assert.Equal(2, spanDescriptors.Count());
- Assert.Contains(spanDescriptor, spanDescriptors);
- Assert.Contains(catchAllDescriptor, spanDescriptors);
+ Assert.Equal(2, spanBinding.Descriptors.Count());
+ Assert.Contains(spanDescriptor, spanBinding.Descriptors);
+ Assert.Contains(catchAllDescriptor, spanBinding.Descriptors);
}
[Fact]
public void GetDescriptors_DuplicateDescriptorsAreNotPartOfTagHelperDescriptorPool()
{
// Arrange
- var divDescriptor = new TagHelperDescriptor
- {
- TagName = "div",
- TypeName = "foo1",
- AssemblyName = "SomeAssembly",
- };
+ var divDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("div"))
+ .Build();
var descriptors = new TagHelperDescriptor[] { divDescriptor, divDescriptor };
- var provider = new TagHelperDescriptorProvider(descriptors);
+ var provider = new TagHelperDescriptorProvider(null, descriptors);
// Act
- var retrievedDescriptors = provider.GetDescriptors(
+ var bindingResult = provider.GetTagHelperBinding(
tagName: "div",
attributes: Enumerable.Empty>(),
parentTagName: "p");
// Assert
- var descriptor = Assert.Single(retrievedDescriptors);
+ var descriptor = Assert.Single(bindingResult.Descriptors);
Assert.Same(divDescriptor, descriptor);
}
- private static TagHelperDescriptor CreatePrefixedDescriptor(string prefix, string tagName, string typeName)
+ [Fact]
+ public void GetTagHelperBinding_DescriptorWithMultipleRules_CorrectlySelectsMatchingRules()
{
- return new TagHelperDescriptor
- {
- Prefix = prefix,
- TagName = tagName,
- TypeName = typeName,
- AssemblyName = "SomeAssembly"
- };
+ // Arrange
+ var multiRuleDescriptor = ITagHelperDescriptorBuilder.Create("foo", "SomeAssembly")
+ .TagMatchingRule(rule => rule
+ .RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
+ .RequireParentTag("body"))
+ .TagMatchingRule(rule => rule
+ .RequireTagName("div"))
+ .TagMatchingRule(rule => rule
+ .RequireTagName("span"))
+ .Build();
+ var descriptors = new TagHelperDescriptor[] { multiRuleDescriptor };
+ var provider = new TagHelperDescriptorProvider(null, descriptors);
+
+ // Act
+ var binding = provider.GetTagHelperBinding(
+ tagName: "div",
+ attributes: Enumerable.Empty>(),
+ parentTagName: "p");
+
+ // Assert
+ var boundDescriptor = Assert.Single(binding.Descriptors);
+ Assert.Same(multiRuleDescriptor, boundDescriptor);
+ var boundRules = binding.GetBoundRules(boundDescriptor);
+ var boundRule = Assert.Single(boundRules);
+ Assert.Equal("div", boundRule.TagName);
}
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperDesignTimeDescriptorComparer.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperDesignTimeDescriptorComparer.cs
deleted file mode 100644
index 6d45cb8b50..0000000000
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperDesignTimeDescriptorComparer.cs
+++ /dev/null
@@ -1,47 +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.Collections.Generic;
-using Microsoft.Extensions.Internal;
-using Xunit;
-
-namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
-{
- internal class TagHelperDesignTimeDescriptorComparer : IEqualityComparer
- {
- public static readonly TagHelperDesignTimeDescriptorComparer Default =
- new TagHelperDesignTimeDescriptorComparer();
-
- private TagHelperDesignTimeDescriptorComparer()
- {
- }
-
- public bool Equals(TagHelperDesignTimeDescriptor descriptorX, TagHelperDesignTimeDescriptor descriptorY)
- {
- if (descriptorX == descriptorY)
- {
- return true;
- }
-
- Assert.NotNull(descriptorX);
- Assert.NotNull(descriptorY);
- Assert.Equal(descriptorX.Summary, descriptorY.Summary, StringComparer.Ordinal);
- Assert.Equal(descriptorX.Remarks, descriptorY.Remarks, StringComparer.Ordinal);
- Assert.Equal(descriptorX.OutputElementHint, descriptorY.OutputElementHint, StringComparer.Ordinal);
-
- return true;
- }
-
- public int GetHashCode(TagHelperDesignTimeDescriptor descriptor)
- {
- var hashCodeCombiner = HashCodeCombiner.Start();
-
- hashCodeCombiner.Add(descriptor.Summary, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.Remarks, StringComparer.Ordinal);
- hashCodeCombiner.Add(descriptor.OutputElementHint, StringComparer.Ordinal);
-
- return hashCodeCombiner;
- }
- }
-}
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperParseTreeRewriterTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperParseTreeRewriterTest.cs
index baba797bfe..099e52f4a2 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperParseTreeRewriterTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperParseTreeRewriterTest.cs
@@ -52,7 +52,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
var errorSink = new ErrorSink();
var parseResult = ParseDocument(documentContent);
var document = parseResult.Root;
- var parseTreeRewriter = new TagHelperParseTreeRewriter(provider: null);
+ var parseTreeRewriter = new TagHelperParseTreeRewriter(null, provider: null);
// Assert - Guard
var rootBlock = Assert.IsType(document);
@@ -108,8 +108,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
new MarkupBlock(
new MarkupTagHelperBlock("p",
new MarkupTagHelperBlock("strong")),
- blockFactory.MarkupTagBlock("")),
- new[] { errorFormatUnclosed(4, "strong") }
+ new MarkupTagHelperBlock("strong")),
+ new[] { errorFormatUnclosed(4, "strong"), errorFormatUnclosed(16, "strong") }
},
{
"<<
",
@@ -160,35 +160,18 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
// Arrange
var descriptors = new TagHelperDescriptor[]
{
- new TagHelperDescriptor
- {
- TagName = "strong",
- TypeName = "StrongTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredParent = "p",
- },
- new TagHelperDescriptor
- {
- TagName = "strong",
- TypeName = "StrongTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredParent = "div",
- },
- new TagHelperDescriptor
- {
- TagName = "*",
- TypeName = "CatchALlTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredParent = "p",
- },
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper",
- AssemblyName = "SomeAssembly"
- }
+ ITagHelperDescriptorBuilder.Create("StrongTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("strong"))
+ .TagMatchingRule(rule => rule.RequireTagName("div"))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("CatchALlTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("*"))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("PTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("p"))
+ .Build(),
};
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, (RazorError[])expectedErrors);
@@ -275,35 +258,27 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
// Arrange
var descriptors = new TagHelperDescriptor[]
{
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper",
- AssemblyName = "SomeAssembly",
- TagStructure = TagStructure.WithoutEndTag,
- },
- new TagHelperDescriptor
- {
- TagName = "strong",
- TypeName = "StrongTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredParent = "p",
- },
- new TagHelperDescriptor
- {
- TagName = "strong",
- TypeName = "StrongTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredParent = "input",
- },
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper",
- AssemblyName = "SomeAssembly"
- }
+ ITagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("input")
+ .RequireTagStructure(TagStructure.WithoutEndTag))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("StrongTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("strong")
+ .RequireParentTag("p"))
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("strong")
+ .RequireParentTag("input"))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("PTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("p"))
+ .Build(),
};
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
@@ -365,28 +340,21 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
// Arrange
var descriptors = new TagHelperDescriptor[]
{
- new TagHelperDescriptor
- {
- TagName = "strong",
- TypeName = "StrongTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredParent = "p",
- },
- new TagHelperDescriptor
- {
- TagName = "strong",
- TypeName = "StrongTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredParent = "div",
- },
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper",
- AssemblyName = "SomeAssembly"
- }
+ ITagHelperDescriptorBuilder.Create("StrongTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("strong")
+ .RequireParentTag("p"))
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("strong")
+ .RequireParentTag("div"))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("PTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("p"))
+ .Build(),
};
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
@@ -401,31 +369,24 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
new MarkupTagHelperBlock("th:p",
new MarkupTagHelperBlock("th:strong")));
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper",
- AssemblyName = "SomeAssembly",
- AllowedChildren = new[] { "strong" },
- Prefix = "th:"
- },
- new TagHelperDescriptor
- {
- TagName = "strong",
- TypeName = "StrongTagHelper",
- AssemblyName = "SomeAssembly",
- Prefix = "th:"
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ ITagHelperDescriptorBuilder.Create("PTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("p"))
+ .AllowChildTag("strong")
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("StrongTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("strong"))
+ .Build(),
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider("th:", descriptors);
// Act & Assert
EvaluateData(
descriptorProvider,
documentContent,
expectedOutput,
- expectedErrors: Enumerable.Empty());
+ expectedErrors: Enumerable.Empty(),
+ tagHelperPrefix: "th:");
}
public static TheoryData InvalidHtmlScriptBlockData
@@ -720,16 +681,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
blockFactory.MarkupTagBlock(""),
factory.Markup(Environment.NewLine)));
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper",
- AssemblyName = "SomeAssembly",
- AllowedChildren = new[] { "br" },
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ ITagHelperDescriptorBuilder.Create("PTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("p"))
+ .AllowChildTag("br")
+ .Build()
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors);
@@ -760,17 +718,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
blockFactory.MarkupTagBlock(""),
blockFactory.MarkupTagBlock("")));
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "strong",
- TypeName = "StrongTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "required" } },
- AllowedChildren = new[] { "br" }
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ ITagHelperDescriptorBuilder.Create("StrongTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("strong")
+ .RequireAttribute(attribute => attribute.Name("required")))
+ .AllowChildTag("br")
+ .Build()
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors);
@@ -788,35 +745,26 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
factory.Markup("Hello World")),
new MarkupTagHelperBlock("br", TagMode.StartTagOnly)));
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper1",
- AssemblyName = "SomeAssembly",
- AllowedChildren = new[] { "strong", "br" }
- },
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper2",
- AssemblyName = "SomeAssembly"
- },
- new TagHelperDescriptor
- {
- TagName = "strong",
- TypeName = "StrongTagHelper",
- AssemblyName = "SomeAssembly"
- },
- new TagHelperDescriptor
- {
- TagName = "br",
- TypeName = "BRTagHelper",
- AssemblyName = "SomeAssembly",
- TagStructure = TagStructure.WithoutEndTag
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ ITagHelperDescriptorBuilder.Create("PTagHelper1", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("p"))
+ .AllowChildTag("strong")
+ .AllowChildTag("br")
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("PTagHelper2", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("p"))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("StrongTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("strong"))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("BRTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("br")
+ .RequireTagStructure(TagStructure.WithoutEndTag))
+ .Build(),
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors: new RazorError[0]);
@@ -834,36 +782,26 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
factory.Markup("Hello World")),
new MarkupTagHelperBlock("br", TagMode.StartTagOnly)));
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper1",
- AssemblyName = "SomeAssembly",
- AllowedChildren = new[] { "strong" }
- },
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper2",
- AssemblyName = "SomeAssembly",
- AllowedChildren = new[] { "br" }
- },
- new TagHelperDescriptor
- {
- TagName = "strong",
- TypeName = "StrongTagHelper",
- AssemblyName = "SomeAssembly"
- },
- new TagHelperDescriptor
- {
- TagName = "br",
- TypeName = "BRTagHelper",
- AssemblyName = "SomeAssembly",
- TagStructure = TagStructure.WithoutEndTag
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ ITagHelperDescriptorBuilder.Create("PTagHelper1", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("p"))
+ .AllowChildTag("strong")
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("PTagHelper2", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("p"))
+ .AllowChildTag("br")
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("StrongTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("strong"))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("BRTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("br")
+ .RequireTagStructure(TagStructure.WithoutEndTag))
+ .Build(),
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors: new RazorError[0]);
@@ -1083,31 +1021,28 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
object expectedErrors)
{
// Arrange
+ var pTagHelperBuilder = ITagHelperDescriptorBuilder.Create("PTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("p"));
+ var strongTagHelperBuilder = ITagHelperDescriptorBuilder.Create("StrongTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("strong"));
+
+ foreach (var childTag in allowedChildren)
+ {
+ pTagHelperBuilder.AllowChildTag(childTag);
+ strongTagHelperBuilder.AllowChildTag(childTag);
+ }
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper",
- AssemblyName = "SomeAssembly",
- AllowedChildren = allowedChildren
- },
- new TagHelperDescriptor
- {
- TagName = "strong",
- TypeName = "StrongTagHelper",
- AssemblyName = "SomeAssembly",
- AllowedChildren = allowedChildren
- },
- new TagHelperDescriptor
- {
- TagName = "br",
- TypeName = "BRTagHelper",
- AssemblyName = "SomeAssembly",
- TagStructure = TagStructure.WithoutEndTag
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ pTagHelperBuilder.Build(),
+ strongTagHelperBuilder.Build(),
+ ITagHelperDescriptorBuilder.Create("BRTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("br")
+ .RequireTagStructure(TagStructure.WithoutEndTag))
+ .Build(),
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, (RazorError[])expectedErrors);
@@ -1119,25 +1054,19 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
// Arrange
var documentContent = "";
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper",
- AssemblyName = "SomeAssembly",
- AllowedChildren = new[] { "custom" },
- },
- new TagHelperDescriptor
- {
- TagName = "*",
- TypeName = "CatchAllTagHelper",
- AssemblyName = "SomeAssembly",
- }
- };
+ {
+ ITagHelperDescriptorBuilder.Create("PTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("p"))
+ .AllowChildTag("custom")
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("CatchAllTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("*"))
+ .Build(),
+ };
var expectedOutput = new MarkupBlock(
new MarkupTagHelperBlock("p",
BlockFactory.MarkupTagBlock("")));
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
var expectedErrors = new[]
{
new RazorError(
@@ -1158,27 +1087,19 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
// Arrange
var documentContent = "";
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "PTagHelper",
- AssemblyName = "SomeAssembly",
- AllowedChildren = new[] { "custom" },
- Prefix = "th:",
- },
- new TagHelperDescriptor
- {
- TagName = "*",
- TypeName = "CatchAllTagHelper",
- AssemblyName = "SomeAssembly",
- Prefix = "th:",
- }
- };
+ {
+ ITagHelperDescriptorBuilder.Create("PTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("p"))
+ .AllowChildTag("custom")
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("CatchAllTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("*"))
+ .Build(),
+ };
var expectedOutput = new MarkupBlock(
new MarkupTagHelperBlock("th:p",
BlockFactory.MarkupTagBlock("")));
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ var descriptorProvider = new TagHelperDescriptorProvider("th:", descriptors);
var expectedErrors = new[]
{
new RazorError(
@@ -1190,7 +1111,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
};
// Act & Assert
- EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors);
+ EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors, "th:");
}
[Fact]
@@ -1200,16 +1121,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
var documentContent = "";
var expectedOutput = new MarkupBlock(new MarkupTagHelperBlock("input", TagMode.StartTagOnly));
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper",
- AssemblyName = "SomeAssembly",
- TagStructure = TagStructure.WithoutEndTag
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ ITagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("input")
+ .RequireTagStructure(TagStructure.WithoutEndTag))
+ .Build()
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors: new RazorError[0]);
@@ -1233,16 +1153,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
var documentContent = "";
var expectedOutput = new MarkupBlock(blockFactory.MarkupTagBlock(""));
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper",
- AssemblyName = "SomeAssembly",
- TagStructure = TagStructure.WithoutEndTag
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ ITagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("input")
+ .RequireTagStructure(TagStructure.WithoutEndTag))
+ .Build()
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors: new[] { expectedError });
@@ -1259,7 +1178,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
"InputTagHelper1",
"InputTagHelper2",
"input",
- nameof(TagHelperDescriptor.TagStructure)),
+ nameof(TagMatchingRule.TagStructure)),
absoluteIndex: 0,
lineIndex: 0,
columnIndex: 0,
@@ -1267,23 +1186,21 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
var documentContent = "";
var expectedOutput = new MarkupBlock(new MarkupTagHelperBlock("input", TagMode.StartTagOnly));
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper1",
- AssemblyName = "SomeAssembly",
- TagStructure = TagStructure.WithoutEndTag
- },
- new TagHelperDescriptor
- {
- TagName = "input",
- TypeName = "InputTagHelper2",
- AssemblyName = "SomeAssembly",
- TagStructure = TagStructure.NormalOrSelfClosing
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ ITagHelperDescriptorBuilder.Create("InputTagHelper1", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("input")
+ .RequireTagStructure(TagStructure.WithoutEndTag))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("InputTagHelper2", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("input")
+ .RequireTagStructure(TagStructure.NormalOrSelfClosing))
+ .Build()
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors: new[] { expectedError });
@@ -1692,34 +1609,28 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
// Arrange
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "pTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "class" } }
- },
- new TagHelperDescriptor
- {
- TagName = "div",
- TypeName = "divTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredAttributes = new[]
- {
- new TagHelperRequiredAttributeDescriptor { Name = "class" },
- new TagHelperRequiredAttributeDescriptor { Name = "style" }
- }
- },
- new TagHelperDescriptor
- {
- TagName = "*",
- TypeName = "catchAllTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "catchAll" } }
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ ITagHelperDescriptorBuilder.Create("pTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("p")
+ .RequireAttribute(attribute => attribute.Name("class")))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("divTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("div")
+ .RequireAttribute(attribute => attribute.Name("class"))
+ .RequireAttribute(attribute => attribute.Name("style")))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("catchAllTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("*")
+ .RequireAttribute(attribute => attribute.Name("catchAll")))
+ .Build()
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
@@ -1959,23 +1870,21 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
// Arrange
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "pTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "class" } }
- },
- new TagHelperDescriptor
- {
- TagName = "*",
- TypeName = "catchAllTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "catchAll" } }
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ ITagHelperDescriptorBuilder.Create("pTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("p")
+ .RequireAttribute(attribute => attribute.Name("class")))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("catchAllTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("*")
+ .RequireAttribute(attribute => attribute.Name("catchAll")))
+ .Build(),
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
@@ -2183,16 +2092,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
// Arrange
var descriptors = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- TagName = "p",
- TypeName = "pTagHelper",
- AssemblyName = "SomeAssembly",
- RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "class" } }
- }
- };
- var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
+ {
+ ITagHelperDescriptorBuilder.Create("pTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule =>
+ rule
+ .RequireTagName("p")
+ .RequireAttribute(attribute => attribute.Name("class")))
+ .Build(),
+ };
+ var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
// Act & Assert
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, (RazorError[])expectedErrors);
@@ -2206,81 +2114,39 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
var blockFactory = new BlockFactory(factory);
var availableDescriptorsColon = new TagHelperDescriptor[]
{
- new TagHelperDescriptor
- {
- Prefix = "th:",
- TagName = "myth",
- TypeName = "mythTagHelper",
- AssemblyName = "SomeAssembly"
- },
- new TagHelperDescriptor
- {
- Prefix = "th:",
- TagName = "myth2",
- TypeName = "mythTagHelper2",
- AssemblyName = "SomeAssembly",
- Attributes = new []
- {
- new TagHelperAttributeDescriptor
- {
- Name = "bound",
- PropertyName = "Bound",
- TypeName = typeof(bool).FullName
- }
- }
- }
- };
- var availableDescriptorsText = new TagHelperDescriptor[]
- {
- new TagHelperDescriptor
- {
- Prefix = "PREFIX",
- TagName = "myth",
- TypeName = "mythTagHelper",
- AssemblyName = "SomeAssembly"
- },
- new TagHelperDescriptor
- {
- Prefix = "PREFIX",
- TagName = "myth2",
- TypeName = "mythTagHelper2",
- AssemblyName = "SomeAssembly",
- Attributes = new []
- {
- new TagHelperAttributeDescriptor
- {
- Name = "bound",
- PropertyName = "Bound",
- TypeName = typeof(bool).FullName
- },
- }
- }
+ ITagHelperDescriptorBuilder.Create("mythTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("myth"))
+ .Build(),
+ ITagHelperDescriptorBuilder.Create("mythTagHelper2", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("myth2"))
+ .BindAttribute(attribute =>
+ attribute
+ .Name("bound")
+ .PropertyName("Bound")
+ .TypeName(typeof(bool).FullName))
+ .Build()
};
var availableDescriptorsCatchAll = new TagHelperDescriptor[]
{
- new TagHelperDescriptor
- {
- Prefix = "myth",
- TagName = "*",
- TypeName = "mythTagHelper",
- AssemblyName = "SomeAssembly"
- }
+ ITagHelperDescriptorBuilder.Create("mythTagHelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName("*"))
+ .Build(),
};
// documentContent, expectedOutput, availableDescriptors
return new TheoryData>
{
{
- "",
- new MarkupBlock(blockFactory.MarkupTagBlock("")),
+ "",
+ new MarkupBlock(blockFactory.MarkupTagBlock("")),
availableDescriptorsCatchAll
},
{
- "words and spaces",
+ "words and spaces",
new MarkupBlock(
- blockFactory.MarkupTagBlock(""),
+ blockFactory.MarkupTagBlock(""),
factory.Markup("words and spaces"),
- blockFactory.MarkupTagBlock("")),
+ blockFactory.MarkupTagBlock("")),
availableDescriptorsCatchAll
},
{
@@ -2289,24 +2155,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
new MarkupTagHelperBlock("th:myth", tagMode: TagMode.SelfClosing)),
availableDescriptorsColon
},
- {
- "",
- new MarkupBlock(
- new MarkupTagHelperBlock("PREFIXmyth", tagMode: TagMode.SelfClosing)),
- availableDescriptorsText
- },
{
"",
new MarkupBlock(
new MarkupTagHelperBlock("th:myth")),
availableDescriptorsColon
},
- {
- "",
- new MarkupBlock(
- new MarkupTagHelperBlock("PREFIXmyth")),
- availableDescriptorsText
- },
{
"",
new MarkupBlock(
@@ -2316,27 +2170,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
blockFactory.MarkupTagBlock(""))),
availableDescriptorsColon
},
- {
- "",
- new MarkupBlock(
- new MarkupTagHelperBlock(
- "PREFIXmyth",
- blockFactory.MarkupTagBlock(""),
- blockFactory.MarkupTagBlock(""))),
- availableDescriptorsText
- },
{
"",
new MarkupBlock(
blockFactory.EscapedMarkupTagBlock("<", "th:myth />")),
availableDescriptorsColon
},
- {
- "",
- new MarkupBlock(
- blockFactory.EscapedMarkupTagBlock("<", "PREFIXmyth />")),
- availableDescriptorsText
- },
{
"!th:myth>",
new MarkupBlock(
@@ -2344,13 +2183,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
blockFactory.EscapedMarkupTagBlock("", "th:myth>")),
availableDescriptorsColon
},
- {
- "!PREFIXmyth>",
- new MarkupBlock(
- blockFactory.EscapedMarkupTagBlock("<", "PREFIXmyth>"),
- blockFactory.EscapedMarkupTagBlock("", "PREFIXmyth>")),
- availableDescriptorsText
- },
{
"",
new MarkupBlock(
@@ -2363,18 +2195,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
})),
availableDescriptorsColon
},
- {
- "",
- new MarkupBlock(
- new MarkupTagHelperBlock(
- "PREFIXmyth",
- tagMode: TagMode.SelfClosing,
- attributes: new List
- {
- new TagHelperAttributeNode("class", factory.Markup("btn"))
- })),
- availableDescriptorsText
- },
{
"",
new MarkupBlock(
@@ -2387,18 +2207,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
})),
availableDescriptorsColon
},
- {
- "",
- new MarkupBlock(
- new MarkupTagHelperBlock(
- "PREFIXmyth2",
- tagMode: TagMode.SelfClosing,
- attributes: new List
- {
- new TagHelperAttributeNode("class", factory.Markup("btn"))
- })),
- availableDescriptorsText
- },
{
"words and spaces",
new MarkupBlock(
@@ -2411,18 +2219,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
children: factory.Markup("words and spaces"))),
availableDescriptorsColon
},
- {
- "words and spaces",
- new MarkupBlock(
- new MarkupTagHelperBlock(
- "PREFIXmyth",
- attributes: new List
- {
- new TagHelperAttributeNode("class", factory.Markup("btn"))
- },
- children: factory.Markup("words and spaces"))),
- availableDescriptorsText
- },
{
"",
new MarkupBlock(
@@ -2445,58 +2241,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
})),
availableDescriptorsColon
},
- {
- "",
- new MarkupBlock(
- new MarkupTagHelperBlock(
- "PREFIXmyth2",
- tagMode: TagMode.SelfClosing,
- attributes: new List
- {
- {
- new TagHelperAttributeNode(
- "bound",
- new MarkupBlock(
- new MarkupBlock(
- new ExpressionBlock(
- factory.CodeTransition(),
- factory.Code("DateTime.Now")
- .AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true)
- .Accepts(AcceptedCharacters.AnyExceptNewline)))))
- }
- })),
- availableDescriptorsText
- },
- {
- "",
- new MarkupBlock(
- new MarkupTagHelperBlock(
- "PREFIXmyth2",
- tagMode: TagMode.SelfClosing,
- attributes: new List
- {
- {
- new TagHelperAttributeNode(
- "bound",
- new MarkupBlock(
- new MarkupBlock(
- factory.CodeMarkup("@"),
- factory
- .CodeMarkup("@")
- .With(SpanChunkGenerator.Null)),
- new MarkupBlock(
- factory
- .EmptyHtml()
- .As(SpanKind.Code)
- .AsCodeMarkup(),
- new ExpressionBlock(
- factory.CSharpCodeMarkup("@"),
- factory.CSharpCodeMarkup("DateTime.Now")
- .With(new ExpressionChunkGenerator())))))
- }
- })),
- availableDescriptorsText
- },
};
}
}
@@ -2509,14 +2253,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
object availableDescriptors)
{
// Arrange
- var descriptorProvider = new TagHelperDescriptorProvider((IEnumerable)availableDescriptors);
+ var descriptorProvider = new TagHelperDescriptorProvider("th:", (IEnumerable)availableDescriptors);
// Act & Assert
EvaluateData(
descriptorProvider,
documentContent,
(MarkupBlock)expectedOutput,
- expectedErrors: Enumerable.Empty());
+ expectedErrors: Enumerable.Empty(),
+ tagHelperPrefix: "th:");
}
public static TheoryData OptOut_WithAttributeTextTagData
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperRewritingTestBase.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperRewritingTestBase.cs
index 3081f1b726..33be250599 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperRewritingTestBase.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/TagHelperRewritingTestBase.cs
@@ -1,12 +1,8 @@
// 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.IO;
using System.Linq;
-using System.Text;
-using Microsoft.AspNetCore.Razor.Evolution;
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{
@@ -41,27 +37,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
foreach (var tagName in tagNames)
{
- descriptors.Add(
- new TagHelperDescriptor
- {
- TagName = tagName,
- TypeName = tagName + "taghelper",
- AssemblyName = "SomeAssembly"
- });
+ var descriptor = ITagHelperDescriptorBuilder.Create(tagName + "taghelper", "SomeAssembly")
+ .TagMatchingRule(rule => rule.RequireTagName(tagName))
+ .Build();
+ descriptors.Add(descriptor);
}
- return new TagHelperDescriptorProvider(descriptors);
+ return new TagHelperDescriptorProvider(null, descriptors);
}
internal void EvaluateData(
TagHelperDescriptorProvider provider,
string documentContent,
MarkupBlock expectedOutput,
- IEnumerable expectedErrors)
+ IEnumerable expectedErrors,
+ string tagHelperPrefix = null)
{
var syntaxTree = ParseDocument(documentContent);
var errorSink = new ErrorSink();
- var parseTreeRewriter = new TagHelperParseTreeRewriter(provider);
+ var parseTreeRewriter = new TagHelperParseTreeRewriter(tagHelperPrefix, provider);
var actualTree = parseTreeRewriter.Rewrite(syntaxTree.Root, errorSink);
var allErrors = syntaxTree.Diagnostics.Concat(errorSink.Errors.Select(error => RazorDiagnostic.Create(error)));
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TagHelperBinderSyntaxTreePassTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TagHelperBinderSyntaxTreePassTest.cs
index d6b793d7d1..4edbc0f227 100644
--- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TagHelperBinderSyntaxTreePassTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TagHelperBinderSyntaxTreePassTest.cs
@@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Razor.Evolution.Legacy;
using Xunit;
using System.Linq;
using Moq;
+using System.Text;
namespace Microsoft.AspNetCore.Razor.Evolution
{
@@ -20,16 +21,14 @@ namespace Microsoft.AspNetCore.Razor.Evolution
{
builder.AddTagHelpers(new[]
{
- new TagHelperDescriptor
- {
- AssemblyName = "TestAssembly",
- TagName = "form",
- },
- new TagHelperDescriptor
- {
- AssemblyName = "TestAssembly",
- TagName = "input",
- }
+ CreateTagHelperDescriptor(
+ tagName: "form",
+ typeName: null,
+ assemblyName: "TestAssembly"),
+ CreateTagHelperDescriptor(
+ tagName: "input",
+ typeName: null,
+ assemblyName: "TestAssembly"),
});
});
@@ -59,39 +58,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution
public void Execute_DirectiveWithoutQuotes_RewritesTagHelpers_TagHelperMatchesElementTwice()
{
// Arrange
+ var descriptor = CreateTagHelperDescriptor(
+ tagName: "form",
+ typeName: "TestFormTagHelper",
+ assemblyName: "TestAssembly",
+ ruleBuilders: new Action[]
+ {
+ ruleBuilder => ruleBuilder
+ .RequireAttribute(attribute => attribute
+ .Name("a")
+ .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)),
+ ruleBuilder => ruleBuilder
+ .RequireAttribute(attribute => attribute
+ .Name("b")
+ .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)),
+ });
+
var engine = RazorEngine.Create(builder =>
{
- builder.AddTagHelpers(new[]
- {
- new TagHelperDescriptor
- {
- AssemblyName = "TestAssembly",
- TagName = "form",
- TypeName = "TestFormTagHelper",
- RequiredAttributes = new List()
- {
- new TagHelperRequiredAttributeDescriptor()
- {
- Name = "a",
- NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch
- }
- },
- },
- new TagHelperDescriptor
- {
- AssemblyName = "TestAssembly",
- TagName = "form",
- TypeName = "TestFormTagHelper",
- RequiredAttributes = new List()
- {
- new TagHelperRequiredAttributeDescriptor()
- {
- Name = "b",
- NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch
- }
- },
- }
- });
+ builder.AddTagHelpers(new[] { descriptor });
});
var pass = new TagHelperBinderSyntaxTreePass()
@@ -117,46 +102,32 @@ namespace Microsoft.AspNetCore.Razor.Evolution
var formTagHelper = Assert.IsType(rewrittenTree.Root.Children[2]);
Assert.Equal("form", formTagHelper.TagName);
- Assert.Single(formTagHelper.Descriptors);
+ Assert.Equal(2, formTagHelper.Binding.GetBoundRules(descriptor).Count());
}
[Fact]
public void Execute_DirectiveWithQuotes_RewritesTagHelpers_TagHelperMatchesElementTwice()
{
// Arrange
+ var descriptor = CreateTagHelperDescriptor(
+ tagName: "form",
+ typeName: "TestFormTagHelper",
+ assemblyName: "TestAssembly",
+ ruleBuilders: new Action[]
+ {
+ ruleBuilder => ruleBuilder
+ .RequireAttribute(attribute => attribute
+ .Name("a")
+ .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)),
+ ruleBuilder => ruleBuilder
+ .RequireAttribute(attribute => attribute
+ .Name("b")
+ .NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)),
+ });
+
var engine = RazorEngine.Create(builder =>
{
- builder.AddTagHelpers(new[]
- {
- new TagHelperDescriptor
- {
- AssemblyName = "TestAssembly",
- TagName = "form",
- TypeName = "TestFormTagHelper",
- RequiredAttributes = new List