Port TagHelperDescriptorFactory tests.

- Ported the existing descriptor factory tests and fixed issues with the current implementation.
- Ported documentation tests with the exception of the localization variants.
- Updated the DefaultTagHelperResolver to filter TagHelper types based on accessibility.
- Added DefaultTagHelperResolver tests.

#851
This commit is contained in:
N. Taylor Mullen 2017-01-13 10:35:10 -08:00
parent 208da8ca12
commit 8215d28ada
21 changed files with 4586 additions and 101 deletions

View File

@ -3,12 +3,13 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
using Microsoft.CodeAnalysis;
namespace Microsoft.CodeAnalysis.Razor
{
@ -32,25 +33,32 @@ namespace Microsoft.CodeAnalysis.Razor
private readonly INamedTypeSymbol _htmlAttributeNameAttributeSymbol;
private readonly INamedTypeSymbol _htmlAttributeNotBoundAttributeSymbol;
private readonly INamedTypeSymbol _htmlTargetElementAttributeSymbol;
private readonly INamedTypeSymbol _outputElementHintAttributeSymbol;
private readonly INamedTypeSymbol _iDictionarySymbol;
private readonly INamedTypeSymbol _restrictChildrenAttributeSymbol;
private readonly INamedTypeSymbol _editorBrowsableAttributeSymbol;
public static ICollection<char> InvalidNonWhitespaceNameCharacters { get; } = new HashSet<char>(
new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*' });
public DefaultTagHelperDescriptorFactory(Compilation compilation)
{
Compilation = compilation;
private static readonly SymbolDisplayFormat FullNameTypeDisplayFormat =
SymbolDisplayFormat.FullyQualifiedFormat
.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted)
.WithMiscellaneousOptions(SymbolDisplayFormat.FullyQualifiedFormat.MiscellaneousOptions & (~SymbolDisplayMiscellaneousOptions.UseSpecialTypes));
public DefaultTagHelperDescriptorFactory(Compilation compilation, bool designTime)
{
DesignTime = designTime;
_htmlAttributeNameAttributeSymbol = compilation.GetTypeByMetadataName(TagHelperTypes.HtmlAttributeNameAttribute);
_htmlAttributeNotBoundAttributeSymbol = compilation.GetTypeByMetadataName(TagHelperTypes.HtmlAttributeNotBoundAttribute);
_htmlTargetElementAttributeSymbol = compilation.GetTypeByMetadataName(TagHelperTypes.HtmlTargetElementAttribute);
_outputElementHintAttributeSymbol = compilation.GetTypeByMetadataName(TagHelperTypes.OutputElementHintAttribute);
_restrictChildrenAttributeSymbol = compilation.GetTypeByMetadataName(TagHelperTypes.RestrictChildrenAttribute);
_iDictionarySymbol = Compilation.GetTypeByMetadataName(TagHelperTypes.IDictionary);
_editorBrowsableAttributeSymbol = compilation.GetTypeByMetadataName(typeof(EditorBrowsableAttribute).FullName);
_iDictionarySymbol = compilation.GetTypeByMetadataName(TagHelperTypes.IDictionary);
}
protected Compilation Compilation { get; }
protected bool DesignTime { get; }
/// <inheritdoc />
public virtual IEnumerable<TagHelperDescriptor> CreateDescriptors(
@ -68,6 +76,11 @@ namespace Microsoft.CodeAnalysis.Razor
throw new ArgumentNullException(nameof(errorSink));
}
if (ShouldSkipDescriptorCreation(type))
{
return Enumerable.Empty<TagHelperDescriptor>();
}
var attributeDescriptors = GetAttributeDescriptors(type, errorSink);
var targetElementAttributes = GetValidHtmlTargetElementAttributes(type, errorSink);
var allowedChildren = GetAllowedChildren(type, errorSink);
@ -98,7 +111,36 @@ namespace Microsoft.CodeAnalysis.Razor
IEnumerable<AttributeData> targetElementAttributes,
IEnumerable<string> allowedChildren)
{
TagHelperDesignTimeDescriptor typeDesignTimeDescriptor = null;
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,
};
}
}
var typeName = GetFullName(type);
@ -123,7 +165,7 @@ namespace Microsoft.CodeAnalysis.Razor
allowedChildren: allowedChildren,
tagStructure: default(TagStructure),
parentTag: null,
designTimeDescriptor: typeDesignTimeDescriptor)
designTimeDescriptor: designTimeDescriptor)
};
}
@ -135,7 +177,7 @@ namespace Microsoft.CodeAnalysis.Razor
attributeDescriptors,
attribute,
allowedChildren,
typeDesignTimeDescriptor));
designTimeDescriptor));
}
private IEnumerable<string> GetAllowedChildren(INamedTypeSymbol type, ErrorSink errorSink)
@ -156,7 +198,7 @@ namespace Microsoft.CodeAnalysis.Razor
allowedChildren.Add((string)value.Value);
}
}
var validAllowedChildren = GetValidAllowedChildren(allowedChildren, GetFullName(type), errorSink);
if (validAllowedChildren.Any())
@ -180,13 +222,21 @@ namespace Microsoft.CodeAnalysis.Razor
foreach (var name in allowedChildren)
{
var valid = TryValidateName(
if (string.IsNullOrWhiteSpace(name))
{
var whitespaceError = Workspaces.Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace(
TagHelperTypes.RestrictChildrenAttribute,
tagHelperName);
errorSink.OnError(SourceLocation.Zero, whitespaceError, length: 0);
}
else if (TryValidateName(
name,
whitespaceError: "invalid",
characterErrorBuilder: (invalidCharacter) => "invalid",
errorSink: errorSink);
if (valid)
invalidCharacter => Workspaces.Resources.FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName(
TagHelperTypes.RestrictChildrenAttribute,
name,
tagHelperName,
invalidCharacter),
errorSink))
{
validAllowedChildren.Add(name);
}
@ -251,7 +301,7 @@ namespace Microsoft.CodeAnalysis.Razor
{
if (attibute.ConstructorArguments.Length == 0)
{
return null;
return TagHelperDescriptorProvider.ElementCatchAllTarget;
}
else
{
@ -317,12 +367,29 @@ namespace Microsoft.CodeAnalysis.Razor
/// </summary>
internal static bool ValidateParentTagName(string parentTag, ErrorSink errorSink)
{
return parentTag == null ||
TryValidateName(
if (parentTag == null)
{
return true;
}
else if (string.IsNullOrWhiteSpace(parentTag))
{
var error = Workspaces.Resources.FormatHtmlTargetElementAttribute_NameCannotBeNullOrWhitespace(
Workspaces.Resources.TagHelperDescriptorFactory_ParentTag);
errorSink.OnError(SourceLocation.Zero, error, length: 0);
return false;
}
else if (!TryValidateName(
parentTag,
invalidCharacter => Workspaces.Resources.FormatHtmlTargetElementAttribute_InvalidName(
Workspaces.Resources.TagHelperDescriptorFactory_ParentTag.ToLower(),
parentTag,
"invalid",
characterErrorBuilder: (invalidCharacter) => "invalid",
errorSink: errorSink);
invalidCharacter),
errorSink))
{
return false;
}
return true;
}
private static bool TryGetRequiredAttributeDescriptors(
@ -340,50 +407,53 @@ namespace Microsoft.CodeAnalysis.Razor
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 ? "invalid" : "invalid";
var targetName = targetingAttributes ?
Workspaces.Resources.TagHelperDescriptorFactory_Attribute :
Workspaces.Resources.TagHelperDescriptorFactory_Tag;
var validName = TryValidateName(
if (string.IsNullOrWhiteSpace(name))
{
var error = Workspaces.Resources.FormatHtmlTargetElementAttribute_NameCannotBeNullOrWhitespace(targetName);
errorSink.OnError(SourceLocation.Zero, error, length: 0);
return false;
}
else if (!TryValidateName(
name,
whitespaceError: "invalid",
characterErrorBuilder: (invalidCharacter) => "invalid",
errorSink: errorSink);
invalidCharacter => Workspaces.Resources.FormatHtmlTargetElementAttribute_InvalidName(
targetName.ToLower(),
name,
invalidCharacter),
errorSink))
{
return false;
}
return validName;
return true;
}
private static bool TryValidateName(
string name,
string whitespaceError,
Func<char, string> characterErrorBuilder,
ErrorSink errorSink)
{
var validName = true;
if (string.IsNullOrWhiteSpace(name))
foreach (var character in name)
{
errorSink.OnError(SourceLocation.Zero, whitespaceError, length: 0);
validName = false;
}
else
{
foreach (var character in name)
if (char.IsWhiteSpace(character) ||
InvalidNonWhitespaceNameCharacters.Contains(character))
{
if (char.IsWhiteSpace(character) ||
InvalidNonWhitespaceNameCharacters.Contains(character))
{
var error = characterErrorBuilder(character);
errorSink.OnError(SourceLocation.Zero, error, length: 0);
var error = characterErrorBuilder(character);
errorSink.OnError(SourceLocation.Zero, error, length: 0);
validName = false;
}
validName = false;
}
}
@ -392,15 +462,19 @@ namespace Microsoft.CodeAnalysis.Razor
private IEnumerable<TagHelperAttributeDescriptor> GetAttributeDescriptors(INamedTypeSymbol type, ErrorSink errorSink)
{
var attributeDescriptors = new List<TagHelperAttributeDescriptor>();
// Keep indexer descriptors separate to avoid sorting the combined list later.
var indexerDescriptors = new List<TagHelperAttributeDescriptor>();
var accessibleProperties = type.GetMembers().OfType<IPropertySymbol>().Where(IsAccessibleProperty);
var accessibleProperties = GetAccessibleProperties(type);
foreach (var property in accessibleProperties)
{
if (ShouldSkipDescriptorCreation(property))
{
continue;
}
var attributeNameAttribute = property
.GetAttributes()
.Where(a => a.AttributeClass == _htmlAttributeNameAttributeSymbol)
@ -408,8 +482,8 @@ namespace Microsoft.CodeAnalysis.Razor
bool hasExplicitName;
string attributeName;
if (attributeNameAttribute == null ||
attributeNameAttribute.ConstructorArguments.Length == 0 ||
if (attributeNameAttribute == null ||
attributeNameAttribute.ConstructorArguments.Length == 0 ||
string.IsNullOrEmpty((string)attributeNameAttribute.ConstructorArguments[0].Value))
{
hasExplicitName = false;
@ -436,7 +510,11 @@ namespace Microsoft.CodeAnalysis.Razor
// Specified HtmlAttributeNameAttribute.Name though property has no public setter.
errorSink.OnError(
SourceLocation.Zero,
"invalid",
Workspaces.Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty(
GetFullName(type),
property.Name,
TagHelperTypes.HtmlAttributeNameAttribute,
TagHelperTypes.HtmlAttributeName.Name),
length: 0);
continue;
}
@ -478,6 +556,33 @@ namespace Microsoft.CodeAnalysis.Razor
return attributeDescriptors;
}
private IEnumerable<IPropertySymbol> GetAccessibleProperties(INamedTypeSymbol typeSymbol)
{
var accessibleProperties = new Dictionary<string, IPropertySymbol>(StringComparer.Ordinal);
do
{
var members = typeSymbol.GetMembers();
for (var i = 0; i < members.Length; i++)
{
var property = members[i] as IPropertySymbol;
if (property != null &&
property.Parameters.Length == 0 &&
property.GetMethod != null &&
property.GetMethod.DeclaredAccessibility == Accessibility.Public &&
property.GetAttributes().Where(a => a.AttributeClass == _htmlAttributeNotBoundAttributeSymbol).FirstOrDefault() == null &&
!accessibleProperties.ContainsKey(property.Name))
{
accessibleProperties.Add(property.Name, property);
}
}
typeSymbol = typeSymbol.BaseType;
}
while (typeSymbol != null);
return accessibleProperties.Values;
}
// Internal for testing.
internal static bool ValidateTagHelperAttributeDescriptor(
TagHelperAttributeDescriptor attributeDescriptor,
@ -487,20 +592,22 @@ namespace Microsoft.CodeAnalysis.Razor
string nameOrPrefix;
if (attributeDescriptor.IsIndexer)
{
nameOrPrefix = "invalid";
nameOrPrefix = Workspaces.Resources.TagHelperDescriptorFactory_Prefix;
}
else if (string.IsNullOrEmpty(attributeDescriptor.Name))
{
errorSink.OnError(
SourceLocation.Zero,
"invalid",
Workspaces.Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty(
GetFullName(parentType),
attributeDescriptor.PropertyName),
length: 0);
return false;
}
else
{
nameOrPrefix = "invalid";
nameOrPrefix = Workspaces.Resources.TagHelperDescriptorFactory_Name;
}
return ValidateTagHelperAttributeNameOrPrefix(
@ -533,7 +640,10 @@ namespace Microsoft.CodeAnalysis.Razor
// Provide a single error if the entire name is whitespace, not an error per character.
errorSink.OnError(
SourceLocation.Zero,
"invalid",
Workspaces.Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace(
GetFullName(parentType),
propertyName,
nameOrPrefix),
length: 0);
return false;
@ -545,7 +655,12 @@ namespace Microsoft.CodeAnalysis.Razor
{
errorSink.OnError(
SourceLocation.Zero,
"invalid",
Workspaces.Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart(
GetFullName(parentType),
propertyName,
nameOrPrefix,
attributeNameOrPrefix,
DataDashPrefix),
length: 0);
return false;
@ -558,7 +673,12 @@ namespace Microsoft.CodeAnalysis.Razor
{
errorSink.OnError(
SourceLocation.Zero,
"invalid",
Workspaces.Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter(
GetFullName(parentType),
propertyName,
nameOrPrefix,
attributeNameOrPrefix,
character),
length: 0);
isValid = false;
@ -619,7 +739,7 @@ namespace Microsoft.CodeAnalysis.Razor
{
dictionaryType = null;
}
if (dictionaryType == null ||
dictionaryType.TypeArguments[0].SpecialType != SpecialType.System_String)
{
@ -630,7 +750,12 @@ namespace Microsoft.CodeAnalysis.Razor
isInvalid = true;
errorSink.OnError(
SourceLocation.Zero,
"invalid",
Workspaces.Resources.FormatTagHelperDescriptorFactory_InvalidAttributePrefixNotNull(
GetFullName(parentType),
property.Name,
TagHelperTypes.HtmlAttributeNameAttribute,
TagHelperTypes.HtmlAttributeName.DictionaryAttributePrefix,
"IDictionary<string, TValue>"),
length: 0);
}
else if (attributeNameAttribute != null && !hasPublicSetter)
@ -640,7 +765,11 @@ namespace Microsoft.CodeAnalysis.Razor
isInvalid = true;
errorSink.OnError(
SourceLocation.Zero,
"invalid",
Workspaces.Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameAttribute(
GetFullName(parentType),
property.Name,
TagHelperTypes.HtmlAttributeNameAttribute,
"IDictionary<string, TValue>"),
length: 0);
}
@ -656,7 +785,12 @@ namespace Microsoft.CodeAnalysis.Razor
isInvalid = true;
errorSink.OnError(
SourceLocation.Zero,
"invalid",
Workspaces.Resources.FormatTagHelperDescriptorFactory_InvalidAttributePrefixNull(
GetFullName(parentType),
property.Name,
TagHelperTypes.HtmlAttributeNameAttribute,
TagHelperTypes.HtmlAttributeName.DictionaryAttributePrefix,
"IDictionary<string, TValue>"),
length: 0);
return null;
@ -687,6 +821,28 @@ namespace Microsoft.CodeAnalysis.Razor
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,
@ -695,16 +851,28 @@ namespace Microsoft.CodeAnalysis.Razor
TypeName = typeName,
IsStringProperty = isStringProperty,
IsIndexer = isIndexer,
DesignTimeDescriptor = designTimeDescriptor,
};
}
private bool IsAccessibleProperty(IPropertySymbol property)
private bool ShouldSkipDescriptorCreation(ISymbol symbol)
{
// Accessible properties are those with public getters and without [HtmlAttributeNotBound].
return property.Parameters.Length == 0 &&
property.GetMethod != null &&
property.GetMethod.DeclaredAccessibility == Accessibility.Public &&
property.GetAttributes().Where(a => a.AttributeClass == _htmlAttributeNotBoundAttributeSymbol).FirstOrDefault() == null;
if (DesignTime)
{
var editorBrowsableAttribute = symbol.GetAttributes().Where(a => a.AttributeClass == _editorBrowsableAttributeSymbol).FirstOrDefault();
if (editorBrowsableAttribute == null)
{
return false;
}
if (editorBrowsableAttribute.ConstructorArguments.Length > 0)
{
return (EditorBrowsableState)editorBrowsableAttribute.ConstructorArguments[0].Value == EditorBrowsableState.Never;
}
}
return false;
}
/// <summary>
@ -719,15 +887,12 @@ namespace Microsoft.CodeAnalysis.Razor
/// ONE1TWO2THREE3 => one1two2three3
/// First_Second_ThirdHi => first_second_third-hi
/// </example>
private static string ToHtmlCase(string name)
internal static string ToHtmlCase(string name)
{
return HtmlCaseRegex.Replace(name, HtmlCaseRegexReplacement).ToLowerInvariant();
}
private static string GetFullName(ITypeSymbol type)
{
return type.ContainingNamespace.ToDisplayString() + "." + type.Name;
}
private static string GetFullName(ITypeSymbol type) => type.ToDisplayString(FullNameTypeDisplayFormat);
// Internal for testing
internal class RequiredAttributeParser
@ -809,7 +974,7 @@ namespace Microsoft.CodeAnalysis.Razor
{
errorSink.OnError(
SourceLocation.Zero,
"invalid",
Workspaces.Resources.FormatTagHelperDescriptorFactory_InvalidRequiredAttributeCharacter(Current, _requiredAttributes),
length: 0);
return false;
}
@ -891,7 +1056,7 @@ namespace Microsoft.CodeAnalysis.Razor
{
errorSink.OnError(
SourceLocation.Zero,
"invalid",
Workspaces.Resources.FormatTagHelperDescriptorFactory_PartialRequiredAttributeOperator(_requiredAttributes, op),
length: 0);
return null;
}
@ -900,7 +1065,7 @@ namespace Microsoft.CodeAnalysis.Razor
{
errorSink.OnError(
SourceLocation.Zero,
"invalid",
Workspaces.Resources.FormatTagHelperDescriptorFactory_InvalidRequiredAttributeOperator(Current, _requiredAttributes),
length: 0);
return null;
}
@ -925,7 +1090,7 @@ namespace Microsoft.CodeAnalysis.Razor
{
errorSink.OnError(
SourceLocation.Zero,
"invalid",
Workspaces.Resources.FormatTagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes(_requiredAttributes, quote),
length: 0);
return null;
}
@ -1000,7 +1165,7 @@ namespace Microsoft.CodeAnalysis.Razor
{
errorSink.OnError(
SourceLocation.Zero,
"invalid",
Workspaces.Resources.FormatTagHelperDescriptorFactory_CouldNotFindMatchingEndBrace(_requiredAttributes),
length: 0);
return null;
}
@ -1008,7 +1173,7 @@ namespace Microsoft.CodeAnalysis.Razor
{
errorSink.OnError(
SourceLocation.Zero,
"invalid",
Workspaces.Resources.FormatTagHelperDescriptorFactory_InvalidRequiredAttributeCharacter(Current, _requiredAttributes),
length: 0);
return null;
}
@ -1028,7 +1193,7 @@ namespace Microsoft.CodeAnalysis.Razor
{
errorSink.OnError(
SourceLocation.Zero,
"invalid",
Workspaces.Resources.FormatTagHelperDescriptorFactory_CouldNotFindMatchingEndBrace(_requiredAttributes),
length: 0);
return false;

View File

@ -2,8 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
@ -11,10 +9,15 @@ namespace Microsoft.CodeAnalysis.Razor
{
internal class DefaultTagHelperResolver : TagHelperResolver
{
public override async Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync(Project project, CancellationToken cancellationToken = default(CancellationToken))
public DefaultTagHelperResolver(bool designTime)
{
var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
DesignTime = designTime;
}
public bool DesignTime { get; }
public override IReadOnlyList<TagHelperDescriptor> GetTagHelpers(Compilation compilation)
{
var results = new List<TagHelperDescriptor>();
// If ITagHelper isn't defined, then we couldn't possibly find anything.
@ -38,7 +41,7 @@ namespace Microsoft.CodeAnalysis.Razor
}
var errors = new ErrorSink();
var factory = new DefaultTagHelperDescriptorFactory(compilation);
var factory = new DefaultTagHelperDescriptorFactory(compilation, DesignTime);
foreach (var type in types)
{
@ -49,7 +52,7 @@ namespace Microsoft.CodeAnalysis.Razor
}
// Visits top-level types and finds interface implementations.
private class Visitor : SymbolVisitor
internal class Visitor : SymbolVisitor
{
private INamedTypeSymbol _interface;
private List<INamedTypeSymbol> _results;
@ -62,7 +65,7 @@ namespace Microsoft.CodeAnalysis.Razor
public override void VisitNamedType(INamedTypeSymbol symbol)
{
if (symbol.AllInterfaces.Contains(_interface))
if (IsTagHelper(symbol))
{
_results.Add(symbol);
}
@ -75,6 +78,14 @@ namespace Microsoft.CodeAnalysis.Razor
Visit(member);
}
}
internal bool IsTagHelper(INamedTypeSymbol symbol)
{
return symbol.DeclaredAccessibility == Accessibility.Public &&
!symbol.IsAbstract &&
!symbol.IsGenericType &&
symbol.AllInterfaces.Contains(_interface);
}
}
}
}
}

View File

@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.Razor
{
public ILanguageService CreateLanguageService(HostLanguageServices languageServices)
{
return new DefaultTagHelperResolver();
return new DefaultTagHelperResolver(designTime: true);
}
}
}

View File

@ -7,9 +7,6 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;cshtml;razor</PackageTags>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<RootNamespace>Microsoft.CodeAnalysis.Razor</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />

View File

@ -0,0 +1,398 @@
// <auto-generated />
namespace Microsoft.CodeAnalysis.Razor.Workspaces
{
using System.Globalization;
using System.Reflection;
using System.Resources;
internal static class Resources
{
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.CodeAnalysis.Razor.Workspaces.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// {0} cannot be null or an empty string.
/// </summary>
internal static string Argument_Cannot_Be_Null_Or_Empty
{
get { return GetString("Argument_Cannot_Be_Null_Or_Empty"); }
}
/// <summary>
/// {0} cannot be null or an empty string.
/// </summary>
internal static string FormatArgument_Cannot_Be_Null_Or_Empty(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("Argument_Cannot_Be_Null_Or_Empty"), p0);
}
/// <summary>
/// Tag helpers cannot target {0} name '{1}' because it contains a '{2}' character.
/// </summary>
internal static string HtmlTargetElementAttribute_InvalidName
{
get { return GetString("HtmlTargetElementAttribute_InvalidName"); }
}
/// <summary>
/// Tag helpers cannot target {0} name '{1}' because it contains a '{2}' character.
/// </summary>
internal static string FormatHtmlTargetElementAttribute_InvalidName(object p0, object p1, object p2)
{
return string.Format(CultureInfo.CurrentCulture, GetString("HtmlTargetElementAttribute_InvalidName"), p0, p1, p2);
}
/// <summary>
/// {0} name cannot be null or whitespace.
/// </summary>
internal static string HtmlTargetElementAttribute_NameCannotBeNullOrWhitespace
{
get { return GetString("HtmlTargetElementAttribute_NameCannotBeNullOrWhitespace"); }
}
/// <summary>
/// {0} name cannot be null or whitespace.
/// </summary>
internal static string FormatHtmlTargetElementAttribute_NameCannotBeNullOrWhitespace(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("HtmlTargetElementAttribute_NameCannotBeNullOrWhitespace"), p0);
}
/// <summary>
/// Attribute
/// </summary>
internal static string TagHelperDescriptorFactory_Attribute
{
get { return GetString("TagHelperDescriptorFactory_Attribute"); }
}
/// <summary>
/// Attribute
/// </summary>
internal static string FormatTagHelperDescriptorFactory_Attribute()
{
return GetString("TagHelperDescriptorFactory_Attribute");
}
/// <summary>
/// Could not find matching ']' for required attribute '{0}'.
/// </summary>
internal static string TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace
{
get { return GetString("TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace"); }
}
/// <summary>
/// Could not find matching ']' for required attribute '{0}'.
/// </summary>
internal static string FormatTagHelperDescriptorFactory_CouldNotFindMatchingEndBrace(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace"), p0);
}
/// <summary>
/// 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}'.
/// </summary>
internal static string TagHelperDescriptorFactory_InvalidAttributeNameAttribute
{
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameAttribute"); }
}
/// <summary>
/// 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}'.
/// </summary>
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);
}
/// <summary>
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.
/// </summary>
internal static string TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty
{
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty"); }
}
/// <summary>
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.
/// </summary>
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);
}
/// <summary>
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.
/// </summary>
internal static string TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty
{
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty"); }
}
/// <summary>
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.
/// </summary>
internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty"), p0, p1);
}
/// <summary>
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} contains a '{4}' character.
/// </summary>
internal static string TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter
{
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter"); }
}
/// <summary>
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} contains a '{4}' character.
/// </summary>
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);
}
/// <summary>
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} starts with '{4}'.
/// </summary>
internal static string TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart
{
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart"); }
}
/// <summary>
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} starts with '{4}'.
/// </summary>
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);
}
/// <summary>
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a whitespace {2}.
/// </summary>
internal static string TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace
{
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace"); }
}
/// <summary>
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a whitespace {2}.
/// </summary>
internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace(object p0, object p1, object p2)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace"), p0, p1, p2);
}
/// <summary>
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
/// </summary>
internal static string TagHelperDescriptorFactory_InvalidAttributePrefixNotNull
{
get { return GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull"); }
}
/// <summary>
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
/// </summary>
internal static string FormatTagHelperDescriptorFactory_InvalidAttributePrefixNotNull(object p0, object p1, object p2, object p3, object p4)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull"), p0, p1, p2, p3, p4);
}
/// <summary>
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.
/// </summary>
internal static string TagHelperDescriptorFactory_InvalidAttributePrefixNull
{
get { return GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull"); }
}
/// <summary>
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.
/// </summary>
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);
}
/// <summary>
/// Invalid required attribute character '{0}' in required attribute '{1}'. Separate required attributes with commas.
/// </summary>
internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter
{
get { return GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter"); }
}
/// <summary>
/// Invalid required attribute character '{0}' in required attribute '{1}'. Separate required attributes with commas.
/// </summary>
internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeCharacter(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter"), p0, p1);
}
/// <summary>
/// Required attribute '{0}' has mismatched quotes '{1}' around value.
/// </summary>
internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes
{
get { return GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes"); }
}
/// <summary>
/// Required attribute '{0}' has mismatched quotes '{1}' around value.
/// </summary>
internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes"), p0, p1);
}
/// <summary>
/// Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.
/// </summary>
internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeOperator
{
get { return GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeOperator"); }
}
/// <summary>
/// Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.
/// </summary>
internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeOperator(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeOperator"), p0, p1);
}
/// <summary>
/// Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.
/// </summary>
internal static string TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName
{
get { return GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName"); }
}
/// <summary>
/// Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.
/// </summary>
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);
}
/// <summary>
/// Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.
/// </summary>
internal static string TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace
{
get { return GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace"); }
}
/// <summary>
/// Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.
/// </summary>
internal static string FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace"), p0, p1);
}
/// <summary>
/// name
/// </summary>
internal static string TagHelperDescriptorFactory_Name
{
get { return GetString("TagHelperDescriptorFactory_Name"); }
}
/// <summary>
/// name
/// </summary>
internal static string FormatTagHelperDescriptorFactory_Name()
{
return GetString("TagHelperDescriptorFactory_Name");
}
/// <summary>
/// Parent Tag
/// </summary>
internal static string TagHelperDescriptorFactory_ParentTag
{
get { return GetString("TagHelperDescriptorFactory_ParentTag"); }
}
/// <summary>
/// Parent Tag
/// </summary>
internal static string FormatTagHelperDescriptorFactory_ParentTag()
{
return GetString("TagHelperDescriptorFactory_ParentTag");
}
/// <summary>
/// Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.
/// </summary>
internal static string TagHelperDescriptorFactory_PartialRequiredAttributeOperator
{
get { return GetString("TagHelperDescriptorFactory_PartialRequiredAttributeOperator"); }
}
/// <summary>
/// Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.
/// </summary>
internal static string FormatTagHelperDescriptorFactory_PartialRequiredAttributeOperator(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_PartialRequiredAttributeOperator"), p0, p1);
}
/// <summary>
/// prefix
/// </summary>
internal static string TagHelperDescriptorFactory_Prefix
{
get { return GetString("TagHelperDescriptorFactory_Prefix"); }
}
/// <summary>
/// prefix
/// </summary>
internal static string FormatTagHelperDescriptorFactory_Prefix()
{
return GetString("TagHelperDescriptorFactory_Prefix");
}
/// <summary>
/// Tag
/// </summary>
internal static string TagHelperDescriptorFactory_Tag
{
get { return GetString("TagHelperDescriptorFactory_Tag"); }
}
/// <summary>
/// Tag
/// </summary>
internal static string FormatTagHelperDescriptorFactory_Tag()
{
return GetString("TagHelperDescriptorFactory_Tag");
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
System.Diagnostics.Debug.Assert(value != null);
if (formatterNames != null)
{
for (var i = 0; i < formatterNames.Length; i++)
{
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
}
}
return value;
}
}
}

View File

@ -0,0 +1,189 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Argument_Cannot_Be_Null_Or_Empty" xml:space="preserve">
<value>{0} cannot be null or an empty string.</value>
</data>
<data name="HtmlTargetElementAttribute_InvalidName" xml:space="preserve">
<value>Tag helpers cannot target {0} name '{1}' because it contains a '{2}' character.</value>
</data>
<data name="HtmlTargetElementAttribute_NameCannotBeNullOrWhitespace" xml:space="preserve">
<value>{0} name cannot be null or whitespace.</value>
</data>
<data name="TagHelperDescriptorFactory_Attribute" xml:space="preserve">
<value>Attribute</value>
</data>
<data name="TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace" xml:space="preserve">
<value>Could not find matching ']' for required attribute '{0}'.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributeNameAttribute" xml:space="preserve">
<value>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}'.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} contains a '{4}' character.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} starts with '{4}'.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a whitespace {2}.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributePrefixNotNull" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributePrefixNull" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter" xml:space="preserve">
<value>Invalid required attribute character '{0}' in required attribute '{1}'. Separate required attributes with commas.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes" xml:space="preserve">
<value>Required attribute '{0}' has mismatched quotes '{1}' around value.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidRequiredAttributeOperator" xml:space="preserve">
<value>Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName" xml:space="preserve">
<value>Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace" xml:space="preserve">
<value>Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.</value>
</data>
<data name="TagHelperDescriptorFactory_Name" xml:space="preserve">
<value>name</value>
</data>
<data name="TagHelperDescriptorFactory_ParentTag" xml:space="preserve">
<value>Parent Tag</value>
</data>
<data name="TagHelperDescriptorFactory_PartialRequiredAttributeOperator" xml:space="preserve">
<value>Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.</value>
</data>
<data name="TagHelperDescriptorFactory_Prefix" xml:space="preserve">
<value>prefix</value>
</data>
<data name="TagHelperDescriptorFactory_Tag" xml:space="preserve">
<value>Tag</value>
</data>
</root>

View File

@ -4,15 +4,21 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.CodeAnalysis.Razor
{
internal abstract class TagHelperResolver : ILanguageService
{
public abstract Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync(
public abstract IReadOnlyList<TagHelperDescriptor> GetTagHelpers(Compilation compilation);
public virtual async Task<IReadOnlyList<TagHelperDescriptor>> GetTagHelpersAsync(
Project project,
CancellationToken cancellationToken = default(CancellationToken));
CancellationToken cancellationToken = default(CancellationToken))
{
var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
return GetTagHelpers(compilation);
}
}
}

View File

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Razor
{
{
internal static class TagHelperTypes
{
public const string ITagHelper = "Microsoft.AspNetCore.Razor.TagHelpers.ITagHelper";
@ -15,10 +15,13 @@ namespace Microsoft.CodeAnalysis.Razor
public const string HtmlTargetElementAttribute = "Microsoft.AspNetCore.Razor.TagHelpers.HtmlTargetElementAttribute";
public const string OutputElementHintAttribute = "Microsoft.AspNetCore.Razor.TagHelpers.OutputElementHintAttribute";
public const string RestrictChildrenAttribute = "Microsoft.AspNetCore.Razor.TagHelpers.RestrictChildrenAttribute";
public static class HtmlAttributeName
{
public const string Name = "Name";
public const string DictionaryAttributePrefix = "DictionaryAttributePrefix";
}

View File

@ -0,0 +1,82 @@
// 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;
using Microsoft.CodeAnalysis.Razor.Workspaces;
namespace Microsoft.CodeAnalysis.Razor
{
/// <summary>
/// Extracts summary and remarks XML documentation from XML member documentation.
/// </summary>
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'.
_element = XElement.Parse(content);
Debug.Assert(_element.Name == "member");
}
/// <summary>
/// Retrieves the <c>&lt;summary&gt;</c> documentation.
/// </summary>
/// <returns><c>&lt;summary&gt;</c> documentation.</returns>
public string GetSummary()
{
var summaryElement = _element.Element("summary");
if (summaryElement != null)
{
var summaryValue = GetElementValue(summaryElement);
return summaryValue;
}
return null;
}
/// <summary>
/// Retrieves the <c>&lt;remarks&gt;</c> documentation.
/// </summary>
/// <returns><c>&lt;remarks&gt;</c> documentation.</returns>
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();
}
}
}

View File

@ -9,14 +9,13 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Evolution;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
using Microsoft.CodeAnalysis.Razor;
namespace Microsoft.CodeAnalysis.Remote.Razor
{
internal class RazorLanguageService : ServiceHubServiceBase
{
public RazorLanguageService(Stream stream, IServiceProvider serviceProvider)
public RazorLanguageService(Stream stream, IServiceProvider serviceProvider)
: base(stream, serviceProvider)
{
}
@ -28,7 +27,7 @@ namespace Microsoft.CodeAnalysis.Remote.Razor
var solution = await GetSolutionAsync().ConfigureAwait(false);
var project = solution.GetProject(projectId);
var resolver = new DefaultTagHelperResolver();
var resolver = new DefaultTagHelperResolver(designTime: true);
var results = await resolver.GetTagHelpersAsync(project, cancellationToken).ConfigureAwait(false);
return results;

View File

@ -0,0 +1,97 @@
// 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.AspNetCore.Razor.Evolution;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
using Microsoft.Extensions.Internal;
using Xunit;
namespace Microsoft.CodeAnalysis.Razor.Workspaces.Test.Comparers
{
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;
}
}
}

View File

@ -0,0 +1,44 @@
// 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 Microsoft.Extensions.Internal;
using Xunit;
namespace Microsoft.CodeAnalysis.Razor.Workspaces.Test.Comparers
{
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;
}
}
}

View File

@ -0,0 +1,57 @@
// 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 Microsoft.Extensions.Internal;
using Xunit;
namespace Microsoft.CodeAnalysis.Razor.Workspaces.Test.Comparers
{
internal class TagHelperAttributeDescriptorComparer : IEqualityComparer<TagHelperAttributeDescriptor>
{
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;
}
}
}

View File

@ -0,0 +1,48 @@
// 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 Microsoft.Extensions.Internal;
using Xunit;
namespace Microsoft.CodeAnalysis.Razor.Workspaces.Test.Comparers
{
internal class TagHelperAttributeDesignTimeDescriptorComparer :
IEqualityComparer<TagHelperAttributeDesignTimeDescriptor>
{
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;
}
}
}

View File

@ -0,0 +1,48 @@
// 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 Microsoft.Extensions.Internal;
using Xunit;
namespace Microsoft.CodeAnalysis.Razor.Workspaces.Test.Comparers
{
internal class TagHelperDesignTimeDescriptorComparer : IEqualityComparer<TagHelperDesignTimeDescriptor>
{
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;
}
}
}

View File

@ -0,0 +1,125 @@
// 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.TagHelpers;
using Microsoft.CodeAnalysis.Razor.Workspaces.Test;
using Xunit;
namespace Microsoft.CodeAnalysis.Razor.Workspaces
{
public class DefaultTagHelperResolverTest
{
private static Compilation Compilation { get; } = TestCompilation.Create();
private static INamedTypeSymbol ITagHelperSymbol { get; } = Compilation.GetTypeByMetadataName(TagHelperTypes.ITagHelper);
private DefaultTagHelperResolver.Visitor TestVisitor => new DefaultTagHelperResolver.Visitor(ITagHelperSymbol, new List<INamedTypeSymbol>());
[Fact]
public void IsTagHelper_PlainTagHelper_ReturnsTrue()
{
// Arrange
var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Valid_PlainTagHelper).FullName);
// Act
var isTagHelper = TestVisitor.IsTagHelper(tagHelperSymbol);
// Assert
Assert.True(isTagHelper);
}
[Fact]
public void IsTagHelper_InheritedTagHelper_ReturnsTrue()
{
// Arrange
var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Valid_InheritedTagHelper).FullName);
// Act
var isTagHelper = TestVisitor.IsTagHelper(tagHelperSymbol);
// Assert
Assert.True(isTagHelper);
}
[Fact]
public void IsTagHelper_AbstractTagHelper_ReturnsFalse()
{
// Arrange
var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_AbstractTagHelper).FullName);
// Act
var isTagHelper = TestVisitor.IsTagHelper(tagHelperSymbol);
// Assert
Assert.False(isTagHelper);
}
[Fact]
public void IsTagHelper_GenericTagHelper_ReturnsFalse()
{
// Arrange
var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_GenericTagHelper<>).FullName);
// Act
var isTagHelper = TestVisitor.IsTagHelper(tagHelperSymbol);
// Assert
Assert.False(isTagHelper);
}
[Fact]
public void IsTagHelper_InternalTagHelper_ReturnsFalse()
{
// Arrange
var tagHelperSymbol = Compilation.GetTypeByMetadataName(typeof(Invalid_InternalTagHelper).FullName);
// Act
var isTagHelper = TestVisitor.IsTagHelper(tagHelperSymbol);
// Assert
Assert.False(isTagHelper);
}
[Fact]
public void GetTagHelpers_NestedTagHelpersAreNotFound()
{
// Arrange
var resolver = new DefaultTagHelperResolver(designTime: false);
// Act
var descriptors = resolver.GetTagHelpers(Compilation);
// Assert
var matchingDescriptors = descriptors
.Where(descriptor => string.Equals(descriptor.TypeName, typeof(Invalid_NestedPublicTagHelper).FullName, StringComparison.Ordinal));
Assert.Empty(matchingDescriptors);
}
public class Invalid_NestedPublicTagHelper : TagHelper
{
}
}
public abstract class Invalid_AbstractTagHelper : TagHelper
{
}
public class Invalid_GenericTagHelper<T> : TagHelper
{
}
internal class Invalid_InternalTagHelper : TagHelper
{
}
public class Valid_PlainTagHelper : TagHelper
{
}
public class Valid_InheritedTagHelper : Valid_PlainTagHelper
{
}
}

View File

@ -1,6 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<Import Project="..\..\build\common.props" />
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<TargetFrameworks>netcoreapp1.1;net451</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.1' ">
@ -14,8 +16,11 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor.Evolution\Microsoft.AspNetCore.Razor.Evolution.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor.Runtime\Microsoft.AspNetCore.Razor.Runtime.csproj" />
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor\Microsoft.AspNetCore.Razor.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="1.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0-preview-20161123-03" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0-beta4-build1194" />
<PackageReference Include="xunit" Version="2.2.0-beta4-build3444" />
@ -23,4 +28,7 @@
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp1.1' ">
<PackageReference Include="Microsoft.NETCore.App" Version="1.2.0-*" />
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,452 @@
// 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.ComponentModel;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Microsoft.CodeAnalysis.Razor.Workspaces.Test
{
public enum CustomEnum
{
FirstValue,
SecondValue
}
public class EnumTagHelper : TagHelper
{
public int NonEnumProperty { get; set; }
public CustomEnum EnumProperty { get; set; }
}
[HtmlTargetElement("p")]
[HtmlTargetElement("input")]
public class MultiEnumTagHelper : EnumTagHelper
{
}
[HtmlTargetElement("input", ParentTag = "div")]
public class RequiredParentTagHelper : TagHelper
{
}
[HtmlTargetElement("p", ParentTag = "div")]
[HtmlTargetElement("input", ParentTag = "section")]
public class MultiSpecifiedRequiredParentTagHelper : TagHelper
{
}
[HtmlTargetElement("p")]
[HtmlTargetElement("input", ParentTag = "div")]
public class MultiWithUnspecifiedRequiredParentTagHelper : TagHelper
{
}
[RestrictChildren("p")]
public class RestrictChildrenTagHelper
{
}
[RestrictChildren("p", "strong")]
public class DoubleRestrictChildrenTagHelper
{
}
[HtmlTargetElement("p")]
[HtmlTargetElement("div")]
[RestrictChildren("p", "strong")]
public class MultiTargetRestrictChildrenTagHelper
{
}
[HtmlTargetElement("input", TagStructure = TagStructure.WithoutEndTag)]
public class TagStructureTagHelper : TagHelper
{
}
[HtmlTargetElement("p", TagStructure = TagStructure.NormalOrSelfClosing)]
[HtmlTargetElement("input", TagStructure = TagStructure.WithoutEndTag)]
public class MultiSpecifiedTagStructureTagHelper : TagHelper
{
}
[HtmlTargetElement("p")]
[HtmlTargetElement("input", TagStructure = TagStructure.WithoutEndTag)]
public class MultiWithUnspecifiedTagStructureTagHelper : TagHelper
{
}
[EditorBrowsable(EditorBrowsableState.Always)]
public class DefaultEditorBrowsableTagHelper : TagHelper
{
[EditorBrowsable(EditorBrowsableState.Always)]
public int Property { get; set; }
}
public class HiddenPropertyEditorBrowsableTagHelper : TagHelper
{
[EditorBrowsable(EditorBrowsableState.Never)]
public int Property { get; set; }
}
public class MultiPropertyEditorBrowsableTagHelper : TagHelper
{
[EditorBrowsable(EditorBrowsableState.Never)]
public int Property { get; set; }
public virtual int Property2 { get; set; }
}
public class OverriddenPropertyEditorBrowsableTagHelper : MultiPropertyEditorBrowsableTagHelper
{
[EditorBrowsable(EditorBrowsableState.Never)]
public override int Property2 { get; set; }
}
[EditorBrowsable(EditorBrowsableState.Never)]
public class EditorBrowsableTagHelper : TagHelper
{
[EditorBrowsable(EditorBrowsableState.Never)]
public virtual int Property { get; set; }
}
public class InheritedEditorBrowsableTagHelper : EditorBrowsableTagHelper
{
public override int Property { get; set; }
}
[EditorBrowsable(EditorBrowsableState.Advanced)]
public class OverriddenEditorBrowsableTagHelper : EditorBrowsableTagHelper
{
[EditorBrowsable(EditorBrowsableState.Advanced)]
public override int Property { get; set; }
}
[HtmlTargetElement("p")]
[HtmlTargetElement("div")]
[EditorBrowsable(EditorBrowsableState.Never)]
public class MultiEditorBrowsableTagHelper : TagHelper
{
}
[HtmlTargetElement(Attributes = "class*")]
public class AttributeWildcardTargetingTagHelper : TagHelper
{
}
[HtmlTargetElement(Attributes = "class*,style*")]
public class MultiAttributeWildcardTargetingTagHelper : TagHelper
{
}
[HtmlTargetElement(Attributes = "class")]
public class AttributeTargetingTagHelper : TagHelper
{
}
[HtmlTargetElement(Attributes = "class,style")]
public class MultiAttributeTargetingTagHelper : TagHelper
{
}
[HtmlTargetElement(Attributes = "custom")]
[HtmlTargetElement(Attributes = "class,style")]
public class MultiAttributeAttributeTargetingTagHelper : TagHelper
{
}
[HtmlTargetElement(Attributes = "style")]
public class InheritedAttributeTargetingTagHelper : AttributeTargetingTagHelper
{
}
[HtmlTargetElement("input", Attributes = "class")]
public class RequiredAttributeTagHelper : TagHelper
{
}
[HtmlTargetElement("div", Attributes = "class")]
public class InheritedRequiredAttributeTagHelper : RequiredAttributeTagHelper
{
}
[HtmlTargetElement("div", Attributes = "class")]
[HtmlTargetElement("input", Attributes = "class")]
public class MultiAttributeRequiredAttributeTagHelper : TagHelper
{
}
[HtmlTargetElement("input", Attributes = "style")]
[HtmlTargetElement("input", Attributes = "class")]
public class MultiAttributeSameTagRequiredAttributeTagHelper : TagHelper
{
}
[HtmlTargetElement("input", Attributes = "class,style")]
public class MultiRequiredAttributeTagHelper : TagHelper
{
}
[HtmlTargetElement("div", Attributes = "style")]
public class InheritedMultiRequiredAttributeTagHelper : MultiRequiredAttributeTagHelper
{
}
[HtmlTargetElement("div", Attributes = "class,style")]
[HtmlTargetElement("input", Attributes = "class,style")]
public class MultiTagMultiRequiredAttributeTagHelper : TagHelper
{
}
[HtmlTargetElement("p")]
[HtmlTargetElement("div")]
public class MultiTagTagHelper
{
public string ValidAttribute { get; set; }
}
public class InheritedMultiTagTagHelper : MultiTagTagHelper
{
}
[HtmlTargetElement("p")]
[HtmlTargetElement("p")]
[HtmlTargetElement("div")]
[HtmlTargetElement("div")]
public class DuplicateTagNameTagHelper
{
}
[HtmlTargetElement("data-condition")]
public class OverrideNameTagHelper
{
}
public class InheritedSingleAttributeTagHelper : SingleAttributeTagHelper
{
}
public class DuplicateAttributeNameTagHelper
{
public string MyNameIsLegion { get; set; }
[HtmlAttributeName("my-name-is-legion")]
public string Fred { get; set; }
}
public class NotBoundAttributeTagHelper
{
public object BoundProperty { get; set; }
[HtmlAttributeNotBound]
public string NotBoundProperty { get; set; }
[HtmlAttributeName("unused")]
[HtmlAttributeNotBound]
public string NamedNotBoundProperty { get; set; }
}
public class OverriddenAttributeTagHelper
{
[HtmlAttributeName("SomethingElse")]
public virtual string ValidAttribute1 { get; set; }
[HtmlAttributeName("Something-Else")]
public string ValidAttribute2 { get; set; }
}
public class InheritedOverriddenAttributeTagHelper : OverriddenAttributeTagHelper
{
public override string ValidAttribute1 { get; set; }
}
public class InheritedNotOverriddenAttributeTagHelper : OverriddenAttributeTagHelper
{
}
public class ALLCAPSTAGHELPER : TagHelper
{
public int ALLCAPSATTRIBUTE { get; set; }
}
public class CAPSOnOUTSIDETagHelper : TagHelper
{
public int CAPSOnOUTSIDEATTRIBUTE { get; set; }
}
public class capsONInsideTagHelper : TagHelper
{
public int capsONInsideattribute { get; set; }
}
public class One1Two2Three3TagHelper : TagHelper
{
public int One1Two2Three3Attribute { get; set; }
}
public class ONE1TWO2THREE3TagHelper : TagHelper
{
public int ONE1TWO2THREE3Attribute { get; set; }
}
public class First_Second_ThirdHiTagHelper : TagHelper
{
public int First_Second_ThirdAttribute { get; set; }
}
public class UNSuffixedCLASS : TagHelper
{
public int UNSuffixedATTRIBUTE { get; set; }
}
public class InvalidBoundAttribute : TagHelper
{
public string DataSomething { get; set; }
}
public class InvalidBoundAttributeWithValid : SingleAttributeTagHelper
{
public string DataSomething { get; set; }
}
public class OverriddenInvalidBoundAttributeWithValid : TagHelper
{
[HtmlAttributeName("valid-something")]
public string DataSomething { get; set; }
}
public class OverriddenValidBoundAttributeWithInvalid : TagHelper
{
[HtmlAttributeName("data-something")]
public string ValidSomething { get; set; }
}
public class OverriddenValidBoundAttributeWithInvalidUpperCase : TagHelper
{
[HtmlAttributeName("DATA-SOMETHING")]
public string ValidSomething { get; set; }
}
public class DefaultValidHtmlAttributePrefix : TagHelper
{
public IDictionary<string, string> DictionaryProperty { get; set; }
}
public class SingleValidHtmlAttributePrefix : TagHelper
{
[HtmlAttributeName("valid-name")]
public IDictionary<string, string> DictionaryProperty { get; set; }
}
public class MultipleValidHtmlAttributePrefix : TagHelper
{
[HtmlAttributeName("valid-name1", DictionaryAttributePrefix = "valid-prefix1-")]
public Dictionary<string, object> DictionaryProperty { get; set; }
[HtmlAttributeName("valid-name2", DictionaryAttributePrefix = "valid-prefix2-")]
public DictionarySubclass DictionarySubclassProperty { get; set; }
[HtmlAttributeName("valid-name3", DictionaryAttributePrefix = "valid-prefix3-")]
public DictionaryWithoutParameterlessConstructor DictionaryWithoutParameterlessConstructorProperty { get; set; }
[HtmlAttributeName("valid-name4", DictionaryAttributePrefix = "valid-prefix4-")]
public GenericDictionarySubclass<object> GenericDictionarySubclassProperty { get; set; }
[HtmlAttributeName("valid-name5", DictionaryAttributePrefix = "valid-prefix5-")]
public SortedDictionary<string, int> SortedDictionaryProperty { get; set; }
[HtmlAttributeName("valid-name6")]
public string StringProperty { get; set; }
public IDictionary<string, int> GetOnlyDictionaryProperty { get; }
[HtmlAttributeName(DictionaryAttributePrefix = "valid-prefix6")]
public IDictionary<string, string> GetOnlyDictionaryPropertyWithAttributePrefix { get; }
}
public class SingleInvalidHtmlAttributePrefix : TagHelper
{
[HtmlAttributeName("valid-name", DictionaryAttributePrefix = "valid-prefix")]
public string StringProperty { get; set; }
}
public class MultipleInvalidHtmlAttributePrefix : TagHelper
{
[HtmlAttributeName("valid-name1")]
public long LongProperty { get; set; }
[HtmlAttributeName("valid-name2", DictionaryAttributePrefix = "valid-prefix2-")]
public Dictionary<int, string> DictionaryOfIntProperty { get; set; }
[HtmlAttributeName("valid-name3", DictionaryAttributePrefix = "valid-prefix3-")]
public IReadOnlyDictionary<string, object> ReadOnlyDictionaryProperty { get; set; }
[HtmlAttributeName("valid-name4", DictionaryAttributePrefix = "valid-prefix4-")]
public int IntProperty { get; set; }
[HtmlAttributeName("valid-name5", DictionaryAttributePrefix = "valid-prefix5-")]
public DictionaryOfIntSubclass DictionaryOfIntSubclassProperty { get; set; }
[HtmlAttributeName(DictionaryAttributePrefix = "valid-prefix6")]
public IDictionary<int, string> GetOnlyDictionaryAttributePrefix { get; }
[HtmlAttributeName("invalid-name7")]
public IDictionary<string, object> GetOnlyDictionaryPropertyWithAttributeName { get; }
}
public class DictionarySubclass : Dictionary<string, string>
{
}
public class DictionaryWithoutParameterlessConstructor : Dictionary<string, string>
{
public DictionaryWithoutParameterlessConstructor(int count)
: base()
{
}
}
public class DictionaryOfIntSubclass : Dictionary<int, string>
{
}
public class GenericDictionarySubclass<TValue> : Dictionary<string, TValue>
{
}
[OutputElementHint("strong")]
public class OutputElementHintTagHelper : TagHelper
{
}
[HtmlTargetElement("a")]
[HtmlTargetElement("p")]
[OutputElementHint("div")]
public class MulitpleDescriptorTagHelperWithOutputElementHint : TagHelper
{
}
public class NonPublicAccessorTagHelper : TagHelper
{
public string ValidAttribute { get; set; }
public string InvalidPrivateSetAttribute { get; private set; }
public string InvalidPrivateGetAttribute { private get; set; }
protected string InvalidProtectedAttribute { get; set; }
internal string InvalidInternalAttribute { get; set; }
protected internal string InvalidProtectedInternalAttribute { get; set; }
}
public class SingleAttributeTagHelper : TagHelper
{
public int IntAttribute { get; set; }
}
public class MissingAccessorTagHelper : TagHelper
{
public string ValidAttribute { get; set; }
public string InvalidNoGetAttribute { set { } }
public string InvalidNoSetAttribute { get { return string.Empty; } }
}
}

View File

@ -0,0 +1,30 @@
// 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.CodeAnalysis.CSharp;
namespace Microsoft.CodeAnalysis.Razor.Workspaces.Test
{
public static class TestCompilation
{
public static Compilation Create(SyntaxTree syntaxTree = null)
{
IEnumerable<SyntaxTree> syntaxTrees = null;
if (syntaxTree != null)
{
syntaxTrees = new[] { syntaxTree };
}
var references = AppDomain.CurrentDomain
.GetAssemblies()
.Select(assembly => MetadataReference.CreateFromFile(assembly.Location));
var compilation = CSharpCompilation.Create("TestAssembly", syntaxTrees, references);
return compilation;
}
}
}

View File

@ -22,7 +22,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
if (client == null)
{
// The OOP host is turned off, so let's do this in process.
var resolver = new CodeAnalysis.Razor.DefaultTagHelperResolver();
var resolver = new CodeAnalysis.Razor.DefaultTagHelperResolver(designTime: true);
return await resolver.GetTagHelpersAsync(project, CancellationToken.None).ConfigureAwait(false);
}