diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DefaultDocumentWriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DefaultDocumentWriter.cs index defe0adcb5..7818775b04 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DefaultDocumentWriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DefaultDocumentWriter.cs @@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration _context.RenderNode = visitor.Visit; _context.BasicWriter = _context.Options.DesignTimeMode ? (BasicWriter)new DesignTimeBasicWriter() : new RuntimeBasicWriter(); - _context.TagHelperWriter = new DefaultTagHelperWriter(); + _context.TagHelperWriter = _context.Options.DesignTimeMode ? (TagHelperWriter)new DesignTimeTagHelperWriter() : new RuntimeTagHelperWriter(); visitor.VisitDocument(node); _context.RenderChildren = null; @@ -188,6 +188,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration Context.BasicWriter.WriteCSharpStatement(Context, node); } + public override void VisitDeclareTagHelperFields(DeclareTagHelperFieldsIRNode node) + { + Context.TagHelperWriter.WriteDeclareTagHelperFields(Context, node); + } + public override void VisitDefault(RazorIRNode node) { // This is a temporary bridge to the renderer, which allows us to move functionality piecemeal diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeBasicWriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeBasicWriter.cs index 589fc4e97d..26c0d1686e 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeBasicWriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeBasicWriter.cs @@ -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 System.Linq; using Microsoft.AspNetCore.Razor.Evolution.Intermediate; namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeCSharpRenderer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeCSharpRenderer.cs index 11732bd9bd..2ca27afdc9 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeCSharpRenderer.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeCSharpRenderer.cs @@ -316,20 +316,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration } } - public override void VisitDeclareTagHelperFields(DeclareTagHelperFieldsIRNode node) - { - foreach (var tagHelperTypeName in node.UsedTagHelperTypeNames) - { - var tagHelperVariableName = GetTagHelperVariableName(tagHelperTypeName); - Context.Writer - .Write("private global::") - .WriteVariableDeclaration( - tagHelperTypeName, - tagHelperVariableName, - value: null); - } - } - private void RenderTagHelperAttributeInline( RazorIRNode node, SourceSpan documentLocation) diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DefaultTagHelperWriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeTagHelperWriter.cs similarity index 62% rename from src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DefaultTagHelperWriter.cs rename to src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeTagHelperWriter.cs index 17bc154e40..7323bf8bc2 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DefaultTagHelperWriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeTagHelperWriter.cs @@ -6,8 +6,22 @@ using Microsoft.AspNetCore.Razor.Evolution.Intermediate; namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration { - public class DefaultTagHelperWriter : TagHelperWriter + public class DesignTimeTagHelperWriter : TagHelperWriter { + public override void WriteDeclareTagHelperFields(CSharpRenderingContext context, DeclareTagHelperFieldsIRNode node) + { + foreach (var tagHelperTypeName in node.UsedTagHelperTypeNames) + { + var tagHelperVariableName = GetTagHelperVariableName(tagHelperTypeName); + context.Writer + .Write("private global::") + .WriteVariableDeclaration( + tagHelperTypeName, + tagHelperVariableName, + value: null); + } + } + public override void WriteAddTagHelperHtmlAttribute(CSharpRenderingContext context, AddTagHelperHtmlAttributeIRNode node) { throw new NotImplementedException(); @@ -32,5 +46,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration { throw new NotImplementedException(); } + + private static string GetTagHelperVariableName(string tagHelperTypeName) => "__" + tagHelperTypeName.Replace('.', '_'); } } diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeBasicWriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeBasicWriter.cs index 3204e88c79..78c633e569 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeBasicWriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeBasicWriter.cs @@ -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 System.Linq; using Microsoft.AspNetCore.Razor.Evolution.Intermediate; namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs index 1f4420f534..cfb824b042 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs @@ -601,85 +601,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration .WriteEndMethodInvocation(); } - public override void VisitDeclareTagHelperFields(DeclareTagHelperFieldsIRNode node) - { - Context.Writer.WriteLineHiddenDirective(); - - // 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. - Context.Writer - .WritePragma("warning disable 0414") - .Write("private ") - .WriteVariableDeclaration("string", "__tagHelperStringValueBuffer" /* ORIGINAL: StringValueBufferVariableName */, value: null) - .WritePragma("warning restore 0414"); - - Context.Writer - .Write("private global::") - .WriteVariableDeclaration( - "Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext" /* ORIGINAL: ExecutionContextTypeName */, - "__tagHelperExecutionContext" /* ORIGINAL: ExecutionContextVariableName */, - value: null); - - Context.Writer - .Write("private global::") - .Write("Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner" /* ORIGINAL: RunnerTypeName */) - .Write(" ") - .Write("__tagHelperRunner" /* ORIGINAL: RunnerVariableName */) - .Write(" = new global::") - .Write("Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner" /* ORIGINAL: RunnerTypeName */) - .WriteLine("();"); - - const string backedScopeManageVariableName = "__backed" + "__tagHelperScopeManager" /* ORIGINAL: ScopeManagerVariableName */; - Context.Writer - .Write("private global::") - .WriteVariableDeclaration( - "Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager", - backedScopeManageVariableName, - value: null); - - Context.Writer - .Write("private global::") - .Write("Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager" /* ORIGINAL: ScopeManagerTypeName */) - .Write(" ") - .WriteLine("__tagHelperScopeManager" /* ORIGINAL: ScopeManagerVariableName */); - - using (Context.Writer.BuildScope()) - { - Context.Writer.WriteLine("get"); - using (Context.Writer.BuildScope()) - { - Context.Writer - .Write("if (") - .Write(backedScopeManageVariableName) - .WriteLine(" == null)"); - - using (Context.Writer.BuildScope()) - { - Context.Writer - .WriteStartAssignment(backedScopeManageVariableName) - .WriteStartNewObject("Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager" /* ORIGINAL: ScopeManagerTypeName */) - .Write("StartTagHelperWritingScope" /* ORIGINAL: StartTagHelperWritingScopeMethodName */) - .WriteParameterSeparator() - .Write("EndTagHelperWritingScope" /* ORIGINAL: EndTagHelperWritingScopeMethodName */) - .WriteEndMethodInvocation(); - } - - Context.Writer.WriteReturn(backedScopeManageVariableName); - } - } - - foreach (var tagHelperTypeName in node.UsedTagHelperTypeNames) - { - var tagHelperVariableName = GetTagHelperVariableName(tagHelperTypeName); - Context.Writer - .Write("private global::") - .WriteVariableDeclaration( - tagHelperTypeName, - tagHelperVariableName, - value: null); - } - } - private void RenderTagHelperAttributeInline( RazorIRNode node, SourceSpan documentLocation) diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeTagHelperWriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeTagHelperWriter.cs new file mode 100644 index 0000000000..502377777a --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeTagHelperWriter.cs @@ -0,0 +1,135 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Razor.Evolution.Intermediate; + +namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration +{ + public class RuntimeTagHelperWriter : TagHelperWriter + { + public string StringValueBufferVariableName { get; set; } = "__tagHelperStringValueBuffer"; + + public string ExecutionContextTypeName { get; set; } = "Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext"; + + public string ExecutionContextVariableName { get; set; } = "__tagHelperExecutionContext"; + + public string RunnerTypeName { get; set; } = "Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner"; + + public string RunnerVariableName { get; set; } = "__tagHelperRunner"; + + public string ScopeManagerTypeName { get; set; } = "Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager"; + + public string ScopeManagerVariableName { get; set; } = "__tagHelperScopeManager"; + + public string StartTagHelperWritingScopeMethodName { get; set; } = "StartTagHelperWritingScope"; + + public string EndTagHelperWritingScopeMethodName { get; set; } = "EndTagHelperWritingScope"; + + public override void WriteDeclareTagHelperFields(CSharpRenderingContext context, DeclareTagHelperFieldsIRNode node) + { + context.Writer.WriteLineHiddenDirective(); + + // 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. + context.Writer + .WritePragma("warning disable 0414") + .Write("private ") + .WriteVariableDeclaration("string", StringValueBufferVariableName, value: null) + .WritePragma("warning restore 0414"); + + context.Writer + .Write("private global::") + .WriteVariableDeclaration( + ExecutionContextTypeName, + ExecutionContextVariableName, + value: null); + + context.Writer + .Write("private global::") + .Write(RunnerTypeName) + .Write(" ") + .Write(RunnerVariableName) + .Write(" = new global::") + .Write(RunnerTypeName) + .WriteLine("();"); + + var backedScopeManageVariableName = "__backed" + ScopeManagerVariableName; + context.Writer + .Write("private global::") + .WriteVariableDeclaration( + ScopeManagerTypeName, + backedScopeManageVariableName, + value: null); + + context.Writer + .Write("private global::") + .Write(ScopeManagerTypeName) + .Write(" ") + .WriteLine(ScopeManagerVariableName); + + using (context.Writer.BuildScope()) + { + context.Writer.WriteLine("get"); + using (context.Writer.BuildScope()) + { + context.Writer + .Write("if (") + .Write(backedScopeManageVariableName) + .WriteLine(" == null)"); + + using (context.Writer.BuildScope()) + { + context.Writer + .WriteStartAssignment(backedScopeManageVariableName) + .WriteStartNewObject(ScopeManagerTypeName) + .Write(StartTagHelperWritingScopeMethodName) + .WriteParameterSeparator() + .Write(EndTagHelperWritingScopeMethodName) + .WriteEndMethodInvocation(); + } + + context.Writer.WriteReturn(backedScopeManageVariableName); + } + } + + foreach (var tagHelperTypeName in node.UsedTagHelperTypeNames) + { + var tagHelperVariableName = GetTagHelperVariableName(tagHelperTypeName); + context.Writer + .Write("private global::") + .WriteVariableDeclaration( + tagHelperTypeName, + tagHelperVariableName, + value: null); + } + } + + public override void WriteAddTagHelperHtmlAttribute(CSharpRenderingContext context, AddTagHelperHtmlAttributeIRNode node) + { + throw new NotImplementedException(); + } + + public override void WriteCreateTagHelper(CSharpRenderingContext context, CreateTagHelperIRNode node) + { + throw new NotImplementedException(); + } + + public override void WriteExecuteTagHelpers(CSharpRenderingContext context, ExecuteTagHelpersIRNode node) + { + throw new NotImplementedException(); + } + + public override void WriteInitializeTagHelperStructure(CSharpRenderingContext context, InitializeTagHelperStructureIRNode node) + { + throw new NotImplementedException(); + } + + public override void WriteSetTagHelperProperty(CSharpRenderingContext context, SetTagHelperPropertyIRNode node) + { + throw new NotImplementedException(); + } + + private static string GetTagHelperVariableName(string tagHelperTypeName) => "__" + tagHelperTypeName.Replace('.', '_'); + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/TagHelperWriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/TagHelperWriter.cs index efa8985193..dfaf9e7b1f 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/TagHelperWriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/TagHelperWriter.cs @@ -7,6 +7,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration { public abstract class TagHelperWriter { + public abstract void WriteDeclareTagHelperFields(CSharpRenderingContext context, DeclareTagHelperFieldsIRNode node); + public abstract void WriteInitializeTagHelperStructure(CSharpRenderingContext context, InitializeTagHelperStructureIRNode node); public abstract void WriteSetTagHelperProperty(CSharpRenderingContext context, SetTagHelperPropertyIRNode node); diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/DesignTimeTagHelperWriterTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/DesignTimeTagHelperWriterTest.cs new file mode 100644 index 0000000000..a7ae3e8803 --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/DesignTimeTagHelperWriterTest.cs @@ -0,0 +1,37 @@ +// 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.Evolution.Intermediate; +using Xunit; + +namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration +{ + public class DesignTimeTagHelperWriterTest + { + [Fact] + public void WriteDeclareTagHelperFields_DeclaresUsedTagHelperTypes() + { + // Arrange + var writer = new DesignTimeTagHelperWriter(); + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + }; + var node = new DeclareTagHelperFieldsIRNode(); + node.UsedTagHelperTypeNames.Add("PTagHelper"); + node.UsedTagHelperTypeNames.Add("MyTagHelper"); + + // Act + writer.WriteDeclareTagHelperFields(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Equal( +@"private global::PTagHelper __PTagHelper = null; +private global::MyTagHelper __MyTagHelper = null; +", + csharp, + ignoreLineEndingDifferences: true); + } + } +} diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RuntimeTagHelperWriterTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RuntimeTagHelperWriterTest.cs new file mode 100644 index 0000000000..733e4d90e2 --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RuntimeTagHelperWriterTest.cs @@ -0,0 +1,95 @@ +// 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.Evolution.Intermediate; +using Xunit; + +namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration +{ + public class RuntimeTagHelperWriterTest + { + [Fact] + public void WriteDeclareTagHelperFields_DeclaresRequiredFields() + { + // Arrange + var writer = new RuntimeTagHelperWriter(); + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + }; + var node = new DeclareTagHelperFieldsIRNode(); + + // Act + writer.WriteDeclareTagHelperFields(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Equal( +@"#line hidden +#pragma warning disable 0414 +private string __tagHelperStringValueBuffer = null; +#pragma warning restore 0414 +private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext __tagHelperExecutionContext = null; +private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner __tagHelperRunner = new global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner(); +private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager __backed__tagHelperScopeManager = null; +private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager __tagHelperScopeManager +{ + get + { + if (__backed__tagHelperScopeManager == null) + { + __backed__tagHelperScopeManager = new Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager(StartTagHelperWritingScope, EndTagHelperWritingScope); + } + return __backed__tagHelperScopeManager; + } +} +", + csharp, + ignoreLineEndingDifferences: true); + } + + [Fact] + public void WriteDeclareTagHelperFields_DeclaresUsedTagHelperTypes() + { + // Arrange + var writer = new RuntimeTagHelperWriter(); + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + }; + var node = new DeclareTagHelperFieldsIRNode(); + node.UsedTagHelperTypeNames.Add("PTagHelper"); + node.UsedTagHelperTypeNames.Add("MyTagHelper"); + + // Act + writer.WriteDeclareTagHelperFields(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Equal( +@"#line hidden +#pragma warning disable 0414 +private string __tagHelperStringValueBuffer = null; +#pragma warning restore 0414 +private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext __tagHelperExecutionContext = null; +private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner __tagHelperRunner = new global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner(); +private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager __backed__tagHelperScopeManager = null; +private global::Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager __tagHelperScopeManager +{ + get + { + if (__backed__tagHelperScopeManager == null) + { + __backed__tagHelperScopeManager = new Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperScopeManager(StartTagHelperWritingScope, EndTagHelperWritingScope); + } + return __backed__tagHelperScopeManager; + } +} +private global::PTagHelper __PTagHelper = null; +private global::MyTagHelper __MyTagHelper = null; +", + csharp, + ignoreLineEndingDifferences: true); + } + } +}