Support importing components with @using directives (dotnet/aspnetcore-tooling#276)
* Support importing components with @using directives
* Suppress taghelper directive completion in component documents
* feedback
* More feedback
* Update tests
* Update CodeAnalysis.Razor tests
* Flow filekind
* Changes
* More code gen tests
* More tests
* fix
* Added more tests
* Made stuff internal
* Filter out temporary tag helper descriptors
* update
* Do the needful
\n\nCommit migrated from 343f37748e
This commit is contained in:
parent
91a383ad3b
commit
92d931c229
|
|
@ -8,7 +8,6 @@ Document -
|
|||
UsingDirective - (102:4,1 [37] ) - Microsoft.AspNetCore.Components
|
||||
ClassDeclaration - - public - AspNetCore_d3c3d6059615673cb46fc4974164d61eabadb890 - Microsoft.AspNetCore.Components.ComponentBase - IDisposable
|
||||
DesignTimeDirective -
|
||||
DirectiveToken - (14:0,14 [36] ) - "*, Microsoft.AspNetCore.Components"
|
||||
DirectiveToken - (12:0,12 [11] BasicComponent.cshtml) - IDisposable
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - #pragma warning disable 0414
|
||||
|
|
|
|||
|
|
@ -457,6 +457,30 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
context.CodeWriter.Write(");");
|
||||
context.CodeWriter.WriteLine();
|
||||
}
|
||||
|
||||
// We want to generate something that references the Component type to avoid
|
||||
// the "usings directive is unnecessary" message.
|
||||
// Looks like:
|
||||
// __o = typeof(SomeNamespace.SomeComponent);
|
||||
using (context.CodeWriter.BuildLinePragma(node.Source.Value))
|
||||
{
|
||||
context.CodeWriter.Write(DesignTimeVariable);
|
||||
context.CodeWriter.Write(" = ");
|
||||
context.CodeWriter.Write("typeof(");
|
||||
context.CodeWriter.Write(node.TagName);
|
||||
if (node.Component.IsGenericTypedComponent())
|
||||
{
|
||||
context.CodeWriter.Write("<");
|
||||
var typeArgumentCount = node.Component.GetTypeParameters().Count();
|
||||
for (var i = 1; i < typeArgumentCount; i++)
|
||||
{
|
||||
context.CodeWriter.Write(",");
|
||||
}
|
||||
context.CodeWriter.Write(">");
|
||||
}
|
||||
context.CodeWriter.Write(");");
|
||||
context.CodeWriter.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteComponentAttribute(CodeRenderingContext context, ComponentAttributeIntermediateNode node)
|
||||
|
|
|
|||
|
|
@ -12,9 +12,22 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
{
|
||||
internal static class ComponentDiagnosticFactory
|
||||
{
|
||||
public static readonly RazorDiagnosticDescriptor CodeBlockInAttribute =
|
||||
new RazorDiagnosticDescriptor(
|
||||
"BL9979",
|
||||
private const string DiagnosticPrefix = "RZ";
|
||||
|
||||
public static readonly RazorDiagnosticDescriptor UnsupportedTagHelperDirective = new RazorDiagnosticDescriptor(
|
||||
$"{DiagnosticPrefix}9978",
|
||||
() =>
|
||||
"The directives @addTagHelper, @removeTagHelper and @tagHelperPrefix are not valid in a component document." +
|
||||
"Use '@using <namespace>' directive instead.",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
public static RazorDiagnostic Create_UnsupportedTagHelperDirective(SourceSpan? source)
|
||||
{
|
||||
return RazorDiagnostic.Create(UnsupportedTagHelperDirective, source ?? SourceSpan.Undefined);
|
||||
}
|
||||
|
||||
public static readonly RazorDiagnosticDescriptor CodeBlockInAttribute = new RazorDiagnosticDescriptor(
|
||||
$"{DiagnosticPrefix}9979",
|
||||
() =>
|
||||
"Code blocks delimited by '@{...}' like '@{{ {0} }}' for attributes are no longer supported " +
|
||||
"These features have been changed to use attribute syntax. " +
|
||||
|
|
@ -31,7 +44,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
}
|
||||
|
||||
public static readonly RazorDiagnosticDescriptor UnclosedTag = new RazorDiagnosticDescriptor(
|
||||
"BL9980",
|
||||
$"{DiagnosticPrefix}9980",
|
||||
() => "Unclosed tag '{0}' with no matching end tag.",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -41,7 +54,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
}
|
||||
|
||||
public static readonly RazorDiagnosticDescriptor UnexpectedClosingTag = new RazorDiagnosticDescriptor(
|
||||
"BL9981",
|
||||
$"{DiagnosticPrefix}9981",
|
||||
() => "Unexpected closing tag '{0}' with no matching start tag.",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -51,7 +64,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
}
|
||||
|
||||
public static readonly RazorDiagnosticDescriptor UnexpectedClosingTagForVoidElement = new RazorDiagnosticDescriptor(
|
||||
"BL9983",
|
||||
$"{DiagnosticPrefix}9983",
|
||||
() => "Unexpected closing tag '{0}'. The element '{0}' is a void element, and should be used without a closing tag.",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -61,7 +74,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
}
|
||||
|
||||
public static readonly RazorDiagnosticDescriptor InvalidHtmlContent = new RazorDiagnosticDescriptor(
|
||||
"BL9984",
|
||||
$"{DiagnosticPrefix}9984",
|
||||
() => "Found invalid HTML content. Text '{0}'",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -71,7 +84,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
}
|
||||
|
||||
public static readonly RazorDiagnosticDescriptor MultipleComponents = new RazorDiagnosticDescriptor(
|
||||
"BL9985",
|
||||
$"{DiagnosticPrefix}9985",
|
||||
() => "Multiple components use the tag '{0}'. Components: {1}",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -81,7 +94,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
}
|
||||
|
||||
public static readonly RazorDiagnosticDescriptor UnsupportedComplexContent = new RazorDiagnosticDescriptor(
|
||||
"BL9986",
|
||||
$"{DiagnosticPrefix}9986",
|
||||
() => "Component attributes do not support complex content (mixed C# and markup). Attribute: '{0}', text '{1}'",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -93,7 +106,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly RazorDiagnosticDescriptor PageDirective_CannotBeImported =
|
||||
new RazorDiagnosticDescriptor(
|
||||
"BL9987",
|
||||
$"{DiagnosticPrefix}9987",
|
||||
() => ComponentResources.PageDirectiveCannotBeImported,
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -107,7 +120,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly RazorDiagnosticDescriptor PageDirective_MustSpecifyRoute =
|
||||
new RazorDiagnosticDescriptor(
|
||||
"BL9988",
|
||||
$"{DiagnosticPrefix}9988",
|
||||
() => "The @page directive must specify a route template. The route template must be enclosed in quotes and begin with the '/' character.",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -119,7 +132,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly RazorDiagnosticDescriptor BindAttribute_Duplicates =
|
||||
new RazorDiagnosticDescriptor(
|
||||
"BL9989",
|
||||
$"{DiagnosticPrefix}9989",
|
||||
() => "The attribute '{0}' was matched by multiple bind attributes. Duplicates:{1}",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -135,7 +148,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly RazorDiagnosticDescriptor EventHandler_Duplicates =
|
||||
new RazorDiagnosticDescriptor(
|
||||
"BL9990",
|
||||
$"{DiagnosticPrefix}9990",
|
||||
() => "The attribute '{0}' was matched by multiple event handlers attributes. Duplicates:{1}",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -151,7 +164,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly RazorDiagnosticDescriptor BindAttribute_InvalidSyntax =
|
||||
new RazorDiagnosticDescriptor(
|
||||
"BL9991",
|
||||
$"{DiagnosticPrefix}9991",
|
||||
() => "The attribute names could not be inferred from bind attribute '{0}'. Bind attributes should be of the form" +
|
||||
"'bind', 'bind-value' or 'bind-value-change'",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
|
@ -166,7 +179,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
}
|
||||
|
||||
public static readonly RazorDiagnosticDescriptor DisallowedScriptTag = new RazorDiagnosticDescriptor(
|
||||
"BL9992",
|
||||
$"{DiagnosticPrefix}9992",
|
||||
() => "Script tags should not be placed inside components because they cannot be updated dynamically. To fix this, move the script tag to the 'index.html' file or another static location. For more information see https://go.microsoft.com/fwlink/?linkid=872131",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -180,7 +193,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly RazorDiagnosticDescriptor TemplateInvalidLocation =
|
||||
new RazorDiagnosticDescriptor(
|
||||
"BL9994",
|
||||
$"{DiagnosticPrefix}9994",
|
||||
() => "Razor templates cannot be used in attributes.",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -191,7 +204,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly RazorDiagnosticDescriptor ChildContentSetByAttributeAndBody =
|
||||
new RazorDiagnosticDescriptor(
|
||||
"BL9995",
|
||||
$"{DiagnosticPrefix}9995",
|
||||
() => "The child content property '{0}' is set by both the attribute and the element contents.",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -202,7 +215,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly RazorDiagnosticDescriptor ChildContentMixedWithExplicitChildContent =
|
||||
new RazorDiagnosticDescriptor(
|
||||
"BL9996",
|
||||
$"{DiagnosticPrefix}9996",
|
||||
() => "Unrecognized child content inside component '{0}'. The component '{0}' accepts child content through the " +
|
||||
"following top-level items: {1}.",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
|
@ -215,7 +228,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly RazorDiagnosticDescriptor ChildContentHasInvalidAttribute =
|
||||
new RazorDiagnosticDescriptor(
|
||||
"BL9997",
|
||||
$"{DiagnosticPrefix}9997",
|
||||
() => "Unrecognized attribute '{0}' on child content element '{1}'.",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -226,7 +239,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly RazorDiagnosticDescriptor ChildContentHasInvalidParameter =
|
||||
new RazorDiagnosticDescriptor(
|
||||
"BL9998",
|
||||
$"{DiagnosticPrefix}9998",
|
||||
() => "Invalid parameter name. The parameter name attribute '{0}' on child content element '{1}' can only include literal text.",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -237,7 +250,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly RazorDiagnosticDescriptor ChildContentRepeatedParameterName =
|
||||
new RazorDiagnosticDescriptor(
|
||||
"BL9999",
|
||||
$"{DiagnosticPrefix}9999",
|
||||
() => "The child content element '{0}' of component '{1}' uses the same parameter name ('{2}') as enclosing child content " +
|
||||
"element '{3}' of component '{4}'. Specify the parameter name like: '<{0} Context=\"another_name\"> to resolve the ambiguity",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
|
@ -265,7 +278,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly RazorDiagnosticDescriptor GenericComponentMissingTypeArgument =
|
||||
new RazorDiagnosticDescriptor(
|
||||
"BL10000",
|
||||
$"{DiagnosticPrefix}10000",
|
||||
() => "The component '{0}' is missing required type arguments. Specify the missing types using the attributes: {1}.",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
@ -282,7 +295,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly RazorDiagnosticDescriptor GenericComponentTypeInferenceUnderspecified =
|
||||
new RazorDiagnosticDescriptor(
|
||||
"BL10001",
|
||||
$"{DiagnosticPrefix}10001",
|
||||
() => "The type of component '{0}' cannot be inferred based on the values provided. Consider specifying the type arguments " +
|
||||
"directly using the following attributes: {1}.",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
|
@ -300,7 +313,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly RazorDiagnosticDescriptor ChildContentHasInvalidParameterOnComponent =
|
||||
new RazorDiagnosticDescriptor(
|
||||
"BL10002",
|
||||
$"{DiagnosticPrefix}10002",
|
||||
() => "Invalid parameter name. The parameter name attribute '{0}' on component '{1}' can only include literal text.",
|
||||
RazorDiagnosticSeverity.Error);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
{
|
||||
public static readonly string ComponentDocumentKind = "component.1.0";
|
||||
private static readonly object BuildRenderTreeBaseCallAnnotation = new object();
|
||||
private static readonly char[] PathSeparators = new char[] { '/', '\\' };
|
||||
private static readonly char[] NamespaceSeparators = new char[] { '.' };
|
||||
|
||||
/// <summary>
|
||||
/// The fallback value of the root namespace. Only used if the fallback root namespace
|
||||
|
|
@ -57,13 +55,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
ClassDeclarationIntermediateNode @class,
|
||||
MethodDeclarationIntermediateNode method)
|
||||
{
|
||||
var options = codeDocument.GetDocumentIntermediateNode().Options;
|
||||
if (!TryComputeNamespaceAndClass(
|
||||
options,
|
||||
codeDocument.Source.FilePath,
|
||||
codeDocument.Source.RelativePath,
|
||||
out var computedNamespace,
|
||||
out var computedClass))
|
||||
if (!codeDocument.TryComputeNamespaceAndClass(out var computedNamespace, out var computedClass))
|
||||
{
|
||||
// If we can't compute a nice namespace (no relative path) then just generate something
|
||||
// mangled.
|
||||
|
|
@ -74,7 +66,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
if (MangleClassNames)
|
||||
{
|
||||
computedClass = "__" + computedClass;
|
||||
computedClass = ComponentMetadata.MangleClassName(computedClass);
|
||||
}
|
||||
|
||||
@namespace.Content = computedNamespace;
|
||||
|
|
@ -121,59 +113,6 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
method.Children.Insert(0, callBase);
|
||||
}
|
||||
|
||||
// In general documents will have a relative path (relative to the project root).
|
||||
// We can only really compute a nice class/namespace when we know a relative path.
|
||||
//
|
||||
// However all kinds of thing are possible in tools. We shouldn't barf here if the document isn't
|
||||
// set up correctly.
|
||||
private bool TryComputeNamespaceAndClass(
|
||||
RazorCodeGenerationOptions options,
|
||||
string filePath,
|
||||
string relativePath,
|
||||
out string @namespace,
|
||||
out string @class)
|
||||
{
|
||||
if (filePath == null || relativePath == null || filePath.Length <= relativePath.Length)
|
||||
{
|
||||
@namespace = null;
|
||||
@class = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var rootNamespace = options.RootNamespace;
|
||||
if (string.IsNullOrEmpty(rootNamespace))
|
||||
{
|
||||
@namespace = null;
|
||||
@class = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var builder = new StringBuilder();
|
||||
|
||||
// Sanitize the base namespace, but leave the dots.
|
||||
var segments = rootNamespace.Split(NamespaceSeparators, StringSplitOptions.RemoveEmptyEntries);
|
||||
builder.Append(CSharpIdentifier.SanitizeIdentifier(segments[0]));
|
||||
for (var i = 1; i < segments.Length; i++)
|
||||
{
|
||||
builder.Append('.');
|
||||
builder.Append(CSharpIdentifier.SanitizeIdentifier(segments[i]));
|
||||
}
|
||||
|
||||
segments = relativePath.Split(PathSeparators, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
// Skip the last segment because it's the FileName.
|
||||
for (var i = 0; i < segments.Length - 1; i++)
|
||||
{
|
||||
builder.Append('.');
|
||||
builder.Append(CSharpIdentifier.SanitizeIdentifier(segments[i]));
|
||||
}
|
||||
|
||||
@namespace = builder.ToString();
|
||||
@class = CSharpIdentifier.SanitizeIdentifier(Path.GetFileNameWithoutExtension(relativePath));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static bool IsBuildRenderTreeBaseCall(CSharpCodeIntermediateNode node)
|
||||
=> node.Annotations[BuildRenderTreeBaseCallAnnotation] != null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,28 +40,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
var imports = new List<RazorProjectItem>()
|
||||
{
|
||||
new VirtualProjectItem(DefaultUsingImportContent),
|
||||
new VirtualProjectItem(@"@addTagHelper ""*, Microsoft.AspNetCore.Components"""),
|
||||
};
|
||||
|
||||
// Try and infer a namespace from the project directory. We don't yet have the ability to pass
|
||||
// the namespace through from the project.
|
||||
if (projectItem.PhysicalPath != null && projectItem.FilePath != null)
|
||||
{
|
||||
// Avoiding the path-specific APIs here, we want to handle all styles of paths
|
||||
// on all platforms
|
||||
var trimLength = projectItem.FilePath.Length + (projectItem.FilePath.StartsWith("/") ? 0 : 1);
|
||||
if (projectItem.PhysicalPath.Length > trimLength)
|
||||
{
|
||||
var baseDirectory = projectItem.PhysicalPath.Substring(0, projectItem.PhysicalPath.Length - trimLength);
|
||||
var lastSlash = baseDirectory.LastIndexOfAny(PathSeparators);
|
||||
var baseNamespace = lastSlash == -1 ? baseDirectory : baseDirectory.Substring(lastSlash + 1);
|
||||
if (!string.IsNullOrEmpty(baseNamespace))
|
||||
{
|
||||
imports.Add(new VirtualProjectItem($@"@addTagHelper ""*, {baseNamespace}"""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We add hierarchical imports second so any default directive imports can be overridden.
|
||||
imports.AddRange(GetHierarchicalImports(ProjectEngine.FileSystem, projectItem));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
// Metadata used for Components interactions with the tag helper system
|
||||
internal static class ComponentMetadata
|
||||
{
|
||||
private static readonly string MangledClassNamePrefix = "__generated__";
|
||||
|
||||
// There's a bug in the 15.7 preview 1 Razor that prevents 'Kind' from being serialized
|
||||
// this affects both tooling and build. For now our workaround is to ignore 'Kind' and
|
||||
// use our own metadata entry to denote non-Component tag helpers.
|
||||
|
|
@ -13,6 +17,35 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public static readonly string ImportsFileName = "_Imports.razor";
|
||||
|
||||
public static bool IsComponentTagHelperKind(string tagHelperKind)
|
||||
{
|
||||
return tagHelperKind == Component.TagHelperKind ||
|
||||
tagHelperKind == ChildContent.TagHelperKind ||
|
||||
tagHelperKind == EventHandler.TagHelperKind ||
|
||||
tagHelperKind == Bind.TagHelperKind ||
|
||||
tagHelperKind == Ref.TagHelperKind;
|
||||
}
|
||||
|
||||
public static string MangleClassName(string className)
|
||||
{
|
||||
if (string.IsNullOrEmpty(className))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return MangledClassNamePrefix + className;
|
||||
}
|
||||
|
||||
public static bool IsMangledClass(string className)
|
||||
{
|
||||
if (string.IsNullOrEmpty(className))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return className.StartsWith(MangledClassNamePrefix, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
public static class Bind
|
||||
{
|
||||
public static readonly string RuntimeName = "Components.None";
|
||||
|
|
@ -68,6 +101,10 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
public readonly static string GenericTypedKey = "Components.GenericTyped";
|
||||
|
||||
public readonly static string TypeParameterKey = "Components.TypeParameter";
|
||||
|
||||
public readonly static string NameMatchKey = "Components.NameMatch";
|
||||
|
||||
public readonly static string FullyQualifiedNameMatch = "Components.FullyQualifiedNameMatch";
|
||||
}
|
||||
|
||||
public static class EventHandler
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
|
|
@ -152,6 +151,22 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
string.Equals(ComponentMetadata.Ref.TagHelperKind, kind);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the component matches a tag with a fully qualified name.
|
||||
/// </summary>
|
||||
/// <param name="tagHelper">The <see cref="TagHelperDescriptor"/>.</param>
|
||||
public static bool IsComponentFullyQualifiedNameMatch(this TagHelperDescriptor tagHelper)
|
||||
{
|
||||
if (tagHelper == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(tagHelper));
|
||||
}
|
||||
|
||||
return
|
||||
tagHelper.Metadata.TryGetValue(ComponentMetadata.Component.NameMatchKey, out var matchType) &&
|
||||
string.Equals(ComponentMetadata.Component.FullyQualifiedNameMatch, matchType);
|
||||
}
|
||||
|
||||
public static string GetEventArgsType(this TagHelperDescriptor tagHelper)
|
||||
{
|
||||
if (tagHelper == null)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
internal class DefaultRazorParserOptions : RazorParserOptions
|
||||
{
|
||||
public DefaultRazorParserOptions(DirectiveDescriptor[] directives, bool designTime, bool parseLeadingDirectives, RazorLanguageVersion version)
|
||||
public DefaultRazorParserOptions(DirectiveDescriptor[] directives, bool designTime, bool parseLeadingDirectives, RazorLanguageVersion version, string fileKind)
|
||||
{
|
||||
if (directives == null)
|
||||
{
|
||||
|
|
@ -20,6 +20,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
ParseLeadingDirectives = parseLeadingDirectives;
|
||||
Version = version;
|
||||
FeatureFlags = RazorParserFeatureFlags.Create(Version);
|
||||
FileKind = fileKind;
|
||||
}
|
||||
|
||||
public override bool DesignTime { get; }
|
||||
|
|
@ -30,6 +31,8 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
public override RazorLanguageVersion Version { get; }
|
||||
|
||||
internal override string FileKind { get; }
|
||||
|
||||
internal override RazorParserFeatureFlags FeatureFlags { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,10 +23,11 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
FileKind = fileKind;
|
||||
}
|
||||
|
||||
public DefaultRazorParserOptionsBuilder(bool designTime, RazorLanguageVersion version)
|
||||
public DefaultRazorParserOptionsBuilder(bool designTime, RazorLanguageVersion version, string fileKind)
|
||||
{
|
||||
_designTime = designTime;
|
||||
LanguageVersion = version;
|
||||
FileKind = fileKind;
|
||||
}
|
||||
|
||||
public override RazorConfiguration Configuration { get; }
|
||||
|
|
@ -43,7 +44,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
public override RazorParserOptions Build()
|
||||
{
|
||||
return new DefaultRazorParserOptions(Directives.ToArray(), DesignTime, ParseLeadingDirectives, LanguageVersion);
|
||||
return new DefaultRazorParserOptions(Directives.ToArray(), DesignTime, ParseLeadingDirectives, LanguageVersion, FileKind);
|
||||
}
|
||||
|
||||
public override void SetDesignTime(bool designTime)
|
||||
|
|
|
|||
|
|
@ -11,12 +11,14 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
private readonly bool _designTime;
|
||||
private readonly RazorLanguageVersion _version;
|
||||
private readonly string _fileKind;
|
||||
private IConfigureRazorParserOptionsFeature[] _configureOptions;
|
||||
|
||||
public DefaultRazorParserOptionsFeature(bool designTime, RazorLanguageVersion version)
|
||||
public DefaultRazorParserOptionsFeature(bool designTime, RazorLanguageVersion version, string fileKind)
|
||||
{
|
||||
_designTime = designTime;
|
||||
_version = version;
|
||||
_fileKind = fileKind;
|
||||
}
|
||||
|
||||
protected override void OnInitialized()
|
||||
|
|
@ -26,7 +28,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
public RazorParserOptions GetOptions()
|
||||
{
|
||||
var builder = new DefaultRazorParserOptionsBuilder(_designTime, _version);
|
||||
var builder = new DefaultRazorParserOptionsBuilder(_designTime, _version, _fileKind);
|
||||
for (var i = 0; i < _configureOptions.Length; i++)
|
||||
{
|
||||
_configureOptions[i].Configure(builder);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language.Components;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.AspNetCore.Razor.Language.Syntax;
|
||||
|
||||
|
|
@ -11,6 +12,8 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
internal class DefaultRazorTagHelperBinderPhase : RazorEnginePhaseBase, IRazorTagHelperBinderPhase
|
||||
{
|
||||
private static readonly char[] NamespaceSeparators = new char[] { '.' };
|
||||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument)
|
||||
{
|
||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
||||
|
|
@ -33,20 +36,31 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
//
|
||||
// The imports come logically before the main razor file and are in the order they
|
||||
// should be processed.
|
||||
var visitor = new DirectiveVisitor(descriptors);
|
||||
DirectiveVisitor visitor = null;
|
||||
if (FileKinds.IsComponent(codeDocument.GetFileKind()))
|
||||
{
|
||||
codeDocument.TryComputeNamespaceAndClass(out var currentNamespace, out var _);
|
||||
visitor = new ComponentDirectiveVisitor(codeDocument.Source.FilePath, descriptors, currentNamespace);
|
||||
}
|
||||
else
|
||||
{
|
||||
visitor = new TagHelperDirectiveVisitor(descriptors);
|
||||
}
|
||||
var imports = codeDocument.GetImportSyntaxTrees();
|
||||
if (imports != null)
|
||||
{
|
||||
for (var i = 0; i < imports.Count; i++)
|
||||
{
|
||||
var import = imports[i];
|
||||
visitor.Visit(import.Root);
|
||||
visitor.Visit(import);
|
||||
}
|
||||
}
|
||||
|
||||
visitor.Visit(syntaxTree.Root);
|
||||
visitor.Visit(syntaxTree);
|
||||
|
||||
// This will always be null for a component document.
|
||||
var tagHelperPrefix = visitor.TagHelperPrefix;
|
||||
|
||||
descriptors = visitor.Matches.ToArray();
|
||||
|
||||
var context = TagHelperDocumentContext.Create(tagHelperPrefix, descriptors);
|
||||
|
|
@ -86,20 +100,38 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
return string.Equals(descriptor.Name, typePattern, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
internal class DirectiveVisitor : SyntaxRewriter
|
||||
|
||||
|
||||
internal abstract class DirectiveVisitor : SyntaxWalker
|
||||
{
|
||||
public abstract HashSet<TagHelperDescriptor> Matches { get; }
|
||||
|
||||
public abstract string TagHelperPrefix { get; }
|
||||
|
||||
public abstract void Visit(RazorSyntaxTree tree);
|
||||
}
|
||||
|
||||
internal class TagHelperDirectiveVisitor : DirectiveVisitor
|
||||
{
|
||||
private IReadOnlyList<TagHelperDescriptor> _tagHelpers;
|
||||
private string _tagHelperPrefix;
|
||||
|
||||
public DirectiveVisitor(IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
public TagHelperDirectiveVisitor(IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
_tagHelpers = tagHelpers;
|
||||
// We don't want to consider components in a view document.
|
||||
_tagHelpers = tagHelpers.Where(t => !ComponentMetadata.IsComponentTagHelperKind(t.Kind)).ToList();
|
||||
}
|
||||
|
||||
public string TagHelperPrefix { get; private set; }
|
||||
public override string TagHelperPrefix => _tagHelperPrefix;
|
||||
|
||||
public HashSet<TagHelperDescriptor> Matches { get; } = new HashSet<TagHelperDescriptor>();
|
||||
public override HashSet<TagHelperDescriptor> Matches { get; } = new HashSet<TagHelperDescriptor>();
|
||||
|
||||
public override SyntaxNode VisitRazorDirective(RazorDirectiveSyntax node)
|
||||
public override void Visit(RazorSyntaxTree tree)
|
||||
{
|
||||
Visit(tree.Root);
|
||||
}
|
||||
|
||||
public override void VisitRazorDirective(RazorDirectiveSyntax node)
|
||||
{
|
||||
var descendantLiterals = node.DescendantNodes();
|
||||
foreach (var child in descendantLiterals)
|
||||
|
|
@ -167,12 +199,10 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
if (!string.IsNullOrEmpty(tagHelperPrefix.DirectiveText))
|
||||
{
|
||||
// We only expect to see a single one of these per file, but that's enforced at another level.
|
||||
TagHelperPrefix = tagHelperPrefix.DirectiveText;
|
||||
_tagHelperPrefix = tagHelperPrefix.DirectiveText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return base.VisitRazorDirective(node);
|
||||
}
|
||||
|
||||
private bool AssemblyContainsTagHelpers(string assemblyName, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
|
|
@ -188,5 +218,231 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ComponentDirectiveVisitor : DirectiveVisitor
|
||||
{
|
||||
private IReadOnlyList<TagHelperDescriptor> _tagHelpers;
|
||||
private string _filePath;
|
||||
private RazorSourceDocument _source;
|
||||
|
||||
public ComponentDirectiveVisitor(string filePath, IReadOnlyList<TagHelperDescriptor> tagHelpers, string currentNamespace)
|
||||
{
|
||||
_filePath = filePath;
|
||||
|
||||
// We don't want to consider non-component tag helpers in a component document.
|
||||
_tagHelpers = tagHelpers.Where(t => ComponentMetadata.IsComponentTagHelperKind(t.Kind) && !IsTagHelperFromMangledClass(t)).ToList();
|
||||
|
||||
for (var i = 0; i < _tagHelpers.Count; i++)
|
||||
{
|
||||
var tagHelper = _tagHelpers[i];
|
||||
if (tagHelper.IsComponentFullyQualifiedNameMatch())
|
||||
{
|
||||
// If the component descriptor matches for a fully qualified name, using directives shouldn't matter.
|
||||
Matches.Add(tagHelper);
|
||||
continue;
|
||||
}
|
||||
|
||||
var typeName = tagHelper.GetTypeName();
|
||||
if (tagHelper.IsChildContentTagHelper())
|
||||
{
|
||||
// If this is a child content tag helper, we want to add it if it's original type is in scope.
|
||||
// E.g, if the type name is `Test.MyComponent.ChildContent`, we want to add it if `Test.MyComponent` is in scope.
|
||||
TrySplitNamespaceAndType(typeName, out typeName, out var _);
|
||||
}
|
||||
|
||||
if (currentNamespace != null && IsTypeInScope(typeName, currentNamespace))
|
||||
{
|
||||
// Also, if the type is already in scope of the document's namespace, using isn't necessary.
|
||||
Matches.Add(tagHelper);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override HashSet<TagHelperDescriptor> Matches { get; } = new HashSet<TagHelperDescriptor>();
|
||||
|
||||
// There is no support for tag helper prefix in component documents.
|
||||
public override string TagHelperPrefix => null;
|
||||
|
||||
public override void Visit(RazorSyntaxTree tree)
|
||||
{
|
||||
_source = tree.Source;
|
||||
Visit(tree.Root);
|
||||
}
|
||||
|
||||
public override void VisitRazorDirective(RazorDirectiveSyntax node)
|
||||
{
|
||||
var descendantLiterals = node.DescendantNodes();
|
||||
foreach (var child in descendantLiterals)
|
||||
{
|
||||
if (!(child is CSharpStatementLiteralSyntax literal))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var context = literal.GetSpanContext();
|
||||
if (context == null)
|
||||
{
|
||||
// We can't find a chunk generator.
|
||||
continue;
|
||||
}
|
||||
else if (context.ChunkGenerator is AddTagHelperChunkGenerator addTagHelper)
|
||||
{
|
||||
// Make sure this node exists in the file we're parsing and not in its imports.
|
||||
if (_filePath.Equals(_source.FilePath, StringComparison.Ordinal))
|
||||
{
|
||||
addTagHelper.Diagnostics.Add(
|
||||
ComponentDiagnosticFactory.Create_UnsupportedTagHelperDirective(node.GetSourceSpan(_source)));
|
||||
}
|
||||
}
|
||||
else if (context.ChunkGenerator is RemoveTagHelperChunkGenerator removeTagHelper)
|
||||
{
|
||||
// Make sure this node exists in the file we're parsing and not in its imports.
|
||||
if (_filePath.Equals(_source.FilePath, StringComparison.Ordinal))
|
||||
{
|
||||
removeTagHelper.Diagnostics.Add(
|
||||
ComponentDiagnosticFactory.Create_UnsupportedTagHelperDirective(node.GetSourceSpan(_source)));
|
||||
}
|
||||
}
|
||||
else if (context.ChunkGenerator is TagHelperPrefixDirectiveChunkGenerator tagHelperPrefix)
|
||||
{
|
||||
// Make sure this node exists in the file we're parsing and not in its imports.
|
||||
if (_filePath.Equals(_source.FilePath, StringComparison.Ordinal))
|
||||
{
|
||||
tagHelperPrefix.Diagnostics.Add(
|
||||
ComponentDiagnosticFactory.Create_UnsupportedTagHelperDirective(node.GetSourceSpan(_source)));
|
||||
}
|
||||
}
|
||||
else if (context.ChunkGenerator is AddImportChunkGenerator usingStatement && !usingStatement.IsStatic)
|
||||
{
|
||||
// Get the namespace from the using statement.
|
||||
var @namespace = usingStatement.ParsedNamespace;
|
||||
if (@namespace.Contains('='))
|
||||
{
|
||||
// We don't support usings with alias.
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var i = 0; i < _tagHelpers.Count; i++)
|
||||
{
|
||||
var tagHelper = _tagHelpers[i];
|
||||
if (tagHelper.IsComponentFullyQualifiedNameMatch())
|
||||
{
|
||||
// We've already added these to our list of matches.
|
||||
continue;
|
||||
}
|
||||
|
||||
var typeName = tagHelper.GetTypeName();
|
||||
if (typeName != null && IsTypeInNamespace(typeName, @namespace))
|
||||
{
|
||||
// If the type is at the top-level or if the type's namespace matches the using's namespace, add it.
|
||||
Matches.Add(tagHelper);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsTypeInNamespace(string typeName, string @namespace)
|
||||
{
|
||||
if (!TrySplitNamespaceAndType(typeName, out var typeNamespace, out var _) || typeNamespace == string.Empty)
|
||||
{
|
||||
// Either the typeName is not the full type name or this type is at the top level.
|
||||
return true;
|
||||
}
|
||||
|
||||
return typeNamespace.Equals(@namespace, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
// Check if the given type is already in scope given the namespace of the current document.
|
||||
// E.g,
|
||||
// If the namespace of the document is `MyComponents.Components.Shared`,
|
||||
// then the types `MyComponents.FooComponent`, `MyComponents.Components.BarComponent`, `MyComponents.Components.Shared.BazComponent` are all in scope.
|
||||
// Whereas `MyComponents.SomethingElse.OtherComponent` is not in scope.
|
||||
internal static bool IsTypeInScope(string typeName, string currentNamespace)
|
||||
{
|
||||
if (!TrySplitNamespaceAndType(typeName, out var typeNamespace, out var _) || typeNamespace == string.Empty)
|
||||
{
|
||||
// Either the typeName is not the full type name or this type is at the top level.
|
||||
return true;
|
||||
}
|
||||
|
||||
var typeNamespaceSegments = typeNamespace.Split(NamespaceSeparators, StringSplitOptions.RemoveEmptyEntries);
|
||||
var currentNamespaceSegments = currentNamespace.Split(NamespaceSeparators, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (typeNamespaceSegments.Length > currentNamespaceSegments.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < typeNamespaceSegments.Length; i++)
|
||||
{
|
||||
if (!typeNamespaceSegments[i].Equals(currentNamespaceSegments[i], StringComparison.Ordinal))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// We need to filter out the duplicate tag helper descriptors that come from the
|
||||
// open file in the editor. We mangle the class name for its generated code, so using that here to filter these out.
|
||||
internal static bool IsTagHelperFromMangledClass(TagHelperDescriptor tagHelper)
|
||||
{
|
||||
if (!TrySplitNamespaceAndType(tagHelper.GetTypeName(), out var _, out var className))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ComponentMetadata.IsMangledClass(className);
|
||||
}
|
||||
|
||||
// Internal for testing.
|
||||
internal static bool TrySplitNamespaceAndType(string fullTypeName, out string @namespace, out string typeName)
|
||||
{
|
||||
@namespace = string.Empty;
|
||||
typeName = string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(fullTypeName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var nestingLevel = 0;
|
||||
var splitLocation = -1;
|
||||
for (var i = fullTypeName.Length - 1; i >= 0; i--)
|
||||
{
|
||||
var c = fullTypeName[i];
|
||||
if (c == Type.Delimiter && nestingLevel == 0)
|
||||
{
|
||||
splitLocation = i;
|
||||
break;
|
||||
}
|
||||
else if (c == '>')
|
||||
{
|
||||
nestingLevel++;
|
||||
}
|
||||
else if (c == '<')
|
||||
{
|
||||
nestingLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
if (splitLocation == -1)
|
||||
{
|
||||
typeName = fullTypeName;
|
||||
return true;
|
||||
}
|
||||
|
||||
@namespace = fullTypeName.Substring(0, splitLocation);
|
||||
|
||||
var typeNameStartLocation = splitLocation + 1;
|
||||
if (typeNameStartLocation < fullTypeName.Length)
|
||||
{
|
||||
typeName = fullTypeName.Substring(typeNameStartLocation, fullTypeName.Length - typeNameStartLocation);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,13 +7,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
|||
{
|
||||
internal class AddImportChunkGenerator : SpanChunkGenerator
|
||||
{
|
||||
public AddImportChunkGenerator(string ns)
|
||||
public AddImportChunkGenerator(string usingContent, string parsedNamespace, bool isStatic)
|
||||
{
|
||||
Namespace = ns;
|
||||
Namespace = usingContent;
|
||||
ParsedNamespace = parsedNamespace;
|
||||
IsStatic = isStatic;
|
||||
}
|
||||
|
||||
public string Namespace { get; }
|
||||
|
||||
public string ParsedNamespace { get; }
|
||||
|
||||
public bool IsStatic { get; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Import:" + Namespace + ";";
|
||||
|
|
|
|||
|
|
@ -2066,11 +2066,14 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
|||
var directiveBuilder = pooledResult.Builder;
|
||||
Assert(CSharpKeyword.Using);
|
||||
AcceptAndMoveNext();
|
||||
var isStatic = false;
|
||||
var nonNamespaceTokenCount = TokenBuilder.Count;
|
||||
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
|
||||
var start = CurrentStart;
|
||||
if (At(SyntaxKind.Identifier))
|
||||
{
|
||||
// non-static using
|
||||
nonNamespaceTokenCount = TokenBuilder.Count;
|
||||
TryParseNamespaceOrTypeName(directiveBuilder);
|
||||
var whitespace = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true));
|
||||
if (At(SyntaxKind.Assign))
|
||||
|
|
@ -2094,15 +2097,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
|
|||
else if (At(CSharpKeyword.Static))
|
||||
{
|
||||
// static using
|
||||
isStatic = true;
|
||||
AcceptAndMoveNext();
|
||||
AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true));
|
||||
nonNamespaceTokenCount = TokenBuilder.Count;
|
||||
TryParseNamespaceOrTypeName(directiveBuilder);
|
||||
}
|
||||
|
||||
var usingStatementTokens = TokenBuilder.ToList().Nodes;
|
||||
var usingContentTokens = usingStatementTokens.Skip(1);
|
||||
var parsedNamespaceTokens = usingStatementTokens
|
||||
.Skip(nonNamespaceTokenCount)
|
||||
.Where(s => s.Kind != SyntaxKind.CSharpComment && s.Kind != SyntaxKind.Whitespace && s.Kind != SyntaxKind.NewLine);
|
||||
|
||||
SpanContext.EditHandler.AcceptedCharacters = AcceptedCharactersInternal.AnyExceptNewline;
|
||||
SpanContext.ChunkGenerator = new AddImportChunkGenerator(new LocationTagged<string>(
|
||||
string.Concat(TokenBuilder.ToList().Nodes.Skip(1).Select(s => s.Content)),
|
||||
start));
|
||||
SpanContext.ChunkGenerator = new AddImportChunkGenerator(
|
||||
string.Concat(usingContentTokens.Select(s => s.Content)),
|
||||
string.Concat(parsedNamespaceTokens.Select(s => s.Content)),
|
||||
isStatic);
|
||||
|
||||
// Optional ";"
|
||||
if (EnsureCurrent())
|
||||
|
|
|
|||
|
|
@ -3,12 +3,17 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public static class RazorCodeDocumentExtensions
|
||||
{
|
||||
private static readonly char[] PathSeparators = new char[] { '/', '\\' };
|
||||
private static readonly char[] NamespaceSeparators = new char[] { '.' };
|
||||
|
||||
public static TagHelperDocumentContext GetTagHelperContext(this RazorCodeDocument document)
|
||||
{
|
||||
if (document == null)
|
||||
|
|
@ -189,6 +194,71 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
document.Items[typeof(FileKinds)] = fileKind;
|
||||
}
|
||||
|
||||
// In general documents will have a relative path (relative to the project root).
|
||||
// We can only really compute a nice class/namespace when we know a relative path.
|
||||
//
|
||||
// However all kinds of thing are possible in tools. We shouldn't barf here if the document isn't
|
||||
// set up correctly.
|
||||
internal static bool TryComputeNamespaceAndClass(this RazorCodeDocument document, out string @namespace, out string @class)
|
||||
{
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
var filePath = document.Source.FilePath;
|
||||
var relativePath = document.Source.RelativePath;
|
||||
if (filePath == null || relativePath == null || filePath.Length <= relativePath.Length)
|
||||
{
|
||||
@namespace = null;
|
||||
@class = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
filePath = NormalizePath(filePath);
|
||||
relativePath = NormalizePath(relativePath);
|
||||
var options = document.GetCodeGenerationOptions() ?? document.GetDocumentIntermediateNode()?.Options;
|
||||
var rootNamespace = options?.RootNamespace;
|
||||
if (string.IsNullOrEmpty(rootNamespace))
|
||||
{
|
||||
@namespace = null;
|
||||
@class = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var builder = new StringBuilder();
|
||||
|
||||
// Sanitize the base namespace, but leave the dots.
|
||||
var segments = rootNamespace.Split(NamespaceSeparators, StringSplitOptions.RemoveEmptyEntries);
|
||||
builder.Append(CSharpIdentifier.SanitizeIdentifier(segments[0]));
|
||||
for (var i = 1; i < segments.Length; i++)
|
||||
{
|
||||
builder.Append('.');
|
||||
builder.Append(CSharpIdentifier.SanitizeIdentifier(segments[i]));
|
||||
}
|
||||
|
||||
segments = relativePath.Split(PathSeparators, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
// Skip the last segment because it's the FileName.
|
||||
for (var i = 0; i < segments.Length - 1; i++)
|
||||
{
|
||||
builder.Append('.');
|
||||
builder.Append(CSharpIdentifier.SanitizeIdentifier(segments[i]));
|
||||
}
|
||||
|
||||
@namespace = builder.ToString();
|
||||
@class = CSharpIdentifier.SanitizeIdentifier(Path.GetFileNameWithoutExtension(relativePath));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string NormalizePath(string path)
|
||||
{
|
||||
path = path.Replace('\\', '/');
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private class ImportSyntaxTreesHolder
|
||||
{
|
||||
public ImportSyntaxTreesHolder(IReadOnlyList<RazorSyntaxTree> syntaxTrees)
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
private static void AddDefaultRuntimeFeatures(RazorConfiguration configuration, ICollection<IRazorEngineFeature> features)
|
||||
{
|
||||
// Configure options
|
||||
features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: configuration.LanguageVersion));
|
||||
features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: configuration.LanguageVersion, fileKind: null));
|
||||
features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
|
||||
|
||||
// Intermediate Node Passes
|
||||
|
|
@ -124,7 +124,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
private static void AddDefaultDesignTimeFeatures(RazorConfiguration configuration, ICollection<IRazorEngineFeature> features)
|
||||
{
|
||||
// Configure options
|
||||
features.Add(new DefaultRazorParserOptionsFeature(designTime: true, version: configuration.LanguageVersion));
|
||||
features.Add(new DefaultRazorParserOptionsFeature(designTime: true, version: configuration.LanguageVersion, fileKind: null));
|
||||
features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: true));
|
||||
features.Add(new SuppressChecksumOptionsFeature());
|
||||
|
||||
|
|
|
|||
|
|
@ -14,17 +14,23 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
Array.Empty<DirectiveDescriptor>(),
|
||||
designTime: false,
|
||||
parseLeadingDirectives: false,
|
||||
version: RazorLanguageVersion.Latest);
|
||||
version: RazorLanguageVersion.Latest,
|
||||
fileKind: null);
|
||||
}
|
||||
|
||||
public static RazorParserOptions Create(Action<RazorParserOptionsBuilder> configure)
|
||||
{
|
||||
return Create(configure, fileKind: null);
|
||||
}
|
||||
|
||||
public static RazorParserOptions Create(Action<RazorParserOptionsBuilder> configure, string fileKind)
|
||||
{
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
var builder = new DefaultRazorParserOptionsBuilder(designTime: false, version: RazorLanguageVersion.Latest);
|
||||
var builder = new DefaultRazorParserOptionsBuilder(designTime: false, version: RazorLanguageVersion.Latest, fileKind);
|
||||
configure(builder);
|
||||
var options = builder.Build();
|
||||
|
||||
|
|
@ -32,13 +38,18 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
}
|
||||
|
||||
public static RazorParserOptions CreateDesignTime(Action<RazorParserOptionsBuilder> configure)
|
||||
{
|
||||
return CreateDesignTime(configure, fileKind: null);
|
||||
}
|
||||
|
||||
public static RazorParserOptions CreateDesignTime(Action<RazorParserOptionsBuilder> configure, string fileKind)
|
||||
{
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
var builder = new DefaultRazorParserOptionsBuilder(designTime: true, version: RazorLanguageVersion.Latest);
|
||||
var builder = new DefaultRazorParserOptionsBuilder(designTime: true, version: RazorLanguageVersion.Latest, fileKind);
|
||||
configure(builder);
|
||||
var options = builder.Build();
|
||||
|
||||
|
|
@ -61,6 +72,8 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
public virtual RazorLanguageVersion Version { get; } = RazorLanguageVersion.Latest;
|
||||
|
||||
internal virtual string FileKind { get; }
|
||||
|
||||
internal virtual RazorParserFeatureFlags FeatureFlags { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
// Legacy options features
|
||||
//
|
||||
// These features are obsolete as of 2.1. Our code will resolve this but not invoke them.
|
||||
features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: RazorLanguageVersion.Version_2_0));
|
||||
features.Add(new DefaultRazorParserOptionsFeature(designTime: false, version: RazorLanguageVersion.Version_2_0, fileKind: null));
|
||||
features.Add(new DefaultRazorCodeGenerationOptionsFeature(designTime: false));
|
||||
|
||||
// Syntax Tree passes
|
||||
|
|
|
|||
|
|
@ -445,6 +445,11 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
attribute.SetPropertyName(valueAttribute.GetPropertyName());
|
||||
});
|
||||
|
||||
if (tagHelper.IsComponentFullyQualifiedNameMatch())
|
||||
{
|
||||
builder.Metadata[ComponentMetadata.Component.NameMatchKey] = ComponentMetadata.Component.FullyQualifiedNameMatch;
|
||||
}
|
||||
|
||||
results.Add(builder.Build());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,13 +63,20 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
for (var i = 0; i < types.Count; i++)
|
||||
{
|
||||
var type = types[i];
|
||||
var descriptor = CreateDescriptor(symbols, type);
|
||||
context.Results.Add(descriptor);
|
||||
|
||||
foreach (var childContent in descriptor.GetChildContentProperties())
|
||||
// Components have very simple matching rules.
|
||||
// 1. The type name (short) matches the tag name.
|
||||
// 2. The fully qualified name matches the tag name.
|
||||
var shortNameMatchingDescriptor = CreateShortNameMatchingDescriptor(symbols, type);
|
||||
context.Results.Add(shortNameMatchingDescriptor);
|
||||
var fullyQualifiedNameMatchingDescriptor = CreateFullyQualifiedNameMatchingDescriptor(symbols, type);
|
||||
context.Results.Add(fullyQualifiedNameMatchingDescriptor);
|
||||
|
||||
foreach (var childContent in shortNameMatchingDescriptor.GetChildContentProperties())
|
||||
{
|
||||
// Synthesize a separate tag helper for each child content property that's declared.
|
||||
context.Results.Add(CreateChildContentDescriptor(symbols, descriptor, childContent));
|
||||
context.Results.Add(CreateChildContentDescriptor(symbols, shortNameMatchingDescriptor, childContent));
|
||||
context.Results.Add(CreateChildContentDescriptor(symbols, fullyQualifiedNameMatchingDescriptor, childContent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -81,7 +88,26 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
return compilation.WithOptions(newCompilationOptions);
|
||||
}
|
||||
|
||||
private TagHelperDescriptor CreateDescriptor(ComponentSymbols symbols, INamedTypeSymbol type)
|
||||
private TagHelperDescriptor CreateShortNameMatchingDescriptor(ComponentSymbols symbols, INamedTypeSymbol type)
|
||||
{
|
||||
var builder = CreateDescriptorBuilder(symbols, type);
|
||||
builder.TagMatchingRule(r => r.TagName = type.Name);
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
private TagHelperDescriptor CreateFullyQualifiedNameMatchingDescriptor(ComponentSymbols symbols, INamedTypeSymbol type)
|
||||
{
|
||||
var builder = CreateDescriptorBuilder(symbols, type);
|
||||
var containingNamespace = type.ContainingNamespace.ToDisplayString();
|
||||
var fullName = $"{containingNamespace}.{type.Name}";
|
||||
builder.TagMatchingRule(r => r.TagName = fullName);
|
||||
builder.Metadata[ComponentMetadata.Component.NameMatchKey] = ComponentMetadata.Component.FullyQualifiedNameMatch;
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
private TagHelperDescriptorBuilder CreateDescriptorBuilder(ComponentSymbols symbols, INamedTypeSymbol type)
|
||||
{
|
||||
var typeName = type.ToDisplayString(FullNameTypeDisplayFormat);
|
||||
var assemblyName = type.ContainingAssembly.Identity.Name;
|
||||
|
|
@ -113,9 +139,6 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
builder.Documentation = xml;
|
||||
}
|
||||
|
||||
// Components have very simple matching rules. The type name (short) matches the tag name.
|
||||
builder.TagMatchingRule(r => r.TagName = type.Name);
|
||||
|
||||
foreach (var property in GetProperties(symbols, type))
|
||||
{
|
||||
if (property.kind == PropertyKind.Ignored)
|
||||
|
|
@ -135,8 +158,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
CreateContextParameter(builder, childContentName: null);
|
||||
}
|
||||
|
||||
var descriptor = builder.Build();
|
||||
return descriptor;
|
||||
return builder;
|
||||
}
|
||||
|
||||
private void CreateProperty(TagHelperDescriptorBuilder builder, IPropertySymbol property, PropertyKind kind)
|
||||
|
|
@ -270,6 +292,11 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
CreateContextParameter(builder, attribute.Name);
|
||||
}
|
||||
|
||||
if (component.IsComponentFullyQualifiedNameMatch())
|
||||
{
|
||||
builder.Metadata[ComponentMetadata.Component.NameMatchKey] = ComponentMetadata.Component.FullyQualifiedNameMatch;
|
||||
}
|
||||
|
||||
var descriptor = builder.Build();
|
||||
|
||||
return descriptor;
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
// 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.Language;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.AspNetCore.Razor.Language.Components;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public abstract class BaseTagHelperDescriptorProviderTest
|
||||
public abstract class TagHelperDescriptorProviderTestBase
|
||||
{
|
||||
static BaseTagHelperDescriptorProviderTest()
|
||||
static TagHelperDescriptorProviderTestBase()
|
||||
{
|
||||
BaseCompilation = TestCompilation.Create(typeof(ComponentTagHelperDescriptorProviderTest).Assembly);
|
||||
CSharpParseOptions = new CSharpParseOptions(LanguageVersion.CSharp7_3);
|
||||
|
|
@ -39,5 +39,28 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
.ToArray();
|
||||
}
|
||||
|
||||
protected static TagHelperDescriptor[] AssertAndExcludeFullyQualifiedNameMatchComponents(
|
||||
TagHelperDescriptor[] components,
|
||||
int expectedCount)
|
||||
{
|
||||
var componentLookup = new Dictionary<string, List<TagHelperDescriptor>>();
|
||||
var fullyQualifiedNameMatchComponents = components.Where(c => c.IsComponentFullyQualifiedNameMatch()).ToArray();
|
||||
Assert.Equal(expectedCount, fullyQualifiedNameMatchComponents.Length);
|
||||
|
||||
var shortNameMatchComponents = components.Where(c => !c.IsComponentFullyQualifiedNameMatch()).ToArray();
|
||||
|
||||
// For every fully qualified name component, we want to make sure we have a corresponding short name component.
|
||||
foreach (var fullNameComponent in fullyQualifiedNameMatchComponents)
|
||||
{
|
||||
Assert.Contains(shortNameMatchComponents, component =>
|
||||
{
|
||||
return component.Name == fullNameComponent.Name &&
|
||||
component.Kind == fullNameComponent.Kind &&
|
||||
component.BoundAttributes.SequenceEqual(fullNameComponent.BoundAttributes, BoundAttributeDescriptorComparer.Default);
|
||||
});
|
||||
}
|
||||
|
||||
return shortNameMatchComponents;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public class BindTagHelperDescriptorProviderTest : BaseTagHelperDescriptorProviderTest
|
||||
public class BindTagHelperDescriptorProviderTest : TagHelperDescriptorProviderTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void Execute_FindsBindTagHelperOnComponentType_Delegate_CreatesDescriptor()
|
||||
|
|
@ -55,6 +55,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var matches = GetBindTagHelpers(context);
|
||||
matches = AssertAndExcludeFullyQualifiedNameMatchComponents(matches, expectedCount: 1);
|
||||
var bind = Assert.Single(matches);
|
||||
|
||||
// These are features Bind Tags Helpers don't use. Verifying them once here and
|
||||
|
|
@ -170,6 +171,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var matches = GetBindTagHelpers(context);
|
||||
matches = AssertAndExcludeFullyQualifiedNameMatchComponents(matches, expectedCount: 1);
|
||||
var bind = Assert.Single(matches);
|
||||
|
||||
// These are features Bind Tags Helpers don't use. Verifying them once here and
|
||||
|
|
@ -283,6 +285,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var matches = GetBindTagHelpers(context);
|
||||
matches = AssertAndExcludeFullyQualifiedNameMatchComponents(matches, expectedCount: 0);
|
||||
Assert.Empty(matches);
|
||||
}
|
||||
|
||||
|
|
@ -314,6 +317,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var matches = GetBindTagHelpers(context);
|
||||
matches = AssertAndExcludeFullyQualifiedNameMatchComponents(matches, expectedCount: 0);
|
||||
var bind = Assert.Single(matches);
|
||||
|
||||
// These are features Bind Tags Helpers don't use. Verifying them once here and
|
||||
|
|
@ -448,6 +452,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var matches = GetBindTagHelpers(context);
|
||||
matches = AssertAndExcludeFullyQualifiedNameMatchComponents(matches, expectedCount: 0);
|
||||
var bind = Assert.Single(matches);
|
||||
|
||||
Assert.Equal("myprop", bind.Metadata[ComponentMetadata.Bind.ValueAttribute]);
|
||||
|
|
@ -502,6 +507,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var matches = GetBindTagHelpers(context);
|
||||
matches = AssertAndExcludeFullyQualifiedNameMatchComponents(matches, expectedCount: 0);
|
||||
var bind = Assert.Single(matches);
|
||||
|
||||
Assert.Equal("myprop", bind.Metadata[ComponentMetadata.Bind.ValueAttribute]);
|
||||
|
|
@ -557,6 +563,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var matches = GetBindTagHelpers(context);
|
||||
matches = AssertAndExcludeFullyQualifiedNameMatchComponents(matches, expectedCount: 0);
|
||||
var bind = Assert.Single(matches);
|
||||
|
||||
Assert.Equal("myprop", bind.Metadata[ComponentMetadata.Bind.ValueAttribute]);
|
||||
|
|
@ -624,6 +631,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var matches = GetBindTagHelpers(context);
|
||||
matches = AssertAndExcludeFullyQualifiedNameMatchComponents(matches, expectedCount: 0);
|
||||
var bind = Assert.Single(matches);
|
||||
|
||||
Assert.Equal("myprop", bind.Metadata[ComponentMetadata.Bind.ValueAttribute]);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public class ComponentTagHelperDescriptorProviderTest : BaseTagHelperDescriptorProviderTest
|
||||
public class ComponentTagHelperDescriptorProviderTest : TagHelperDescriptorProviderTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void Execute_FindsIComponentType_CreatesDescriptor()
|
||||
|
|
@ -45,6 +45,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 1);
|
||||
var component = Assert.Single(components);
|
||||
|
||||
// These are features Components don't use. Verifying them once here and
|
||||
|
|
@ -159,6 +160,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 1);
|
||||
var component = Assert.Single(components);
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -222,6 +224,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 1);
|
||||
var component = Assert.Single(components);
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -263,6 +266,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 1);
|
||||
var component = Assert.Single(components);
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -315,6 +319,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 1);
|
||||
var component = Assert.Single(components);
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -361,6 +366,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 1);
|
||||
var component = Assert.Single(components);
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -424,6 +430,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 1);
|
||||
var component = Assert.Single(components);
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -498,6 +505,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 1);
|
||||
var component = Assert.Single(components);
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -547,6 +555,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 1);
|
||||
var component = Assert.Single(components);
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -608,6 +617,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 1);
|
||||
var component = Assert.Single(components);
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -657,6 +667,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 1);
|
||||
var component = Assert.Single(components);
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -711,6 +722,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 1);
|
||||
var component = Assert.Single(components);
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -773,6 +785,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 2);
|
||||
var component = Assert.Single(components, c => c.IsComponentTagHelper());
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -829,6 +842,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 2);
|
||||
var component = Assert.Single(components, c => c.IsComponentTagHelper());
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -903,6 +917,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 2);
|
||||
var component = Assert.Single(components, c => c.IsComponentTagHelper());
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -974,6 +989,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 2);
|
||||
var component = Assert.Single(components, c => c.IsComponentTagHelper());
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -1055,6 +1071,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 2);
|
||||
var component = Assert.Single(components, c => c.IsComponentTagHelper());
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -1136,6 +1153,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 2);
|
||||
var component = Assert.Single(components, c => c.IsComponentTagHelper());
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -1221,6 +1239,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 2);
|
||||
var component = Assert.Single(components, c => c.IsComponentTagHelper());
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -1306,6 +1325,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 4);
|
||||
var component = Assert.Single(components, c => c.IsComponentTagHelper());
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
@ -1397,6 +1417,7 @@ namespace Test
|
|||
|
||||
// Assert
|
||||
var components = ExcludeBuiltInComponents(context);
|
||||
components = AssertAndExcludeFullyQualifiedNameMatchComponents(components, expectedCount: 1);
|
||||
var component = Assert.Single(components);
|
||||
|
||||
Assert.Equal("TestAssembly", component.AssemblyName);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public class EventHandlerTagHelperDescriptorProviderTest : BaseTagHelperDescriptorProviderTest
|
||||
public class EventHandlerTagHelperDescriptorProviderTest : TagHelperDescriptorProviderTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void Execute_EventHandler_CreatesDescriptor()
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
{
|
||||
public class RefTagHelperDescriptorProviderTest : BaseTagHelperDescriptorProviderTest
|
||||
public class RefTagHelperDescriptorProviderTest : TagHelperDescriptorProviderTestBase
|
||||
{
|
||||
[Fact]
|
||||
public void Execute_CreatesDescriptor()
|
||||
|
|
|
|||
Loading…
Reference in New Issue