Remove old razor

This commit is contained in:
Ryan Brandenburg 2017-03-16 15:54:15 -07:00
parent 8f9ff1abd9
commit a7eb30ddca
480 changed files with 3 additions and 77298 deletions

View File

@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.0
VisualStudioVersion = 15.0.26228.9
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3C0D6505-79B3-49D0-B4C3-176F0F1836ED}"
EndProject
@ -8,8 +8,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{92463391-8
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor", "src\Microsoft.AspNetCore.Razor\Microsoft.AspNetCore.Razor.csproj", "{EDA30434-C567-44DC-B8B6-2566A7F77163}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Test", "test\Microsoft.AspNetCore.Razor.Test\Microsoft.AspNetCore.Razor.Test.csproj", "{87C7338B-0C06-4C7B-BE75-A2368AE26797}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Runtime", "src\Microsoft.AspNetCore.Razor.Runtime\Microsoft.AspNetCore.Razor.Runtime.csproj", "{D0196096-1B01-4133-AACE-1A10A0F7247C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F8C12DD6-659D-405A-AA27-FB22AD92A010}"
@ -61,14 +59,6 @@ Global
{EDA30434-C567-44DC-B8B6-2566A7F77163}.Release|Any CPU.Build.0 = Release|Any CPU
{EDA30434-C567-44DC-B8B6-2566A7F77163}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{EDA30434-C567-44DC-B8B6-2566A7F77163}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{87C7338B-0C06-4C7B-BE75-A2368AE26797}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{87C7338B-0C06-4C7B-BE75-A2368AE26797}.Debug|Any CPU.Build.0 = Debug|Any CPU
{87C7338B-0C06-4C7B-BE75-A2368AE26797}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
{87C7338B-0C06-4C7B-BE75-A2368AE26797}.DebugNoVSIX|Any CPU.Build.0 = Debug|Any CPU
{87C7338B-0C06-4C7B-BE75-A2368AE26797}.Release|Any CPU.ActiveCfg = Release|Any CPU
{87C7338B-0C06-4C7B-BE75-A2368AE26797}.Release|Any CPU.Build.0 = Release|Any CPU
{87C7338B-0C06-4C7B-BE75-A2368AE26797}.ReleaseNoVSIX|Any CPU.ActiveCfg = Release|Any CPU
{87C7338B-0C06-4C7B-BE75-A2368AE26797}.ReleaseNoVSIX|Any CPU.Build.0 = Release|Any CPU
{D0196096-1B01-4133-AACE-1A10A0F7247C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D0196096-1B01-4133-AACE-1A10A0F7247C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D0196096-1B01-4133-AACE-1A10A0F7247C}.DebugNoVSIX|Any CPU.ActiveCfg = Debug|Any CPU
@ -177,7 +167,6 @@ Global
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{EDA30434-C567-44DC-B8B6-2566A7F77163} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
{87C7338B-0C06-4C7B-BE75-A2368AE26797} = {92463391-81BE-462B-AC3C-78C6C760741F}
{D0196096-1B01-4133-AACE-1A10A0F7247C} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
{7BE58880-36AD-4CD5-9E16-2A5AFEA790EF} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}
{932F3C9C-A6C0-40D3-BA50-9309886242FC} = {3C0D6505-79B3-49D0-B4C3-176F0F1836ED}

View File

@ -1,57 +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.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.Extensions.Internal;
using Xunit;
namespace Microsoft.AspNetCore.Razor.TagHelpers.Testing
{
internal class TagHelperAttributeDescriptorComparer : IEqualityComparer<TagHelperAttributeDescriptor>
{
public static readonly TagHelperAttributeDescriptorComparer Default =
new TagHelperAttributeDescriptorComparer();
private TagHelperAttributeDescriptorComparer()
{
}
public bool Equals(TagHelperAttributeDescriptor descriptorX, TagHelperAttributeDescriptor descriptorY)
{
if (descriptorX == descriptorY)
{
return true;
}
Assert.NotNull(descriptorX);
Assert.NotNull(descriptorY);
Assert.Equal(descriptorX.IsIndexer, descriptorY.IsIndexer);
Assert.Equal(descriptorX.Name, descriptorY.Name, StringComparer.Ordinal);
Assert.Equal(descriptorX.PropertyName, descriptorY.PropertyName, StringComparer.Ordinal);
Assert.Equal(descriptorX.TypeName, descriptorY.TypeName, StringComparer.Ordinal);
Assert.Equal(descriptorX.IsEnum, descriptorY.IsEnum);
Assert.Equal(descriptorX.IsStringProperty, descriptorY.IsStringProperty);
return TagHelperAttributeDesignTimeDescriptorComparer.Default.Equals(
descriptorX.DesignTimeDescriptor,
descriptorY.DesignTimeDescriptor);
}
public int GetHashCode(TagHelperAttributeDescriptor descriptor)
{
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(descriptor.IsIndexer);
hashCodeCombiner.Add(descriptor.Name, StringComparer.Ordinal);
hashCodeCombiner.Add(descriptor.PropertyName, StringComparer.Ordinal);
hashCodeCombiner.Add(descriptor.TypeName, StringComparer.Ordinal);
hashCodeCombiner.Add(descriptor.IsEnum);
hashCodeCombiner.Add(descriptor.IsStringProperty);
hashCodeCombiner.Add(TagHelperAttributeDesignTimeDescriptorComparer.Default.GetHashCode(
descriptor.DesignTimeDescriptor));
return hashCodeCombiner;
}
}
}

View File

@ -1,43 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.Extensions.Internal;
using Xunit;
namespace Microsoft.AspNetCore.Razor.TagHelpers.Testing
{
internal class CaseSensitiveTagHelperRequiredAttributeDescriptorComparer : TagHelperRequiredAttributeDescriptorComparer
{
public new static readonly CaseSensitiveTagHelperRequiredAttributeDescriptorComparer Default =
new CaseSensitiveTagHelperRequiredAttributeDescriptorComparer();
private CaseSensitiveTagHelperRequiredAttributeDescriptorComparer()
: base()
{
}
public override bool Equals(TagHelperRequiredAttributeDescriptor descriptorX, TagHelperRequiredAttributeDescriptor descriptorY)
{
if (descriptorX == descriptorY)
{
return true;
}
Assert.True(base.Equals(descriptorX, descriptorY));
Assert.Equal(descriptorX.Name, descriptorY.Name, StringComparer.Ordinal);
return true;
}
public override int GetHashCode(TagHelperRequiredAttributeDescriptor descriptor)
{
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(base.GetHashCode(descriptor));
hashCodeCombiner.Add(descriptor.Name, StringComparer.Ordinal);
return hashCodeCombiner.CombinedHash;
}
}
}

View File

@ -1,96 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.Extensions.Internal;
using Xunit;
namespace Microsoft.AspNetCore.Razor.TagHelpers.Testing
{
internal class CaseSensitiveTagHelperDescriptorComparer : TagHelperDescriptorComparer
{
public new static readonly CaseSensitiveTagHelperDescriptorComparer Default =
new CaseSensitiveTagHelperDescriptorComparer();
private CaseSensitiveTagHelperDescriptorComparer()
: base()
{
}
public override bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
{
if (descriptorX == descriptorY)
{
return true;
}
Assert.True(base.Equals(descriptorX, descriptorY));
// Normal comparer doesn't care about the case, required attribute order, allowed children order,
// attributes or prefixes. In tests we do.
Assert.Equal(descriptorX.TagName, descriptorY.TagName, StringComparer.Ordinal);
Assert.Equal(descriptorX.Prefix, descriptorY.Prefix, StringComparer.Ordinal);
Assert.Equal(
descriptorX.RequiredAttributes,
descriptorY.RequiredAttributes,
CaseSensitiveTagHelperRequiredAttributeDescriptorComparer.Default);
Assert.Equal(descriptorX.RequiredParent, descriptorY.RequiredParent, StringComparer.Ordinal);
if (descriptorX.AllowedChildren != descriptorY.AllowedChildren)
{
Assert.Equal(descriptorX.AllowedChildren, descriptorY.AllowedChildren, StringComparer.Ordinal);
}
Assert.Equal(
descriptorX.Attributes,
descriptorY.Attributes,
TagHelperAttributeDescriptorComparer.Default);
Assert.Equal(
descriptorX.DesignTimeDescriptor,
descriptorY.DesignTimeDescriptor,
TagHelperDesignTimeDescriptorComparer.Default);
return true;
}
public override int GetHashCode(TagHelperDescriptor descriptor)
{
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(base.GetHashCode(descriptor));
hashCodeCombiner.Add(descriptor.TagName, StringComparer.Ordinal);
hashCodeCombiner.Add(descriptor.Prefix, StringComparer.Ordinal);
if (descriptor.DesignTimeDescriptor != null)
{
hashCodeCombiner.Add(
TagHelperDesignTimeDescriptorComparer.Default.GetHashCode(descriptor.DesignTimeDescriptor));
}
foreach (var requiredAttribute in descriptor.RequiredAttributes.OrderBy(attribute => attribute.Name))
{
hashCodeCombiner.Add(
CaseSensitiveTagHelperRequiredAttributeDescriptorComparer.Default.GetHashCode(requiredAttribute));
}
if (descriptor.AllowedChildren != null)
{
foreach (var child in descriptor.AllowedChildren.OrderBy(child => child))
{
hashCodeCombiner.Add(child, StringComparer.Ordinal);
}
}
var orderedAttributeHashCodes = descriptor.Attributes
.Select(attribute => TagHelperAttributeDescriptorComparer.Default.GetHashCode(attribute))
.OrderBy(hashcode => hashcode);
foreach (var attributeHashCode in orderedAttributeHashCodes)
{
hashCodeCombiner.Add(attributeHashCode);
}
return hashCodeCombiner.CombinedHash;
}
}
}

View File

@ -1,48 +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.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.Extensions.Internal;
using Xunit;
namespace Microsoft.AspNetCore.Razor.TagHelpers.Testing
{
internal class TagHelperAttributeDesignTimeDescriptorComparer :
IEqualityComparer<TagHelperAttributeDesignTimeDescriptor>
{
public static readonly TagHelperAttributeDesignTimeDescriptorComparer Default =
new TagHelperAttributeDesignTimeDescriptorComparer();
private TagHelperAttributeDesignTimeDescriptorComparer()
{
}
public bool Equals(
TagHelperAttributeDesignTimeDescriptor descriptorX,
TagHelperAttributeDesignTimeDescriptor descriptorY)
{
if (descriptorX == descriptorY)
{
return true;
}
Assert.NotNull(descriptorX);
Assert.NotNull(descriptorY);
Assert.Equal(descriptorX.Summary, descriptorY.Summary, StringComparer.Ordinal);
Assert.Equal(descriptorX.Remarks, descriptorY.Remarks, StringComparer.Ordinal);
return true;
}
public int GetHashCode(TagHelperAttributeDesignTimeDescriptor descriptor)
{
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(descriptor.Summary, StringComparer.Ordinal);
hashCodeCombiner.Add(descriptor.Remarks, StringComparer.Ordinal);
return hashCodeCombiner;
}
}
}

View File

@ -1,48 +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.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.Extensions.Internal;
using Xunit;
namespace Microsoft.AspNetCore.Razor.TagHelpers.Testing
{
internal class TagHelperDesignTimeDescriptorComparer : IEqualityComparer<TagHelperDesignTimeDescriptor>
{
public static readonly TagHelperDesignTimeDescriptorComparer Default =
new TagHelperDesignTimeDescriptorComparer();
private TagHelperDesignTimeDescriptorComparer()
{
}
public bool Equals(TagHelperDesignTimeDescriptor descriptorX, TagHelperDesignTimeDescriptor descriptorY)
{
if (descriptorX == descriptorY)
{
return true;
}
Assert.NotNull(descriptorX);
Assert.NotNull(descriptorY);
Assert.Equal(descriptorX.Summary, descriptorY.Summary, StringComparer.Ordinal);
Assert.Equal(descriptorX.Remarks, descriptorY.Remarks, StringComparer.Ordinal);
Assert.Equal(descriptorX.OutputElementHint, descriptorY.OutputElementHint, StringComparer.Ordinal);
return true;
}
public int GetHashCode(TagHelperDesignTimeDescriptor descriptor)
{
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(descriptor.Summary, StringComparer.Ordinal);
hashCodeCombiner.Add(descriptor.Remarks, StringComparer.Ordinal);
hashCodeCombiner.Add(descriptor.OutputElementHint, StringComparer.Ordinal);
return hashCodeCombiner;
}
}
}

View File

@ -17,12 +17,8 @@ Microsoft.AspNetCore.Razor.TagHelpers.ITagHelper</Description>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.Razor\Microsoft.AspNetCore.Razor.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Html.Abstractions" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.HashCodeCombiner.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.TaskCache.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.5' ">
<PackageReference Include="System.Reflection.TypeExtensions" Version="$(CoreFxVersion)" />
</ItemGroup>
</Project>

View File

@ -117,97 +117,7 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="TagHelperDescriptorResolver_InvalidTagHelperLookupText" xml:space="preserve">
<value>Invalid tag helper directive look up text '{0}'. The correct look up text format is: "typeName, assemblyName".</value>
</data>
<data name="TagHelperTypeResolver_CannotResolveTagHelperAssembly" xml:space="preserve">
<value>Cannot resolve TagHelper containing assembly '{0}'. Error: {1}</value>
</data>
<data name="TagHelperTypeResolver_TagHelperAssemblyNameCannotBeEmptyOrNull" xml:space="preserve">
<value>Tag helper directive assembly name cannot be null or empty.</value>
</data>
<data name="ScopeManager_EndCannotBeCalledWithoutACallToBegin" xml:space="preserve">
<value>Must call '{2}.{1}' before calling '{2}.{0}'.</value>
</data>
<data name="HtmlTargetElementAttribute_NameCannotBeNullOrWhitespace" xml:space="preserve">
<value>{0} name cannot be null or whitespace.</value>
</data>
<data name="ArgumentCannotBeNullOrEmpty" xml:space="preserve">
<value>The value cannot be null or empty.</value>
</data>
<data name="TagHelperDescriptorResolver_EncounteredUnexpectedError" xml:space="preserve">
<value>Encountered an unexpected error when attempting to resolve tag helper directive '{0}' with value '{1}'. Error: {2}</value>
</data>
<data name="HtmlTargetElementAttribute_InvalidName" xml:space="preserve">
<value>Tag helpers cannot target {0} name '{1}' because it contains a '{2}' character.</value>
</data>
<data name="TagHelperDescriptorResolver_InvalidTagHelperDirective" xml:space="preserve">
<value>Invalid tag helper directive '{0}'. Cannot have multiple '{0}' directives on a page.</value>
</data>
<data name="TagHelperDescriptorResolver_InvalidTagHelperPrefixValue" xml:space="preserve">
<value>Invalid tag helper directive '{0}' value. '{1}' is not allowed in prefix '{2}'.</value>
</data>
<data name="TagHelperDescriptorFactory_Attribute" xml:space="preserve">
<value>Attribute</value>
</data>
<data name="TagHelperDescriptorFactory_Name" xml:space="preserve">
<value>name</value>
</data>
<data name="TagHelperDescriptorFactory_Prefix" xml:space="preserve">
<value>prefix</value>
</data>
<data name="TagHelperDescriptorFactory_Tag" xml:space="preserve">
<value>Tag</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributeNameAttribute" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. An '{2}' must not be associated with a property with no public setter unless its type implements '{3}'.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} contains a '{4}' character.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} starts with '{4}'.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a whitespace {2}.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributePrefixNull" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidAttributePrefixNotNull" xml:space="preserve">
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName" xml:space="preserve">
<value>Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace" xml:space="preserve">
<value>Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.</value>
</data>
<data name="TagHelperDescriptorFactory_ParentTag" xml:space="preserve">
<value>Parent Tag</value>
</data>
<data name="ArgumentMustBeAnInstanceOf" xml:space="preserve">
<value>Argument must be an instance of '{0}'.</value>
</data>
<data name="TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace" xml:space="preserve">
<value>Could not find matching ']' for required attribute '{0}'.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter" xml:space="preserve">
<value>Invalid required attribute character '{0}' in required attribute '{1}'. Separate required attributes with commas.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes" xml:space="preserve">
<value>Required attribute '{0}' has mismatched quotes '{1}' around value.</value>
</data>
<data name="TagHelperDescriptorFactory_PartialRequiredAttributeOperator" xml:space="preserve">
<value>Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.</value>
</data>
<data name="TagHelperDescriptorFactory_InvalidRequiredAttributeOperator" xml:space="preserve">
<value>Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.</value>
</data>
</root>

View File

@ -1,12 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
{
internal static class Constants
{
public static readonly TimeSpan RegexMatchTimeout = TimeSpan.FromSeconds(10);
}
}

View File

@ -1,28 +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.AspNetCore.Razor.Compilation.TagHelpers;
namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
{
/// <summary>
/// Factory for <see cref="TagHelperDescriptor"/> instances.
/// </summary>
public interface ITagHelperDescriptorFactory
{
/// <summary>
/// Creates a <see cref="TagHelperDescriptor"/> from the given <paramref name="type"/>.
/// </summary>
/// <param name="assemblyName">The assembly name that contains <paramref name="type"/>.</param>
/// <param name="type">The <see cref="Type"/> to create a <see cref="TagHelperDescriptor"/> from.
/// </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>
IEnumerable<TagHelperDescriptor> CreateDescriptors(string assemblyName, Type type, ErrorSink errorSink);
}
}

View File

@ -1,28 +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 System.Reflection;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
{
/// <summary>
/// Locates valid <see cref="ITagHelper"/>s within an assembly.
/// </summary>
public interface ITagHelperTypeResolver
{
/// <summary>
/// Locates valid <see cref="ITagHelper"/> types from the <see cref="Assembly"/> named <paramref name="name"/>.
/// </summary>
/// <param name="name">The name of an <see cref="Assembly"/> to search.</param>
/// <param name="documentLocation">The <see cref="SourceLocation"/> of the associated
/// <see cref="Parser.SyntaxTree.SyntaxTreeNode"/> responsible for the current <see cref="Resolve"/> call.
/// </param>
/// <param name="errorSink">The <see cref="ErrorSink"/> used to record errors found when resolving
/// <see cref="ITagHelper"/> types.</param>
/// <returns>An <see cref="IEnumerable{Type}"/> of valid <see cref="ITagHelper"/> types.</returns>
IEnumerable<Type> Resolve(string name, SourceLocation documentLocation, ErrorSink errorSink);
}
}

View File

@ -1,30 +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.Reflection;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
{
/// <summary>
/// Default convention for determining if a type is a tag helper.
/// </summary>
public static class TagHelperConventions
{
private static readonly TypeInfo ITagHelperTypeInfo = typeof(ITagHelper).GetTypeInfo();
/// <summary>
/// Indicates whether or not the <see cref="TypeInfo"/> is a tag helper.
/// </summary>
/// <param name="typeInfo">The <see cref="TypeInfo"/>.</param>
/// <returns>true if <paramref name="typeInfo"/> is a tag helper; false otherwise.</returns>
public static bool IsTagHelper(TypeInfo typeInfo)
{
return !typeInfo.IsNested &&
typeInfo.IsPublic &&
!typeInfo.IsAbstract &&
!typeInfo.IsGenericType &&
ITagHelperTypeInfo.IsAssignableFrom(typeInfo);
}
}
}

View File

@ -1,299 +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 System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.AspNetCore.Razor.Parser;
namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
{
/// <summary>
/// Used to resolve <see cref="TagHelperDescriptor"/>s.
/// </summary>
public class TagHelperDescriptorResolver : ITagHelperDescriptorResolver
{
private static readonly IReadOnlyDictionary<TagHelperDirectiveType, string> _directiveNames =
new Dictionary<TagHelperDirectiveType, string>
{
{ TagHelperDirectiveType.AddTagHelper, SyntaxConstants.CSharp.AddTagHelperKeyword },
{ TagHelperDirectiveType.RemoveTagHelper, SyntaxConstants.CSharp.RemoveTagHelperKeyword },
{ TagHelperDirectiveType.TagHelperPrefix, SyntaxConstants.CSharp.TagHelperPrefixKeyword },
};
private readonly ITagHelperTypeResolver _typeResolver;
private readonly ITagHelperDescriptorFactory _descriptorFactory;
/// <summary>
/// Instantiates a new instance of the <see cref="TagHelperDescriptorResolver"/> class.
/// </summary>
/// <param name="designTime">Indicates whether resolved <see cref="TagHelperDescriptor"/>s should include
/// design time specific information.</param>
public TagHelperDescriptorResolver(bool designTime)
: this(new TagHelperTypeResolver(), new TagHelperDescriptorFactory(designTime))
{
}
/// <summary>
/// Instantiates a new instance of <see cref="TagHelperDescriptorResolver"/> class with the
/// specified <paramref name="typeResolver"/>.
/// </summary>
/// <param name="typeResolver">The <see cref="TagHelperTypeResolver"/>.</param>
/// <param name="descriptorFactory">The <see cref="TagHelperDescriptorFactory"/>.</param>
public TagHelperDescriptorResolver(
ITagHelperTypeResolver typeResolver,
ITagHelperDescriptorFactory descriptorFactory)
{
_typeResolver = typeResolver;
_descriptorFactory = descriptorFactory;
}
/// <inheritdoc />
public IEnumerable<TagHelperDescriptor> Resolve(TagHelperDescriptorResolutionContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
var resolvedDescriptors = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
// tagHelperPrefix directives do not affect which TagHelperDescriptors are added or removed from the final
// list, need to remove them.
var actionableDirectiveDescriptors = context.DirectiveDescriptors.Where(
directive => directive.DirectiveType != TagHelperDirectiveType.TagHelperPrefix);
foreach (var directiveDescriptor in actionableDirectiveDescriptors)
{
try
{
var lookupInfo = GetLookupInfo(directiveDescriptor, context.ErrorSink);
// Could not resolve the lookup info.
if (lookupInfo == null)
{
return Enumerable.Empty<TagHelperDescriptor>();
}
if (directiveDescriptor.DirectiveType == TagHelperDirectiveType.RemoveTagHelper)
{
resolvedDescriptors.RemoveWhere(descriptor => MatchesLookupInfo(descriptor, lookupInfo));
}
else if (directiveDescriptor.DirectiveType == TagHelperDirectiveType.AddTagHelper)
{
var descriptors = ResolveDescriptorsInAssembly(
lookupInfo.AssemblyName,
lookupInfo.AssemblyNameLocation,
context.ErrorSink);
// Only use descriptors that match our lookup info
descriptors = descriptors.Where(descriptor => MatchesLookupInfo(descriptor, lookupInfo));
resolvedDescriptors.UnionWith(descriptors);
}
}
catch (Exception ex)
{
string directiveName;
_directiveNames.TryGetValue(directiveDescriptor.DirectiveType, out directiveName);
Debug.Assert(!string.IsNullOrEmpty(directiveName));
context.ErrorSink.OnError(
directiveDescriptor.Location,
Resources.FormatTagHelperDescriptorResolver_EncounteredUnexpectedError(
"@" + directiveName,
directiveDescriptor.DirectiveText,
ex.Message),
GetErrorLength(directiveDescriptor.DirectiveText));
}
}
var prefixedDescriptors = PrefixDescriptors(context, resolvedDescriptors);
return prefixedDescriptors;
}
/// <summary>
/// Resolves all <see cref="TagHelperDescriptor"/>s for <see cref="Razor.TagHelpers.ITagHelper"/>s from the
/// given <paramref name="assemblyName"/>.
/// </summary>
/// <param name="assemblyName">
/// 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
/// within the given <paramref name="assemblyName"/>.</param>
/// <returns><see cref="TagHelperDescriptor"/>s for <see cref="Razor.TagHelpers.ITagHelper"/>s from the given
/// <paramref name="assemblyName"/>.</returns>
// This is meant to be overridden by tooling to enable assembly level caching.
protected virtual IEnumerable<TagHelperDescriptor> ResolveDescriptorsInAssembly(
string assemblyName,
SourceLocation documentLocation,
ErrorSink errorSink)
{
// Resolve valid tag helper types from the assembly.
var tagHelperTypes = _typeResolver.Resolve(assemblyName, documentLocation, errorSink);
// Convert types to TagHelperDescriptors
var descriptors = tagHelperTypes.SelectMany(
type => _descriptorFactory.CreateDescriptors(assemblyName, type, errorSink));
return descriptors;
}
private static IEnumerable<TagHelperDescriptor> PrefixDescriptors(
TagHelperDescriptorResolutionContext context,
IEnumerable<TagHelperDescriptor> descriptors)
{
var tagHelperPrefix = ResolveTagHelperPrefix(context);
if (!string.IsNullOrEmpty(tagHelperPrefix))
{
return descriptors.Select(descriptor => new TagHelperDescriptor(descriptor)
{
Prefix = tagHelperPrefix
});
}
return descriptors;
}
private static string ResolveTagHelperPrefix(TagHelperDescriptorResolutionContext context)
{
var prefixDirectiveDescriptors = context.DirectiveDescriptors.Where(
descriptor => descriptor.DirectiveType == TagHelperDirectiveType.TagHelperPrefix);
TagHelperDirectiveDescriptor prefixDirective = null;
foreach (var directive in prefixDirectiveDescriptors)
{
if (prefixDirective == null)
{
prefixDirective = directive;
}
else
{
// For each invalid @tagHelperPrefix we need to create an error.
context.ErrorSink.OnError(
directive.Location,
Resources.FormatTagHelperDescriptorResolver_InvalidTagHelperDirective(
SyntaxConstants.CSharp.TagHelperPrefixKeyword),
GetErrorLength(directive.DirectiveText));
}
}
var prefix = prefixDirective?.DirectiveText;
if (prefix != null && !EnsureValidPrefix(prefix, prefixDirective.Location, context.ErrorSink))
{
prefix = null;
}
return prefix;
}
private static bool EnsureValidPrefix(
string prefix,
SourceLocation directiveLocation,
ErrorSink errorSink)
{
foreach (var character in prefix)
{
// Prefixes are correlated with tag names, tag names cannot have whitespace.
if (char.IsWhiteSpace(character) ||
TagHelperDescriptorFactory.InvalidNonWhitespaceNameCharacters.Contains(character))
{
errorSink.OnError(
directiveLocation,
Resources.FormatTagHelperDescriptorResolver_InvalidTagHelperPrefixValue(
SyntaxConstants.CSharp.TagHelperPrefixKeyword,
character,
prefix),
prefix.Length);
return false;
}
}
return true;
}
private static bool MatchesLookupInfo(TagHelperDescriptor descriptor, LookupInfo lookupInfo)
{
if (!string.Equals(descriptor.AssemblyName, lookupInfo.AssemblyName, StringComparison.Ordinal))
{
return false;
}
if (lookupInfo.TypePattern.EndsWith("*", StringComparison.Ordinal))
{
if (lookupInfo.TypePattern.Length == 1)
{
// TypePattern is "*".
return true;
}
var lookupTypeName = lookupInfo.TypePattern.Substring(0, lookupInfo.TypePattern.Length - 1);
return descriptor.TypeName.StartsWith(lookupTypeName, StringComparison.Ordinal);
}
return string.Equals(descriptor.TypeName, lookupInfo.TypePattern, StringComparison.Ordinal);
}
private static LookupInfo GetLookupInfo(
TagHelperDirectiveDescriptor directiveDescriptor,
ErrorSink errorSink)
{
var lookupText = directiveDescriptor.DirectiveText;
var lookupStrings = lookupText?.Split(new[] { ',' });
// Ensure that we have valid lookupStrings to work with. The valid format is "typeName, assemblyName"
if (lookupStrings == null ||
lookupStrings.Any(string.IsNullOrWhiteSpace) ||
lookupStrings.Length != 2)
{
errorSink.OnError(
directiveDescriptor.Location,
Resources.FormatTagHelperDescriptorResolver_InvalidTagHelperLookupText(lookupText),
GetErrorLength(lookupText));
return null;
}
var trimmedAssemblyName = lookupStrings[1].Trim();
// + 1 is for the comma separator in the lookup text.
var assemblyNameIndex =
lookupStrings[0].Length + 1 + lookupStrings[1].IndexOf(trimmedAssemblyName, StringComparison.Ordinal);
var assemblyNamePrefix = directiveDescriptor.DirectiveText.Substring(0, assemblyNameIndex);
var assemblyNameLocation = SourceLocation.Advance(directiveDescriptor.Location, assemblyNamePrefix);
return new LookupInfo
{
TypePattern = lookupStrings[0].Trim(),
AssemblyName = trimmedAssemblyName,
AssemblyNameLocation = assemblyNameLocation,
};
}
private static int GetErrorLength(string directiveText)
{
var nonNullLength = directiveText == null ? 1 : directiveText.Length;
var normalizeEmptyStringLength = Math.Max(nonNullLength, 1);
return normalizeEmptyStringLength;
}
private class LookupInfo
{
public string AssemblyName { get; set; }
public string TypePattern { get; set; }
public SourceLocation AssemblyNameLocation { get; set; }
}
}
}

View File

@ -1,240 +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.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
{
/// <summary>
/// Factory for providing <see cref="TagHelperDesignTimeDescriptor"/>s from <see cref="Type"/>s and
/// <see cref="TagHelperAttributeDesignTimeDescriptor"/>s from <see cref="PropertyInfo"/>s.
/// </summary>
public class TagHelperDesignTimeDescriptorFactory
{
private readonly ConcurrentDictionary<int, XmlDocumentationProvider> _documentationProviderCache =
new ConcurrentDictionary<int, XmlDocumentationProvider>();
/// <summary>
/// Creates a <see cref="TagHelperDesignTimeDescriptor"/> from the given <paramref name="type"/>.
/// </summary>
/// <param name="type">
/// The <see cref="Type"/> to create a <see cref="TagHelperDesignTimeDescriptor"/> from.
/// </param>
/// <returns>A <see cref="TagHelperDesignTimeDescriptor"/> that describes design time specific information
/// for the given <paramref name="type"/>.</returns>
public TagHelperDesignTimeDescriptor CreateDescriptor(Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
var id = XmlDocumentationProvider.GetId(type);
var documentationDescriptor = CreateDocumentationDescriptor(type.GetTypeInfo().Assembly, id);
var outputElementHintAttribute = type
.GetTypeInfo()
.GetCustomAttributes(inherit: false)
?.OfType<OutputElementHintAttribute>()
.FirstOrDefault();
var outputElementHint = outputElementHintAttribute?.OutputElement;
if (documentationDescriptor != null || outputElementHint != null)
{
return new TagHelperDesignTimeDescriptor
{
Summary = documentationDescriptor?.Summary,
Remarks = documentationDescriptor?.Remarks,
OutputElementHint = outputElementHint
};
}
return null;
}
/// <summary>
/// Creates a <see cref="TagHelperAttributeDesignTimeDescriptor"/> from the given
/// <paramref name="propertyInfo"/>.
/// </summary>
/// <param name="propertyInfo">
/// The <see cref="PropertyInfo"/> to create a <see cref="TagHelperAttributeDesignTimeDescriptor"/> from.
/// </param>
/// <returns>A <see cref="TagHelperAttributeDesignTimeDescriptor"/> that describes design time specific
/// information for the given <paramref name="propertyInfo"/>.</returns>
public TagHelperAttributeDesignTimeDescriptor CreateAttributeDescriptor(PropertyInfo propertyInfo)
{
if (propertyInfo == null)
{
throw new ArgumentNullException(nameof(propertyInfo));
}
var id = XmlDocumentationProvider.GetId(propertyInfo);
var declaringAssembly = propertyInfo.DeclaringType.GetTypeInfo().Assembly;
var documentationDescriptor = CreateDocumentationDescriptor(declaringAssembly, id);
if (documentationDescriptor != null)
{
return new TagHelperAttributeDesignTimeDescriptor
{
Summary = documentationDescriptor.Summary,
Remarks = documentationDescriptor.Remarks
};
}
return null;
}
/// <summary>
/// Retrieves <paramref name="assembly"/>'s location on disk.
/// </summary>
/// <param name="assembly">The assembly.</param>
/// <returns>The path to the given <paramref name="assembly"/>.</returns>
public virtual string GetAssemblyLocation(Assembly assembly)
{
var assemblyLocation = assembly.Location;
return assemblyLocation;
}
private XmlDocumentationProvider GetXmlDocumentationProvider(Assembly assembly)
{
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(assembly);
hashCodeCombiner.Add(CultureInfo.CurrentCulture);
var cacheKey = hashCodeCombiner.CombinedHash;
var documentationProvider = _documentationProviderCache.GetOrAdd(cacheKey, valueFactory: _ =>
{
var assemblyLocation = GetAssemblyLocation(assembly);
// Couldn't resolve a valid assemblyLocation.
if (string.IsNullOrEmpty(assemblyLocation))
{
return null;
}
var xmlDocumentationFile = GetXmlDocumentationFile(assemblyLocation);
// Only want to process the file if it exists.
if (xmlDocumentationFile != null)
{
return new XmlDocumentationProvider(xmlDocumentationFile.FullName);
}
return null;
});
return documentationProvider;
}
private DocumentationDescriptor CreateDocumentationDescriptor(Assembly assembly, string id)
{
var documentationProvider = GetXmlDocumentationProvider(assembly);
if (documentationProvider != null)
{
var summary = documentationProvider.GetSummary(id);
var remarks = documentationProvider.GetRemarks(id);
if (!string.IsNullOrEmpty(summary) || !string.IsNullOrEmpty(remarks))
{
return new DocumentationDescriptor
{
Summary = summary,
Remarks = remarks
};
}
}
return null;
}
private static FileInfo GetXmlDocumentationFile(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;
}
private class DocumentationDescriptor
{
public string Summary { get; set; }
public string Remarks { get; set; }
}
}
}

View File

@ -1,93 +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 System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
{
/// <summary>
/// Class that locates valid <see cref="ITagHelper"/>s within an assembly.
/// </summary>
public class TagHelperTypeResolver : ITagHelperTypeResolver
{
private static readonly TypeInfo ITagHelperTypeInfo = typeof(ITagHelper).GetTypeInfo();
/// <inheritdoc />
public IEnumerable<Type> Resolve(
string name,
SourceLocation documentLocation,
ErrorSink errorSink)
{
if (errorSink == null)
{
throw new ArgumentNullException(nameof(errorSink));
}
if (string.IsNullOrEmpty(name))
{
var errorLength = name == null ? 1 : Math.Max(name.Length, 1);
errorSink.OnError(
documentLocation,
Resources.TagHelperTypeResolver_TagHelperAssemblyNameCannotBeEmptyOrNull,
errorLength);
return Type.EmptyTypes;
}
var assemblyName = new AssemblyName(name);
IEnumerable<TypeInfo> libraryTypes;
try
{
libraryTypes = GetExportedTypes(assemblyName);
}
catch (Exception ex)
{
errorSink.OnError(
documentLocation,
Resources.FormatTagHelperTypeResolver_CannotResolveTagHelperAssembly(
assemblyName.Name,
ex.Message),
name.Length);
return Type.EmptyTypes;
}
return libraryTypes.Where(IsTagHelper).Select(t => t.AsType());
}
/// <summary>
/// Returns all exported types from the given <paramref name="assemblyName"/>
/// </summary>
/// <param name="assemblyName">The <see cref="AssemblyName"/> to get <see cref="TypeInfo"/>s from.</param>
/// <returns>
/// An <see cref="IEnumerable{TypeInfo}"/> of types exported from the given <paramref name="assemblyName"/>.
/// </returns>
protected virtual IEnumerable<TypeInfo> GetExportedTypes(AssemblyName assemblyName)
{
var assembly = Assembly.Load(assemblyName);
return assembly.ExportedTypes.Select(type => type.GetTypeInfo());
}
/// <summary>
/// Indicates if a <see cref="TypeInfo"/> should be treated as a tag helper.
/// </summary>
/// <param name="typeInfo">The <see cref="TypeInfo"/> to inspect.</param>
/// <returns><c>true</c> if <paramref name="typeInfo"/> should be treated as a tag helper;
/// <c>false</c> otherwise</returns>
protected virtual bool IsTagHelper(TypeInfo typeInfo)
{
if (typeInfo == null)
{
throw new ArgumentNullException(nameof(typeInfo));
}
return TagHelperConventions.IsTagHelper(typeInfo);
}
}
}

View File

@ -1,127 +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 System.Linq;
using System.Reflection;
using System.Text;
using System.Xml.Linq;
namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
{
/// <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>&lt;summary&gt;</c> documentation for the given <paramref name="id"/>.
/// </summary>
/// <param name="id">The id to lookup.</param>
/// <returns><c>&lt;summary&gt;</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>&lt;remarks&gt;</c> documentation for the given <paramref name="id"/>.
/// </summary>
/// <param name="id">The id to lookup.</param>
/// <returns><c>&lt;remarks&gt;</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(Type type)
{
if (type == null)
{
throw new ArgumentNullException(nameof(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(PropertyInfo propertyInfo)
{
if (propertyInfo == null)
{
throw new ArgumentNullException(nameof(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();
}
}
}

View File

@ -324,12 +324,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
return this;
}
private string DebuggerToString()
{
return GetContent();
}
// Overrides Write(string) to find if the content written is empty/whitespace.
private class EmptyOrWhiteSpaceWriter : TextWriter
{

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
namespace Microsoft.AspNetCore.Razor.TagHelpers
{
@ -12,7 +11,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public sealed class HtmlTargetElementAttribute : Attribute
{
public const string ElementCatchAllTarget = TagHelperDescriptorProvider.ElementCatchAllTarget;
public const string ElementCatchAllTarget = "*";
/// <summary>
/// Instantiates a new instance of the <see cref="HtmlTargetElementAttribute"/> class that targets all HTML

View File

@ -1,50 +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 Microsoft.AspNetCore.Razor.Chunks.Generators;
using Microsoft.AspNetCore.Razor.CodeGenerators;
using Microsoft.AspNetCore.Razor.Parser;
namespace Microsoft.AspNetCore.Razor
{
/// <summary>
/// Defines the C# Code Language for Razor
/// </summary>
public class CSharpRazorCodeLanguage : RazorCodeLanguage
{
private const string CSharpLanguageName = "csharp";
/// <summary>
/// Returns the name of the language: "csharp"
/// </summary>
public override string LanguageName
{
get { return CSharpLanguageName; }
}
/// <summary>
/// Constructs a new instance of the code parser for this language
/// </summary>
public override ParserBase CreateCodeParser()
{
return new CSharpCodeParser();
}
/// <summary>
/// Constructs a new instance of the chunk generator for this language with the specified settings
/// </summary>
public override RazorChunkGenerator CreateChunkGenerator(
string className,
string rootNamespaceName,
string sourceFileName,
RazorEngineHost host)
{
return new RazorChunkGenerator(className, rootNamespaceName, sourceFileName, host);
}
public override CodeGenerator CreateCodeGenerator(CodeGeneratorContext chunkGeneratorContext)
{
return new CSharpCodeGenerator(chunkGeneratorContext);
}
}
}

View File

@ -1,16 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks
{
/// <summary>
/// A <see cref="Chunk"/> used to look up <see cref="Compilation.TagHelpers.TagHelperDescriptor"/>s.
/// </summary>
public class AddTagHelperChunk : Chunk
{
/// <summary>
/// Text used to look up <see cref="Compilation.TagHelpers.TagHelperDescriptor"/>s.
/// </summary>
public string LookupText { get; set; }
}
}

View File

@ -1,13 +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 Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class Chunk
{
public SourceLocation Start { get; set; }
public SyntaxTreeNode Association { get; set; }
}
}

View File

@ -1,9 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class ChunkTree : ParentChunk
{
}
}

View File

@ -1,194 +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.Collections.Generic;
using System.Diagnostics;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class ChunkTreeBuilder
{
private readonly Stack<ParentChunk> _parentStack;
private Chunk _lastChunk;
public ChunkTreeBuilder()
{
Root = new ChunkTree();
_parentStack = new Stack<ParentChunk>();
_parentStack.Push(Root);
}
public ParentChunk Current => _parentStack.Peek();
public ChunkTree Root { get; }
public void AddChunk(Chunk chunk, SyntaxTreeNode association, bool topLevel = false)
{
_lastChunk = chunk;
chunk.Start = association.Start;
chunk.Association = association;
// If we're not in the middle of a parent chunk
if (topLevel)
{
Root.Children.Add(chunk);
}
else
{
Current.Children.Add(chunk);
}
}
public void AddTagHelperPrefixDirectiveChunk(string prefix, SyntaxTreeNode association)
{
AddChunk(
new TagHelperPrefixDirectiveChunk
{
Prefix = prefix
},
association,
topLevel: true);
}
public void AddAddTagHelperChunk(string lookupText, SyntaxTreeNode association)
{
AddChunk(new AddTagHelperChunk
{
LookupText = lookupText
}, association, topLevel: true);
}
public void AddRemoveTagHelperChunk(string lookupText, SyntaxTreeNode association)
{
AddChunk(new RemoveTagHelperChunk
{
LookupText = lookupText
}, association, topLevel: true);
}
public void AddLiteralChunk(string literal, SyntaxTreeNode association)
{
ParentLiteralChunk parentLiteralChunk;
// We try to join literal chunks where possible, so that we have fewer 'writes' in the generated code.
//
// Possible cases here:
// - We just added a LiteralChunk and we need to add another - so merge them into ParentLiteralChunk.
// - We have a ParentLiteralChunk - merge the new chunk into it.
// - We just added something <else> - just add the LiteralChunk like normal.
if (_lastChunk is LiteralChunk)
{
parentLiteralChunk = new ParentLiteralChunk()
{
Start = _lastChunk.Start,
};
parentLiteralChunk.Children.Add(_lastChunk);
parentLiteralChunk.Children.Add(new LiteralChunk
{
Association = association,
Start = association.Start,
Text = literal,
});
Debug.Assert(Current.Children[Current.Children.Count - 1] == _lastChunk);
Current.Children.RemoveAt(Current.Children.Count - 1);
Current.Children.Add(parentLiteralChunk);
_lastChunk = parentLiteralChunk;
}
else if ((parentLiteralChunk = _lastChunk as ParentLiteralChunk) != null)
{
parentLiteralChunk.Children.Add(new LiteralChunk
{
Association = association,
Start = association.Start,
Text = literal,
});
_lastChunk = parentLiteralChunk;
}
else
{
AddChunk(new LiteralChunk
{
Text = literal,
}, association);
}
}
public void AddExpressionChunk(string expression, SyntaxTreeNode association)
{
AddChunk(new ExpressionChunk
{
Code = expression
}, association);
}
public void AddStatementChunk(string code, SyntaxTreeNode association)
{
AddChunk(new StatementChunk
{
Code = code,
}, association);
}
public void AddUsingChunk(string usingNamespace, SyntaxTreeNode association)
{
AddChunk(new UsingChunk
{
Namespace = usingNamespace,
}, association, topLevel: true);
}
public void AddTypeMemberChunk(string code, SyntaxTreeNode association)
{
AddChunk(new TypeMemberChunk
{
Code = code,
}, association, topLevel: true);
}
public void AddLiteralCodeAttributeChunk(string code, SyntaxTreeNode association)
{
AddChunk(new LiteralCodeAttributeChunk
{
Code = code,
}, association);
}
public void AddSetBaseTypeChunk(string typeName, SyntaxTreeNode association)
{
AddChunk(new SetBaseTypeChunk
{
TypeName = typeName.Trim()
}, association, topLevel: true);
}
public T StartParentChunk<T>(SyntaxTreeNode association) where T : ParentChunk, new()
{
return StartParentChunk<T>(association, topLevel: false);
}
public T StartParentChunk<T>(SyntaxTreeNode association, bool topLevel) where T : ParentChunk, new()
{
var parentChunk = new T();
return StartParentChunk<T>(parentChunk, association, topLevel);
}
public T StartParentChunk<T>(T parentChunk, SyntaxTreeNode association, bool topLevel) where T : ParentChunk
{
AddChunk(parentChunk, association, topLevel);
_parentStack.Push(parentChunk);
return parentChunk;
}
public void EndParentChunk()
{
_lastChunk = _parentStack.Pop();
}
}
}

View File

@ -1,14 +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 Microsoft.AspNetCore.Razor.Text;
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class CodeAttributeChunk : ParentChunk
{
public string Attribute { get; set; }
public LocationTagged<string> Prefix { get; set; }
public LocationTagged<string> Suffix { get; set; }
}
}

View File

@ -1,12 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Razor.Text;
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class DynamicCodeAttributeChunk : ParentChunk
{
public LocationTagged<string> Prefix { get; set; }
}
}

View File

@ -1,9 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class ExpressionBlockChunk : ParentChunk
{
}
}

View File

@ -1,15 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class ExpressionChunk : Chunk
{
public string Code { get; set; }
public override string ToString()
{
return Start + " = " + Code;
}
}
}

View File

@ -1,48 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public class AddImportChunkGenerator : SpanChunkGenerator
{
public AddImportChunkGenerator(string ns)
{
Namespace = ns;
}
public string Namespace { get; }
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
var ns = Namespace;
if (!string.IsNullOrEmpty(ns) && char.IsWhiteSpace(ns[0]))
{
ns = ns.Substring(1);
}
context.ChunkTreeBuilder.AddUsingChunk(ns, target);
}
public override string ToString()
{
return "Import:" + Namespace + ";";
}
public override bool Equals(object obj)
{
var other = obj as AddImportChunkGenerator;
return other != null &&
string.Equals(Namespace, other.Namespace, StringComparison.Ordinal);
}
public override int GetHashCode()
{
// Hash code should include only immutable properties.
return Namespace == null ? 0 : StringComparer.Ordinal.GetHashCode(Namespace);
}
}
}

View File

@ -1,62 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
/// <summary>
/// A <see cref="SpanChunkGenerator"/> responsible for generating <see cref="AddTagHelperChunk"/>s.
/// </summary>
public class AddTagHelperChunkGenerator : SpanChunkGenerator
{
/// <summary>
/// Initializes a new instance of <see cref="AddTagHelperChunkGenerator"/>.
/// </summary>
/// <param name="lookupText">
/// Text used to look up <see cref="Compilation.TagHelpers.TagHelperDescriptor"/>s that should be added.
/// </param>
public AddTagHelperChunkGenerator(string lookupText)
{
LookupText = lookupText;
}
/// <summary>
/// Gets the text used to look up <see cref="Compilation.TagHelpers.TagHelperDescriptor"/>s that should be added.
/// </summary>
public string LookupText { get; }
/// <summary>
/// Generates <see cref="AddTagHelperChunk"/>s.
/// </summary>
/// <param name="target">
/// The <see cref="Span"/> responsible for this <see cref="AddTagHelperChunkGenerator"/>.
/// </param>
/// <param name="context">A <see cref="ChunkGeneratorContext"/> instance that contains information about
/// the current chunk generation process.</param>
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.AddAddTagHelperChunk(LookupText, target);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
var other = obj as AddTagHelperChunkGenerator;
return base.Equals(other) &&
string.Equals(LookupText, other.LookupText, StringComparison.Ordinal);
}
/// <inheritdoc />
public override int GetHashCode()
{
var combiner = HashCodeCombiner.Start();
combiner.Add(base.GetHashCode());
combiner.Add(LookupText, StringComparer.Ordinal);
return combiner.CombinedHash;
}
}
}

View File

@ -1,65 +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.Globalization;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.Text;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public class AttributeBlockChunkGenerator : ParentChunkGenerator
{
public AttributeBlockChunkGenerator(string name, LocationTagged<string> prefix, LocationTagged<string> suffix)
{
Name = name;
Prefix = prefix;
Suffix = suffix;
}
public string Name { get; }
public LocationTagged<string> Prefix { get; }
public LocationTagged<string> Suffix { get; }
public override void GenerateStartParentChunk(Block target, ChunkGeneratorContext context)
{
var chunk = context.ChunkTreeBuilder.StartParentChunk<CodeAttributeChunk>(target);
chunk.Attribute = Name;
chunk.Prefix = Prefix;
chunk.Suffix = Suffix;
}
public override void GenerateEndParentChunk(Block target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.EndParentChunk();
}
public override string ToString()
{
return string.Format(CultureInfo.CurrentCulture, "Attr:{0},{1:F},{2:F}", Name, Prefix, Suffix);
}
public override bool Equals(object obj)
{
var other = obj as AttributeBlockChunkGenerator;
return other != null &&
string.Equals(other.Name, Name, StringComparison.Ordinal) &&
Equals(other.Prefix, Prefix) &&
Equals(other.Suffix, Suffix);
}
public override int GetHashCode()
{
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(Name, StringComparer.Ordinal);
hashCodeCombiner.Add(Prefix);
hashCodeCombiner.Add(Suffix);
return hashCodeCombiner;
}
}
}

View File

@ -1,44 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public class ChunkGeneratorContext
{
protected ChunkGeneratorContext(ChunkGeneratorContext context)
: this(
context.Host,
context.ClassName,
context.RootNamespace,
context.SourceFile,
// True because we're pulling from the provided context's source file.
shouldGenerateLinePragmas: true)
{
ChunkTreeBuilder = context.ChunkTreeBuilder;
}
public ChunkGeneratorContext(
RazorEngineHost host,
string className,
string rootNamespace,
string sourceFile,
bool shouldGenerateLinePragmas)
{
ChunkTreeBuilder = new ChunkTreeBuilder();
Host = host;
SourceFile = shouldGenerateLinePragmas ? sourceFile : null;
RootNamespace = rootNamespace;
ClassName = className;
}
public string SourceFile { get; internal set; }
public string RootNamespace { get; }
public string ClassName { get; }
public RazorEngineHost Host { get; }
public ChunkTreeBuilder ChunkTreeBuilder { get; set; }
}
}

View File

@ -1,56 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Globalization;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.Text;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public class DynamicAttributeBlockChunkGenerator : ParentChunkGenerator
{
public DynamicAttributeBlockChunkGenerator(LocationTagged<string> prefix, int offset, int line, int col)
: this(prefix, new SourceLocation(offset, line, col))
{
}
public DynamicAttributeBlockChunkGenerator(LocationTagged<string> prefix, SourceLocation valueStart)
{
Prefix = prefix;
ValueStart = valueStart;
}
public LocationTagged<string> Prefix { get; }
public SourceLocation ValueStart { get; }
public override void GenerateStartParentChunk(Block target, ChunkGeneratorContext context)
{
var chunk = context.ChunkTreeBuilder.StartParentChunk<DynamicCodeAttributeChunk>(target);
chunk.Start = ValueStart;
chunk.Prefix = Prefix;
}
public override void GenerateEndParentChunk(Block target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.EndParentChunk();
}
public override string ToString()
{
return string.Format(CultureInfo.CurrentCulture, "DynAttr:{0:F}", Prefix);
}
public override bool Equals(object obj)
{
var other = obj as DynamicAttributeBlockChunkGenerator;
return other != null &&
Equals(other.Prefix, Prefix);
}
public override int GetHashCode()
{
return Prefix == null ? 0 : Prefix.GetHashCode();
}
}
}

View File

@ -1,43 +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 Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public class ExpressionChunkGenerator : ISpanChunkGenerator, IParentChunkGenerator
{
private static readonly int TypeHashCode = typeof(ExpressionChunkGenerator).GetHashCode();
public void GenerateStartParentChunk(Block target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.StartParentChunk<ExpressionBlockChunk>(target);
}
public void GenerateChunk(Span target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.AddExpressionChunk(target.Content, target);
}
public void GenerateEndParentChunk(Block target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.EndParentChunk();
}
public override string ToString()
{
return "Expr";
}
public override bool Equals(object obj)
{
return obj != null &&
GetType() == obj.GetType();
}
public override int GetHashCode()
{
return TypeHashCode;
}
}
}

View File

@ -1,13 +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 Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public interface IParentChunkGenerator
{
void GenerateStartParentChunk(Block target, ChunkGeneratorContext context);
void GenerateEndParentChunk(Block target, ChunkGeneratorContext context);
}
}

View File

@ -1,12 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public interface ISpanChunkGenerator
{
void GenerateChunk(Span target, ChunkGeneratorContext context);
}
}

View File

@ -1,83 +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.Globalization;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.Text;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public class LiteralAttributeChunkGenerator : SpanChunkGenerator
{
public LiteralAttributeChunkGenerator(
LocationTagged<string> prefix,
LocationTagged<SpanChunkGenerator> valueGenerator)
{
Prefix = prefix;
ValueGenerator = valueGenerator;
}
public LiteralAttributeChunkGenerator(LocationTagged<string> prefix, LocationTagged<string> value)
{
Prefix = prefix;
Value = value;
}
public LocationTagged<string> Prefix { get; }
public LocationTagged<string> Value { get; }
public LocationTagged<SpanChunkGenerator> ValueGenerator { get; }
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
var chunk = context.ChunkTreeBuilder.StartParentChunk<LiteralCodeAttributeChunk>(target);
chunk.Prefix = Prefix;
chunk.Value = Value;
if (ValueGenerator != null)
{
chunk.ValueLocation = ValueGenerator.Location;
ValueGenerator.Value.GenerateChunk(target, context);
chunk.ValueLocation = ValueGenerator.Location;
}
context.ChunkTreeBuilder.EndParentChunk();
}
public override string ToString()
{
if (ValueGenerator == null)
{
return string.Format(CultureInfo.CurrentCulture, "LitAttr:{0:F},{1:F}", Prefix, Value);
}
else
{
return string.Format(CultureInfo.CurrentCulture, "LitAttr:{0:F},<Sub:{1:F}>", Prefix, ValueGenerator);
}
}
public override bool Equals(object obj)
{
var other = obj as LiteralAttributeChunkGenerator;
return other != null &&
Equals(other.Prefix, Prefix) &&
Equals(other.Value, Value) &&
Equals(other.ValueGenerator, ValueGenerator);
}
public override int GetHashCode()
{
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(Prefix);
hashCodeCombiner.Add(Value);
hashCodeCombiner.Add(ValueGenerator);
return hashCodeCombiner;
}
}
}

View File

@ -1,20 +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 Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public class MarkupChunkGenerator : SpanChunkGenerator
{
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.AddLiteralChunk(target.Content, target);
}
public override string ToString()
{
return "Markup";
}
}
}

View File

@ -1,49 +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 Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public abstract class ParentChunkGenerator : IParentChunkGenerator
{
private static readonly int TypeHashCode = typeof(ParentChunkGenerator).GetHashCode();
public static readonly IParentChunkGenerator Null = new NullParentChunkGenerator();
public virtual void GenerateStartParentChunk(Block target, ChunkGeneratorContext context)
{
}
public virtual void GenerateEndParentChunk(Block target, ChunkGeneratorContext context)
{
}
public override bool Equals(object obj)
{
return obj != null &&
GetType() == obj.GetType();
}
public override int GetHashCode()
{
return TypeHashCode;
}
private class NullParentChunkGenerator : IParentChunkGenerator
{
public void GenerateStartParentChunk(Block target, ChunkGeneratorContext context)
{
}
public void GenerateEndParentChunk(Block target, ChunkGeneratorContext context)
{
}
public override string ToString()
{
return "None";
}
}
}
}

View File

@ -1,93 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Parser;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public class RazorChunkGenerator : ParserVisitor
{
private ChunkGeneratorContext _context;
public RazorChunkGenerator(
string className,
string rootNamespaceName,
string sourceFileName,
RazorEngineHost host)
{
if (rootNamespaceName == null)
{
throw new ArgumentNullException(nameof(rootNamespaceName));
}
if (host == null)
{
throw new ArgumentNullException(nameof(host));
}
if (string.IsNullOrEmpty(className))
{
throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, nameof(className));
}
ClassName = className;
RootNamespaceName = rootNamespaceName;
SourceFileName = sourceFileName;
GenerateLinePragmas = !string.IsNullOrEmpty(SourceFileName);
Host = host;
}
// Data pulled from constructor
public string ClassName { get; private set; }
public string RootNamespaceName { get; private set; }
public string SourceFileName { get; private set; }
public RazorEngineHost Host { get; private set; }
// Generation settings
public bool GenerateLinePragmas { get; set; }
public bool DesignTimeMode { get; set; }
public ChunkGeneratorContext Context
{
get
{
EnsureContextInitialized();
return _context;
}
}
public override void VisitStartBlock(Block block)
{
block.ChunkGenerator.GenerateStartParentChunk(block, Context);
}
public override void VisitEndBlock(Block block)
{
block.ChunkGenerator.GenerateEndParentChunk(block, Context);
}
public override void VisitSpan(Span span)
{
span.ChunkGenerator.GenerateChunk(span, Context);
}
private void EnsureContextInitialized()
{
if (_context == null)
{
_context = new ChunkGeneratorContext(Host,
ClassName,
RootNamespaceName,
SourceFileName,
GenerateLinePragmas);
Initialize(_context);
}
}
protected virtual void Initialize(ChunkGeneratorContext context)
{
}
}
}

View File

@ -1,10 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public class RazorCommentChunkGenerator : ParentChunkGenerator
{
}
}

View File

@ -1,62 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
/// <summary>
/// A <see cref="SpanChunkGenerator"/> responsible for generating <see cref="RemoveTagHelperChunk"/>s.
/// </summary>
public class RemoveTagHelperChunkGenerator : SpanChunkGenerator
{
/// <summary>
/// Initializes a new instance of <see cref="RemoveTagHelperChunkGenerator"/>.
/// </summary>
/// <param name="lookupText">
/// Text used to look up <see cref="Compilation.TagHelpers.TagHelperDescriptor"/>s that should be removed.
/// </param>
public RemoveTagHelperChunkGenerator(string lookupText)
{
LookupText = lookupText;
}
/// <summary>
/// Text used to look up <see cref="Compilation.TagHelpers.TagHelperDescriptor"/>s that should be removed.
/// </summary>
public string LookupText { get; }
/// <summary>
/// Generates <see cref="RemoveTagHelperChunk"/>s.
/// </summary>
/// <param name="target">
/// The <see cref="Span"/> responsible for this <see cref="RemoveTagHelperChunkGenerator"/>.
/// </param>
/// <param name="context">A <see cref="ChunkGeneratorContext"/> instance that contains information about
/// the current chunk generation process.</param>
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.AddRemoveTagHelperChunk(LookupText, target);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
var other = obj as RemoveTagHelperChunkGenerator;
return base.Equals(other) &&
string.Equals(LookupText, other.LookupText, StringComparison.Ordinal);
}
/// <inheritdoc />
public override int GetHashCode()
{
var combiner = HashCodeCombiner.Start();
combiner.Add(base.GetHashCode());
combiner.Add(LookupText, StringComparer.Ordinal);
return combiner.CombinedHash;
}
}
}

View File

@ -1,47 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public class SectionChunkGenerator : ParentChunkGenerator
{
public SectionChunkGenerator(string sectionName)
{
SectionName = sectionName;
}
public string SectionName { get; }
public override void GenerateStartParentChunk(Block target, ChunkGeneratorContext context)
{
var chunk = context.ChunkTreeBuilder.StartParentChunk<SectionChunk>(target);
chunk.Name = SectionName;
}
public override void GenerateEndParentChunk(Block target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.EndParentChunk();
}
public override bool Equals(object obj)
{
var other = obj as SectionChunkGenerator;
return base.Equals(other) &&
string.Equals(SectionName, other.SectionName, StringComparison.Ordinal);
}
public override int GetHashCode()
{
return SectionName == null ? 0 : StringComparer.Ordinal.GetHashCode(SectionName);
}
public override string ToString()
{
return "Section:" + SectionName;
}
}
}

View File

@ -1,40 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public class SetBaseTypeChunkGenerator : SpanChunkGenerator
{
public SetBaseTypeChunkGenerator(string baseType)
{
BaseType = baseType;
}
public string BaseType { get; }
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.AddSetBaseTypeChunk(BaseType, target);
}
public override string ToString()
{
return "Base:" + BaseType;
}
public override bool Equals(object obj)
{
var other = obj as SetBaseTypeChunkGenerator;
return other != null &&
string.Equals(BaseType, other.BaseType, StringComparison.Ordinal);
}
public override int GetHashCode()
{
return BaseType.GetHashCode();
}
}
}

View File

@ -1,41 +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 Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public abstract class SpanChunkGenerator : ISpanChunkGenerator
{
private static readonly int TypeHashCode = typeof(SpanChunkGenerator).GetHashCode();
public static readonly ISpanChunkGenerator Null = new NullSpanChunkGenerator();
public virtual void GenerateChunk(Span target, ChunkGeneratorContext context)
{
}
public override bool Equals(object obj)
{
return obj != null &&
GetType() == obj.GetType();
}
public override int GetHashCode()
{
return TypeHashCode;
}
private class NullSpanChunkGenerator : ISpanChunkGenerator
{
public void GenerateChunk(Span target, ChunkGeneratorContext context)
{
}
public override string ToString()
{
return "None";
}
}
}
}

View File

@ -1,20 +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 Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public class StatementChunkGenerator : SpanChunkGenerator
{
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.AddStatementChunk(target.Content, target);
}
public override string ToString()
{
return "Stmt";
}
}
}

View File

@ -1,112 +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.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.Parser.TagHelpers;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
/// <summary>
/// A <see cref="ParentChunkGenerator"/> that is responsible for generating valid <see cref="TagHelperChunk"/>s.
/// </summary>
public class TagHelperChunkGenerator : ParentChunkGenerator
{
private IEnumerable<TagHelperDescriptor> _tagHelperDescriptors;
/// <summary>
/// Instantiates a new <see cref="TagHelperChunkGenerator"/>.
/// </summary>
/// <param name="tagHelperDescriptors">
/// <see cref="TagHelperDescriptor"/>s associated with the current HTML tag.
/// </param>
public TagHelperChunkGenerator(IEnumerable<TagHelperDescriptor> tagHelperDescriptors)
{
_tagHelperDescriptors = tagHelperDescriptors;
}
/// <summary>
/// Starts the generation of a <see cref="TagHelperChunk"/>.
/// </summary>
/// <param name="target">
/// The <see cref="Block"/> responsible for this <see cref="TagHelperChunkGenerator"/>.
/// </param>
/// <param name="context">A <see cref="ChunkGeneratorContext"/> instance that contains information about
/// the current chunk generation process.</param>
public override void GenerateStartParentChunk(Block target, ChunkGeneratorContext context)
{
var tagHelperBlock = target as TagHelperBlock;
Debug.Assert(
tagHelperBlock != null,
$"A {nameof(TagHelperChunkGenerator)} must only be used with {nameof(TagHelperBlock)}s.");
var attributes = new List<TagHelperAttributeTracker>();
// We need to create a chunk generator to create chunks for each of the attributes.
var chunkGenerator = context.Host.CreateChunkGenerator(
context.ClassName,
context.RootNamespace,
context.SourceFile);
foreach (var attribute in tagHelperBlock.Attributes)
{
ParentChunk attributeChunkValue = null;
if (attribute.Value != null)
{
// Populates the chunk tree with chunks associated with attributes
attribute.Value.Accept(chunkGenerator);
var chunks = chunkGenerator.Context.ChunkTreeBuilder.Root.Children;
var first = chunks.FirstOrDefault();
attributeChunkValue = new ParentChunk
{
Association = first?.Association,
Children = chunks,
Start = first == null ? SourceLocation.Zero : first.Start
};
}
var attributeChunk = new TagHelperAttributeTracker(
attribute.Name,
attributeChunkValue,
attribute.ValueStyle);
attributes.Add(attributeChunk);
// Reset the chunk tree builder so we can build a new one for the next attribute
chunkGenerator.Context.ChunkTreeBuilder = new ChunkTreeBuilder();
}
var unprefixedTagName = tagHelperBlock.TagName.Substring(_tagHelperDescriptors.First().Prefix.Length);
context.ChunkTreeBuilder.StartParentChunk(
new TagHelperChunk(
unprefixedTagName,
tagHelperBlock.TagMode,
attributes,
_tagHelperDescriptors),
target,
topLevel: false);
}
/// <summary>
/// Ends the generation of a <see cref="TagHelperChunk"/> capturing all previously visited children
/// since the <see cref="GenerateStartParentChunk"/> method was called.
/// </summary>
/// <param name="target">
/// The <see cref="Block"/> responsible for this <see cref="TagHelperChunkGenerator"/>.
/// </param>
/// <param name="context">A <see cref="ChunkGeneratorContext"/> instance that contains information about
/// the current chunk generation process.</param>
public override void GenerateEndParentChunk(Block target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.EndParentChunk();
}
}
}

View File

@ -1,63 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
/// <summary>
/// A <see cref="SpanChunkGenerator"/> responsible for generating
/// <see cref="TagHelperPrefixDirectiveChunk"/>s.
/// </summary>
public class TagHelperPrefixDirectiveChunkGenerator : SpanChunkGenerator
{
/// <summary>
/// Initializes a new instance of <see cref="TagHelperPrefixDirectiveChunkGenerator"/>.
/// </summary>
/// <param name="prefix">
/// Text used as a required prefix when matching HTML.
/// </param>
public TagHelperPrefixDirectiveChunkGenerator(string prefix)
{
Prefix = prefix;
}
/// <summary>
/// Text used as a required prefix when matching HTML.
/// </summary>
public string Prefix { get; }
/// <summary>
/// Generates <see cref="TagHelperPrefixDirectiveChunk"/>s.
/// </summary>
/// <param name="target">
/// The <see cref="Span"/> responsible for this <see cref="TagHelperPrefixDirectiveChunkGenerator"/>.
/// </param>
/// <param name="context">A <see cref="ChunkGeneratorContext"/> instance that contains information about
/// the current chunk generation process.</param>
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.AddTagHelperPrefixDirectiveChunk(Prefix, target);
}
/// <inheritdoc />
public override bool Equals(object obj)
{
var other = obj as TagHelperPrefixDirectiveChunkGenerator;
return base.Equals(other) &&
string.Equals(Prefix, other.Prefix, StringComparison.Ordinal);
}
/// <inheritdoc />
public override int GetHashCode()
{
var combiner = HashCodeCombiner.Start();
combiner.Add(base.GetHashCode());
combiner.Add(Prefix, StringComparer.Ordinal);
return combiner.CombinedHash;
}
}
}

View File

@ -1,20 +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 Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public class TemplateBlockChunkGenerator : ParentChunkGenerator
{
public override void GenerateStartParentChunk(Block target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.StartParentChunk<TemplateChunk>(target);
}
public override void GenerateEndParentChunk(Block target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.EndParentChunk();
}
}
}

View File

@ -1,20 +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 Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.Chunks.Generators
{
public class TypeMemberChunkGenerator : SpanChunkGenerator
{
public override void GenerateChunk(Span target, ChunkGeneratorContext context)
{
context.ChunkTreeBuilder.AddTypeMemberChunk(target.Content, target);
}
public override string ToString()
{
return "TypeMember";
}
}
}

View File

@ -1,15 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class LiteralChunk : Chunk
{
public string Text { get; set; }
public override string ToString()
{
return Start + " = " + Text;
}
}
}

View File

@ -1,15 +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 Microsoft.AspNetCore.Razor.Text;
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class LiteralCodeAttributeChunk : ParentChunk
{
public string Code { get; set; }
public LocationTagged<string> Prefix { get; set; }
public LocationTagged<string> Value { get; set; }
public SourceLocation ValueLocation { get; set; }
}
}

View File

@ -1,17 +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.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class ParentChunk : Chunk
{
public ParentChunk()
{
Children = new List<Chunk>();
}
public IList<Chunk> Children { get; set; }
}
}

View File

@ -1,21 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Text;
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class ParentLiteralChunk : ParentChunk
{
public string GetText()
{
var builder = new StringBuilder();
for (var i = 0; i < Children.Count; i++)
{
builder.Append(((LiteralChunk)Children[i]).Text);
}
return builder.ToString();
}
}
}

View File

@ -1,16 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks
{
/// <summary>
/// A <see cref="Chunk"/> that represents a pre-allocated tag helper attribute.
/// </summary>
public class PreallocatedTagHelperAttributeChunk : Chunk
{
/// <summary>
/// The variable holding the pre-allocated attribute.
/// </summary>
public string AttributeVariableAccessor { get; set; }
}
}

View File

@ -1,18 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks
{
/// <summary>
/// A <see cref="Chunk"/> used to look up <see cref="Compilation.TagHelpers.TagHelperDescriptor"/>s that should be ignored
/// within the Razor page.
/// </summary>
public class RemoveTagHelperChunk : Chunk
{
/// <summary>
/// Text used to look up <see cref="Compilation.TagHelpers.TagHelperDescriptor"/>s that should be ignored within the Razor
/// page.
/// </summary>
public string LookupText { get; set; }
}
}

View File

@ -1,10 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class SectionChunk : ParentChunk
{
public string Name { get; set; }
}
}

View File

@ -1,10 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class SetBaseTypeChunk : Chunk
{
public string TypeName { get; set; }
}
}

View File

@ -1,15 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class StatementChunk : Chunk
{
public string Code { get; set; }
public override string ToString()
{
return Start + " = " + Code;
}
}
}

View File

@ -1,23 +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 Microsoft.AspNetCore.Razor.TagHelpers;
namespace Microsoft.AspNetCore.Razor.Chunks
{
public struct TagHelperAttributeTracker
{
public TagHelperAttributeTracker(string name, Chunk value, HtmlAttributeValueStyle valueStyle)
{
Name = name;
Value = value;
ValueStyle = valueStyle;
}
public string Name { get; }
public Chunk Value { get; }
public HtmlAttributeValueStyle ValueStyle { get; }
}
}

View File

@ -1,56 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
namespace Microsoft.AspNetCore.Razor.Chunks
{
/// <summary>
/// A <see cref="ParentChunk"/> that represents a special HTML tag.
/// </summary>
public class TagHelperChunk : ParentChunk
{
/// <summary>
/// Instantiates a new <see cref="TagHelperChunk"/>.
/// </summary>
/// <param name="tagName">The tag name associated with the tag helpers HTML element.</param>
/// <param name="tagMode">HTML syntax of the element in the Razor source.</param>
/// <param name="attributes">The attributes associated with the tag helpers HTML element.</param>
/// <param name="descriptors">
/// The <see cref="TagHelperDescriptor"/>s associated with this tag helpers HTML element.
/// </param>
public TagHelperChunk(
string tagName,
TagMode tagMode,
IList<TagHelperAttributeTracker> attributes,
IEnumerable<TagHelperDescriptor> descriptors)
{
TagName = tagName;
TagMode = tagMode;
Attributes = attributes;
Descriptors = descriptors;
}
/// <summary>
/// The HTML attributes.
/// </summary>
public IList<TagHelperAttributeTracker> Attributes { get; set; }
/// <summary>
/// The <see cref="TagHelperDescriptor"/>s that are associated with the tag helpers HTML element.
/// </summary>
public IEnumerable<TagHelperDescriptor> Descriptors { get; set; }
/// <summary>
/// The HTML tag name.
/// </summary>
public string TagName { get; set; }
/// <summary>
/// Gets the HTML syntax of the element in the Razor source.
/// </summary>
public TagMode TagMode { get; }
}
}

View File

@ -1,17 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks
{
/// <summary>
/// A <see cref="Chunk"/> for the <c>@tagHelperPrefix</c> directive.
/// </summary>
public class TagHelperPrefixDirectiveChunk : Chunk
{
/// <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; set; }
}
}

View File

@ -1,9 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class TemplateChunk : ParentChunk
{
}
}

View File

@ -1,10 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class TypeMemberChunk : Chunk
{
public string Code { get; set; }
}
}

View File

@ -1,10 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Chunks
{
public class UsingChunk : Chunk
{
public string Namespace { get; set; }
}
}

View File

@ -1,188 +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 System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.Chunks;
using Microsoft.AspNetCore.Razor.CodeGenerators.Visitors;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
public class CSharpCodeGenerator : CodeGenerator
{
// See http://msdn.microsoft.com/en-us/library/system.codedom.codechecksumpragma.checksumalgorithmid.aspx
private const string Sha1AlgorithmId = "{ff1816ec-aa5e-4d10-87f7-6f4963833460}";
private const int DisableAsyncWarning = 1998;
public CSharpCodeGenerator(CodeGeneratorContext context)
: base(context)
{
}
protected ChunkTree Tree { get { return Context.ChunkTreeBuilder.Root; } }
public RazorEngineHost Host { get { return Context.Host; } }
/// <summary>
/// Protected for testing.
/// </summary>
/// <returns>A new instance of <see cref="CSharpCodeWriter"/>.</returns>
protected virtual CSharpCodeWriter CreateCodeWriter()
{
return new CSharpCodeWriter();
}
public override CodeGeneratorResult Generate()
{
var writer = CreateCodeWriter();
if (!Host.DesignTimeMode && !string.IsNullOrEmpty(Context.Checksum))
{
writer.Write("#pragma checksum \"")
.Write(Context.SourceFile)
.Write("\" \"")
.Write(Sha1AlgorithmId)
.Write("\" \"")
.Write(Context.Checksum)
.WriteLine("\"");
}
using (writer.BuildNamespace(Context.RootNamespace))
{
// Write out using directives
AddImports(Tree, writer, Host.NamespaceImports);
// Separate the usings and the class
writer.WriteLine();
using (BuildClassDeclaration(writer))
{
if (Host.DesignTimeMode)
{
writer.WriteLine("private static object @__o;");
}
var csharpCodeVisitor = CreateCSharpCodeVisitor(writer, Context);
new CSharpTypeMemberVisitor(csharpCodeVisitor, writer, Context).Accept(Tree.Children);
CreateCSharpDesignTimeCodeVisitor(csharpCodeVisitor, writer, Context)
.AcceptTree(Tree);
new CSharpTagHelperFieldDeclarationVisitor(writer, Context).Accept(Tree.Children);
BuildConstructor(writer);
// Add space in-between constructor and method body
writer.WriteLine();
using (writer.BuildDisableWarningScope(DisableAsyncWarning))
{
using (writer.BuildMethodDeclaration("public override async", "Task", Host.GeneratedClassContext.ExecuteMethodName))
{
new CSharpTagHelperPropertyInitializationVisitor(writer, Context).Accept(Tree.Children);
csharpCodeVisitor.Accept(Tree.Children);
}
}
BuildAfterExecuteContent(writer, Tree.Children);
}
}
return new CodeGeneratorResult(writer.GenerateCode(), writer.LineMappingManager.Mappings);
}
/// <summary>
/// Provides an entry point to append code (after execute content) to a generated Razor class.
/// </summary>
/// <param name="writer">The <see cref="CSharpCodeWriter"/> to receive the additional content.</param>
/// <param name="chunks">The list of <see cref="Chunk"/>s for the generated program.</param>
protected virtual void BuildAfterExecuteContent(CSharpCodeWriter writer, IList<Chunk> chunks)
{
}
protected virtual CSharpCodeVisitor CreateCSharpCodeVisitor(
CSharpCodeWriter writer,
CodeGeneratorContext context)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
return new CSharpCodeVisitor(writer, context);
}
protected virtual CSharpDesignTimeCodeVisitor CreateCSharpDesignTimeCodeVisitor(
CSharpCodeVisitor csharpCodeVisitor,
CSharpCodeWriter writer,
CodeGeneratorContext context)
{
if (csharpCodeVisitor == null)
{
throw new ArgumentNullException(nameof(csharpCodeVisitor));
}
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
return new CSharpDesignTimeCodeVisitor(csharpCodeVisitor, writer, context);
}
protected virtual CSharpCodeWritingScope BuildClassDeclaration(CSharpCodeWriter writer)
{
var baseTypeVisitor = new CSharpBaseTypeVisitor(writer, Context);
baseTypeVisitor.Accept(Tree.Children);
var baseType = baseTypeVisitor.CurrentBaseType ?? Host.DefaultBaseClass;
var baseTypes = string.IsNullOrEmpty(baseType) ? Enumerable.Empty<string>() : new string[] { baseType };
return writer.BuildClassDeclaration("public", Context.ClassName, baseTypes);
}
protected virtual void BuildConstructor(CSharpCodeWriter writer)
{
writer.WriteLineHiddenDirective();
using (writer.BuildConstructor(Context.ClassName))
{
// Any constructor based logic that we need to add?
}
}
private void AddImports(ChunkTree chunkTree, CSharpCodeWriter writer, IEnumerable<string> defaultImports)
{
// Write out using directives
var usingVisitor = new CSharpUsingVisitor(writer, Context);
foreach (var chunk in Tree.Children)
{
usingVisitor.Accept(chunk);
}
defaultImports = defaultImports.Except(usingVisitor.ImportedUsings);
foreach (string import in defaultImports)
{
writer.WriteUsing(import);
}
var taskNamespace = typeof(Task).Namespace;
// We need to add the task namespace but ONLY if it hasn't been added by the default imports or using imports yet.
if (!defaultImports.Contains(taskNamespace) && !usingVisitor.ImportedUsings.Contains(taskNamespace))
{
writer.WriteUsing(taskNamespace);
}
}
}
}

View File

@ -1,579 +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 System.Diagnostics;
using System.Globalization;
using System.Linq;
using Microsoft.AspNetCore.Razor.Chunks.Generators;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.Text;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
public class CSharpCodeWriter : CodeWriter
{
private const string InstanceMethodFormat = "{0}.{1}";
private static readonly char[] CStyleStringLiteralEscapeChars = {
'\r',
'\t',
'\"',
'\'',
'\\',
'\0',
'\n',
'\u2028',
'\u2029',
};
public CSharpCodeWriter()
{
LineMappingManager = new LineMappingManager();
}
public LineMappingManager LineMappingManager { get; private set; }
public new CSharpCodeWriter Write(string data)
{
return (CSharpCodeWriter)base.Write(data);
}
public new CSharpCodeWriter Indent(int size)
{
return (CSharpCodeWriter)base.Indent(size);
}
public new CSharpCodeWriter ResetIndent()
{
return (CSharpCodeWriter)base.ResetIndent();
}
public new CSharpCodeWriter SetIndent(int size)
{
return (CSharpCodeWriter)base.SetIndent(size);
}
public new CSharpCodeWriter IncreaseIndent(int size)
{
return (CSharpCodeWriter)base.IncreaseIndent(size);
}
public new CSharpCodeWriter DecreaseIndent(int size)
{
return (CSharpCodeWriter)base.DecreaseIndent(size);
}
public new CSharpCodeWriter WriteLine(string data)
{
return (CSharpCodeWriter)base.WriteLine(data);
}
public new CSharpCodeWriter WriteLine()
{
return (CSharpCodeWriter)base.WriteLine();
}
public CSharpCodeWriter WriteVariableDeclaration(string type, string name, string value)
{
Write(type).Write(" ").Write(name);
if (!string.IsNullOrEmpty(value))
{
Write(" = ").Write(value);
}
else
{
Write(" = null");
}
WriteLine(";");
return this;
}
public CSharpCodeWriter WriteComment(string comment)
{
return Write("// ").WriteLine(comment);
}
public CSharpCodeWriter WriteBooleanLiteral(bool value)
{
return Write(value.ToString().ToLowerInvariant());
}
public CSharpCodeWriter WriteStartAssignment(string name)
{
return Write(name).Write(" = ");
}
public CSharpCodeWriter WriteParameterSeparator()
{
return Write(", ");
}
public CSharpCodeWriter WriteStartNewObject(string typeName)
{
return Write("new ").Write(typeName).Write("(");
}
public CSharpCodeWriter WriteLocationTaggedString(LocationTagged<string> value)
{
WriteStringLiteral(value.Value);
WriteParameterSeparator();
Write(value.Location.AbsoluteIndex.ToString(CultureInfo.InvariantCulture));
return this;
}
public CSharpCodeWriter WriteStringLiteral(string literal)
{
if (literal.Length >= 256 && literal.Length <= 1500 && literal.IndexOf('\0') == -1)
{
WriteVerbatimStringLiteral(literal);
}
else
{
WriteCStyleStringLiteral(literal);
}
return this;
}
public CSharpCodeWriter WriteLineHiddenDirective()
{
return WriteLine("#line hidden");
}
public CSharpCodeWriter WritePragma(string value)
{
return Write("#pragma ").WriteLine(value);
}
public CSharpCodeWriter WriteUsing(string name)
{
return WriteUsing(name, endLine: true);
}
public CSharpCodeWriter WriteUsing(string name, bool endLine)
{
Write("using ");
Write(name);
if (endLine)
{
WriteLine(";");
}
return this;
}
public CSharpCodeWriter WriteLineDefaultDirective()
{
return WriteLine("#line default");
}
public CSharpCodeWriter WriteStartReturn()
{
return Write("return ");
}
public CSharpCodeWriter WriteReturn(string value)
{
return WriteReturn(value, endLine: true);
}
public CSharpCodeWriter WriteReturn(string value, bool endLine)
{
Write("return ").Write(value);
if (endLine)
{
Write(";");
}
return WriteLine();
}
/// <summary>
/// Writes a <c>#line</c> pragma directive for the line number at the specified <paramref name="location"/>.
/// </summary>
/// <param name="location">The location to generate the line pragma for.</param>
/// <param name="file">The file to generate the line pragma for.</param>
/// <returns>The current instance of <see cref="CSharpCodeWriter"/>.</returns>
public CSharpCodeWriter WriteLineNumberDirective(SourceLocation location, string file)
{
if (location.FilePath != null)
{
file = location.FilePath;
}
if (Builder.Length >= NewLine.Length && !IsAfterNewLine)
{
WriteLine();
}
var lineNumberAsString = (location.LineIndex + 1).ToString(CultureInfo.InvariantCulture);
return Write("#line ").Write(lineNumberAsString).Write(" \"").Write(file).WriteLine("\"");
}
public CSharpCodeWriter WriteStartMethodInvocation(string methodName)
{
return WriteStartMethodInvocation(methodName, new string[0]);
}
public CSharpCodeWriter WriteStartMethodInvocation(string methodName, params string[] genericArguments)
{
Write(methodName);
if (genericArguments.Length > 0)
{
Write("<").Write(string.Join(", ", genericArguments)).Write(">");
}
return Write("(");
}
public CSharpCodeWriter WriteEndMethodInvocation()
{
return WriteEndMethodInvocation(endLine: true);
}
public CSharpCodeWriter WriteEndMethodInvocation(bool endLine)
{
Write(")");
if (endLine)
{
WriteLine(";");
}
return this;
}
// Writes a method invocation for the given instance name.
public CSharpCodeWriter WriteInstanceMethodInvocation(string instanceName,
string methodName,
params string[] parameters)
{
if (instanceName == null)
{
throw new ArgumentNullException(nameof(instanceName));
}
if (methodName == null)
{
throw new ArgumentNullException(nameof(methodName));
}
return WriteInstanceMethodInvocation(instanceName, methodName, endLine: true, parameters: parameters);
}
// Writes a method invocation for the given instance name.
public CSharpCodeWriter WriteInstanceMethodInvocation(string instanceName,
string methodName,
bool endLine,
params string[] parameters)
{
if (instanceName == null)
{
throw new ArgumentNullException(nameof(instanceName));
}
if (methodName == null)
{
throw new ArgumentNullException(nameof(methodName));
}
return WriteMethodInvocation(
string.Format(CultureInfo.InvariantCulture, InstanceMethodFormat, instanceName, methodName),
endLine,
parameters);
}
public CSharpCodeWriter WriteStartInstanceMethodInvocation(string instanceName,
string methodName)
{
if (instanceName == null)
{
throw new ArgumentNullException(nameof(instanceName));
}
if (methodName == null)
{
throw new ArgumentNullException(nameof(methodName));
}
return WriteStartMethodInvocation(
string.Format(CultureInfo.InvariantCulture, InstanceMethodFormat, instanceName, methodName));
}
public CSharpCodeWriter WriteMethodInvocation(string methodName, params string[] parameters)
{
return WriteMethodInvocation(methodName, endLine: true, parameters: parameters);
}
public CSharpCodeWriter WriteMethodInvocation(string methodName, bool endLine, params string[] parameters)
{
return WriteStartMethodInvocation(methodName)
.Write(string.Join(", ", parameters))
.WriteEndMethodInvocation(endLine);
}
public CSharpCodeWriter WriteAutoPropertyDeclaration(string accessibility, string typeName, string name)
{
return Write(accessibility)
.Write(" ")
.Write(typeName)
.Write(" ")
.Write(name)
.Write(" { get; set; }")
.WriteLine();
}
public CSharpDisableWarningScope BuildDisableWarningScope(int warning)
{
return new CSharpDisableWarningScope(this, warning);
}
public CSharpCodeWritingScope BuildScope()
{
return new CSharpCodeWritingScope(this);
}
public CSharpCodeWritingScope BuildLambda(bool endLine, params string[] parameterNames)
{
return BuildLambda(endLine, async: false, parameterNames: parameterNames);
}
public CSharpCodeWritingScope BuildAsyncLambda(bool endLine, params string[] parameterNames)
{
return BuildLambda(endLine, async: true, parameterNames: parameterNames);
}
private CSharpCodeWritingScope BuildLambda(bool endLine, bool async, string[] parameterNames)
{
if (async)
{
Write("async");
}
Write("(").Write(string.Join(", ", parameterNames)).Write(") => ");
var scope = new CSharpCodeWritingScope(this);
if (endLine)
{
// End the lambda with a semicolon
scope.OnClose += () =>
{
WriteLine(";");
};
}
return scope;
}
public CSharpCodeWritingScope BuildNamespace(string name)
{
Write("namespace ").WriteLine(name);
return new CSharpCodeWritingScope(this);
}
public CSharpCodeWritingScope BuildClassDeclaration(string accessibility, string name)
{
return BuildClassDeclaration(accessibility, name, Enumerable.Empty<string>());
}
public CSharpCodeWritingScope BuildClassDeclaration(string accessibility, string name, string baseType)
{
return BuildClassDeclaration(accessibility, name, new string[] { baseType });
}
public CSharpCodeWritingScope BuildClassDeclaration(
string accessibility,
string name,
IEnumerable<string> baseTypes)
{
Write(accessibility).Write(" class ").Write(name);
if (baseTypes.Count() > 0)
{
Write(" : ");
Write(string.Join(", ", baseTypes));
}
WriteLine();
return new CSharpCodeWritingScope(this);
}
public CSharpCodeWritingScope BuildConstructor(string name)
{
return BuildConstructor("public", name);
}
public CSharpCodeWritingScope BuildConstructor(string accessibility, string name)
{
return BuildConstructor(accessibility, name, Enumerable.Empty<KeyValuePair<string, string>>());
}
public CSharpCodeWritingScope BuildConstructor(
string accessibility,
string name,
IEnumerable<KeyValuePair<string, string>> parameters)
{
Write(accessibility)
.Write(" ")
.Write(name)
.Write("(")
.Write(string.Join(", ", parameters.Select(p => p.Key + " " + p.Value)))
.WriteLine(")");
return new CSharpCodeWritingScope(this);
}
public CSharpCodeWritingScope BuildMethodDeclaration(string accessibility, string returnType, string name)
{
return BuildMethodDeclaration(accessibility, returnType, name, Enumerable.Empty<KeyValuePair<string, string>>());
}
public CSharpCodeWritingScope BuildMethodDeclaration(
string accessibility,
string returnType,
string name,
IEnumerable<KeyValuePair<string, string>> parameters)
{
Write(accessibility)
.Write(" ")
.Write(returnType)
.Write(" ")
.Write(name)
.Write("(")
.Write(string.Join(", ", parameters.Select(p => p.Key + " " + p.Value)))
.WriteLine(")");
return new CSharpCodeWritingScope(this);
}
public CSharpLineMappingWriter BuildLineMapping(
SourceLocation documentLocation,
int contentLength,
string sourceFilename)
{
return new CSharpLineMappingWriter(this, documentLocation, contentLength, sourceFilename);
}
private void WriteVerbatimStringLiteral(string literal)
{
Write("@\"");
// We need to find the index of each '"' (double-quote) to escape it.
var start = 0;
int end;
while ((end = literal.IndexOf('\"', start)) > -1)
{
Write(literal, start, end - start);
Write("\"\"");
start = end + 1;
}
Debug.Assert(end == -1); // We've hit all of the double-quotes.
// Write the remainder after the last double-quote.
Write(literal, start, literal.Length - start);
Write("\"");
}
private void WriteCStyleStringLiteral(string literal)
{
// From CSharpCodeGenerator.QuoteSnippetStringCStyle in CodeDOM
Write("\"");
// We need to find the index of each escapable character to escape it.
var start = 0;
int end;
while ((end = literal.IndexOfAny(CStyleStringLiteralEscapeChars, start)) > -1)
{
Write(literal, start, end - start);
switch (literal[end])
{
case '\r':
Write("\\r");
break;
case '\t':
Write("\\t");
break;
case '\"':
Write("\\\"");
break;
case '\'':
Write("\\\'");
break;
case '\\':
Write("\\\\");
break;
case '\0':
Write("\\\0");
break;
case '\n':
Write("\\n");
break;
case '\u2028':
case '\u2029':
Write("\\u");
Write(((int)literal[end]).ToString("X4", CultureInfo.InvariantCulture));
break;
default:
Debug.Assert(false, "Unknown escape character.");
break;
}
start = end + 1;
}
Debug.Assert(end == -1); // We've hit all of chars that need escaping.
// Write the remainder after the last escaped char.
Write(literal, start, literal.Length - start);
Write("\"");
}
public CSharpCodeWriter WriteStartInstrumentationContext(
ChunkGeneratorContext context,
SyntaxTreeNode syntaxNode,
bool isLiteral)
{
return WriteStartInstrumentationContext(
context,
syntaxNode.Start.AbsoluteIndex,
syntaxNode.Length,
isLiteral);
}
public CSharpCodeWriter WriteStartInstrumentationContext(
ChunkGeneratorContext context,
int absoluteIndex,
int length,
bool isLiteral)
{
WriteStartMethodInvocation(context.Host.GeneratedClassContext.BeginContextMethodName);
Write(absoluteIndex.ToString(CultureInfo.InvariantCulture));
WriteParameterSeparator();
Write(length.ToString(CultureInfo.InvariantCulture));
WriteParameterSeparator();
Write(isLiteral ? "true" : "false");
return WriteEndMethodInvocation();
}
public CSharpCodeWriter WriteEndInstrumentationContext(ChunkGeneratorContext context)
{
return WriteMethodInvocation(context.Host.GeneratedClassContext.EndContextMethodName);
}
}
}

View File

@ -1,71 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
public struct CSharpCodeWritingScope : IDisposable
{
private CodeWriter _writer;
private bool _autoSpace;
private int _tabSize;
private int _startIndent;
public CSharpCodeWritingScope(CodeWriter writer) : this(writer, true) { }
public CSharpCodeWritingScope(CodeWriter writer, int tabSize) : this(writer, tabSize, true) { }
// TODO: Make indents (tabs) environment specific
public CSharpCodeWritingScope(CodeWriter writer, bool autoSpace) : this(writer, 4, autoSpace) { }
public CSharpCodeWritingScope(CodeWriter writer, int tabSize, bool autoSpace)
{
_writer = writer;
_autoSpace = true;
_tabSize = tabSize;
_startIndent = -1; // Set in WriteStartScope
OnClose = () => { };
WriteStartScope();
}
public Action OnClose;
public void Dispose()
{
WriteEndScope();
OnClose();
}
private void WriteStartScope()
{
TryAutoSpace(" ");
_writer.WriteLine("{").IncreaseIndent(_tabSize);
_startIndent = _writer.CurrentIndent;
}
private void WriteEndScope()
{
TryAutoSpace(_writer.NewLine);
// Ensure the scope hasn't been modified
if (_writer.CurrentIndent == _startIndent)
{
_writer.DecreaseIndent(_tabSize);
}
_writer.WriteLine("}");
}
private void TryAutoSpace(string spaceCharacter)
{
if (_autoSpace &&
_writer.Builder.Length > 0 &&
!char.IsWhiteSpace(_writer.Builder[_writer.Builder.Length - 1]))
{
_writer.Write(spaceCharacter);
}
}
}
}

View File

@ -1,29 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
public struct CSharpDisableWarningScope : IDisposable
{
private CSharpCodeWriter _writer;
int _warningNumber;
public CSharpDisableWarningScope(CSharpCodeWriter writer) : this(writer, 219)
{ }
public CSharpDisableWarningScope(CSharpCodeWriter writer, int warningNumber)
{
_writer = writer;
_warningNumber = warningNumber;
_writer.WritePragma("warning disable " + _warningNumber);
}
public void Dispose()
{
_writer.WritePragma("warning restore " + _warningNumber);
}
}
}

View File

@ -1,137 +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;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
public class CSharpLineMappingWriter : IDisposable
{
private readonly CSharpCodeWriter _writer;
private readonly MappingLocation _documentMapping;
private readonly int _startIndent;
private readonly bool _writePragmas;
private readonly bool _addLineMapping;
private SourceLocation _generatedLocation;
private int _generatedContentLength;
private CSharpLineMappingWriter(CSharpCodeWriter writer, bool addLineMappings)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
_writer = writer;
_addLineMapping = addLineMappings;
_startIndent = _writer.CurrentIndent;
_writer.ResetIndent();
}
public CSharpLineMappingWriter(CSharpCodeWriter writer, SourceLocation documentLocation, int contentLength)
: this(writer, addLineMappings: true)
{
_documentMapping = new MappingLocation(documentLocation, contentLength);
_generatedLocation = _writer.GetCurrentSourceLocation();
}
public CSharpLineMappingWriter(
CSharpCodeWriter writer,
SourceLocation documentLocation,
int contentLength,
string sourceFilename)
: this(writer, documentLocation, contentLength)
{
_writePragmas = true;
_writer.WriteLineNumberDirective(documentLocation, sourceFilename);
_generatedLocation = _writer.GetCurrentSourceLocation();
}
/// <summary>
/// Initializes a new instance of <see cref="CSharpLineMappingWriter"/> used for generation of runtime
/// line mappings. The constructed instance of <see cref="CSharpLineMappingWriter"/> does not track
/// mappings between the Razor content and the generated content.
/// </summary>
/// <param name="writer">The <see cref="CSharpCodeWriter"/> to write output to.</param>
/// <param name="documentLocation">The <see cref="SourceLocation"/> of the Razor content being mapping.</param>
/// <param name="sourceFileName">The input file path.</param>
public CSharpLineMappingWriter(
CSharpCodeWriter writer,
SourceLocation documentLocation,
string sourceFileName)
: this(writer, addLineMappings: false)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
_writePragmas = true;
_writer.WriteLineNumberDirective(documentLocation, sourceFileName);
}
public void MarkLineMappingStart()
{
_generatedLocation = _writer.GetCurrentSourceLocation();
}
public void MarkLineMappingEnd()
{
_generatedContentLength = _writer.Builder.Length - _generatedLocation.AbsoluteIndex;
}
public void Dispose()
{
if (_addLineMapping)
{
// Verify that the generated length has not already been calculated
if (_generatedContentLength == 0)
{
_generatedContentLength = _writer.Builder.Length - _generatedLocation.AbsoluteIndex;
}
var generatedLocation = new MappingLocation(_generatedLocation, _generatedContentLength);
var documentMapping = _documentMapping;
if (documentMapping.ContentLength == -1)
{
documentMapping = new MappingLocation(
location: new SourceLocation(
_documentMapping.FilePath,
_documentMapping.AbsoluteIndex,
_documentMapping.LineIndex,
_documentMapping.CharacterIndex),
contentLength: _generatedContentLength);
}
_writer.LineMappingManager.AddMapping(
documentLocation: documentMapping,
generatedLocation: generatedLocation);
}
if (_writePragmas)
{
// Need to add an additional line at the end IF there wasn't one already written.
// This is needed to work with the C# editor's handling of #line ...
var builder = _writer.Builder;
var endsWithNewline = builder.Length > 0 && builder[builder.Length - 1] == '\n';
// Always write at least 1 empty line to potentially separate code from pragmas.
_writer.WriteLine();
// Check if the previous empty line wasn't enough to separate code from pragmas.
if (!endsWithNewline)
{
_writer.WriteLine();
}
_writer.WriteLineDefaultDirective()
.WriteLineHiddenDirective();
}
// Reset indent back to when it was started
_writer.SetIndent(_startIndent);
}
}
}

View File

@ -1,180 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Parser;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
public class CSharpPaddingBuilder
{
private static readonly char[] _newLineChars = { '\r', '\n' };
private readonly RazorEngineHost _host;
public CSharpPaddingBuilder(RazorEngineHost host)
{
_host = host;
}
// Special case for statement padding to account for brace positioning in the editor.
public string BuildStatementPadding(Span target)
{
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
var padding = CalculatePadding(target, generatedStart: 0);
// We treat statement padding specially so for brace positioning, so that in the following example:
// @if (foo > 0)
// {
// }
//
// the braces shows up under the @ rather than under the if.
if (_host.DesignTimeMode &&
padding > 0 &&
target.Previous.Kind == SpanKind.Transition && // target.Previous is guaranteed to not be null if you have padding.
string.Equals(target.Previous.Content, SyntaxConstants.TransitionString, StringComparison.Ordinal))
{
padding--;
}
var generatedCode = BuildPaddingInternal(padding);
return generatedCode;
}
public string BuildExpressionPadding(Span target)
{
return BuildExpressionPadding(target, generatedStart: 0);
}
public string BuildExpressionPadding(Span target, int generatedStart)
{
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
var padding = CalculatePadding(target, generatedStart);
return BuildPaddingInternal(padding);
}
internal int CalculatePadding(Span target, int generatedStart)
{
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
int padding;
padding = CollectSpacesAndTabs(target, _host.TabSize) - generatedStart;
// if we add generated text that is longer than the padding we wanted to insert we have no recourse and we have to skip padding
// example:
// Razor code at column zero: @somecode()
// Generated code will be:
// In design time: __o = somecode();
// In Run time: Write(somecode());
//
// In both cases the padding would have been 1 space to remote the space the @ symbol takes, which will be smaller than the 6
// chars the hidden generated code takes.
if (padding < 0)
{
padding = 0;
}
return padding;
}
private string BuildPaddingInternal(int padding)
{
if (_host.DesignTimeMode && _host.IsIndentingWithTabs)
{
var spaces = padding % _host.TabSize;
var tabs = padding / _host.TabSize;
return new string('\t', tabs) + new string(' ', spaces);
}
else
{
return new string(' ', padding);
}
}
private static int CollectSpacesAndTabs(Span target, int tabSize)
{
var firstSpanInLine = target;
string currentContent = null;
while (firstSpanInLine.Previous != null)
{
// When scanning previous spans we need to be break down the spans with spaces. The parser combines
// whitespace into existing spans so you'll see tabs, newlines etc. within spans. We only care about
// the \t in existing spans.
var previousContent = firstSpanInLine.Previous.Content ?? string.Empty;
var lastNewLineIndex = previousContent.LastIndexOfAny(_newLineChars);
if (lastNewLineIndex < 0)
{
firstSpanInLine = firstSpanInLine.Previous;
}
else
{
if (lastNewLineIndex != previousContent.Length - 1)
{
firstSpanInLine = firstSpanInLine.Previous;
currentContent = previousContent.Substring(lastNewLineIndex + 1);
}
break;
}
}
// We need to walk from the beginning of the line, because space + tab(tabSize) = tabSize columns, but tab(tabSize) + space = tabSize+1 columns.
var currentSpanInLine = firstSpanInLine;
if (currentContent == null)
{
currentContent = currentSpanInLine.Content;
}
var padding = 0;
while (currentSpanInLine != target)
{
if (currentContent != null)
{
for (int i = 0; i < currentContent.Length; i++)
{
if (currentContent[i] == '\t')
{
// Example:
// <space><space><tab><tab>:
// iter 1) 1
// iter 2) 2
// iter 3) 4 = 2 + (4 - 2)
// iter 4) 8 = 4 + (4 - 0)
padding = padding + (tabSize - (padding % tabSize));
}
else
{
padding++;
}
}
}
currentSpanInLine = currentSpanInLine.Next;
currentContent = currentSpanInLine.Content;
}
return padding;
}
}
}

View File

@ -1,833 +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 System.Diagnostics;
using System.Globalization;
using System.Linq;
using Microsoft.AspNetCore.Razor.Chunks;
using Microsoft.AspNetCore.Razor.CodeGenerators.Visitors;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
/// <summary>
/// Renders tag helper rendering code.
/// </summary>
public class CSharpTagHelperCodeRenderer
{
internal static readonly string ExecutionContextVariableName = "__tagHelperExecutionContext";
internal static readonly string StringValueBufferVariableName = "__tagHelperStringValueBuffer";
internal static readonly string ScopeManagerVariableName = "__tagHelperScopeManager";
internal static readonly string RunnerVariableName = "__tagHelperRunner";
private readonly CSharpCodeWriter _writer;
private readonly CodeGeneratorContext _context;
private readonly IChunkVisitor _bodyVisitor;
private readonly IChunkVisitor _literalBodyVisitor;
private readonly TagHelperAttributeCodeVisitor _attributeCodeVisitor;
private readonly GeneratedTagHelperContext _tagHelperContext;
private readonly bool _designTimeMode;
/// <summary>
/// Instantiates a new <see cref="CSharpTagHelperCodeRenderer"/>.
/// </summary>
/// <param name="bodyVisitor">The <see cref="IChunkVisitor"/> used to render chunks found in the body.</param>
/// <param name="writer">The <see cref="CSharpCodeWriter"/> used to write code.</param>
/// <param name="context">A <see cref="CodeGeneratorContext"/> instance that contains information about
/// the current code generation process.</param>
public CSharpTagHelperCodeRenderer(
IChunkVisitor bodyVisitor,
CSharpCodeWriter writer,
CodeGeneratorContext context)
{
if (bodyVisitor == null)
{
throw new ArgumentNullException(nameof(bodyVisitor));
}
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
_bodyVisitor = bodyVisitor;
_writer = writer;
_context = context;
_tagHelperContext = context.Host.GeneratedClassContext.GeneratedTagHelperContext;
_designTimeMode = context.Host.DesignTimeMode;
_literalBodyVisitor = new CSharpLiteralCodeVisitor(this, writer, context);
_attributeCodeVisitor = new TagHelperAttributeCodeVisitor(writer, context);
AttributeValueCodeRenderer = new TagHelperAttributeValueCodeRenderer();
}
public TagHelperAttributeValueCodeRenderer AttributeValueCodeRenderer { get; set; }
/// <summary>
/// Renders the code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">A <see cref="TagHelperChunk"/> to render.</param>
public void RenderTagHelper(TagHelperChunk chunk)
{
// Remove any duplicate TagHelperDescriptors that reference the same type name. Duplicates can occur when
// multiple HtmlTargetElement attributes are on a TagHelper type and matches overlap for an HTML element.
// Having more than one descriptor with the same TagHelper type results in generated code that runs
// the same TagHelper X many times (instead of once) over a single HTML element.
var tagHelperDescriptors = chunk.Descriptors.Distinct(TypeBasedTagHelperDescriptorComparer.Default);
RenderBeginTagHelperScope(chunk.TagName, chunk.TagMode, chunk.Children);
RenderTagHelpersCreation(chunk, tagHelperDescriptors);
RenderAttributes(chunk.Attributes, tagHelperDescriptors);
// No need to run anything in design time mode.
if (!_designTimeMode)
{
RenderRunTagHelpers();
RenderTagHelperOutput(chunk);
RenderEndTagHelpersScope();
}
}
public static bool TryGetPlainTextValue(Chunk chunk, out string plainText)
{
var parentChunk = chunk as ParentChunk;
plainText = null;
if (parentChunk == null || parentChunk.Children.Count != 1)
{
return false;
}
LiteralChunk literalChildChunk;
if ((literalChildChunk = parentChunk.Children[0] as LiteralChunk) != null)
{
plainText = literalChildChunk.Text;
return true;
}
ParentLiteralChunk parentLiteralChunk;
if ((parentLiteralChunk = parentChunk.Children[0] as ParentLiteralChunk) != null)
{
plainText = parentLiteralChunk.GetText();
return true;
}
return false;
}
internal static string GetVariableName(TagHelperDescriptor descriptor)
{
return "__" + descriptor.TypeName.Replace('.', '_');
}
private void RenderBeginTagHelperScope(string tagName, TagMode tagMode, IList<Chunk> children)
{
// Scopes/execution contexts are a runtime feature.
if (_designTimeMode)
{
// Render all of the tag helper children inline for IntelliSense.
_bodyVisitor.Accept(children);
return;
}
// Call into the tag helper scope manager to start a new tag helper scope.
// Also capture the value as the current execution context.
_writer
.WriteStartAssignment(ExecutionContextVariableName)
.WriteStartInstanceMethodInvocation(
ScopeManagerVariableName,
_tagHelperContext.ScopeManagerBeginMethodName);
// Assign a unique ID for this instance of the source HTML tag. This must be unique
// per call site, e.g. if the tag is on the view twice, there should be two IDs.
_writer.WriteStringLiteral(tagName)
.WriteParameterSeparator()
.Write("global::")
.Write(typeof(TagMode).FullName)
.Write(".")
.Write(tagMode.ToString())
.WriteParameterSeparator()
.WriteStringLiteral(GenerateUniqueId())
.WriteParameterSeparator();
// We remove the target writer so TagHelper authors can retrieve content.
var oldWriter = _context.TargetWriterName;
_context.TargetWriterName = null;
using (_writer.BuildAsyncLambda(endLine: false))
{
// Render all of the tag helper children.
_bodyVisitor.Accept(children);
}
_context.TargetWriterName = oldWriter;
_writer.WriteEndMethodInvocation();
}
/// <summary>
/// Generates a unique ID for an HTML element.
/// </summary>
/// <returns>
/// A globally unique ID.
/// </returns>
protected virtual string GenerateUniqueId()
{
return Guid.NewGuid().ToString("N");
}
private void RenderTagHelpersCreation(
TagHelperChunk chunk,
IEnumerable<TagHelperDescriptor> tagHelperDescriptors)
{
// This is to maintain value accessors for attributes when creating the TagHelpers.
// Ultimately it enables us to do scenarios like this:
// myTagHelper1.Foo = DateTime.Now;
// myTagHelper2.Foo = myTagHelper1.Foo;
var htmlAttributeValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var tagHelperDescriptor in tagHelperDescriptors)
{
var tagHelperVariableName = GetVariableName(tagHelperDescriptor);
// Create the tag helper
_writer.WriteStartAssignment(tagHelperVariableName)
.WriteStartMethodInvocation(
_tagHelperContext.CreateTagHelperMethodName,
"global::" + tagHelperDescriptor.TypeName)
.WriteEndMethodInvocation();
// Execution contexts and throwing errors for null dictionary properties are a runtime feature.
if (_designTimeMode)
{
continue;
}
_writer.WriteInstanceMethodInvocation(
ExecutionContextVariableName,
_tagHelperContext.ExecutionContextAddMethodName,
tagHelperVariableName);
// Track dictionary properties we have confirmed are non-null.
var confirmedDictionaries = new HashSet<string>(StringComparer.Ordinal);
// Ensure that all created TagHelpers have initialized dictionary bound properties which are used
// via TagHelper indexers.
foreach (var chunkAttribute in chunk.Attributes)
{
var associatedAttributeDescriptor = tagHelperDescriptor.Attributes.FirstOrDefault(
attributeDescriptor => attributeDescriptor.IsNameMatch(chunkAttribute.Name));
if (associatedAttributeDescriptor != null &&
associatedAttributeDescriptor.IsIndexer &&
confirmedDictionaries.Add(associatedAttributeDescriptor.PropertyName))
{
// Throw a reasonable Exception at runtime if the dictionary property is null.
_writer
.Write("if (")
.Write(tagHelperVariableName)
.Write(".")
.Write(associatedAttributeDescriptor.PropertyName)
.WriteLine(" == null)");
using (_writer.BuildScope())
{
// System is in Host.NamespaceImports for all MVC scenarios. No need to generate FullName
// of InvalidOperationException type.
_writer
.Write("throw ")
.WriteStartNewObject(nameof(InvalidOperationException))
.WriteStartMethodInvocation(_tagHelperContext.FormatInvalidIndexerAssignmentMethodName)
.WriteStringLiteral(chunkAttribute.Name)
.WriteParameterSeparator()
.WriteStringLiteral(tagHelperDescriptor.TypeName)
.WriteParameterSeparator()
.WriteStringLiteral(associatedAttributeDescriptor.PropertyName)
.WriteEndMethodInvocation(endLine: false) // End of method call
.WriteEndMethodInvocation(endLine: true); // End of new expression / throw statement
}
}
}
}
}
private void RenderAttributes(
IList<TagHelperAttributeTracker> chunkAttributes,
IEnumerable<TagHelperDescriptor> tagHelperDescriptors)
{
var renderedBoundAttributeNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
// Go through the HTML attributes in source order, assigning to properties or indexers or adding to
// TagHelperExecutionContext.HtmlAttributes' as we go.
foreach (var attribute in chunkAttributes)
{
var attributeValueChunk = attribute.Value;
var associatedDescriptors = tagHelperDescriptors.Where(descriptor =>
descriptor.Attributes.Any(attributeDescriptor => attributeDescriptor.IsNameMatch(attribute.Name)));
// Bound attributes have associated descriptors. First attribute value wins if there are duplicates;
// later values of duplicate bound attributes are treated as if they were unbound.
if (associatedDescriptors.Any() && renderedBoundAttributeNames.Add(attribute.Name))
{
if (attributeValueChunk == null)
{
// Minimized attributes are not valid for bound attributes. TagHelperBlockRewriter has already
// logged an error if it was a bound attribute; so we can skip.
continue;
}
// We need to capture the tag helper's property value accessor so we can retrieve it later
// if there are more tag helpers that need the value.
string valueAccessor = null;
foreach (var associatedDescriptor in associatedDescriptors)
{
var associatedAttributeDescriptor = associatedDescriptor.Attributes.First(
attributeDescriptor => attributeDescriptor.IsNameMatch(attribute.Name));
var tagHelperVariableName = GetVariableName(associatedDescriptor);
valueAccessor = RenderBoundAttribute(
attribute,
tagHelperVariableName,
valueAccessor,
associatedAttributeDescriptor);
}
}
else
{
RenderUnboundAttribute(attribute);
}
}
}
private string RenderBoundAttribute(
TagHelperAttributeTracker attribute,
string tagHelperVariableName,
string previousValueAccessor,
TagHelperAttributeDescriptor attributeDescriptor)
{
var currentValueAccessor = string.Format(
CultureInfo.InvariantCulture,
"{0}.{1}",
tagHelperVariableName,
attributeDescriptor.PropertyName);
if (attributeDescriptor.IsIndexer)
{
var dictionaryKey = attribute.Name.Substring(attributeDescriptor.Name.Length);
currentValueAccessor += $"[\"{dictionaryKey}\"]";
}
// If this attribute value has not been seen before, need to record its value.
if (previousValueAccessor == null)
{
var preallocatedAttributeValueChunk = attribute.Value as PreallocatedTagHelperAttributeChunk;
if (preallocatedAttributeValueChunk != null)
{
RenderBoundPreAllocatedAttribute(preallocatedAttributeValueChunk, currentValueAccessor);
return currentValueAccessor;
}
// Bufferable attributes are attributes that can have Razor code inside of them. Such
// attributes have string values and may be calculated using a temporary TextWriter or other
// buffer.
var bufferableAttribute = attributeDescriptor.IsStringProperty;
RenderNewAttributeValueAssignment(
attributeDescriptor,
bufferableAttribute,
attribute.Value,
currentValueAccessor);
if (_designTimeMode)
{
// Execution contexts are a runtime feature.
return currentValueAccessor;
}
// We need to inform the context of the attribute value.
_writer
.WriteStartInstanceMethodInvocation(
ExecutionContextVariableName,
_tagHelperContext.ExecutionContextAddTagHelperAttributeMethodName)
.WriteStringLiteral(attribute.Name)
.WriteParameterSeparator()
.Write(currentValueAccessor)
.WriteParameterSeparator()
.Write($"global::{typeof(HtmlAttributeValueStyle).FullName}.{attribute.ValueStyle}")
.WriteEndMethodInvocation();
return currentValueAccessor;
}
else
{
// The attribute value has already been determined and accessor was passed to us as
// previousValueAccessor, we don't want to evaluate the value twice so lets just use the
// previousValueLocation.
_writer
.WriteStartAssignment(currentValueAccessor)
.Write(previousValueAccessor)
.WriteLine(";");
return previousValueAccessor;
}
}
// Render bound attributes that are of string type and are preallocated.
private void RenderBoundPreAllocatedAttribute(
PreallocatedTagHelperAttributeChunk preallocatedAttributeValueChunk,
string valueAccessor)
{
var attributeValueAccessor = string.Format(
CultureInfo.InvariantCulture,
"{0}.{1}",
preallocatedAttributeValueChunk.AttributeVariableAccessor,
_tagHelperContext.TagHelperAttributeValuePropertyName);
_writer
.WriteStartAssignment(valueAccessor)
.Write("(string)")
.Write(attributeValueAccessor)
.WriteLine(";");
if (_designTimeMode)
{
// Execution contexts are a runtime feature.
return;
}
_writer
.WriteStartInstanceMethodInvocation(
ExecutionContextVariableName,
_tagHelperContext.ExecutionContextAddTagHelperAttributeMethodName)
.Write(preallocatedAttributeValueChunk.AttributeVariableAccessor)
.WriteEndMethodInvocation();
}
// Render assignment of attribute value to the value accessor.
private void RenderNewAttributeValueAssignment(
TagHelperAttributeDescriptor attributeDescriptor,
bool bufferableAttribute,
Chunk attributeValueChunk,
string valueAccessor)
{
// Plain text values are non Razor code (@DateTime.Now) values. If an attribute is bufferable it
// may be more than just a plain text value, it may also contain Razor code which is why we attempt
// to retrieve a plain text value here.
string textValue;
var isPlainTextValue = TryGetPlainTextValue(attributeValueChunk, out textValue);
if (bufferableAttribute)
{
if (!isPlainTextValue)
{
// If we haven't recorded a value and we need to buffer an attribute value and the value is not
// plain text then we need to prepare the value prior to setting it below.
BuildBufferedWritingScope(attributeValueChunk, htmlEncodeValues: false);
}
_writer.WriteStartAssignment(valueAccessor);
if (isPlainTextValue)
{
// If the attribute is bufferable but has a plain text value that means the value
// is a string which needs to be surrounded in quotes.
RenderQuotedAttributeValue(textValue, attributeDescriptor);
}
else
{
// The value contains more than plain text e.g. stringAttribute ="Time: @DateTime.Now".
RenderBufferedAttributeValue(attributeDescriptor);
}
_writer.WriteLine(";");
}
else
{
// Write out simple assignment for non-string property value. Try to keep the whole
// statement together and the #line pragma correct to make debugging possible.
using (var lineMapper = new CSharpLineMappingWriter(
_writer,
attributeValueChunk.Start,
_context.SourceFile))
{
// Place the assignment LHS to align RHS with original attribute value's indentation.
// Unfortunately originalIndent is incorrect if original line contains tabs. Unable to
// use a CSharpPaddingBuilder because the Association has no Previous node; lost the
// original Span sequence when the parse tree was rewritten.
var originalIndent = attributeValueChunk.Start.CharacterIndex;
var generatedLength = valueAccessor.Length + " = ".Length;
var newIndent = originalIndent - generatedLength;
if (newIndent > 0)
{
_writer.Indent(newIndent);
}
_writer.WriteStartAssignment(valueAccessor);
lineMapper.MarkLineMappingStart();
// Write out code expression for this attribute value. Property is not a string.
// So quoting or buffering are not helpful.
RenderCodeAttributeValue(attributeValueChunk, attributeDescriptor, isPlainTextValue);
// End the assignment to the attribute.
lineMapper.MarkLineMappingEnd();
_writer.WriteLine(";");
}
}
}
private void RenderUnboundAttribute(TagHelperAttributeTracker attribute)
{
// Render children to provide IntelliSense at design time. No need for the execution context logic, it's
// a runtime feature.
if (_designTimeMode)
{
if (attribute.Value != null)
{
_bodyVisitor.Accept(attribute.Value);
}
return;
}
Debug.Assert(attribute.Value != null);
var attributeValueStyleParameter = $"global::{typeof(HtmlAttributeValueStyle).FullName}.{attribute.ValueStyle}";
// All simple text and minimized attributes will be pre-allocated.
var preallocatedValue = attribute.Value as PreallocatedTagHelperAttributeChunk;
if (preallocatedValue != null)
{
_writer
.WriteStartInstanceMethodInvocation(
ExecutionContextVariableName,
_tagHelperContext.ExecutionContextAddHtmlAttributeMethodName)
.Write(preallocatedValue.AttributeVariableAccessor)
.WriteEndMethodInvocation();
}
else if (IsDynamicAttributeValue(attribute.Value))
{
// Dynamic attribute value should be run through the conditional attribute removal system. It's
// unbound and contains C#.
// TagHelper attribute rendering is buffered by default. We do not want to write to the current
// writer.
var currentTargetWriter = _context.TargetWriterName;
var currentWriteAttributeMethodName = _context.Host.GeneratedClassContext.WriteAttributeValueMethodName;
_context.TargetWriterName = null;
Debug.Assert(attribute.Value is ParentChunk);
var children = ((ParentChunk)attribute.Value).Children;
var attributeCount = children.Count(c => c is DynamicCodeAttributeChunk || c is LiteralCodeAttributeChunk);
_writer
.WriteStartMethodInvocation(_tagHelperContext.BeginAddHtmlAttributeValuesMethodName)
.Write(ExecutionContextVariableName)
.WriteParameterSeparator()
.WriteStringLiteral(attribute.Name)
.WriteParameterSeparator()
.Write(attributeCount.ToString(CultureInfo.InvariantCulture))
.WriteParameterSeparator()
.Write(attributeValueStyleParameter)
.WriteEndMethodInvocation();
_attributeCodeVisitor.Accept(attribute.Value);
_writer.WriteMethodInvocation(
_tagHelperContext.EndAddHtmlAttributeValuesMethodName,
ExecutionContextVariableName);
_context.TargetWriterName = currentTargetWriter;
}
else
{
// This is a data-* attribute which includes C#. Do not perform the conditional attribute removal or
// other special cases used when IsDynamicAttributeValue(). But the attribute must still be buffered to
// determine its final value.
// Attribute value is not plain text, must be buffered to determine its final value.
BuildBufferedWritingScope(attribute.Value, htmlEncodeValues: true);
_writer
.WriteStartInstanceMethodInvocation(
ExecutionContextVariableName,
_tagHelperContext.ExecutionContextAddHtmlAttributeMethodName)
.WriteStringLiteral(attribute.Name)
.WriteParameterSeparator()
.WriteStartMethodInvocation(_tagHelperContext.MarkAsHtmlEncodedMethodName);
RenderBufferedAttributeValueAccessor(_writer);
_writer
.WriteEndMethodInvocation(endLine: false)
.WriteParameterSeparator()
.Write(attributeValueStyleParameter)
.WriteEndMethodInvocation();
}
}
private void RenderEndTagHelpersScope()
{
_writer.WriteStartAssignment(ExecutionContextVariableName)
.WriteInstanceMethodInvocation(ScopeManagerVariableName,
_tagHelperContext.ScopeManagerEndMethodName);
}
private void RenderTagHelperOutput(TagHelperChunk chunk)
{
var tagHelperOutputAccessor =
$"{ExecutionContextVariableName}.{_tagHelperContext.ExecutionContextOutputPropertyName}";
if (ContainsChildContent(chunk.Children))
{
_writer
.Write("if (!")
.Write(tagHelperOutputAccessor)
.Write(".")
.Write(_tagHelperContext.TagHelperOutputIsContentModifiedPropertyName)
.WriteLine(")");
using (_writer.BuildScope())
{
_writer
.Write("await ")
.WriteInstanceMethodInvocation(
ExecutionContextVariableName,
_tagHelperContext.ExecutionContextSetOutputContentAsyncMethodName);
}
}
_writer
.WriteStartInstrumentationContext(_context, chunk.Association, isLiteral: false);
if (!string.IsNullOrEmpty(_context.TargetWriterName))
{
_writer
.WriteStartMethodInvocation(_context.Host.GeneratedClassContext.WriteToMethodName)
.Write(_context.TargetWriterName)
.WriteParameterSeparator();
}
else
{
_writer.WriteStartMethodInvocation(_context.Host.GeneratedClassContext.WriteMethodName);
}
_writer
.Write(tagHelperOutputAccessor)
.WriteEndMethodInvocation()
.WriteEndInstrumentationContext(_context);
}
private void RenderRunTagHelpers()
{
_writer
.Write("await ")
.WriteStartInstanceMethodInvocation(RunnerVariableName, _tagHelperContext.RunnerRunAsyncMethodName)
.Write(ExecutionContextVariableName)
.WriteEndMethodInvocation();
}
private void RenderBufferedAttributeValue(TagHelperAttributeDescriptor attributeDescriptor)
{
// Pass complexValue: false because variable.ToString() replaces any original complexity in the expression.
RenderAttributeValue(
attributeDescriptor,
valueRenderer: (writer) =>
{
RenderBufferedAttributeValueAccessor(writer);
},
complexValue: false);
}
private void RenderCodeAttributeValue(
Chunk attributeValueChunk,
TagHelperAttributeDescriptor attributeDescriptor,
bool isPlainTextValue)
{
RenderAttributeValue(
attributeDescriptor,
valueRenderer: (writer) =>
{
if (attributeDescriptor.IsEnum && isPlainTextValue)
{
writer
.Write("global::")
.Write(attributeDescriptor.TypeName)
.Write(".");
}
var visitor =
new CSharpTagHelperAttributeValueVisitor(writer, _context, attributeDescriptor.TypeName);
visitor.Accept(attributeValueChunk);
},
complexValue: !isPlainTextValue);
}
private void RenderQuotedAttributeValue(string value, TagHelperAttributeDescriptor attributeDescriptor)
{
RenderAttributeValue(
attributeDescriptor,
valueRenderer: (writer) =>
{
writer.WriteStringLiteral(value);
},
complexValue: false);
}
// Render a buffered writing scope for the HTML attribute value.
private void BuildBufferedWritingScope(Chunk htmlAttributeChunk, bool htmlEncodeValues)
{
// We're building a writing scope around the provided chunks which captures everything written from the
// page. Therefore, we do not want to write to any other buffer since we're using the pages buffer to
// ensure we capture all content that's written, directly or indirectly.
var oldWriter = _context.TargetWriterName;
_context.TargetWriterName = null;
// Need to disable instrumentation inside of writing scopes, the instrumentation will not detect
// content written inside writing scopes.
var oldInstrumentation = _context.Host.EnableInstrumentation;
try
{
_context.Host.EnableInstrumentation = false;
// Scopes are a runtime feature.
if (!_designTimeMode)
{
_writer.WriteMethodInvocation(_tagHelperContext.BeginWriteTagHelperAttributeMethodName);
}
var visitor = htmlEncodeValues ? _bodyVisitor : _literalBodyVisitor;
visitor.Accept(htmlAttributeChunk);
// Scopes are a runtime feature.
if (!_designTimeMode)
{
_writer
.WriteStartAssignment(StringValueBufferVariableName)
.WriteMethodInvocation(_tagHelperContext.EndWriteTagHelperAttributeMethodName);
}
}
finally
{
// Reset instrumentation back to what it was, leaving the writing scope.
_context.Host.EnableInstrumentation = oldInstrumentation;
// Reset the writer/buffer back to what it was, leaving the writing scope.
_context.TargetWriterName = oldWriter;
}
}
private void RenderAttributeValue(TagHelperAttributeDescriptor attributeDescriptor,
Action<CSharpCodeWriter> valueRenderer,
bool complexValue)
{
AttributeValueCodeRenderer.RenderAttributeValue(
attributeDescriptor,
_writer,
_context,
valueRenderer,
complexValue);
}
private void RenderBufferedAttributeValueAccessor(CSharpCodeWriter writer)
{
if (_designTimeMode)
{
// There is no value buffer in design time mode but we still want to write out a value. We write a
// value to ensure the tag helper's property type is string.
writer.Write("string.Empty");
}
else
{
writer.Write(StringValueBufferVariableName);
}
}
private static bool ContainsChildContent(IList<Chunk> children)
{
// False will be returned if there are no children or if there are only non-TagHelper ParentChunk leaf
// nodes.
foreach (var child in children)
{
var parentChunk = child as ParentChunk;
if (parentChunk == null ||
parentChunk is TagHelperChunk ||
ContainsChildContent(parentChunk.Children))
{
return true;
}
}
return false;
}
public static bool IsDynamicAttributeValue(Chunk attributeValueChunk)
{
var parentChunk = attributeValueChunk as ParentChunk;
if (parentChunk != null)
{
return parentChunk.Children.Any(child => child is DynamicCodeAttributeChunk);
}
return false;
}
// A CSharpCodeVisitor which does not HTML encode values. Used when rendering bound string attribute values.
private class CSharpLiteralCodeVisitor : CSharpCodeVisitor
{
public CSharpLiteralCodeVisitor(
CSharpTagHelperCodeRenderer tagHelperRenderer,
CSharpCodeWriter writer,
CodeGeneratorContext context)
: base(writer, context)
{
// Ensure that no matter how this class is used, we don't create numerous CSharpTagHelperCodeRenderer
// instances.
TagHelperRenderer = tagHelperRenderer;
}
protected override string WriteMethodName
{
get
{
return Context.Host.GeneratedClassContext.WriteLiteralMethodName;
}
}
protected override string WriteToMethodName
{
get
{
return Context.Host.GeneratedClassContext.WriteLiteralToMethodName;
}
}
}
private class TagHelperAttributeCodeVisitor : CSharpCodeVisitor
{
public TagHelperAttributeCodeVisitor(
CSharpCodeWriter writer,
CodeGeneratorContext context)
: base(writer, context)
{
}
protected override string WriteAttributeValueMethodName =>
Context.Host.GeneratedClassContext.GeneratedTagHelperContext.AddHtmlAttributeValueMethodName;
}
}
}

View File

@ -1,22 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
public abstract class CodeGenerator
{
private readonly CodeGeneratorContext _context;
public CodeGenerator(CodeGeneratorContext context)
{
_context = context;
}
protected CodeGeneratorContext Context
{
get { return _context; }
}
public abstract CodeGeneratorResult Generate();
}
}

View File

@ -1,75 +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 Microsoft.AspNetCore.Razor.Chunks.Generators;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
/// <summary>
/// Context object with information used to generate a Razor page.
/// </summary>
public class CodeGeneratorContext : ChunkGeneratorContext
{
/// <summary>
/// Instantiates a new instance of the <see cref="CodeGeneratorContext"/> object.
/// </summary>
/// <param name="generatorContext">A <see cref="ChunkGeneratorContext"/> to copy information from.</param>
/// <param name="errorSink">
/// The <see cref="ErrorSink"/> used to collect <see cref="RazorError"/>s encountered
/// when parsing the current Razor document.
/// </param>
public CodeGeneratorContext(ChunkGeneratorContext generatorContext, ErrorSink errorSink)
: base(generatorContext)
{
ErrorSink = errorSink;
ExpressionRenderingMode = ExpressionRenderingMode.WriteToOutput;
}
// Internal for testing.
internal CodeGeneratorContext(
RazorEngineHost host,
string className,
string rootNamespace,
string sourceFile,
bool shouldGenerateLinePragmas,
ErrorSink errorSink)
: base(host, className, rootNamespace, sourceFile, shouldGenerateLinePragmas)
{
ErrorSink = errorSink;
ExpressionRenderingMode = ExpressionRenderingMode.WriteToOutput;
}
/// <summary>
/// The current C# rendering mode.
/// </summary>
/// <remarks>
/// <see cref="ExpressionRenderingMode.WriteToOutput"/> forces C# generation to write
/// <see cref="Chunks.Chunk"/>s to the output page, i.e. WriteLiteral("Hello World").
/// <see cref="ExpressionRenderingMode.InjectCode"/> writes <see cref="Chunks.Chunk"/> values in their
/// rawest form, i.g. "Hello World".
/// </remarks>
public ExpressionRenderingMode ExpressionRenderingMode { get; set; }
/// <summary>
/// The C# writer to write <see cref="Chunks.Chunk"/> information to.
/// </summary>
/// <remarks>
/// If <see cref="TargetWriterName"/> is <c>null</c> values will be written using a default write method
/// i.e. WriteLiteral("Hello World").
/// If <see cref="TargetWriterName"/> is not <c>null</c> values will be written to the given
/// <see cref="TargetWriterName"/>, i.e. WriteLiteralTo(myWriter, "Hello World").
/// </remarks>
public string TargetWriterName { get; set; }
/// <summary>
/// Gets or sets the <c>SHA1</c> based checksum for the file whose location is defined by
/// <see cref="ChunkGeneratorContext.SourceFile"/>.
/// </summary>
public string Checksum { get; set; }
/// <summary>
/// Used to aggregate <see cref="RazorError"/>s.
/// </summary>
public ErrorSink ErrorSink { get; }
}
}

View File

@ -1,19 +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.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
public class CodeGeneratorResult
{
public CodeGeneratorResult(string code, IList<LineMapping> designTimeLineMappings)
{
Code = code;
DesignTimeLineMappings = designTimeLineMappings;
}
public string Code { get; private set; }
public IList<LineMapping> DesignTimeLineMappings { get; private set; }
}
}

View File

@ -1,203 +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.Text;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
public class CodeWriter : IDisposable
{
private static readonly char[] NewLineCharacters = { '\r', '\n' };
private string _cache = string.Empty;
private bool _dirty;
private int _absoluteIndex;
private int _currentLineIndex;
private int _currentLineCharacterIndex;
public StringBuilder Builder { get; } = new StringBuilder();
public int CurrentIndent { get; private set; }
public bool IsAfterNewLine { get; private set; }
public string NewLine { get; set; } = Environment.NewLine;
public CodeWriter ResetIndent()
{
return SetIndent(0);
}
public CodeWriter IncreaseIndent(int size)
{
CurrentIndent += size;
return this;
}
public CodeWriter DecreaseIndent(int size)
{
CurrentIndent -= size;
return this;
}
public CodeWriter SetIndent(int size)
{
CurrentIndent = size;
return this;
}
public CodeWriter Indent(int size)
{
if (IsAfterNewLine)
{
Builder.Append(' ', size);
_currentLineCharacterIndex += size;
_absoluteIndex += size;
_dirty = true;
IsAfterNewLine = false;
}
return this;
}
public CodeWriter Write(string data)
{
if (data == null)
{
return this;
}
return Write(data, 0, data.Length);
}
public CodeWriter Write(string data, int index, int count)
{
if (data == null || count == 0)
{
return this;
}
Indent(CurrentIndent);
Builder.Append(data, index, count);
_dirty = true;
IsAfterNewLine = false;
_absoluteIndex += count;
// The data string might contain a partial newline where the previously
// written string has part of the newline.
var i = index;
int? trailingPartStart = null;
if (
// Check the last character of the previous write operation.
Builder.Length - count - 1 >= 0 &&
Builder[Builder.Length - count - 1] == '\r' &&
// Check the first character of the current write operation.
Builder[Builder.Length - count] == '\n')
{
// This is newline that's spread across two writes. Skip the first character of the
// current write operation.
//
// We don't need to increment our newline counter because we already did that when we
// saw the \r.
i += 1;
trailingPartStart = 1;
}
// Iterate the string, stopping at each occurrence of a newline character. This lets us count the
// newline occurrences and keep the index of the last one.
while ((i = data.IndexOfAny(NewLineCharacters, i)) >= 0)
{
// Newline found.
_currentLineIndex++;
_currentLineCharacterIndex = 0;
i++;
// We might have stopped at a \r, so check if it's followed by \n and then advance the index to
// start the next search after it.
if (count > i &&
data[i - 1] == '\r' &&
data[i] == '\n')
{
i++;
}
// The 'suffix' of the current line starts after this newline token.
trailingPartStart = i;
}
if (trailingPartStart == null)
{
// No newlines, just add the length of the data buffer
_currentLineCharacterIndex += count;
}
else
{
// Newlines found, add the trailing part of 'data'
_currentLineCharacterIndex += (count - trailingPartStart.Value);
}
return this;
}
public CodeWriter WriteLine()
{
Builder.Append(NewLine);
_currentLineIndex++;
_currentLineCharacterIndex = 0;
_absoluteIndex += NewLine.Length;
_dirty = true;
IsAfterNewLine = true;
return this;
}
public CodeWriter WriteLine(string data)
{
return Write(data).WriteLine();
}
public string GenerateCode()
{
if (_dirty)
{
_cache = Builder.ToString();
_dirty = false;
}
return _cache;
}
public SourceLocation GetCurrentSourceLocation()
{
return new SourceLocation(_absoluteIndex, _currentLineIndex, _currentLineCharacterIndex);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Builder.Clear();
}
}
public void Dispose()
{
Dispose(disposing: true);
}
}
}

View File

@ -1,29 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
public enum ExpressionRenderingMode
{
/// <summary>
/// Indicates that expressions should be written to the output stream
/// </summary>
/// <example>
/// If @foo is rendered with WriteToOutput, the code generator would output the following code:
///
/// Write(foo);
/// </example>
WriteToOutput,
/// <summary>
/// Indicates that expressions should simply be placed as-is in the code, and the context in which
/// the code exists will be used to render it
/// </summary>
/// <example>
/// If @foo is rendered with InjectCode, the code generator would output the following code:
///
/// foo
/// </example>
InjectCode
}
}

View File

@ -1,219 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
public struct GeneratedClassContext
{
public static readonly string DefaultWriteMethodName = "Write";
public static readonly string DefaultWriteLiteralMethodName = "WriteLiteral";
public static readonly string DefaultExecuteMethodName = "ExecuteAsync";
public static readonly string DefaultBeginWriteAttributeMethodName = "BeginWriteAttribute";
public static readonly string DefaultBeginWriteAttributeToMethodName = "BeginWriteAttributeTo";
public static readonly string DefaultEndWriteAttributeMethodName = "EndWriteAttribute";
public static readonly string DefaultEndWriteAttributeToMethodName = "EndWriteAttributeTo";
public static readonly string DefaultWriteAttributeValueMethodName = "WriteAttributeValue";
public static readonly string DefaultWriteAttributeValueToMethodName = "WriteAttributeValueTo";
public static readonly GeneratedClassContext Default =
new GeneratedClassContext(
DefaultExecuteMethodName,
DefaultWriteMethodName,
DefaultWriteLiteralMethodName,
new GeneratedTagHelperContext());
public GeneratedClassContext(
string executeMethodName,
string writeMethodName,
string writeLiteralMethodName,
GeneratedTagHelperContext generatedTagHelperContext)
: this()
{
if (generatedTagHelperContext == null)
{
throw new ArgumentNullException(nameof(generatedTagHelperContext));
}
if (string.IsNullOrEmpty(executeMethodName))
{
throw new ArgumentException(
CommonResources.Argument_Cannot_Be_Null_Or_Empty,
nameof(executeMethodName));
}
if (string.IsNullOrEmpty(writeMethodName))
{
throw new ArgumentException(
CommonResources.Argument_Cannot_Be_Null_Or_Empty,
nameof(writeMethodName));
}
if (string.IsNullOrEmpty(writeLiteralMethodName))
{
throw new ArgumentException(
CommonResources.Argument_Cannot_Be_Null_Or_Empty,
nameof(writeLiteralMethodName));
}
GeneratedTagHelperContext = generatedTagHelperContext;
WriteMethodName = writeMethodName;
WriteLiteralMethodName = writeLiteralMethodName;
ExecuteMethodName = executeMethodName;
WriteToMethodName = null;
WriteLiteralToMethodName = null;
TemplateTypeName = null;
DefineSectionMethodName = null;
BeginWriteAttributeMethodName = DefaultBeginWriteAttributeMethodName;
BeginWriteAttributeToMethodName = DefaultBeginWriteAttributeToMethodName;
EndWriteAttributeMethodName = DefaultEndWriteAttributeMethodName;
EndWriteAttributeToMethodName = DefaultEndWriteAttributeToMethodName;
WriteAttributeValueMethodName = DefaultWriteAttributeValueMethodName;
WriteAttributeValueToMethodName = DefaultWriteAttributeValueToMethodName;
}
public GeneratedClassContext(
string executeMethodName,
string writeMethodName,
string writeLiteralMethodName,
string writeToMethodName,
string writeLiteralToMethodName,
string templateTypeName,
GeneratedTagHelperContext generatedTagHelperContext)
: this(executeMethodName,
writeMethodName,
writeLiteralMethodName,
generatedTagHelperContext)
{
WriteToMethodName = writeToMethodName;
WriteLiteralToMethodName = writeLiteralToMethodName;
TemplateTypeName = templateTypeName;
}
public GeneratedClassContext(
string executeMethodName,
string writeMethodName,
string writeLiteralMethodName,
string writeToMethodName,
string writeLiteralToMethodName,
string templateTypeName,
string defineSectionMethodName,
GeneratedTagHelperContext generatedTagHelperContext)
: this(executeMethodName,
writeMethodName,
writeLiteralMethodName,
writeToMethodName,
writeLiteralToMethodName,
templateTypeName,
generatedTagHelperContext)
{
DefineSectionMethodName = defineSectionMethodName;
}
public GeneratedClassContext(
string executeMethodName,
string writeMethodName,
string writeLiteralMethodName,
string writeToMethodName,
string writeLiteralToMethodName,
string templateTypeName,
string defineSectionMethodName,
string beginContextMethodName,
string endContextMethodName,
GeneratedTagHelperContext generatedTagHelperContext)
: this(executeMethodName,
writeMethodName,
writeLiteralMethodName,
writeToMethodName,
writeLiteralToMethodName,
templateTypeName,
defineSectionMethodName,
generatedTagHelperContext)
{
BeginContextMethodName = beginContextMethodName;
EndContextMethodName = endContextMethodName;
}
// Required Items
public string WriteMethodName { get; }
public string WriteLiteralMethodName { get; }
public string WriteToMethodName { get; }
public string WriteLiteralToMethodName { get; }
public string ExecuteMethodName { get; }
public GeneratedTagHelperContext GeneratedTagHelperContext { get; }
// Optional Items
public string BeginContextMethodName { get; set; }
public string EndContextMethodName { get; set; }
public string DefineSectionMethodName { get; set; }
public string TemplateTypeName { get; set; }
public string BeginWriteAttributeMethodName { get; set; }
public string BeginWriteAttributeToMethodName { get; set; }
public string EndWriteAttributeMethodName { get; set; }
public string EndWriteAttributeToMethodName { get; set; }
public string WriteAttributeValueMethodName { get; set; }
public string WriteAttributeValueToMethodName { get; set; }
public bool AllowSections
{
get { return !string.IsNullOrEmpty(DefineSectionMethodName); }
}
public bool AllowTemplates
{
get { return !string.IsNullOrEmpty(TemplateTypeName); }
}
public bool SupportsInstrumentation
{
get { return !string.IsNullOrEmpty(BeginContextMethodName) && !string.IsNullOrEmpty(EndContextMethodName); }
}
public override bool Equals(object obj)
{
if (!(obj is GeneratedClassContext))
{
return false;
}
var other = (GeneratedClassContext)obj;
return string.Equals(DefineSectionMethodName, other.DefineSectionMethodName, StringComparison.Ordinal) &&
string.Equals(WriteMethodName, other.WriteMethodName, StringComparison.Ordinal) &&
string.Equals(WriteLiteralMethodName, other.WriteLiteralMethodName, StringComparison.Ordinal) &&
string.Equals(WriteToMethodName, other.WriteToMethodName, StringComparison.Ordinal) &&
string.Equals(WriteLiteralToMethodName, other.WriteLiteralToMethodName, StringComparison.Ordinal) &&
string.Equals(ExecuteMethodName, other.ExecuteMethodName, StringComparison.Ordinal) &&
string.Equals(TemplateTypeName, other.TemplateTypeName, StringComparison.Ordinal) &&
string.Equals(BeginContextMethodName, other.BeginContextMethodName, StringComparison.Ordinal) &&
string.Equals(EndContextMethodName, other.EndContextMethodName, StringComparison.Ordinal);
}
public override int GetHashCode()
{
// Hash code should include only immutable properties.
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(WriteMethodName, StringComparer.Ordinal);
hashCodeCombiner.Add(WriteLiteralMethodName, StringComparer.Ordinal);
hashCodeCombiner.Add(WriteToMethodName, StringComparer.Ordinal);
hashCodeCombiner.Add(WriteLiteralToMethodName, StringComparer.Ordinal);
hashCodeCombiner.Add(ExecuteMethodName, StringComparer.Ordinal);
return hashCodeCombiner;
}
public static bool operator ==(GeneratedClassContext left, GeneratedClassContext right)
{
return left.Equals(right);
}
public static bool operator !=(GeneratedClassContext left, GeneratedClassContext right)
{
return !left.Equals(right);
}
}
}

View File

@ -1,239 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
/// <summary>
/// Contains necessary information for the tag helper code generation process.
/// </summary>
public class GeneratedTagHelperContext
{
/// <summary>
/// Instantiates a new instance of the <see cref="GeneratedTagHelperContext"/> with default values.
/// </summary>
public GeneratedTagHelperContext()
{
BeginAddHtmlAttributeValuesMethodName = "BeginAddHtmlAttributeValues";
EndAddHtmlAttributeValuesMethodName = "EndAddHtmlAttributeValues";
AddHtmlAttributeValueMethodName = "AddHtmlAttributeValue";
CreateTagHelperMethodName = "CreateTagHelper";
RunnerRunAsyncMethodName = "RunAsync";
ScopeManagerBeginMethodName = "Begin";
ScopeManagerEndMethodName = "End";
ExecutionContextAddMethodName = "Add";
ExecutionContextAddTagHelperAttributeMethodName = "AddTagHelperAttribute";
ExecutionContextAddHtmlAttributeMethodName = "AddHtmlAttribute";
ExecutionContextOutputPropertyName = "Output";
FormatInvalidIndexerAssignmentMethodName = "FormatInvalidIndexerAssignment";
MarkAsHtmlEncodedMethodName = "Html.Raw";
StartTagHelperWritingScopeMethodName = "StartTagHelperWritingScope";
EndTagHelperWritingScopeMethodName = "EndTagHelperWritingScope";
BeginWriteTagHelperAttributeMethodName = "BeginWriteTagHelperAttribute";
EndWriteTagHelperAttributeMethodName = "EndWriteTagHelperAttribute";
RunnerTypeName = "Microsoft.AspNetCore.Razor.Runtime.TagHelperRunner";
ScopeManagerTypeName = "Microsoft.AspNetCore.Razor.Runtime.TagHelperScopeManager";
ExecutionContextTypeName = "Microsoft.AspNetCore.Razor.Runtime.TagHelperExecutionContext";
TagHelperContentTypeName = "Microsoft.AspNetCore.Razor.TagHelperContent";
TagHelperContentGetContentMethodName = "GetContent";
HtmlEncoderPropertyName = "HtmlEncoder";
TagHelperOutputIsContentModifiedPropertyName = "IsContentModified";
TagHelperOutputContentPropertyName = "Content";
ExecutionContextSetOutputContentAsyncMethodName = "SetOutputContentAsync";
TagHelperAttributeTypeName = "Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute";
EncodedHtmlStringTypeName = "Microsoft.AspNetCore.Html.HtmlString";
TagHelperAttributeValuePropertyName = "Value";
}
/// <summary>
/// The name of the method used to begin the addition of unbound, complex tag helper attributes to
/// TagHelperExecutionContexts.
/// </summary>
/// <remarks>
/// Method signature should be
/// <code>
/// public void BeginAddHtmlAttributeValues(
/// TagHelperExecutionContext executionContext,
/// string attributeName)
/// </code>
/// </remarks>
public string BeginAddHtmlAttributeValuesMethodName { get; set; }
/// <summary>
/// Method name used to end addition of unbound, complex tag helper attributes to TagHelperExecutionContexts.
/// </summary>
/// <remarks>
/// Method signature should be
/// <code>
/// public void EndAddHtmlAttributeValues(
/// TagHelperExecutionContext executionContext)
/// </code>
/// </remarks>
public string EndAddHtmlAttributeValuesMethodName { get; set; }
/// <summary>
/// Method name used to add individual components of an unbound, complex tag helper attribute to
/// TagHelperExecutionContexts.
/// </summary>
/// <remarks>
/// Method signature:
/// <code>
/// public void AddHtmlAttributeValues(
/// string prefix,
/// int prefixOffset,
/// string value,
/// int valueOffset,
/// int valueLength,
/// bool isLiteral)
/// </code>
/// </remarks>
public string AddHtmlAttributeValueMethodName { get; set; }
/// <summary>
/// The name of the method used to create a tag helper.
/// </summary>
public string CreateTagHelperMethodName { get; set; }
/// <summary>
/// The name of the <see cref="RunnerTypeName"/> method used to run tag helpers.
/// </summary>
public string RunnerRunAsyncMethodName { get; set; }
/// <summary>
/// The name of the <see cref="ExecutionContextTypeName"/> method used to start a scope.
/// </summary>
public string ScopeManagerBeginMethodName { get; set; }
/// <summary>
/// The name of the <see cref="ExecutionContextTypeName"/> method used to end a scope.
/// </summary>
public string ScopeManagerEndMethodName { get; set; }
/// <summary>
/// The name of the <see cref="ExecutionContextTypeName"/> method used to add tag helper attributes.
/// </summary>
public string ExecutionContextAddTagHelperAttributeMethodName { get; set; }
/// <summary>
/// The name of the <see cref="ExecutionContextTypeName"/> method used to add HTML attributes.
/// </summary>
public string ExecutionContextAddHtmlAttributeMethodName { get; set; }
/// <summary>
/// The name of the <see cref="ExecutionContextTypeName"/> method used to add tag helpers.
/// </summary>
public string ExecutionContextAddMethodName { get; set; }
/// <summary>
/// The property name for the tag helper's output.
/// </summary>
public string ExecutionContextOutputPropertyName { get; set; }
/// <summary>
/// The name of the method used to format an error message about using an indexer when the tag helper property
/// is <c>null</c>.
/// </summary>
/// <remarks>
/// Method signature should be
/// <code>
/// public string FormatInvalidIndexerAssignment(
/// string attributeName, // Name of the HTML attribute associated with the indexer.
/// string tagHelperTypeName, // Full name of the tag helper type.
/// string propertyName) // Dictionary property in the tag helper.
/// </code>
/// </remarks>
public string FormatInvalidIndexerAssignmentMethodName { get; set; }
/// <summary>
/// The name of the method used to wrap a <see cref="string"/> value and mark it as HTML-encoded.
/// </summary>
/// <remarks>Used together with <see cref="ExecutionContextAddHtmlAttributeMethodName"/>.</remarks>
public string MarkAsHtmlEncodedMethodName { get; set; }
/// <summary>
/// The name of the method used to start a new writing scope.
/// </summary>
public string StartTagHelperWritingScopeMethodName { get; set; }
/// <summary>
/// The name of the method used to end a writing scope.
/// </summary>
public string EndTagHelperWritingScopeMethodName { get; set; }
/// <summary>
/// The name of the method used to begin an attribute writing scope.
/// </summary>
public string BeginWriteTagHelperAttributeMethodName { get; set; }
/// <summary>
/// The name of the method used to end an attribute writing scope.
/// </summary>
public string EndWriteTagHelperAttributeMethodName { get; set; }
/// <summary>
/// The name of the type used to run tag helpers.
/// </summary>
public string RunnerTypeName { get; set; }
/// <summary>
/// The name of the type used to create scoped <see cref="ExecutionContextTypeName"/> instances.
/// </summary>
public string ScopeManagerTypeName { get; set; }
/// <summary>
/// The name of the type describing a specific tag helper scope.
/// </summary>
/// <remarks>
/// Contains information about in-scope tag helpers, HTML attributes, and the tag helpers' output.
/// </remarks>
public string ExecutionContextTypeName { get; set; }
/// <summary>
/// The name of the type containing tag helper content.
/// </summary>
/// <remarks>
/// Contains the data returned by EndTagHelperWriteScope().
/// </remarks>
public string TagHelperContentTypeName { get; set; }
/// <summary>
/// The name of the property containing the <c>HtmlEncoder</c>.
/// </summary>
public string HtmlEncoderPropertyName { get; set; }
/// <summary>
/// The name of the method used to convert a <c>TagHelperContent</c> into a <see cref="string"/>.
/// </summary>
public string TagHelperContentGetContentMethodName { get; set; }
/// <summary>
/// The name of the property used to indicate the tag helper's content has been modified.
/// </summary>
public string TagHelperOutputIsContentModifiedPropertyName { get; set; }
/// <summary>
/// The name of the property for the tag helper's output content.
/// </summary>
public string TagHelperOutputContentPropertyName { get; set; }
/// <summary>
/// The name of the method on the property <see cref="ExecutionContextOutputPropertyName"/> used to execute
/// child content and set the rendered results on its <see cref="ExecutionContextOutputPropertyName"/> property.
/// </summary>
public string ExecutionContextSetOutputContentAsyncMethodName { get; set; }
/// <summary>
/// The name of the type used to represent tag helper attributes.
/// </summary>
public string TagHelperAttributeTypeName { get; set; }
/// <summary>
/// The name of the type used to represent encoded content.
/// </summary>
public string EncodedHtmlStringTypeName { get; set; }
/// <summary>
/// The name of the Value property of <c>TagHelperAttribute</c>.
/// </summary>
public string TagHelperAttributeValuePropertyName { get; set; }
}
}

View File

@ -1,111 +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.AspNetCore.Razor.Chunks;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
/// <summary>
/// The results of parsing and generating code for a Razor document.
/// </summary>
public class GeneratorResults : ParserResults
{
/// <summary>
/// Instantiates a new <see cref="GeneratorResults"/> instance.
/// </summary>
/// <param name="parserResults">The results of parsing a document.</param>
/// <param name="codeGeneratorResult">The results of generating code for the document.</param>
/// <param name="chunkTree">A <see cref="ChunkTree"/> for the document.</param>
public GeneratorResults(ParserResults parserResults,
CodeGeneratorResult codeGeneratorResult,
ChunkTree chunkTree)
: this(parserResults.Document,
parserResults.TagHelperDescriptors,
parserResults.ErrorSink,
codeGeneratorResult,
chunkTree)
{
if (parserResults == null)
{
throw new ArgumentNullException(nameof(parserResults));
}
if (codeGeneratorResult == null)
{
throw new ArgumentNullException(nameof(codeGeneratorResult));
}
if (chunkTree == null)
{
throw new ArgumentNullException(nameof(chunkTree));
}
}
/// <summary>
/// Instantiates a new <see cref="GeneratorResults"/> instance.
/// </summary>
/// <param name="document">The <see cref="Block"/> for the syntax tree.</param>
/// <param name="tagHelperDescriptors">
/// The <see cref="TagHelperDescriptor"/>s that apply to the current Razor document.
/// </param>
/// <param name="errorSink">
/// The <see cref="ErrorSink"/> used to collect <see cref="RazorError"/>s encountered when parsing the
/// current Razor document.
/// </param>
/// <param name="codeGeneratorResult">The results of generating code for the document.</param>
/// <param name="chunkTree">A <see cref="ChunkTree"/> for the document.</param>
public GeneratorResults(Block document,
IEnumerable<TagHelperDescriptor> tagHelperDescriptors,
ErrorSink errorSink,
CodeGeneratorResult codeGeneratorResult,
ChunkTree chunkTree)
: base(document, tagHelperDescriptors, errorSink)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
if (tagHelperDescriptors == null)
{
throw new ArgumentNullException(nameof(tagHelperDescriptors));
}
if (errorSink == null)
{
throw new ArgumentNullException(nameof(errorSink));
}
if (codeGeneratorResult == null)
{
throw new ArgumentNullException(nameof(codeGeneratorResult));
}
if (chunkTree == null)
{
throw new ArgumentNullException(nameof(chunkTree));
}
GeneratedCode = codeGeneratorResult.Code;
DesignTimeLineMappings = codeGeneratorResult.DesignTimeLineMappings;
ChunkTree = chunkTree;
}
/// <summary>
/// The generated code for the document.
/// </summary>
public string GeneratedCode { get; }
/// <summary>
/// <see cref="LineMapping"/>s used to project code from a file during design time.
/// </summary>
public IList<LineMapping> DesignTimeLineMappings { get; }
/// <summary>
/// A <see cref="Chunks.ChunkTree"/> for the document.
/// </summary>
public ChunkTree ChunkTree { get; }
}
}

View File

@ -1,79 +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.Globalization;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
public class LineMapping
{
public LineMapping(MappingLocation documentLocation, MappingLocation generatedLocation)
{
DocumentLocation = documentLocation;
GeneratedLocation = generatedLocation;
}
public MappingLocation DocumentLocation { get; }
public MappingLocation GeneratedLocation { get; }
public override bool Equals(object obj)
{
var other = obj as LineMapping;
if (ReferenceEquals(other, null))
{
return false;
}
return DocumentLocation.Equals(other.DocumentLocation) &&
GeneratedLocation.Equals(other.GeneratedLocation);
}
public override int GetHashCode()
{
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(DocumentLocation);
hashCodeCombiner.Add(GeneratedLocation);
return hashCodeCombiner;
}
public static bool operator ==(LineMapping left, LineMapping right)
{
if (ReferenceEquals(left, right))
{
// Exact equality e.g. both objects are null.
return true;
}
if (ReferenceEquals(left, null))
{
return false;
}
return left.Equals(right);
}
public static bool operator !=(LineMapping left, LineMapping right)
{
if (ReferenceEquals(left, right))
{
// Exact equality e.g. both objects are null.
return false;
}
if (ReferenceEquals(left, null))
{
return true;
}
return !left.Equals(right);
}
public override string ToString()
{
return string.Format(CultureInfo.CurrentCulture, "{0} -> {1}", DocumentLocation, GeneratedLocation);
}
}
}

View File

@ -1,22 +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.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
public class LineMappingManager
{
public LineMappingManager()
{
Mappings = new List<LineMapping>();
}
public List<LineMapping> Mappings { get; }
public void AddMapping(MappingLocation documentLocation, MappingLocation generatedLocation)
{
Mappings.Add(new LineMapping(documentLocation, generatedLocation));
}
}
}

View File

@ -1,105 +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.Globalization;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
public class MappingLocation
{
public MappingLocation()
{
}
public MappingLocation(SourceLocation location, int contentLength)
{
ContentLength = contentLength;
AbsoluteIndex = location.AbsoluteIndex;
LineIndex = location.LineIndex;
CharacterIndex = location.CharacterIndex;
FilePath = location.FilePath;
}
public int ContentLength { get; }
public int AbsoluteIndex { get; }
public int LineIndex { get; }
public int CharacterIndex { get; }
public string FilePath { get; }
public override bool Equals(object obj)
{
var other = obj as MappingLocation;
if (ReferenceEquals(other, null))
{
return false;
}
return string.Equals(FilePath, other.FilePath, StringComparison.Ordinal) &&
AbsoluteIndex == other.AbsoluteIndex &&
ContentLength == other.ContentLength &&
LineIndex == other.LineIndex &&
CharacterIndex == other.CharacterIndex;
}
public override int GetHashCode()
{
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(FilePath, StringComparer.Ordinal);
hashCodeCombiner.Add(AbsoluteIndex);
hashCodeCombiner.Add(ContentLength);
hashCodeCombiner.Add(LineIndex);
hashCodeCombiner.Add(CharacterIndex);
return hashCodeCombiner;
}
public override string ToString()
{
return string.Format(
CultureInfo.CurrentCulture, "({0}:{1},{2} [{3}] {4})",
AbsoluteIndex,
LineIndex,
CharacterIndex,
ContentLength,
FilePath);
}
public static bool operator ==(MappingLocation left, MappingLocation right)
{
if (ReferenceEquals(left, right))
{
// Exact equality e.g. both objects are null.
return true;
}
if (ReferenceEquals(left, null))
{
return false;
}
return left.Equals(right);
}
public static bool operator !=(MappingLocation left, MappingLocation right)
{
if (ReferenceEquals(left, right))
{
// Exact equality e.g. both objects are null.
return false;
}
if (ReferenceEquals(left, null))
{
return true;
}
return !left.Equals(right);
}
}
}

View File

@ -1,62 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
/// <summary>
/// Renders code for tag helper property initialization.
/// </summary>
public class TagHelperAttributeValueCodeRenderer
{
/// <summary>
/// Called during Razor's code generation process to generate code that instantiates the value of the tag
/// helper's property. Last value written should not be or end with a semicolon.
/// </summary>
/// <param name="attributeDescriptor">
/// The <see cref="TagHelperAttributeDescriptor"/> to generate code for.
/// </param>
/// <param name="writer">The <see cref="CSharpCodeWriter"/> that's used to write code.</param>
/// <param name="context">A <see cref="Chunks.Generators.ChunkGeneratorContext"/> instance that contains
/// information about the current code generation process.</param>
/// <param name="renderAttributeValue">
/// <see cref="Action"/> that renders the raw value of the HTML attribute.
/// </param>
/// <param name="complexValue">
/// Indicates whether or not the source attribute value contains more than simple text. <c>false</c> for plain
/// C# expressions e.g. <c>"PropertyName"</c>. <c>true</c> if the attribute value contain at least one in-line
/// Razor construct e.g. <c>"@(@readonly)"</c>.
/// </param>
public virtual void RenderAttributeValue(
TagHelperAttributeDescriptor attributeDescriptor,
CSharpCodeWriter writer,
CodeGeneratorContext context,
Action<CSharpCodeWriter> renderAttributeValue,
bool complexValue)
{
if (attributeDescriptor == null)
{
throw new ArgumentNullException(nameof(attributeDescriptor));
}
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (renderAttributeValue == null)
{
throw new ArgumentNullException(nameof(renderAttributeValue));
}
renderAttributeValue(writer);
}
}
}

View File

@ -1,32 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Chunks;
namespace Microsoft.AspNetCore.Razor.CodeGenerators.Visitors
{
public class CSharpBaseTypeVisitor : CodeVisitor<CSharpCodeWriter>
{
public CSharpBaseTypeVisitor(CSharpCodeWriter writer, CodeGeneratorContext context)
: base(writer, context)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
}
public string CurrentBaseType { get; set; }
protected override void Visit(SetBaseTypeChunk chunk)
{
CurrentBaseType = chunk.TypeName;
}
}
}

View File

@ -1,623 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using Microsoft.AspNetCore.Razor.Chunks;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.CodeGenerators.Visitors
{
public class CSharpCodeVisitor : CodeVisitor<CSharpCodeWriter>
{
private const string ItemParameterName = "item";
private const string ValueWriterName = "__razor_attribute_value_writer";
private const string TemplateWriterName = "__razor_template_writer";
private const string SectionWriterName = "__razor_section_writer";
private const int MaxStringLiteralLength = 1024;
private CSharpPaddingBuilder _paddingBuilder;
private CSharpTagHelperCodeRenderer _tagHelperCodeRenderer;
public CSharpCodeVisitor(CSharpCodeWriter writer, CodeGeneratorContext context)
: base(writer, context)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
_paddingBuilder = new CSharpPaddingBuilder(context.Host);
}
public CSharpTagHelperCodeRenderer TagHelperRenderer
{
get
{
if (_tagHelperCodeRenderer == null)
{
_tagHelperCodeRenderer = new CSharpTagHelperCodeRenderer(this, Writer, Context);
}
return _tagHelperCodeRenderer;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_tagHelperCodeRenderer = value;
}
}
/// <summary>
/// Method used to write an <see cref="object"/> to the current output.
/// </summary>
/// <remarks>Default is to HTML encode all but a few types.</remarks>
protected virtual string WriteMethodName
{
get
{
return Context.Host.GeneratedClassContext.WriteMethodName;
}
}
/// <summary>
/// Method used to write an <see cref="object"/> to a specified <see cref="System.IO.TextWriter"/>.
/// </summary>
/// <remarks>Default is to HTML encode all but a few types.</remarks>
protected virtual string WriteToMethodName
{
get
{
return Context.Host.GeneratedClassContext.WriteToMethodName;
}
}
/// <summary>
/// Gets the method name used to generate <c>WriteAttribute</c> invocations in the rendered page.
/// </summary>
/// <remarks>Defaults to <see cref="GeneratedClassContext.WriteAttributeValueMethodName"/></remarks>
protected virtual string WriteAttributeValueMethodName =>
Context.Host.GeneratedClassContext.WriteAttributeValueMethodName;
protected override void Visit(TagHelperChunk chunk)
{
TagHelperRenderer.RenderTagHelper(chunk);
}
protected override void Visit(ParentChunk chunk)
{
Accept(chunk.Children);
}
protected override void Visit(TemplateChunk chunk)
{
Writer.Write(ItemParameterName).Write(" => ")
.WriteStartNewObject(Context.Host.GeneratedClassContext.TemplateTypeName);
var currentTargetWriterName = Context.TargetWriterName;
Context.TargetWriterName = TemplateWriterName;
using (Writer.BuildAsyncLambda(endLine: false, parameterNames: TemplateWriterName))
{
Accept(chunk.Children);
}
Context.TargetWriterName = currentTargetWriterName;
Writer.WriteEndMethodInvocation(false).WriteLine();
}
protected override void Visit(ParentLiteralChunk chunk)
{
Debug.Assert(chunk.Children.Count > 1);
if (Context.Host.DesignTimeMode)
{
// Skip generating the chunk if we're in design time or if the chunk is empty.
return;
}
var text = chunk.GetText();
if (Context.Host.EnableInstrumentation)
{
var start = chunk.Start.AbsoluteIndex;
Writer.WriteStartInstrumentationContext(Context, start, text.Length, isLiteral: true);
}
RenderStartWriteLiteral(text);
if (Context.Host.EnableInstrumentation)
{
Writer.WriteEndInstrumentationContext(Context);
}
}
protected override void Visit(LiteralChunk chunk)
{
if (Context.Host.DesignTimeMode || string.IsNullOrEmpty(chunk.Text))
{
// Skip generating the chunk if we're in design time or if the chunk is empty.
return;
}
if (Context.Host.EnableInstrumentation)
{
Writer.WriteStartInstrumentationContext(Context, chunk.Association, isLiteral: true);
}
RenderStartWriteLiteral(chunk.Text);
if (Context.Host.EnableInstrumentation)
{
Writer.WriteEndInstrumentationContext(Context);
}
}
protected override void Visit(ExpressionBlockChunk chunk)
{
if (Context.Host.DesignTimeMode)
{
RenderDesignTimeExpressionBlockChunk(chunk);
}
else
{
RenderRuntimeExpressionBlockChunk(chunk);
}
}
protected override void Visit(ExpressionChunk chunk)
{
Writer.Write(chunk.Code);
}
protected override void Visit(StatementChunk chunk)
{
CreateStatementCodeMapping(chunk.Code, chunk);
Writer.WriteLine();
}
protected override void Visit(DynamicCodeAttributeChunk chunk)
{
if (Context.Host.DesignTimeMode)
{
// Render the children as is without wrapping them in calls to WriteAttribute
Accept(chunk.Children);
return;
}
var currentRenderingMode = Context.ExpressionRenderingMode;
var currentTargetWriterName = Context.TargetWriterName;
CSharpLineMappingWriter lineMappingWriter = null;
var code = chunk.Children.FirstOrDefault();
if (code is ExpressionChunk || code is ExpressionBlockChunk)
{
// We only want to render the #line pragma if the attribute value will be in-lined.
// Ex: WriteAttributeValue("", 0, DateTime.Now, 0, 0, false)
// For non-inlined scenarios: WriteAttributeValue("", 0, (_) => ..., 0, 0, false)
// the line pragma will be generated inside the lambda.
lineMappingWriter = new CSharpLineMappingWriter(Writer, chunk.Start, Context.SourceFile);
}
if (!string.IsNullOrEmpty(currentTargetWriterName))
{
Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteAttributeValueToMethodName)
.Write(currentTargetWriterName)
.WriteParameterSeparator();
}
else
{
Writer.WriteStartMethodInvocation(WriteAttributeValueMethodName);
}
Context.TargetWriterName = ValueWriterName;
if (code is ExpressionChunk || code is ExpressionBlockChunk)
{
Debug.Assert(lineMappingWriter != null);
Writer
.WriteLocationTaggedString(chunk.Prefix)
.WriteParameterSeparator();
Context.ExpressionRenderingMode = ExpressionRenderingMode.InjectCode;
Accept(code);
Writer
.WriteParameterSeparator()
.Write(chunk.Start.AbsoluteIndex.ToString(CultureInfo.InvariantCulture))
.WriteParameterSeparator()
.Write(chunk.Association.Length.ToString(CultureInfo.InvariantCulture))
.WriteParameterSeparator()
.WriteBooleanLiteral(value: false)
.WriteEndMethodInvocation();
lineMappingWriter.Dispose();
}
else
{
Writer
.WriteLocationTaggedString(chunk.Prefix)
.WriteParameterSeparator()
.WriteStartNewObject(Context.Host.GeneratedClassContext.TemplateTypeName);
using (Writer.BuildAsyncLambda(endLine: false, parameterNames: ValueWriterName))
{
Accept(chunk.Children);
}
Writer
.WriteEndMethodInvocation(false)
.WriteParameterSeparator()
.Write(chunk.Start.AbsoluteIndex.ToString(CultureInfo.InvariantCulture))
.WriteParameterSeparator()
.Write(chunk.Association.Length.ToString(CultureInfo.InvariantCulture))
.WriteParameterSeparator()
.WriteBooleanLiteral(false)
.WriteEndMethodInvocation();
}
Context.TargetWriterName = currentTargetWriterName;
Context.ExpressionRenderingMode = currentRenderingMode;
}
protected override void Visit(LiteralCodeAttributeChunk chunk)
{
var visitChildren = chunk.Value == null;
if (Context.Host.DesignTimeMode)
{
// Render the attribute without wrapping it in a call to WriteAttribute
if (visitChildren)
{
Accept(chunk.Children);
}
return;
}
if (!string.IsNullOrEmpty(Context.TargetWriterName))
{
Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteAttributeValueToMethodName)
.Write(Context.TargetWriterName)
.WriteParameterSeparator();
}
else
{
Writer.WriteStartMethodInvocation(WriteAttributeValueMethodName);
}
Writer
.WriteLocationTaggedString(chunk.Prefix)
.WriteParameterSeparator();
if (visitChildren)
{
var currentRenderingMode = Context.ExpressionRenderingMode;
Context.ExpressionRenderingMode = ExpressionRenderingMode.InjectCode;
Accept(chunk.Children);
Context.ExpressionRenderingMode = currentRenderingMode;
Writer
.WriteParameterSeparator()
.Write(chunk.ValueLocation.AbsoluteIndex.ToString(CultureInfo.InvariantCulture))
.WriteParameterSeparator()
.Write(chunk.Association.Length.ToString(CultureInfo.InvariantCulture))
.WriteParameterSeparator()
.WriteBooleanLiteral(false)
.WriteEndMethodInvocation();
}
else
{
Writer
.WriteLocationTaggedString(chunk.Value)
.WriteParameterSeparator()
.Write(chunk.Association.Length.ToString(CultureInfo.InvariantCulture))
.WriteParameterSeparator()
.WriteBooleanLiteral(true)
.WriteEndMethodInvocation();
}
}
protected override void Visit(CodeAttributeChunk chunk)
{
if (Context.Host.DesignTimeMode)
{
// Render the attribute without wrapping it in a "WriteAttribute" invocation
Accept(chunk.Children);
return;
}
if (!string.IsNullOrEmpty(Context.TargetWriterName))
{
Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.BeginWriteAttributeToMethodName)
.Write(Context.TargetWriterName)
.WriteParameterSeparator();
}
else
{
Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.BeginWriteAttributeMethodName);
}
var attributeCount = chunk.Children.Count(c => c is LiteralCodeAttributeChunk || c is DynamicCodeAttributeChunk);
Writer.WriteStringLiteral(chunk.Attribute)
.WriteParameterSeparator()
.WriteLocationTaggedString(chunk.Prefix)
.WriteParameterSeparator()
.WriteLocationTaggedString(chunk.Suffix)
.WriteParameterSeparator()
.Write(attributeCount.ToString(CultureInfo.InvariantCulture))
.WriteEndMethodInvocation();
Accept(chunk.Children);
if (!string.IsNullOrEmpty(Context.TargetWriterName))
{
Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.EndWriteAttributeToMethodName)
.Write(Context.TargetWriterName)
.WriteEndMethodInvocation();
}
else
{
Writer.WriteMethodInvocation(Context.Host.GeneratedClassContext.EndWriteAttributeMethodName);
}
}
protected override void Visit(SectionChunk chunk)
{
Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.DefineSectionMethodName)
.WriteStringLiteral(chunk.Name)
.WriteParameterSeparator();
var currentTargetWriterName = Context.TargetWriterName;
Context.TargetWriterName = SectionWriterName;
using (Writer.BuildAsyncLambda(endLine: false, parameterNames: SectionWriterName))
{
Accept(chunk.Children);
}
Context.TargetWriterName = currentTargetWriterName;
Writer.WriteEndMethodInvocation();
}
public void RenderDesignTimeExpressionBlockChunk(ExpressionBlockChunk chunk)
{
var firstChild = chunk.Children.FirstOrDefault();
if (firstChild == null)
{
return;
}
var currentIndent = Writer.CurrentIndent;
Writer.ResetIndent();
var documentLocation = firstChild.Association.Start;
// This is only here to enable accurate formatting by the C# editor.
Writer.WriteLineNumberDirective(documentLocation, Context.SourceFile);
var designTimeAssignment = "__o = ";
var firstChildExpressionChunk = firstChild as ExpressionChunk;
if (firstChildExpressionChunk != null)
{
// We build the padding with an offset of the design time assignment statement.
Writer.Write(_paddingBuilder.BuildExpressionPadding((Span)firstChildExpressionChunk.Association, designTimeAssignment.Length))
.Write(designTimeAssignment);
// We map the first line of code but do not write the line pragmas associated with it.
CreateRawCodeMapping(firstChildExpressionChunk.Code, documentLocation);
// Render all but the first child.
// The reason why we render the other children differently is because when formatting the C# code
// the formatter expects the start line to have the assignment statement on it.
Accept(chunk.Children.Skip(1).ToList());
}
else
{
// First child is not an expression chunk
Accept(chunk.Children);
}
Writer.WriteLine(";")
.WriteLine()
.WriteLineDefaultDirective()
.WriteLineHiddenDirective()
.SetIndent(currentIndent);
}
public void RenderRuntimeExpressionBlockChunk(ExpressionBlockChunk chunk)
{
// For expression chunks, such as @value, @(value) etc, pick the first Code or Markup span
// from the expression (in this case "value") and use that to calculate the length. This works
// accurately for most parts. The scenarios that don't work are
// (a) Expressions with inline comments (e.g. @(a @* comment *@ b)) - these have multiple code spans
// (b) Expressions with inline templates (e.g. @Foo(@<p>Hello world</p>)).
// Tracked via https://github.com/aspnet/Razor/issues/153
var block = (Block)chunk.Association;
var contentSpan = block.Children
.OfType<Span>()
.FirstOrDefault(s => s.Kind == SpanKind.Code || s.Kind == SpanKind.Markup);
if (Context.ExpressionRenderingMode == ExpressionRenderingMode.InjectCode)
{
Accept(chunk.Children);
}
else if (Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput)
{
if (contentSpan != null)
{
RenderRuntimeExpressionBlockChunkWithContentSpan(chunk, contentSpan);
}
else
{
if (!string.IsNullOrEmpty(Context.TargetWriterName))
{
Writer
.WriteStartMethodInvocation(WriteToMethodName)
.Write(Context.TargetWriterName)
.WriteParameterSeparator();
}
else
{
Writer.WriteStartMethodInvocation(WriteMethodName);
}
Accept(chunk.Children);
Writer.WriteEndMethodInvocation()
.WriteLine();
}
}
}
private void RenderRuntimeExpressionBlockChunkWithContentSpan(ExpressionBlockChunk chunk, Span contentSpan)
{
var generateInstrumentation = ShouldGenerateInstrumentationForExpressions();
if (generateInstrumentation)
{
Writer.WriteStartInstrumentationContext(Context, contentSpan, isLiteral: false);
}
using (var mappingWriter = new CSharpLineMappingWriter(Writer, chunk.Start, Context.SourceFile))
{
if (!string.IsNullOrEmpty(Context.TargetWriterName))
{
var generatedStart =
WriteToMethodName.Length +
Context.TargetWriterName.Length +
3; // 1 for the opening '(' and 2 for ', '
var padding = _paddingBuilder.BuildExpressionPadding(contentSpan, generatedStart);
Writer
.Write(padding)
.WriteStartMethodInvocation(WriteToMethodName)
.Write(Context.TargetWriterName)
.WriteParameterSeparator();
}
else
{
var generatedStart =
WriteMethodName.Length +
1; // for the opening '('
var padding = _paddingBuilder.BuildExpressionPadding(contentSpan, generatedStart);
Writer
.Write(padding)
.WriteStartMethodInvocation(WriteMethodName);
}
Accept(chunk.Children);
Writer.WriteEndMethodInvocation();
}
if (generateInstrumentation)
{
Writer.WriteEndInstrumentationContext(Context);
}
}
public void CreateStatementCodeMapping(string code, Chunk chunk)
{
CreateCodeMapping(_paddingBuilder.BuildStatementPadding((Span)chunk.Association), code, chunk);
}
public void CreateExpressionCodeMapping(string code, Chunk chunk)
{
CreateCodeMapping(_paddingBuilder.BuildExpressionPadding((Span)chunk.Association), code, chunk);
}
public void CreateCodeMapping(string padding, string code, Chunk chunk)
{
using (CSharpLineMappingWriter mappingWriter = Writer.BuildLineMapping(chunk.Start, code.Length, Context.SourceFile))
{
Writer.Write(padding);
mappingWriter.MarkLineMappingStart();
Writer.Write(code);
mappingWriter.MarkLineMappingEnd();
}
}
// Raw CodeMapping's do not write out line pragmas, they just map code.
public void CreateRawCodeMapping(string code, SourceLocation documentLocation)
{
using (new CSharpLineMappingWriter(Writer, documentLocation, code.Length))
{
Writer.Write(code);
}
}
private bool ShouldGenerateInstrumentationForExpressions()
{
// Only generate instrumentation for expression blocks if instrumentation is enabled and we're generating a
// "Write(<expression>)" statement.
return Context.Host.EnableInstrumentation &&
Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput;
}
private void RenderStartWriteLiteral(string text)
{
var charactersRendered = 0;
// Render the string in pieces to avoid Roslyn OOM exceptions at compile time:
// https://github.com/aspnet/External/issues/54
while (charactersRendered < text.Length)
{
if (Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput)
{
if (!string.IsNullOrEmpty(Context.TargetWriterName))
{
Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteLiteralToMethodName)
.Write(Context.TargetWriterName)
.WriteParameterSeparator();
}
else
{
Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteLiteralMethodName);
}
}
string textToRender;
if (text.Length <= MaxStringLiteralLength)
{
textToRender = text;
}
else
{
var charactersToSubstring = Math.Min(MaxStringLiteralLength, text.Length - charactersRendered);
textToRender = text.Substring(charactersRendered, charactersToSubstring);
}
Writer.WriteStringLiteral(textToRender);
charactersRendered += textToRender.Length;
if (Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput)
{
Writer.WriteEndMethodInvocation();
}
}
}
}
}

View File

@ -1,124 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Diagnostics;
using System.Globalization;
using Microsoft.AspNetCore.Razor.Chunks;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.CodeGenerators.Visitors
{
public class CSharpDesignTimeCodeVisitor : CodeVisitor<CSharpCodeWriter>
{
private const string InheritsHelper = "__inheritsHelper";
private const string DesignTimeHelperMethodName = "__RazorDesignTimeHelpers__";
private const string TagHelperDirectiveSyntaxHelper = "__tagHelperDirectiveSyntaxHelper";
private const int DisableVariableNamingWarnings = 219;
private bool _initializedTagHelperDirectiveSyntaxHelper;
public CSharpDesignTimeCodeVisitor(
CSharpCodeVisitor csharpCodeVisitor,
CSharpCodeWriter writer,
CodeGeneratorContext context)
: base(writer, context)
{
if (csharpCodeVisitor == null)
{
throw new ArgumentNullException(nameof(csharpCodeVisitor));
}
CSharpCodeVisitor = csharpCodeVisitor;
}
public CSharpCodeVisitor CSharpCodeVisitor { get; }
public void AcceptTree(ChunkTree tree)
{
if (Context.Host.DesignTimeMode)
{
using (Writer.BuildMethodDeclaration("private", "void", "@" + DesignTimeHelperMethodName))
{
using (Writer.BuildDisableWarningScope(DisableVariableNamingWarnings))
{
AcceptTreeCore(tree);
}
}
}
}
protected virtual void AcceptTreeCore(ChunkTree tree)
{
Accept(tree.Children);
}
protected override void Visit(SetBaseTypeChunk chunk)
{
Debug.Assert(Context.Host.DesignTimeMode);
if (chunk.Start != SourceLocation.Undefined)
{
using (var lineMappingWriter =
Writer.BuildLineMapping(chunk.Start, chunk.TypeName.Length, Context.SourceFile))
{
Writer.Indent(chunk.Start.CharacterIndex);
lineMappingWriter.MarkLineMappingStart();
Writer.Write(chunk.TypeName);
lineMappingWriter.MarkLineMappingEnd();
Writer.Write(" ").Write(InheritsHelper).Write(" = null;");
}
}
}
protected override void Visit(TagHelperPrefixDirectiveChunk chunk)
{
VisitTagHelperDirectiveChunk(chunk);
}
protected override void Visit(AddTagHelperChunk chunk)
{
VisitTagHelperDirectiveChunk(chunk);
}
protected override void Visit(RemoveTagHelperChunk chunk)
{
VisitTagHelperDirectiveChunk(chunk);
}
private void VisitTagHelperDirectiveChunk(Chunk chunk)
{
// We should always be in design time mode because of the calling AcceptTree method verification.
Debug.Assert(Context.Host.DesignTimeMode);
if (!_initializedTagHelperDirectiveSyntaxHelper)
{
_initializedTagHelperDirectiveSyntaxHelper = true;
Writer.WriteVariableDeclaration("string", TagHelperDirectiveSyntaxHelper, "null");
}
var text = ((Span)chunk.Association).Content.Trim();
Writer.WriteStartAssignment(TagHelperDirectiveSyntaxHelper);
if (!text.StartsWith("\"", StringComparison.Ordinal))
{
Writer.Write("\"");
}
using (new CSharpLineMappingWriter(Writer, chunk.Start, text.Length))
{
Writer.Write(text);
}
if (!text.EndsWith("\"", StringComparison.Ordinal))
{
Writer.Write("\"");
}
Writer.WriteLine(";");
}
}
}

View File

@ -1,167 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Chunks;
using Microsoft.AspNetCore.Razor.Parser;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.CodeGenerators.Visitors
{
/// <summary>
/// <see cref="CodeVisitor{CSharpCodeWriter}"/> that writes code for a non-<see langword="string"/> tag helper
/// bound attribute value.
/// </summary>
/// <remarks>
/// Since attribute value is not written out as HTML, does not emit instrumentation. Further this
/// <see cref="CodeVisitor{CSharpCodeWriter}"/> writes identical code at design- and runtime.
/// </remarks>
public class CSharpTagHelperAttributeValueVisitor : CodeVisitor<CSharpCodeWriter>
{
private string _attributeTypeName;
private bool _firstChild;
/// <summary>
/// Initializes a new instance of the <see cref="CSharpTagHelperAttributeValueVisitor"/> class.
/// </summary>
/// <param name="writer">The <see cref="CSharpCodeWriter"/> used to write code.</param>
/// <param name="context">
/// A <see cref="CodeGeneratorContext"/> instance that contains information about the current code generation
/// process.
/// </param>
/// <param name="attributeTypeName">
/// Full name of the property <see cref="System.Type"/> for which this
/// <see cref="CSharpTagHelperAttributeValueVisitor"/> is writing the value.
/// </param>
public CSharpTagHelperAttributeValueVisitor(
CSharpCodeWriter writer,
CodeGeneratorContext context,
string attributeTypeName)
: base(writer, context)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
_attributeTypeName = attributeTypeName;
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="ParentChunk"/> to render.</param>
/// <remarks>
/// Tracks code mappings for all children while writing.
/// </remarks>
protected override void Visit(ParentChunk chunk)
{
// Line mappings are captured in RenderCode(), not this method.
_firstChild = true;
Accept(chunk.Children);
if (_firstChild)
{
// Attribute value was empty.
Context.ErrorSink.OnError(
chunk.Association.Start,
RazorResources.TagHelpers_AttributeExpressionRequired,
chunk.Association.Length);
}
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="ExpressionBlockChunk"/> to render.</param>
protected override void Visit(ExpressionBlockChunk chunk)
{
Accept(chunk.Children);
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="ExpressionChunk"/> to render.</param>
protected override void Visit(ExpressionChunk chunk)
{
RenderCode(chunk.Code, chunk.Start);
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="LiteralChunk"/> to render.</param>
protected override void Visit(LiteralChunk chunk)
{
RenderCode(chunk.Text, chunk.Start);
}
protected override void Visit(ParentLiteralChunk chunk)
{
RenderCode(chunk.GetText(), chunk.Start);
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="SectionChunk"/> to render.</param>
/// <remarks>
/// Unconditionally adds a <see cref="RazorError"/> to inform user of unexpected <c>@section</c> directive.
/// </remarks>
protected override void Visit(SectionChunk chunk)
{
Context.ErrorSink.OnError(
chunk.Association.Start,
RazorResources.FormatTagHelpers_Directives_NotSupported_InAttributes(
SyntaxConstants.CSharp.SectionKeyword),
chunk.Association.Length);
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="StatementChunk"/> to render.</param>
/// <remarks>
/// Unconditionally adds a <see cref="RazorError"/> to inform user of unexpected code block.
/// </remarks>
protected override void Visit(StatementChunk chunk)
{
Context.ErrorSink.OnError(
chunk.Association.Start,
RazorResources.TagHelpers_CodeBlocks_NotSupported_InAttributes,
chunk.Association.Length);
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="TemplateChunk"/> to render.</param>
/// <remarks>
/// Unconditionally adds a <see cref="RazorError"/> to inform user of unexpected template e.g.
/// <c>@&lt;p&gt;paragraph@&lt;/p&gt;</c>.
/// </remarks>
protected override void Visit(TemplateChunk chunk)
{
Context.ErrorSink.OnError(
chunk.Association.Start,
RazorResources.FormatTagHelpers_InlineMarkupBlocks_NotSupported_InAttributes(_attributeTypeName),
chunk.Association.Length);
}
// Tracks the code mapping and writes code for a leaf node in the attribute value Chunk tree.
private void RenderCode(string code, SourceLocation start)
{
_firstChild = false;
using (new CSharpLineMappingWriter(Writer, start, code.Length))
{
Writer.Write(code);
}
}
}
}

View File

@ -1,315 +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 System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Razor.Chunks;
using Microsoft.AspNetCore.Razor.Compilation.TagHelpers;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Razor.CodeGenerators.Visitors
{
public class CSharpTagHelperFieldDeclarationVisitor : CodeVisitor<CSharpCodeWriter>
{
private const string _preAllocatedAttributeVariablePrefix = "__tagHelperAttribute_";
private readonly HashSet<string> _declaredTagHelpers;
private readonly Dictionary<TagHelperAttributeKey, string> _preAllocatedAttributes;
private readonly GeneratedTagHelperContext _tagHelperContext;
private bool _foundTagHelpers;
public CSharpTagHelperFieldDeclarationVisitor(
CSharpCodeWriter writer,
CodeGeneratorContext context)
: base(writer, context)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
_declaredTagHelpers = new HashSet<string>(StringComparer.Ordinal);
_tagHelperContext = Context.Host.GeneratedClassContext.GeneratedTagHelperContext;
_preAllocatedAttributes = new Dictionary<TagHelperAttributeKey, string>();
}
protected override void Visit(TagHelperChunk chunk)
{
// We only want to setup tag helper manager fields if there are tag helpers, and only once
if (!_foundTagHelpers)
{
_foundTagHelpers = true;
// We want to hide declared TagHelper fields so they cannot be stepped over via a debugger.
Writer.WriteLineHiddenDirective();
// Runtime fields aren't useful during design time.
if (!Context.Host.DesignTimeMode)
{
// Need to disable the warning "X is assigned to but never used." for the value buffer since
// whether it's used depends on how a TagHelper is used.
Writer.WritePragma("warning disable 0414");
Writer
.Write("private ")
.WriteVariableDeclaration(
"string",
CSharpTagHelperCodeRenderer.StringValueBufferVariableName,
value: null);
Writer.WritePragma("warning restore 0414");
WritePrivateField(
_tagHelperContext.ExecutionContextTypeName,
CSharpTagHelperCodeRenderer.ExecutionContextVariableName,
value: null);
WritePrivateField(
_tagHelperContext.RunnerTypeName,
CSharpTagHelperCodeRenderer.RunnerVariableName,
value: null);
WritePrivateField(
_tagHelperContext.ScopeManagerTypeName,
CSharpTagHelperCodeRenderer.ScopeManagerVariableName,
value: null);
}
}
foreach (var descriptor in chunk.Descriptors)
{
if (!_declaredTagHelpers.Contains(descriptor.TypeName))
{
_declaredTagHelpers.Add(descriptor.TypeName);
WritePrivateField(
descriptor.TypeName,
CSharpTagHelperCodeRenderer.GetVariableName(descriptor),
value: null);
}
}
if (!Context.Host.DesignTimeMode)
{
PreAllocateTagHelperAttributes(chunk);
}
// We need to dive deeper to ensure we pick up any nested tag helpers.
Accept(chunk.Children);
}
private void PreAllocateTagHelperAttributes(TagHelperChunk chunk)
{
var boundAttributes = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
for (var i = 0; i < chunk.Attributes.Count; i++)
{
var attribute = chunk.Attributes[i];
var associatedAttributeDescriptors = chunk.Descriptors.SelectMany(descriptor => descriptor.Attributes)
.Where(attributeDescriptor => attributeDescriptor.IsNameMatch(attribute.Name));
// If there's no descriptors associated or is a repeated attribute with same name as a bound attribute,
// it is considered as an unbound attribute.
var isUnBoundAttribute = !associatedAttributeDescriptors.Any() || !boundAttributes.Add(attribute.Name);
// Perf: We will preallocate TagHelperAttribute for unbound attributes and simple bound string valued attributes.
if (isUnBoundAttribute || CanPreallocateBoundAttribute(associatedAttributeDescriptors, attribute))
{
string preAllocatedAttributeVariableName = null;
if (attribute.ValueStyle == HtmlAttributeValueStyle.Minimized)
{
Debug.Assert(attribute.Value == null);
var preAllocatedAttributeKey = new TagHelperAttributeKey(
attribute.Name,
value: null,
unBoundAttribute: isUnBoundAttribute,
valueStyle: attribute.ValueStyle);
if (TryCachePreallocatedVariableName(preAllocatedAttributeKey, out preAllocatedAttributeVariableName))
{
Writer
.Write("private static readonly global::")
.Write(_tagHelperContext.TagHelperAttributeTypeName)
.Write(" ")
.Write(preAllocatedAttributeVariableName)
.Write(" = ")
.WriteStartNewObject("global::" + _tagHelperContext.TagHelperAttributeTypeName)
.WriteStringLiteral(attribute.Name)
.WriteEndMethodInvocation();
}
}
else
{
Debug.Assert(attribute.Value != null);
string plainText;
if (CSharpTagHelperCodeRenderer.TryGetPlainTextValue(attribute.Value, out plainText))
{
var preAllocatedAttributeKey = new TagHelperAttributeKey(attribute.Name, plainText, isUnBoundAttribute, attribute.ValueStyle);
if (TryCachePreallocatedVariableName(preAllocatedAttributeKey, out preAllocatedAttributeVariableName))
{
Writer
.Write("private static readonly global::")
.Write(_tagHelperContext.TagHelperAttributeTypeName)
.Write(" ")
.Write(preAllocatedAttributeVariableName)
.Write(" = ")
.WriteStartNewObject("global::" + _tagHelperContext.TagHelperAttributeTypeName)
.WriteStringLiteral(attribute.Name)
.WriteParameterSeparator();
if (isUnBoundAttribute)
{
// For unbound attributes, we need to create HtmlString.
Writer
.WriteStartNewObject("global::" + _tagHelperContext.EncodedHtmlStringTypeName)
.WriteStringLiteral(plainText)
.WriteEndMethodInvocation(endLine: false);
}
else
{
Writer.WriteStringLiteral(plainText);
}
Writer
.WriteParameterSeparator()
.Write($"global::{typeof(HtmlAttributeValueStyle).FullName}.{attribute.ValueStyle}")
.WriteEndMethodInvocation();
}
}
}
if (preAllocatedAttributeVariableName != null)
{
chunk.Attributes[i] = new TagHelperAttributeTracker(
attribute.Name,
new PreallocatedTagHelperAttributeChunk
{
AttributeVariableAccessor = preAllocatedAttributeVariableName
},
attribute.ValueStyle);
}
}
}
}
private static bool CanPreallocateBoundAttribute(
IEnumerable<TagHelperAttributeDescriptor> associatedAttributeDescriptors,
TagHelperAttributeTracker attribute)
{
// If the attribute value is a Dynamic value, it cannot be preallocated.
if (CSharpTagHelperCodeRenderer.IsDynamicAttributeValue(attribute.Value))
{
return false;
}
// Only attributes that are associated with string typed properties can be preallocated.
var attributeName = attribute.Name;
var allStringProperties = associatedAttributeDescriptors
.All(attributeDescriptor => attributeDescriptor.IsStringProperty);
return allStringProperties;
}
public override void Accept(Chunk chunk)
{
if (chunk == null)
{
throw new ArgumentNullException(nameof(chunk));
}
var parentChunk = chunk as ParentChunk;
// If we're any ParentChunk other than TagHelperChunk then we want to dive into its Children
// to search for more TagHelperChunk chunks. This if-statement enables us to not override
// each of the special ParentChunk types and then dive into their children.
if (parentChunk != null && !(parentChunk is TagHelperChunk))
{
Accept(parentChunk.Children);
}
else
{
// If we're a TagHelperChunk or any other non ParentChunk we ".Accept" it. This ensures
// that our overridden Visit(TagHelperChunk) method gets called and is not skipped over.
// If we're a non ParentChunk or a TagHelperChunk then we want to just invoke the Visit
// method for that given chunk (base.Accept indirectly calls the Visit method).
base.Accept(chunk);
}
}
private bool TryCachePreallocatedVariableName(TagHelperAttributeKey key, out string preAllocatedAttributeVariableName)
{
if (!_preAllocatedAttributes.TryGetValue(key, out preAllocatedAttributeVariableName))
{
preAllocatedAttributeVariableName = _preAllocatedAttributeVariablePrefix + _preAllocatedAttributes.Count;
_preAllocatedAttributes[key] = preAllocatedAttributeVariableName;
return true;
}
return false;
}
private void WritePrivateField(string type, string name, string value)
{
Writer
.Write("private global::")
.WriteVariableDeclaration(type, name, value);
}
private struct TagHelperAttributeKey : IEquatable<TagHelperAttributeKey>
{
public TagHelperAttributeKey(string name, string value, bool unBoundAttribute, HtmlAttributeValueStyle valueStyle)
{
Name = name;
Value = value;
UnBoundAttribute = unBoundAttribute;
ValueStyle = valueStyle;
}
public string Name { get; }
public string Value { get; }
public bool UnBoundAttribute { get; }
public HtmlAttributeValueStyle ValueStyle { get; }
public override int GetHashCode()
{
var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(Name, StringComparer.Ordinal);
hashCodeCombiner.Add(Value, StringComparer.Ordinal);
hashCodeCombiner.Add(UnBoundAttribute);
hashCodeCombiner.Add(ValueStyle);
return hashCodeCombiner.CombinedHash;
}
public override bool Equals(object obj)
{
var other = obj as TagHelperAttributeKey?;
if (other != null)
{
return Equals(other.Value);
}
return false;
}
public bool Equals(TagHelperAttributeKey other)
{
return string.Equals(Name, other.Name, StringComparison.Ordinal) &&
string.Equals(Value, other.Value, StringComparison.Ordinal) &&
UnBoundAttribute == other.UnBoundAttribute &&
ValueStyle == other.ValueStyle;
}
}
}
}

View File

@ -1,93 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Chunks;
namespace Microsoft.AspNetCore.Razor.CodeGenerators.Visitors
{
/// <summary>
/// The <see cref="CodeVisitor{T}"/> that generates the code to initialize the TagHelperRunner.
/// </summary>
public class CSharpTagHelperPropertyInitializationVisitor : CodeVisitor<CSharpCodeWriter>
{
private readonly GeneratedTagHelperContext _tagHelperContext;
private bool _foundTagHelpers;
/// <summary>
/// Creates a new instance of <see cref="CSharpTagHelperPropertyInitializationVisitor"/>.
/// </summary>
/// <param name="writer">The <see cref="CSharpCodeWriter"/> used to generate code.</param>
/// <param name="context">The <see cref="CodeGeneratorContext"/>.</param>
public CSharpTagHelperPropertyInitializationVisitor(
CSharpCodeWriter writer,
CodeGeneratorContext context)
: base(writer, context)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
_tagHelperContext = Context.Host.GeneratedClassContext.GeneratedTagHelperContext;
}
/// <inheritdoc />
public override void Accept(Chunk chunk)
{
if (chunk == null)
{
throw new ArgumentNullException(nameof(chunk));
}
// If at any ParentChunk other than a TagHelperChunk, then dive into its Children to search for more
// TagHelperChunk nodes. This method avoids overriding each of the ParentChunk-specific Visit() methods to
// dive into Children.
var parentChunk = chunk as ParentChunk;
if (parentChunk != null && !(parentChunk is TagHelperChunk))
{
Accept(parentChunk.Children);
}
else
{
// If at a TagHelperChunk or any non-ParentChunk, "Accept()" it. This ensures the Visit(TagHelperChunk)
// method below is called.
base.Accept(chunk);
}
}
/// <summary>
/// Writes the TagHelperRunner initialization code to the Writer.
/// </summary>
/// <param name="chunk">The <see cref="TagHelperChunk"/>.</param>
protected override void Visit(TagHelperChunk chunk)
{
if (!_foundTagHelpers && !Context.Host.DesignTimeMode)
{
_foundTagHelpers = true;
Writer
.WriteStartAssignment(CSharpTagHelperCodeRenderer.RunnerVariableName)
.Write(CSharpTagHelperCodeRenderer.RunnerVariableName)
.Write(" ?? ")
.WriteStartNewObject("global::" + _tagHelperContext.RunnerTypeName)
.WriteEndMethodInvocation();
Writer
.WriteStartAssignment(CSharpTagHelperCodeRenderer.ScopeManagerVariableName)
.Write(CSharpTagHelperCodeRenderer.ScopeManagerVariableName)
.Write(" ?? ")
.WriteStartNewObject("global::" + _tagHelperContext.ScopeManagerTypeName)
.Write(_tagHelperContext.StartTagHelperWritingScopeMethodName)
.WriteParameterSeparator()
.Write(_tagHelperContext.EndTagHelperWritingScopeMethodName)
.WriteEndMethodInvocation();
}
}
}
}

View File

@ -1,44 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Chunks;
namespace Microsoft.AspNetCore.Razor.CodeGenerators.Visitors
{
public class CSharpTypeMemberVisitor : CodeVisitor<CSharpCodeWriter>
{
private CSharpCodeVisitor _csharpCodeVisitor;
public CSharpTypeMemberVisitor(CSharpCodeVisitor csharpCodeVisitor,
CSharpCodeWriter writer,
CodeGeneratorContext context)
: base(writer, context)
{
if (csharpCodeVisitor == null)
{
throw new ArgumentNullException(nameof(csharpCodeVisitor));
}
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
_csharpCodeVisitor = csharpCodeVisitor;
}
protected override void Visit(TypeMemberChunk chunk)
{
if (!string.IsNullOrEmpty(chunk.Code))
{
_csharpCodeVisitor.CreateCodeMapping(string.Empty, chunk.Code, chunk);
}
}
}
}

View File

@ -1,86 +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 System.Linq;
using Microsoft.AspNetCore.Razor.Chunks;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNetCore.Razor.CodeGenerators.Visitors
{
public class CSharpUsingVisitor : CodeVisitor<CSharpCodeWriter>
{
public CSharpUsingVisitor(CSharpCodeWriter writer, CodeGeneratorContext context)
: base(writer, context)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
ImportedUsings = new HashSet<string>(StringComparer.Ordinal);
}
public HashSet<string> ImportedUsings { get; set; }
/// <inheritdoc />
public override void Accept(Chunk chunk)
{
if (chunk == null)
{
throw new ArgumentNullException(nameof(chunk));
}
// If at any ParentChunk other than a TagHelperChunk, then dive into its Children to search for more
// TagHelperChunk or UsingChunk nodes. This method avoids overriding each of the ParentChunk-specific
// Visit() methods to dive into Children.
var parentChunk = chunk as ParentChunk;
if (parentChunk != null && !(parentChunk is TagHelperChunk))
{
Accept(parentChunk.Children);
}
else
{
// If at a TagHelperChunk or any non-ParentChunk (e.g. UsingChunk), "Accept()" it. This ensures the
// Visit(UsingChunk) and Visit(TagHelperChunk) methods below are called.
base.Accept(chunk);
}
}
protected override void Visit(UsingChunk chunk)
{
var documentContent = ((Span)chunk.Association).Content.Trim();
var mapSemicolon = false;
if (documentContent.LastOrDefault() == ';')
{
mapSemicolon = true;
}
ImportedUsings.Add(chunk.Namespace);
// Depending on if the user has a semicolon in their @using statement we have to conditionally decide
// to include the semicolon in the line mapping.
using (Writer.BuildLineMapping(chunk.Start, documentContent.Length, Context.SourceFile))
{
Writer.WriteUsing(chunk.Namespace, endLine: false);
if (mapSemicolon)
{
Writer.Write(";");
}
}
if (!mapSemicolon)
{
Writer.WriteLine(";");
}
}
}
}

View File

@ -1,145 +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.AspNetCore.Razor.Chunks;
namespace Microsoft.AspNetCore.Razor.CodeGenerators.Visitors
{
public abstract class ChunkVisitor<TWriter> : IChunkVisitor
where TWriter : CodeWriter
{
public ChunkVisitor(TWriter writer, CodeGeneratorContext context)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
Writer = writer;
Context = context;
}
protected TWriter Writer { get; private set; }
protected CodeGeneratorContext Context { get; private set; }
public void Accept(IList<Chunk> chunks)
{
if (chunks == null)
{
throw new ArgumentNullException(nameof(chunks));
}
foreach (Chunk chunk in chunks)
{
Accept(chunk);
}
}
public virtual void Accept(Chunk chunk)
{
if (chunk == null)
{
throw new ArgumentNullException(nameof(chunk));
}
if (chunk is LiteralChunk)
{
Visit((LiteralChunk)chunk);
}
else if (chunk is ParentLiteralChunk)
{
Visit((ParentLiteralChunk)chunk);
}
else if (chunk is ExpressionBlockChunk)
{
Visit((ExpressionBlockChunk)chunk);
}
else if (chunk is ExpressionChunk)
{
Visit((ExpressionChunk)chunk);
}
else if (chunk is StatementChunk)
{
Visit((StatementChunk)chunk);
}
else if (chunk is TagHelperChunk)
{
Visit((TagHelperChunk)chunk);
}
else if (chunk is TagHelperPrefixDirectiveChunk)
{
Visit((TagHelperPrefixDirectiveChunk)chunk);
}
else if (chunk is AddTagHelperChunk)
{
Visit((AddTagHelperChunk)chunk);
}
else if (chunk is RemoveTagHelperChunk)
{
Visit((RemoveTagHelperChunk)chunk);
}
else if (chunk is TypeMemberChunk)
{
Visit((TypeMemberChunk)chunk);
}
else if (chunk is UsingChunk)
{
Visit((UsingChunk)chunk);
}
else if (chunk is SetBaseTypeChunk)
{
Visit((SetBaseTypeChunk)chunk);
}
else if (chunk is DynamicCodeAttributeChunk)
{
Visit((DynamicCodeAttributeChunk)chunk);
}
else if (chunk is LiteralCodeAttributeChunk)
{
Visit((LiteralCodeAttributeChunk)chunk);
}
else if (chunk is CodeAttributeChunk)
{
Visit((CodeAttributeChunk)chunk);
}
else if (chunk is SectionChunk)
{
Visit((SectionChunk)chunk);
}
else if (chunk is TemplateChunk)
{
Visit((TemplateChunk)chunk);
}
else if (chunk is ParentChunk)
{
Visit((ParentChunk)chunk);
}
}
protected abstract void Visit(LiteralChunk chunk);
protected abstract void Visit(ParentLiteralChunk chunk);
protected abstract void Visit(ExpressionChunk chunk);
protected abstract void Visit(StatementChunk chunk);
protected abstract void Visit(TagHelperChunk chunk);
protected abstract void Visit(TagHelperPrefixDirectiveChunk chunk);
protected abstract void Visit(AddTagHelperChunk chunk);
protected abstract void Visit(RemoveTagHelperChunk chunk);
protected abstract void Visit(UsingChunk chunk);
protected abstract void Visit(ParentChunk chunk);
protected abstract void Visit(DynamicCodeAttributeChunk chunk);
protected abstract void Visit(LiteralCodeAttributeChunk chunk);
protected abstract void Visit(CodeAttributeChunk chunk);
protected abstract void Visit(SectionChunk chunk);
protected abstract void Visit(TypeMemberChunk chunk);
protected abstract void Visit(SetBaseTypeChunk chunk);
protected abstract void Visit(TemplateChunk chunk);
protected abstract void Visit(ExpressionBlockChunk chunk);
}
}

View File

@ -1,81 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Razor.Chunks;
namespace Microsoft.AspNetCore.Razor.CodeGenerators.Visitors
{
public class CodeVisitor<TWriter> : ChunkVisitor<TWriter>
where TWriter : CodeWriter
{
public CodeVisitor(TWriter writer, CodeGeneratorContext context)
: base(writer, context)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
}
protected override void Visit(LiteralChunk chunk)
{
}
protected override void Visit(ParentLiteralChunk chunk)
{
}
protected override void Visit(ExpressionBlockChunk chunk)
{
}
protected override void Visit(ExpressionChunk chunk)
{
}
protected override void Visit(StatementChunk chunk)
{
}
protected override void Visit(UsingChunk chunk)
{
}
protected override void Visit(ParentChunk chunk)
{
}
protected override void Visit(DynamicCodeAttributeChunk chunk)
{
}
protected override void Visit(TagHelperChunk chunk)
{
}
protected override void Visit(TagHelperPrefixDirectiveChunk chunk)
{
}
protected override void Visit(AddTagHelperChunk chunk)
{
}
protected override void Visit(RemoveTagHelperChunk chunk)
{
}
protected override void Visit(LiteralCodeAttributeChunk chunk)
{
}
protected override void Visit(CodeAttributeChunk chunk)
{
}
protected override void Visit(SectionChunk chunk)
{
}
protected override void Visit(TypeMemberChunk chunk)
{
}
protected override void Visit(SetBaseTypeChunk chunk)
{
}
protected override void Visit(TemplateChunk chunk)
{
}
}
}

View File

@ -1,14 +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.Collections.Generic;
using Microsoft.AspNetCore.Razor.Chunks;
namespace Microsoft.AspNetCore.Razor.CodeGenerators.Visitors
{
public interface IChunkVisitor
{
void Accept(IList<Chunk> chunks);
void Accept(Chunk chunk);
}
}

View File

@ -1,144 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Argument_Cannot_Be_Null_Or_Empty" xml:space="preserve">
<value>Value cannot be null or an empty string.</value>
</data>
<data name="Argument_Must_Be_Between" xml:space="preserve">
<value>Value must be between {0} and {1}.</value>
</data>
<data name="Argument_Must_Be_Enum_Member" xml:space="preserve">
<value>Value must be a value from the "{0}" enumeration.</value>
</data>
<data name="Argument_Must_Be_GreaterThan" xml:space="preserve">
<value>Value must be greater than {0}.</value>
</data>
<data name="Argument_Must_Be_GreaterThanOrEqualTo" xml:space="preserve">
<value>Value must be greater than or equal to {0}.</value>
</data>
<data name="Argument_Must_Be_LessThan" xml:space="preserve">
<value>Value must be less than {0}.</value>
</data>
<data name="Argument_Must_Be_LessThanOrEqualTo" xml:space="preserve">
<value>Value must be less than or equal to {0}.</value>
</data>
<data name="Argument_Must_Be_Null_Or_Non_Empty" xml:space="preserve">
<value>Value cannot be an empty string. It must either be null or a non-empty string.</value>
</data>
</root>

View File

@ -1,23 +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.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Compilation.TagHelpers
{
/// <summary>
/// Contract used to resolve <see cref="TagHelperDescriptor"/>s.
/// </summary>
public interface ITagHelperDescriptorResolver
{
/// <summary>
/// Resolves <see cref="TagHelperDescriptor"/>s based on the given <paramref name="resolutionContext"/>.
/// </summary>
/// <param name="resolutionContext">
/// <see cref="TagHelperDescriptorResolutionContext"/> used to resolve descriptors for the Razor page.
/// </param>
/// <returns>An <see cref="IEnumerable{TagHelperDescriptor}"/> of <see cref="TagHelperDescriptor"/>s based
/// on the given <paramref name="resolutionContext"/>.</returns>
IEnumerable<TagHelperDescriptor> Resolve(TagHelperDescriptorResolutionContext resolutionContext);
}
}

View File

@ -1,156 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Reflection;
namespace Microsoft.AspNetCore.Razor.Compilation.TagHelpers
{
/// <summary>
/// A metadata class describing a tag helper attribute.
/// </summary>
public class TagHelperAttributeDescriptor
{
private string _typeName;
private string _name;
private string _propertyName;
/// <summary>
/// Instantiates a new instance of the <see cref="TagHelperAttributeDescriptor"/> class.
/// </summary>
public TagHelperAttributeDescriptor()
{
}
// Internal for testing i.e. for easy TagHelperAttributeDescriptor creation when PropertyInfo is available.
internal TagHelperAttributeDescriptor(string name, PropertyInfo propertyInfo)
{
Name = name;
PropertyName = propertyInfo.Name;
TypeName = propertyInfo.PropertyType.FullName;
IsEnum = propertyInfo.PropertyType.GetTypeInfo().IsEnum;
}
/// <summary>
/// Gets an indication whether this <see cref="TagHelperAttributeDescriptor"/> is used for dictionary indexer
/// assignments.
/// </summary>
/// <value>
/// If <c>true</c> this <see cref="TagHelperAttributeDescriptor"/> should be associated with all HTML
/// attributes that have names starting with <see cref="Name"/>. Otherwise this
/// <see cref="TagHelperAttributeDescriptor"/> is used for property assignment and is only associated with an
/// HTML attribute that has the exact <see cref="Name"/>.
/// </value>
/// <remarks>
/// HTML attribute names are matched case-insensitively, regardless of <see cref="IsIndexer"/>.
/// </remarks>
public bool IsIndexer { get; set; }
/// <summary>
/// Gets or sets an indication whether this property is an <see cref="Enum"/>.
/// </summary>
public bool IsEnum { get; set; }
/// <summary>
/// Gets or sets an indication whether this property is of type <see cref="string"/> or, if
/// <see cref="IsIndexer"/> is <c>true</c>, whether the indexer's value is of type <see cref="string"/>.
/// </summary>
/// <value>
/// If <c>true</c> the <see cref="TypeName"/> is for <see cref="string"/>. This causes the Razor parser
/// to allow empty values for HTML attributes matching this <see cref="TagHelperAttributeDescriptor"/>. If
/// <c>false</c> empty values for such matching attributes lead to errors.
/// </value>
public bool IsStringProperty { get; set; }
/// <summary>
/// The HTML attribute name or, if <see cref="IsIndexer"/> is <c>true</c>, the prefix for matching attribute
/// names.
/// </summary>
public string Name
{
get
{
return _name;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_name = value;
}
}
/// <summary>
/// The name of the CLR property that corresponds to the HTML attribute.
/// </summary>
public string PropertyName
{
get
{
return _propertyName;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_propertyName = value;
}
}
/// <summary>
/// The full name of the named (see <see name="PropertyName"/>) property's <see cref="Type"/> or, if
/// <see cref="IsIndexer"/> is <c>true</c>, the full name of the indexer's value <see cref="Type"/>.
/// </summary>
public string TypeName
{
get
{
return _typeName;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_typeName = value;
IsStringProperty = string.Equals(TypeName, typeof(string).FullName, StringComparison.Ordinal);
}
}
/// <summary>
/// The <see cref="TagHelperAttributeDesignTimeDescriptor"/> that contains design time information about
/// this attribute.
/// </summary>
public TagHelperAttributeDesignTimeDescriptor DesignTimeDescriptor { get; set; }
/// <summary>
/// Determines whether HTML attribute <paramref name="name"/> matches this
/// <see cref="TagHelperAttributeDescriptor"/>.
/// </summary>
/// <param name="name">Name of the HTML attribute to check.</param>
/// <returns>
/// <c>true</c> if this <see cref="TagHelperAttributeDescriptor"/> matches <paramref name="name"/>.
/// <c>false</c> otherwise.
/// </returns>
public bool IsNameMatch(string name)
{
if (IsIndexer)
{
return name.StartsWith(Name, StringComparison.OrdinalIgnoreCase);
}
else
{
return string.Equals(name, Name, StringComparison.OrdinalIgnoreCase);
}
}
}
}

View File

@ -1,21 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Razor.Compilation.TagHelpers
{
/// <summary>
/// A metadata class containing information about tag helper use.
/// </summary>
public class TagHelperAttributeDesignTimeDescriptor
{
/// <summary>
/// A summary of how to use a tag helper.
/// </summary>
public string Summary { get; set; }
/// <summary>
/// Remarks about how to use a tag helper.
/// </summary>
public string Remarks { get; set; }
}
}

View File

@ -1,252 +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 System.Linq;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Microsoft.AspNetCore.Razor.Compilation.TagHelpers
{
/// <summary>
/// A metadata class describing a tag helper.
/// </summary>
public class TagHelperDescriptor
{
private string _prefix = string.Empty;
private string _tagName;
private string _typeName;
private string _assemblyName;
private IDictionary<string, string> _propertyBag;
private IEnumerable<TagHelperAttributeDescriptor> _attributes =
Enumerable.Empty<TagHelperAttributeDescriptor>();
private IEnumerable<TagHelperRequiredAttributeDescriptor> _requiredAttributes =
Enumerable.Empty<TagHelperRequiredAttributeDescriptor>();
/// <summary>
/// Creates a new <see cref="TagHelperDescriptor"/>.
/// </summary>
public TagHelperDescriptor()
{
}
/// <summary>
/// Creates a shallow copy of the given <see cref="TagHelperDescriptor"/>.
/// </summary>
/// <param name="descriptor">The <see cref="TagHelperDescriptor"/> to copy.</param>
public TagHelperDescriptor (TagHelperDescriptor descriptor)
{
Prefix = descriptor.Prefix;
TagName = descriptor.TagName;
TypeName = descriptor.TypeName;
AssemblyName = descriptor.AssemblyName;
Attributes = descriptor.Attributes;
RequiredAttributes = descriptor.RequiredAttributes;
AllowedChildren = descriptor.AllowedChildren;
RequiredParent = descriptor.RequiredParent;
TagStructure = descriptor.TagStructure;
DesignTimeDescriptor = descriptor.DesignTimeDescriptor;
foreach (var property in descriptor.PropertyBag)
{
PropertyBag.Add(property.Key, property.Value);
}
}
/// <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
{
return _prefix;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_prefix = value;
}
}
/// <summary>
/// The tag name that the tag helper should target.
/// </summary>
public string TagName
{
get
{
return _tagName;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_tagName = value;
}
}
/// <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
{
return Prefix + TagName;
}
}
/// <summary>
/// The full name of the tag helper class.
/// </summary>
public string TypeName
{
get
{
return _typeName;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_typeName = value;
}
}
/// <summary>
/// The name of the assembly containing the tag helper class.
/// </summary>
public string AssemblyName
{
get
{
return _assemblyName;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_assemblyName = value;
}
}
/// <summary>
/// The list of attributes the tag helper expects.
/// </summary>
public IEnumerable<TagHelperAttributeDescriptor> Attributes
{
get
{
return _attributes;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_attributes = value;
}
}
/// <summary>
/// The list of required attribute names the tag helper expects to target an element.
/// </summary>
/// <remarks>
/// <c>*</c> at the end of an attribute name acts as a prefix match.
/// </remarks>
public IEnumerable<TagHelperRequiredAttributeDescriptor> RequiredAttributes
{
get
{
return _requiredAttributes;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_requiredAttributes = value;
}
}
/// <summary>
/// Get the names of elements allowed as children.
/// </summary>
/// <remarks><c>null</c> indicates all children are allowed.</remarks>
public IEnumerable<string> AllowedChildren { get; set; }
/// <summary>
/// Get the name of the HTML element required as the immediate parent.
/// </summary>
/// <remarks><c>null</c> indicates no restriction on parent tag.</remarks>
public string RequiredParent { get; set; }
/// <summary>
/// The expected tag structure.
/// </summary>
/// <remarks>
/// If <see cref="TagStructure.Unspecified"/> and no other tag helpers applying to the same element specify
/// their <see cref="TagStructure"/> the <see cref="TagStructure.NormalOrSelfClosing"/> behavior is used:
/// <para>
/// <code>
/// &lt;my-tag-helper&gt;&lt;/my-tag-helper&gt;
/// &lt;!-- OR --&gt;
/// &lt;my-tag-helper /&gt;
/// </code>
/// Otherwise, if another tag helper applying to the same element does specify their behavior, that behavior
/// is used.
/// </para>
/// <para>
/// If <see cref="TagStructure.WithoutEndTag"/> HTML elements can be written in the following formats:
/// <code>
/// &lt;my-tag-helper&gt;
/// &lt;!-- OR --&gt;
/// &lt;my-tag-helper /&gt;
/// </code>
/// </para>
/// </remarks>
public TagStructure TagStructure { get; set; }
/// <summary>
/// The <see cref="TagHelperDesignTimeDescriptor"/> that contains design time information about this
/// tag helper.
/// </summary>
public TagHelperDesignTimeDescriptor DesignTimeDescriptor { get; set; }
/// <summary>
/// A dictionary containing additional information about the <see cref="TagHelperDescriptor"/>.
/// </summary>
public IDictionary<string, string> PropertyBag
{
get
{
if (_propertyBag == null)
{
_propertyBag = new Dictionary<string, string>();
}
return _propertyBag;
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More