Remove old razor
This commit is contained in:
parent
8f9ff1abd9
commit
a7eb30ddca
13
Razor.sln
13
Razor.sln
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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><summary></c> documentation for the given <paramref name="id"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">The id to lookup.</param>
|
||||
/// <returns><c><summary></c> documentation for the given <paramref name="id"/>.</returns>
|
||||
public string GetSummary(string id)
|
||||
{
|
||||
var associatedMemeber = GetMember(id);
|
||||
var summaryElement = associatedMemeber?.Element("summary");
|
||||
|
||||
if (summaryElement != null)
|
||||
{
|
||||
var summaryValue = GetElementValue(summaryElement);
|
||||
|
||||
return summaryValue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the <c><remarks></c> documentation for the given <paramref name="id"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">The id to lookup.</param>
|
||||
/// <returns><c><remarks></c> documentation for the given <paramref name="id"/>.</returns>
|
||||
public string GetRemarks(string id)
|
||||
{
|
||||
var associatedMemeber = GetMember(id);
|
||||
var remarksElement = associatedMemeber?.Element("remarks");
|
||||
|
||||
if (remarksElement != null)
|
||||
{
|
||||
var remarksValue = GetElementValue(remarksElement);
|
||||
|
||||
return remarksValue;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the <see cref="string"/> identifier for the given <paramref name="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">The <see cref="Type"/> to get the identifier for.</param>
|
||||
/// <returns>The <see cref="string"/> identifier for the given <paramref name="type"/>.</returns>
|
||||
public static string GetId(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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(";");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>@<p>paragraph@</p></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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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(";");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
/// <my-tag-helper></my-tag-helper>
|
||||
/// <!-- OR -->
|
||||
/// <my-tag-helper />
|
||||
/// </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>
|
||||
/// <my-tag-helper>
|
||||
/// <!-- OR -->
|
||||
/// <my-tag-helper />
|
||||
/// </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
Loading…
Reference in New Issue