Add TagHelper documentation resolution to TagHelperDescriptorResolver.
- Added TagHelperUseageDescriptor and associated factory for the TagHelperDescriptorFactory to utilize. - TagHelperUseageDescriptors are only created during design time. - CoreCLR is not supported for XML documentation resolution for now. Can revisit this later when we have better tooling integration with CoreCLR. #352
This commit is contained in:
parent
361a53ba3c
commit
ccf8433f27
|
|
@ -38,23 +38,30 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// </summary>
|
||||
/// <param name="assemblyName">The assembly name that contains <paramref name="type"/>.</param>
|
||||
/// <param name="type">The type to create a <see cref="TagHelperDescriptor"/> from.</param>
|
||||
/// <param name="designTime">Indicates if the returned <see cref="TagHelperDescriptor"/>s should include
|
||||
/// design time specific information.</param>
|
||||
/// <param name="errorSink">The <see cref="ErrorSink"/> used to collect <see cref="RazorError"/>s encountered
|
||||
/// when creating <see cref="TagHelperDescriptor"/>s for the given <paramref name="type"/>.</param>
|
||||
/// <returns>
|
||||
/// A collection of <see cref="TagHelperDescriptor"/>s that describe the given <paramref name="type"/>.
|
||||
/// </returns>
|
||||
public static IEnumerable<TagHelperDescriptor> CreateDescriptors(
|
||||
string assemblyName,
|
||||
[NotNull] Type type,
|
||||
bool designTime,
|
||||
[NotNull] ErrorSink errorSink)
|
||||
{
|
||||
var typeInfo = type.GetTypeInfo();
|
||||
var attributeDescriptors = GetAttributeDescriptors(type, errorSink);
|
||||
var attributeDescriptors = GetAttributeDescriptors(type, designTime, errorSink);
|
||||
var targetElementAttributes = GetValidTargetElementAttributes(typeInfo, errorSink);
|
||||
|
||||
var tagHelperDescriptors =
|
||||
BuildTagHelperDescriptors(
|
||||
typeInfo,
|
||||
assemblyName,
|
||||
attributeDescriptors,
|
||||
targetElementAttributes);
|
||||
targetElementAttributes,
|
||||
designTime);
|
||||
|
||||
return tagHelperDescriptors.Distinct(TagHelperDescriptorComparer.Default);
|
||||
}
|
||||
|
|
@ -72,8 +79,18 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
TypeInfo typeInfo,
|
||||
string assemblyName,
|
||||
IEnumerable<TagHelperAttributeDescriptor> attributeDescriptors,
|
||||
IEnumerable<TargetElementAttribute> targetElementAttributes)
|
||||
IEnumerable<TargetElementAttribute> targetElementAttributes,
|
||||
bool designTime)
|
||||
{
|
||||
TagHelperUsageDescriptor typeUsageDescriptor = null;
|
||||
|
||||
#if !DNXCORE50
|
||||
if (designTime)
|
||||
{
|
||||
typeUsageDescriptor = TagHelperUsageDescriptorFactory.CreateDescriptor(typeInfo.GetType());
|
||||
}
|
||||
#endif
|
||||
|
||||
var typeName = typeInfo.FullName;
|
||||
|
||||
// If there isn't an attribute specifying the tag name derive it from the name
|
||||
|
|
@ -93,19 +110,27 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
typeName,
|
||||
assemblyName,
|
||||
attributeDescriptors,
|
||||
requiredAttributes: Enumerable.Empty<string>())
|
||||
requiredAttributes: Enumerable.Empty<string>(),
|
||||
usageDescriptor: typeUsageDescriptor)
|
||||
};
|
||||
}
|
||||
|
||||
return targetElementAttributes.Select(
|
||||
attribute => BuildTagHelperDescriptor(typeName, assemblyName, attributeDescriptors, attribute));
|
||||
attribute =>
|
||||
BuildTagHelperDescriptor(
|
||||
typeName,
|
||||
assemblyName,
|
||||
attributeDescriptors,
|
||||
attribute,
|
||||
typeUsageDescriptor));
|
||||
}
|
||||
|
||||
private static TagHelperDescriptor BuildTagHelperDescriptor(
|
||||
string typeName,
|
||||
string assemblyName,
|
||||
IEnumerable<TagHelperAttributeDescriptor> attributeDescriptors,
|
||||
TargetElementAttribute targetElementAttribute)
|
||||
TargetElementAttribute targetElementAttribute,
|
||||
TagHelperUsageDescriptor usageDescriptor)
|
||||
{
|
||||
var requiredAttributes = GetCommaSeparatedValues(targetElementAttribute.Attributes);
|
||||
|
||||
|
|
@ -114,7 +139,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
typeName,
|
||||
assemblyName,
|
||||
attributeDescriptors,
|
||||
requiredAttributes);
|
||||
requiredAttributes,
|
||||
usageDescriptor);
|
||||
}
|
||||
|
||||
private static TagHelperDescriptor BuildTagHelperDescriptor(
|
||||
|
|
@ -122,7 +148,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
string typeName,
|
||||
string assemblyName,
|
||||
IEnumerable<TagHelperAttributeDescriptor> attributeDescriptors,
|
||||
IEnumerable<string> requiredAttributes)
|
||||
IEnumerable<string> requiredAttributes,
|
||||
TagHelperUsageDescriptor usageDescriptor)
|
||||
{
|
||||
return new TagHelperDescriptor(
|
||||
prefix: string.Empty,
|
||||
|
|
@ -130,7 +157,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
typeName: typeName,
|
||||
assemblyName: assemblyName,
|
||||
attributes: attributeDescriptors,
|
||||
requiredAttributes: requiredAttributes);
|
||||
requiredAttributes: requiredAttributes,
|
||||
usageDescriptor: usageDescriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -225,6 +253,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
|
||||
private static IEnumerable<TagHelperAttributeDescriptor> GetAttributeDescriptors(
|
||||
Type type,
|
||||
bool designTime,
|
||||
ErrorSink errorSink)
|
||||
{
|
||||
var accessibleProperties = type.GetRuntimeProperties().Where(IsAccessibleProperty);
|
||||
|
|
@ -236,7 +265,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
foreach (var property in accessibleProperties)
|
||||
{
|
||||
var attributeNameAttribute = property.GetCustomAttribute<HtmlAttributeNameAttribute>(inherit: false);
|
||||
var descriptor = ToAttributeDescriptor(property, attributeNameAttribute);
|
||||
var descriptor = ToAttributeDescriptor(property, attributeNameAttribute, designTime);
|
||||
if (ValidateTagHelperAttributeDescriptor(descriptor, type, errorSink))
|
||||
{
|
||||
bool isInvalid;
|
||||
|
|
@ -246,6 +275,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
parentType: type,
|
||||
errorSink: errorSink,
|
||||
defaultPrefix: descriptor.Name + "-",
|
||||
designTime: designTime,
|
||||
isInvalid: out isInvalid);
|
||||
|
||||
if (indexerDescriptor != null &&
|
||||
|
|
@ -358,17 +388,19 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
|
||||
private static TagHelperAttributeDescriptor ToAttributeDescriptor(
|
||||
PropertyInfo property,
|
||||
HtmlAttributeNameAttribute attributeNameAttribute)
|
||||
HtmlAttributeNameAttribute attributeNameAttribute,
|
||||
bool designTime)
|
||||
{
|
||||
var attributeName = attributeNameAttribute != null ?
|
||||
attributeNameAttribute.Name :
|
||||
ToHtmlCase(property.Name);
|
||||
|
||||
return new TagHelperAttributeDescriptor(
|
||||
return ToAttributeDescriptor(
|
||||
property,
|
||||
attributeName,
|
||||
property.Name,
|
||||
property.PropertyType.FullName,
|
||||
isIndexer: false);
|
||||
isIndexer: false,
|
||||
designTime: designTime);
|
||||
}
|
||||
|
||||
private static TagHelperAttributeDescriptor ToIndexerAttributeDescriptor(
|
||||
|
|
@ -377,6 +409,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
Type parentType,
|
||||
ErrorSink errorSink,
|
||||
string defaultPrefix,
|
||||
bool designTime,
|
||||
out bool isInvalid)
|
||||
{
|
||||
isInvalid = false;
|
||||
|
|
@ -414,11 +447,36 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
return null;
|
||||
}
|
||||
|
||||
return new TagHelperAttributeDescriptor(
|
||||
name: prefix,
|
||||
propertyName: property.Name,
|
||||
return ToAttributeDescriptor(
|
||||
property,
|
||||
attributeName: prefix,
|
||||
typeName: dictionaryTypeArguments[1].FullName,
|
||||
isIndexer: true);
|
||||
isIndexer: true,
|
||||
designTime: designTime);
|
||||
}
|
||||
|
||||
private static TagHelperAttributeDescriptor ToAttributeDescriptor(
|
||||
PropertyInfo property,
|
||||
string attributeName,
|
||||
string typeName,
|
||||
bool isIndexer,
|
||||
bool designTime)
|
||||
{
|
||||
TagHelperUsageDescriptor propertyUsageDescriptor = null;
|
||||
|
||||
#if !DNXCORE50
|
||||
if (designTime)
|
||||
{
|
||||
propertyUsageDescriptor = TagHelperUsageDescriptorFactory.CreateDescriptor(property);
|
||||
}
|
||||
#endif
|
||||
|
||||
return new TagHelperAttributeDescriptor(
|
||||
attributeName,
|
||||
property.Name,
|
||||
typeName,
|
||||
isIndexer,
|
||||
propertyUsageDescriptor);
|
||||
}
|
||||
|
||||
private static bool IsAccessibleProperty(PropertyInfo property)
|
||||
|
|
|
|||
|
|
@ -26,14 +26,16 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
};
|
||||
|
||||
private readonly TagHelperTypeResolver _typeResolver;
|
||||
private readonly bool _designTime;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of the <see cref="TagHelperDescriptorResolver"/> class.
|
||||
/// </summary>
|
||||
public TagHelperDescriptorResolver()
|
||||
: this(new TagHelperTypeResolver())
|
||||
/// <param name="designTime">Indicates whether resolved <see cref="TagHelperDescriptor"/>s should include
|
||||
/// design time specific information.</param>
|
||||
public TagHelperDescriptorResolver(bool designTime)
|
||||
: this(new TagHelperTypeResolver(), designTime)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -41,9 +43,12 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// specified <paramref name="typeResolver"/>.
|
||||
/// </summary>
|
||||
/// <param name="typeResolver">The <see cref="TagHelperTypeResolver"/>.</param>
|
||||
public TagHelperDescriptorResolver(TagHelperTypeResolver typeResolver)
|
||||
/// <param name="designTime">Indicates whether resolved <see cref="TagHelperDescriptor"/>s should include
|
||||
/// design time specific information.</param>
|
||||
public TagHelperDescriptorResolver(TagHelperTypeResolver typeResolver, bool designTime)
|
||||
{
|
||||
_typeResolver = typeResolver;
|
||||
_designTime = designTime;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -113,7 +118,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
/// The name of the assembly to resolve <see cref="TagHelperDescriptor"/>s from.
|
||||
/// </param>
|
||||
/// <param name="documentLocation">The <see cref="SourceLocation"/> of the directive.</param>
|
||||
/// <param name="errorSink">Used to record errors found when resolving <see cref="TagHelperDescriptor"/>s
|
||||
/// <param name="errorSink">Used to record errors found when resolving <see cref="TagHelperDescriptor"/>s
|
||||
/// within the given <paramref name="assemblyName"/>.</param>
|
||||
/// <returns><see cref="TagHelperDescriptor"/>s for <see cref="ITagHelper"/>s from the given
|
||||
/// <paramref name="assemblyName"/>.</returns>
|
||||
|
|
@ -128,7 +133,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
|
||||
// Convert types to TagHelperDescriptors
|
||||
var descriptors = tagHelperTypes.SelectMany(
|
||||
type => TagHelperDescriptorFactory.CreateDescriptors(assemblyName, type, errorSink));
|
||||
type => TagHelperDescriptorFactory.CreateDescriptors(assemblyName, type, _designTime, errorSink));
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
|
@ -148,7 +153,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
descriptor.TypeName,
|
||||
descriptor.AssemblyName,
|
||||
descriptor.Attributes,
|
||||
descriptor.RequiredAttributes));
|
||||
descriptor.RequiredAttributes,
|
||||
descriptor.UsageDescriptor));
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
|
|
@ -222,7 +228,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
|||
// We need to escape the TypePattern so we can choose to only allow specific regex.
|
||||
var escaped = Regex.Escape(lookupInfo.TypePattern);
|
||||
|
||||
// We surround the escaped with ^ and $ in order ot ensure a regex match matches the entire
|
||||
// We surround the escaped with ^ and $ in order ot ensure a regex match matches the entire
|
||||
// string. We also replace any '*' or '?' characters with regex to match appropriate content.
|
||||
// '*' matches 0 or more characters lazily and '?' matches 1 character.
|
||||
var pattern = "^" + escaped.Replace(@"\?", ".").Replace(@"\*", ".*?") + "$";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,162 @@
|
|||
// 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.
|
||||
|
||||
#if !DNXCORE50 // Cannot accurately resolve the location of the documentation XML file in coreclr.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNet.Razor.TagHelpers;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory for providing <see cref="TagHelperUsageDescriptor"/>s from <see cref="Type"/>s and
|
||||
/// <see cref="PropertyInfo"/>s.
|
||||
/// </summary>
|
||||
public static class TagHelperUsageDescriptorFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a <see cref="TagHelperUsageDescriptor"/> from the given <paramref name="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">The <see cref="Type"/> to create a <see cref="TagHelperUsageDescriptor"/> from.</param>
|
||||
/// <returns>A <see cref="TagHelperUsageDescriptor"/> that describes the summary and remarks XML documentation
|
||||
/// for the given <paramref name="type"/>.</returns>
|
||||
public static TagHelperUsageDescriptor CreateDescriptor([NotNull] Type type)
|
||||
{
|
||||
var id = XmlDocumentationProvider.GetId(type);
|
||||
|
||||
return CreateDescriptorCore(type.Assembly, id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="TagHelperUsageDescriptor"/> from the given <paramref name="propertyInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="propertyInfo">The <see cref="PropertyInfo"/> to create a
|
||||
/// <see cref="TagHelperUsageDescriptor"/> from.</param>
|
||||
/// <returns>A <see cref="TagHelperUsageDescriptor"/> that describes the summary and remarks XML documentation
|
||||
/// for the given <paramref name="propertyInfo"/>.</returns>
|
||||
public static TagHelperUsageDescriptor CreateDescriptor([NotNull] PropertyInfo propertyInfo)
|
||||
{
|
||||
var id = XmlDocumentationProvider.GetId(propertyInfo);
|
||||
var declaringAssembly = propertyInfo.DeclaringType.Assembly;
|
||||
|
||||
return CreateDescriptorCore(declaringAssembly, id);
|
||||
}
|
||||
|
||||
private static TagHelperUsageDescriptor CreateDescriptorCore(Assembly assembly, string id)
|
||||
{
|
||||
var assemblyLocation = assembly.Location;
|
||||
|
||||
if (string.IsNullOrEmpty(assemblyLocation) && !string.IsNullOrEmpty(assembly.CodeBase))
|
||||
{
|
||||
var uri = new UriBuilder(assembly.CodeBase);
|
||||
|
||||
// Normalize the path to a UNC path. This will remove things like file:// from start of the uri.Path.
|
||||
assemblyLocation = Uri.UnescapeDataString(uri.Path);
|
||||
}
|
||||
|
||||
// Couldn't resolve a valid assemblyLocation.
|
||||
if (string.IsNullOrEmpty(assemblyLocation))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var xmlDocumentationFile = GetXmlDocumentationFile(assembly, assemblyLocation);
|
||||
|
||||
// Only want to process the file if it exists.
|
||||
if (xmlDocumentationFile != null)
|
||||
{
|
||||
var documentationProvider = new XmlDocumentationProvider(xmlDocumentationFile.FullName);
|
||||
|
||||
var summary = documentationProvider.GetSummary(id);
|
||||
var remarks = documentationProvider.GetRemarks(id);
|
||||
|
||||
if (!string.IsNullOrEmpty(summary) || !string.IsNullOrEmpty(remarks))
|
||||
{
|
||||
return new TagHelperUsageDescriptor(summary, remarks);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static FileInfo GetXmlDocumentationFile(Assembly assembly, string assemblyLocation)
|
||||
{
|
||||
try
|
||||
{
|
||||
var assemblyDirectory = Path.GetDirectoryName(assemblyLocation);
|
||||
var assemblyName = Path.GetFileName(assemblyLocation);
|
||||
var assemblyXmlDocumentationName = Path.ChangeExtension(assemblyName, ".xml");
|
||||
|
||||
// Check for a localized XML file for the current culture.
|
||||
var xmlDocumentationFile = GetLocalizedXmlDocumentationFile(
|
||||
CultureInfo.CurrentCulture,
|
||||
assemblyDirectory,
|
||||
assemblyXmlDocumentationName);
|
||||
|
||||
if (xmlDocumentationFile == null)
|
||||
{
|
||||
// Check for a culture-neutral XML file next to the assembly
|
||||
xmlDocumentationFile = new FileInfo(
|
||||
Path.Combine(assemblyDirectory, assemblyXmlDocumentationName));
|
||||
|
||||
if (!xmlDocumentationFile.Exists)
|
||||
{
|
||||
xmlDocumentationFile = null;
|
||||
}
|
||||
}
|
||||
|
||||
return xmlDocumentationFile;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// Could not resolve XML file.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<string> ExpandPaths(
|
||||
CultureInfo culture,
|
||||
string assemblyDirectory,
|
||||
string assemblyXmlDocumentationName)
|
||||
{
|
||||
// Following the fall-back process defined by:
|
||||
// https://msdn.microsoft.com/en-us/library/sb6a8618.aspx#cpconpackagingdeployingresourcesanchor1
|
||||
do
|
||||
{
|
||||
var cultureName = culture.Name;
|
||||
var cultureSpecificFileName =
|
||||
Path.ChangeExtension(assemblyXmlDocumentationName, cultureName + ".xml");
|
||||
|
||||
// Look for a culture specific XML file next to the assembly.
|
||||
yield return Path.Combine(assemblyDirectory, cultureSpecificFileName);
|
||||
|
||||
// Look for an XML file with the same name as the assembly in a culture specific directory.
|
||||
yield return Path.Combine(assemblyDirectory, cultureName, assemblyXmlDocumentationName);
|
||||
|
||||
// Look for a culture specific XML file in a culture specific directory.
|
||||
yield return Path.Combine(assemblyDirectory, cultureName, cultureSpecificFileName);
|
||||
|
||||
culture = culture.Parent;
|
||||
} while (culture != null && culture != CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
private static FileInfo GetLocalizedXmlDocumentationFile(
|
||||
CultureInfo culture,
|
||||
string assemblyDirectory,
|
||||
string assemblyXmlDocumentationName)
|
||||
{
|
||||
var localizedXmlPaths = ExpandPaths(culture, assemblyDirectory, assemblyXmlDocumentationName);
|
||||
var xmlDocumentationFile = localizedXmlPaths
|
||||
.Select(path => new FileInfo(path))
|
||||
.FirstOrDefault(file => file.Exists);
|
||||
|
||||
return xmlDocumentationFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
// 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.
|
||||
|
||||
#if !DNXCORE50
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.Runtime
|
||||
{
|
||||
/// <summary>
|
||||
/// Extracts summary and remarks XML documentation from an XML documentation file.
|
||||
/// </summary>
|
||||
public class XmlDocumentationProvider
|
||||
{
|
||||
private readonly IEnumerable<XElement> _members;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of the <see cref="XmlDocumentationProvider"/>.
|
||||
/// </summary>
|
||||
/// <param name="xmlFileLocation">Path to the XML documentation file to read.</param>
|
||||
public XmlDocumentationProvider(string xmlFileLocation)
|
||||
{
|
||||
// XML file processing is defined by: https://msdn.microsoft.com/en-us/library/fsbx0t7x.aspx
|
||||
var xmlDocumentation = XDocument.Load(xmlFileLocation);
|
||||
var documentationRootMembers = xmlDocumentation.Root.Element("members");
|
||||
_members = documentationRootMembers.Elements("member");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the <c><summary></c> documentation for the given <paramref name="id"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">The id to lookup.</param>
|
||||
/// <returns><c><summary></c> documentation for the given <paramref name="id"/>.</returns>
|
||||
public string GetSummary(string id)
|
||||
{
|
||||
var associatedMemeber = GetMember(id);
|
||||
var summaryElement = associatedMemeber?.Element("summary");
|
||||
|
||||
if (summaryElement != null)
|
||||
{
|
||||
var summaryValue = GetElementValue(summaryElement);
|
||||
|
||||
return summaryValue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the <c><remarks></c> documentation for the given <paramref name="id"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">The id to lookup.</param>
|
||||
/// <returns><c><remarks></c> documentation for the given <paramref name="id"/>.</returns>
|
||||
public string GetRemarks(string id)
|
||||
{
|
||||
var associatedMemeber = GetMember(id);
|
||||
var remarksElement = associatedMemeber?.Element("remarks");
|
||||
|
||||
if (remarksElement != null)
|
||||
{
|
||||
var remarksValue = GetElementValue(remarksElement);
|
||||
|
||||
return remarksValue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the <see cref="string"/> identifier for the given <paramref name="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">The <see cref="Type"/> to get the identifier for.</param>
|
||||
/// <returns>The <see cref="string"/> identifier for the given <paramref name="type"/>.</returns>
|
||||
public static string GetId([NotNull] Type type)
|
||||
{
|
||||
return $"T:{type.FullName}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the <see cref="string"/> identifier for the given <paramref name="propertyInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="propertyInfo">The <see cref="PropertyInfo"/> to get the identifier for.</param>
|
||||
/// <returns>The <see cref="string"/> identifier for the given <paramref name="propertyInfo"/>.</returns>
|
||||
public static string GetId([NotNull] PropertyInfo propertyInfo)
|
||||
{
|
||||
var declaringTypeInfo = propertyInfo.DeclaringType;
|
||||
return $"P:{declaringTypeInfo.FullName}.{propertyInfo.Name}";
|
||||
}
|
||||
|
||||
private XElement GetMember(string id)
|
||||
{
|
||||
var associatedMemeber = _members
|
||||
.FirstOrDefault(element =>
|
||||
string.Equals(element.Attribute("name").Value, id, StringComparison.Ordinal));
|
||||
|
||||
return associatedMemeber;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -9,8 +9,18 @@
|
|||
"Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" }
|
||||
},
|
||||
"frameworks": {
|
||||
"net45": { },
|
||||
"dnx451": { },
|
||||
"net45": {
|
||||
"frameworkAssemblies": {
|
||||
"System.Xml": "4.0.0.0",
|
||||
"System.Xml.Linq": "4.0.0.0"
|
||||
}
|
||||
},
|
||||
"dnx451": {
|
||||
"frameworkAssemblies": {
|
||||
"System.Xml": "4.0.0.0",
|
||||
"System.Xml.Linq": "4.0.0.0"
|
||||
}
|
||||
},
|
||||
"dnxcore50": {
|
||||
"dependencies": {
|
||||
"System.Reflection.Extensions": "4.0.0-beta-*",
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
propertyInfo.Name,
|
||||
propertyInfo.PropertyType.FullName,
|
||||
isIndexer: false,
|
||||
isStringProperty: propertyInfo.PropertyType == typeof(string))
|
||||
isStringProperty: propertyInfo.PropertyType == typeof(string),
|
||||
usageDescriptor: null)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -39,6 +40,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
/// If <c>true</c> this <see cref="TagHelperAttributeDescriptor"/> is used for dictionary indexer assignments.
|
||||
/// Otherwise this <see cref="TagHelperAttributeDescriptor"/> is used for property assignment.
|
||||
/// </param>
|
||||
/// <param name="usageDescriptor">The <see cref="TagHelperUsageDescriptor"/> that contains information about
|
||||
/// use of this attribute.</param>
|
||||
/// <remarks>
|
||||
/// HTML attribute names are matched case-insensitively, regardless of <paramref name="isIndexer"/>.
|
||||
/// </remarks>
|
||||
|
|
@ -46,13 +49,15 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
[NotNull] string name,
|
||||
[NotNull] string propertyName,
|
||||
[NotNull] string typeName,
|
||||
bool isIndexer)
|
||||
bool isIndexer,
|
||||
TagHelperUsageDescriptor usageDescriptor)
|
||||
: this(
|
||||
name,
|
||||
propertyName,
|
||||
typeName,
|
||||
isIndexer,
|
||||
isStringProperty: string.Equals(typeName, typeof(string).FullName, StringComparison.Ordinal))
|
||||
isStringProperty: string.Equals(typeName, typeof(string).FullName, StringComparison.Ordinal),
|
||||
usageDescriptor: usageDescriptor)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -62,13 +67,15 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
[NotNull] string propertyName,
|
||||
[NotNull] string typeName,
|
||||
bool isIndexer,
|
||||
bool isStringProperty)
|
||||
bool isStringProperty,
|
||||
TagHelperUsageDescriptor usageDescriptor)
|
||||
{
|
||||
Name = name;
|
||||
PropertyName = propertyName;
|
||||
TypeName = typeName;
|
||||
IsIndexer = isIndexer;
|
||||
IsStringProperty = isStringProperty;
|
||||
UsageDescriptor = usageDescriptor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -111,6 +118,11 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
/// </summary>
|
||||
public string TypeName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="TagHelperUsageDescriptor"/> that contains information about use of this attribute.
|
||||
/// </summary>
|
||||
public TagHelperUsageDescriptor UsageDescriptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether HTML attribute <paramref name="name"/> matches this
|
||||
/// <see cref="TagHelperAttributeDescriptor"/>.
|
||||
|
|
|
|||
|
|
@ -1,68 +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.Framework.Internal;
|
||||
using Microsoft.Internal.Web.Utils;
|
||||
|
||||
namespace Microsoft.AspNet.Razor.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="IEqualityComparer{TagHelperAttributeDescriptor}"/> used to check equality between
|
||||
/// two <see cref="TagHelperAttributeDescriptor"/>s.
|
||||
/// </summary>
|
||||
public class TagHelperAttributeDescriptorComparer : IEqualityComparer<TagHelperAttributeDescriptor>
|
||||
{
|
||||
/// <summary>
|
||||
/// A default instance of the <see cref="TagHelperAttributeDescriptorComparer"/>.
|
||||
/// </summary>
|
||||
public static readonly TagHelperAttributeDescriptorComparer Default =
|
||||
new TagHelperAttributeDescriptorComparer();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="TagHelperAttributeDescriptorComparer"/> instance.
|
||||
/// </summary>
|
||||
protected TagHelperAttributeDescriptorComparer()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// Determines equality based on <see cref="TagHelperAttributeDescriptor.IsIndexer"/>,
|
||||
/// <see cref="TagHelperAttributeDescriptor.Name"/>, <see cref="TagHelperAttributeDescriptor.PropertyName"/>,
|
||||
/// and <see cref="TagHelperAttributeDescriptor.TypeName"/>. Ignores
|
||||
/// <see cref="TagHelperAttributeDescriptor.IsStringProperty"/> because it can be inferred directly from
|
||||
/// <see cref="TagHelperAttributeDescriptor.TypeName"/>.
|
||||
/// </remarks>
|
||||
public virtual bool Equals(TagHelperAttributeDescriptor descriptorX, TagHelperAttributeDescriptor descriptorY)
|
||||
{
|
||||
if (descriptorX == descriptorY)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check Name and TypeName though each property in a particular tag helper has at most two
|
||||
// TagHelperAttributeDescriptors (one for the indexer and one not). May be comparing attributes between
|
||||
// tag helpers and should be as specific as we can.
|
||||
return descriptorX != null &&
|
||||
descriptorX.IsIndexer == descriptorY.IsIndexer &&
|
||||
string.Equals(descriptorX.Name, descriptorY.Name, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(descriptorX.PropertyName, descriptorY.PropertyName, StringComparison.Ordinal) &&
|
||||
string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual int GetHashCode([NotNull] TagHelperAttributeDescriptor descriptor)
|
||||
{
|
||||
// Rarely if ever hash TagHelperAttributeDescriptor. If we do, include the Name and TypeName since context
|
||||
// information is not available in the hash.
|
||||
return HashCodeCombiner.Start()
|
||||
.Add(descriptor.IsIndexer)
|
||||
.Add(descriptor.Name, StringComparer.OrdinalIgnoreCase)
|
||||
.Add(descriptor.PropertyName, StringComparer.Ordinal)
|
||||
.Add(descriptor.TypeName, StringComparer.Ordinal)
|
||||
.CombinedHash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -59,7 +59,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
typeName: typeName,
|
||||
assemblyName: assemblyName,
|
||||
attributes: attributes,
|
||||
requiredAttributes: requiredAttributes)
|
||||
requiredAttributes: requiredAttributes,
|
||||
usageDescriptor: null)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -81,13 +82,16 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
/// <param name="requiredAttributes">
|
||||
/// The attribute names required for the tag helper to target the HTML tag.
|
||||
/// </param>
|
||||
/// <param name="usageDescriptor">The <see cref="TagHelperUsageDescriptor"/> that contains information about
|
||||
/// how to use the tag helper at design time.</param>
|
||||
public TagHelperDescriptor(
|
||||
string prefix,
|
||||
[NotNull] string tagName,
|
||||
[NotNull] string typeName,
|
||||
[NotNull] string assemblyName,
|
||||
[NotNull] IEnumerable<TagHelperAttributeDescriptor> attributes,
|
||||
[NotNull] IEnumerable<string> requiredAttributes)
|
||||
[NotNull] IEnumerable<string> requiredAttributes,
|
||||
TagHelperUsageDescriptor usageDescriptor)
|
||||
{
|
||||
Prefix = prefix ?? string.Empty;
|
||||
TagName = tagName;
|
||||
|
|
@ -96,39 +100,40 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
AssemblyName = assemblyName;
|
||||
Attributes = new List<TagHelperAttributeDescriptor>(attributes);
|
||||
RequiredAttributes = new List<string>(requiredAttributes);
|
||||
UsageDescriptor = usageDescriptor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Text used as a required prefix when matching HTML start and end tags in the Razor source to available
|
||||
/// tag helpers.
|
||||
/// </summary>
|
||||
public string Prefix { get; private set; }
|
||||
public string Prefix { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The tag name that the tag helper should target.
|
||||
/// </summary>
|
||||
public string TagName { get; private set; }
|
||||
public string TagName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The full tag name that is required for the tag helper to target an HTML element.
|
||||
/// </summary>
|
||||
/// <remarks>This is equivalent to <see cref="Prefix"/> and <see cref="TagName"/> concatenated.</remarks>
|
||||
public string FullTagName { get; private set; }
|
||||
public string FullTagName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The full name of the tag helper class.
|
||||
/// </summary>
|
||||
public string TypeName { get; private set; }
|
||||
public string TypeName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the assembly containing the tag helper class.
|
||||
/// </summary>
|
||||
public string AssemblyName { get; private set; }
|
||||
public string AssemblyName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of attributes the tag helper expects.
|
||||
/// </summary>
|
||||
public IReadOnlyList<TagHelperAttributeDescriptor> Attributes { get; private set; }
|
||||
public IReadOnlyList<TagHelperAttributeDescriptor> Attributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of required attribute names the tag helper expects to target an element.
|
||||
|
|
@ -136,6 +141,12 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
/// <remarks>
|
||||
/// <c>*</c> at the end of an attribute name acts as a prefix match.
|
||||
/// </remarks>
|
||||
public IReadOnlyList<string> RequiredAttributes { get; private set; }
|
||||
public IReadOnlyList<string> RequiredAttributes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="TagHelperUsageDescriptor"/> that contains information about how to use the tag helper at
|
||||
/// design time.
|
||||
/// </summary>
|
||||
public TagHelperUsageDescriptor UsageDescriptor { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -31,7 +31,9 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
/// <remarks>
|
||||
/// Determines equality based on <see cref="TagHelperDescriptor.TypeName"/>,
|
||||
/// <see cref="TagHelperDescriptor.AssemblyName"/>, <see cref="TagHelperDescriptor.TagName"/>,
|
||||
/// and <see cref="TagHelperDescriptor.RequiredAttributes"/>.
|
||||
/// and <see cref="TagHelperDescriptor.RequiredAttributes"/>. Ignores
|
||||
/// <see cref="TagHelperDescriptor.UsageDescriptor"/> because it can be inferred directly from
|
||||
/// <see cref="TagHelperDescriptor.TypeName"/> and <see cref="TagHelperDescriptor.AssemblyName"/>.
|
||||
/// </remarks>
|
||||
public virtual bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
// 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.AspNet.Razor.TagHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// A metadata class containing information about tag helper use.
|
||||
/// </summary>
|
||||
public class TagHelperUsageDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of <see cref="TagHelperUsageDescriptor"/>.
|
||||
/// </summary>
|
||||
/// <param name="summary">A summary on how to use a tag helper.</param>
|
||||
/// <param name="remarks">Remarks on how to use a tag helper.</param>
|
||||
public TagHelperUsageDescriptor(string summary, string remarks)
|
||||
{
|
||||
Summary = summary;
|
||||
Remarks = remarks;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A summary of how to use a tag helper.
|
||||
/// </summary>
|
||||
public string Summary { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Remarks about how to use a tag helper.
|
||||
/// </summary>
|
||||
public string Remarks { get; }
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue