diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DefaultDocumentWriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DefaultDocumentWriter.cs index 9c45ba163c..7c934ea0bf 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DefaultDocumentWriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DefaultDocumentWriter.cs @@ -188,6 +188,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration Context.BasicWriter.WriteCSharpStatement(Context, node); } + public override void VisitHtml(HtmlContentIRNode node) + { + Context.BasicWriter.WriteHtmlContent(Context, node); + } + public override void VisitDeclareTagHelperFields(DeclareTagHelperFieldsIRNode node) { Context.TagHelperWriter.WriteDeclareTagHelperFields(Context, node); diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeBasicWriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeBasicWriter.cs index 26c0d1686e..67b10a2f8c 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeBasicWriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeBasicWriter.cs @@ -129,7 +129,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration public override void WriteHtmlContent(CSharpRenderingContext context, HtmlContentIRNode node) { - throw new NotImplementedException(); + // Do nothing } } } diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RedirectedBasicWriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RedirectedRuntimeBasicWriter.cs similarity index 57% rename from src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RedirectedBasicWriter.cs rename to src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RedirectedRuntimeBasicWriter.cs index 5e53f51232..71106e9054 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RedirectedBasicWriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RedirectedRuntimeBasicWriter.cs @@ -6,27 +6,21 @@ using Microsoft.AspNetCore.Razor.Evolution.Intermediate; namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration { - internal class RedirectedBasicWriter : BasicWriter + internal class RedirectedRuntimeBasicWriter : RuntimeBasicWriter { - private readonly BasicWriter _previous; private readonly string _textWriter; - public RedirectedBasicWriter(BasicWriter previous, string textWriter) + public RedirectedRuntimeBasicWriter(string textWriter) { - _previous = previous; _textWriter = textWriter; } - public string WriteCSharpExpressionMethod { get; set; } = "WriteTo"; + public new string WriteCSharpExpressionMethod { get; set; } = "WriteTo"; + + public new string WriteHtmlContentMethod { get; set; } = "WriteLiteralTo"; public override void WriteCSharpExpression(CSharpRenderingContext context, CSharpExpressionIRNode node) { - if (context.Options.DesignTimeMode) - { - _previous.WriteCSharpExpression(context, node); - return; - } - IDisposable linePragmaScope = null; if (node.Source != null) { @@ -58,19 +52,35 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration linePragmaScope?.Dispose(); } - public override void WriteCSharpStatement(CSharpRenderingContext context, CSharpStatementIRNode node) - { - _previous.WriteCSharpStatement(context, node); - } - - public override void WriteHtmlAttribute(CSharpRenderingContext context, HtmlAttributeIRNode node) - { - _previous.WriteHtmlAttribute(context, node); - } - public override void WriteHtmlContent(CSharpRenderingContext context, HtmlContentIRNode node) { - _previous.WriteHtmlContent(context, node); + const int MaxStringLiteralLength = 1024; + + var charactersConsumed = 0; + + // Render the string in pieces to avoid Roslyn OOM exceptions at compile time: https://github.com/aspnet/External/issues/54 + while (charactersConsumed < node.Content.Length) + { + string textToRender; + if (node.Content.Length <= MaxStringLiteralLength) + { + textToRender = node.Content; + } + else + { + var charactersToSubstring = Math.Min(MaxStringLiteralLength, node.Content.Length - charactersConsumed); + textToRender = node.Content.Substring(charactersConsumed, charactersToSubstring); + } + + context.Writer + .WriteStartMethodInvocation(WriteHtmlContentMethod) + .Write(_textWriter) + .WriteParameterSeparator() + .WriteStringLiteral(textToRender) + .WriteEndMethodInvocation(); + + charactersConsumed += textToRender.Length; + } } } } diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RedirectedRuntimeTagHelperWriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RedirectedRuntimeTagHelperWriter.cs new file mode 100644 index 0000000000..183fb59c58 --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RedirectedRuntimeTagHelperWriter.cs @@ -0,0 +1,59 @@ +// 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; + +namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration +{ + internal class RedirectedRuntimeTagHelperWriter : RuntimeTagHelperWriter + { + private readonly string _textWriter; + + public RedirectedRuntimeTagHelperWriter(string textWriter) + { + _textWriter = textWriter; + } + + public new string WriteTagHelperOutputMethod { get; set; } = "WriteTo"; + + public override void WriteExecuteTagHelpers(CSharpRenderingContext context, ExecuteTagHelpersIRNode node) + { + context.Writer + .Write("await ") + .WriteStartInstanceMethodInvocation( + RunnerVariableName, + RunnerRunAsyncMethodName) + .Write(ExecutionContextVariableName) + .WriteEndMethodInvocation(); + + var tagHelperOutputAccessor = $"{ExecutionContextVariableName}.{ExecutionContextOutputPropertyName}"; + + context.Writer + .Write("if (!") + .Write(tagHelperOutputAccessor) + .Write(".") + .Write(TagHelperOutputIsContentModifiedPropertyName) + .WriteLine(")"); + + using (context.Writer.BuildScope()) + { + context.Writer + .Write("await ") + .WriteInstanceMethodInvocation( + ExecutionContextVariableName, + ExecutionContextSetOutputContentAsyncMethodName); + } + + context.Writer + .WriteStartMethodInvocation(WriteTagHelperOutputMethod) + .Write(_textWriter) + .WriteParameterSeparator() + .Write(tagHelperOutputAccessor) + .WriteEndMethodInvocation() + .WriteStartAssignment(ExecutionContextVariableName) + .WriteInstanceMethodInvocation( + ScopeManagerVariableName, + ScopeManagerEndMethodName); + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RedirectedTagHelperWriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RedirectedTagHelperWriter.cs deleted file mode 100644 index 4502b221fb..0000000000 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RedirectedTagHelperWriter.cs +++ /dev/null @@ -1,108 +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.Evolution.Intermediate; - -namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration -{ - internal class RedirectedTagHelperWriter : TagHelperWriter - { - private readonly TagHelperWriter _previous; - private readonly string _textWriter; - - public RedirectedTagHelperWriter(TagHelperWriter previous, string textWriter) - { - _previous = previous; - _textWriter = textWriter; - } - - public string ExecutionContextVariableName { get; set; } = "__tagHelperExecutionContext"; - - public string ExecutionContextOutputPropertyName { get; set; } = "Output"; - - public string ExecutionContextSetOutputContentAsyncMethodName { get; set; } = "SetOutputContentAsync"; - - public string RunnerVariableName { get; set; } = "__tagHelperRunner"; - - public string RunnerRunAsyncMethodName { get; set; } = "RunAsync"; - - public string ScopeManagerVariableName { get; set; } = "__tagHelperScopeManager"; - - public string ScopeManagerEndMethodName { get; set; } = "End"; - - public string TagHelperOutputIsContentModifiedPropertyName { get; set; } = "IsContentModified"; - - public string WriteTagHelperOutputMethod { get; set; } = "WriteTo"; - - public override void WriteAddTagHelperHtmlAttribute(CSharpRenderingContext context, AddTagHelperHtmlAttributeIRNode node) - { - _previous.WriteAddTagHelperHtmlAttribute(context, node); - } - - public override void WriteCreateTagHelper(CSharpRenderingContext context, CreateTagHelperIRNode node) - { - _previous.WriteCreateTagHelper(context, node); - } - - public override void WriteDeclareTagHelperFields(CSharpRenderingContext context, DeclareTagHelperFieldsIRNode node) - { - _previous.WriteDeclareTagHelperFields(context, node); - } - - public override void WriteExecuteTagHelpers(CSharpRenderingContext context, ExecuteTagHelpersIRNode node) - { - if (context.Options.DesignTimeMode) - { - _previous.WriteExecuteTagHelpers(context, node); - return; - } - - context.Writer - .Write("await ") - .WriteStartInstanceMethodInvocation( - RunnerVariableName, - RunnerRunAsyncMethodName) - .Write(ExecutionContextVariableName) - .WriteEndMethodInvocation(); - - var tagHelperOutputAccessor = $"{ExecutionContextVariableName}.{ExecutionContextOutputPropertyName}"; - - context.Writer - .Write("if (!") - .Write(tagHelperOutputAccessor) - .Write(".") - .Write(TagHelperOutputIsContentModifiedPropertyName) - .WriteLine(")"); - - using (context.Writer.BuildScope()) - { - context.Writer - .Write("await ") - .WriteInstanceMethodInvocation( - ExecutionContextVariableName, - ExecutionContextSetOutputContentAsyncMethodName); - } - - context.Writer - .WriteStartMethodInvocation(WriteTagHelperOutputMethod) - .Write(_textWriter) - .WriteParameterSeparator() - .Write(tagHelperOutputAccessor) - .WriteEndMethodInvocation() - .WriteStartAssignment(ExecutionContextVariableName) - .WriteInstanceMethodInvocation( - ScopeManagerVariableName, - ScopeManagerEndMethodName); - } - - public override void WriteInitializeTagHelperStructure(CSharpRenderingContext context, InitializeTagHelperStructureIRNode node) - { - _previous.WriteInitializeTagHelperStructure(context, node); - } - - public override void WriteSetTagHelperProperty(CSharpRenderingContext context, SetTagHelperPropertyIRNode node) - { - _previous.WriteSetTagHelperProperty(context, node); - } - } -} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeBasicWriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeBasicWriter.cs index 78c633e569..fe22b6565d 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeBasicWriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeBasicWriter.cs @@ -10,6 +10,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration { public string WriteCSharpExpressionMethod { get; set; } = "Write"; + public string WriteHtmlContentMethod { get; set; } = "WriteLiteral"; + public override void WriteCSharpExpression(CSharpRenderingContext context, CSharpExpressionIRNode node) { if (context == null) @@ -102,7 +104,31 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration public override void WriteHtmlContent(CSharpRenderingContext context, HtmlContentIRNode node) { - throw new NotImplementedException(); + const int MaxStringLiteralLength = 1024; + + var charactersConsumed = 0; + + // Render the string in pieces to avoid Roslyn OOM exceptions at compile time: https://github.com/aspnet/External/issues/54 + while (charactersConsumed < node.Content.Length) + { + string textToRender; + if (node.Content.Length <= MaxStringLiteralLength) + { + textToRender = node.Content; + } + else + { + var charactersToSubstring = Math.Min(MaxStringLiteralLength, node.Content.Length - charactersConsumed); + textToRender = node.Content.Substring(charactersConsumed, charactersToSubstring); + } + + context.Writer + .WriteStartMethodInvocation(WriteHtmlContentMethod) + .WriteStringLiteral(textToRender) + .WriteEndMethodInvocation(); + + charactersConsumed += textToRender.Length; + } } } } diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs index 4c388e6585..93e1661e4a 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs @@ -34,6 +34,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration public override void VisitHtml(HtmlContentIRNode node) { + // We can't remove this yet, because it's still used recursively in a few places. const int MaxStringLiteralLength = 1024; var charactersConsumed = 0; diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/TemplateTargetExtension.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/TemplateTargetExtension.cs index 1d7ef2b60a..dc8a54d49f 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/TemplateTargetExtension.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/TemplateTargetExtension.cs @@ -1,6 +1,7 @@ // 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 @@ -23,15 +24,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration var initialRenderingConventions = context.RenderingConventions; context.RenderingConventions = new CSharpRedirectRenderingConventions(TemplateWriterName, context.Writer); - using (context.Push(new RedirectedBasicWriter(context.BasicWriter, TemplateWriterName))) - using (context.Push(new RedirectedTagHelperWriter(context.TagHelperWriter, TemplateWriterName))) + IDisposable basicWriterScope = null; + IDisposable tagHelperWriterScope = null; + if (!context.Options.DesignTimeMode) { - using (context.Writer.BuildAsyncLambda(endLine: false, parameterNames: TemplateWriterName)) - { - context.RenderChildren(node); - } + basicWriterScope = context.Push(new RedirectedRuntimeBasicWriter(TemplateWriterName)); + tagHelperWriterScope = context.Push(new RedirectedRuntimeTagHelperWriter(TemplateWriterName)); } + using (context.Writer.BuildAsyncLambda(endLine: false, parameterNames: TemplateWriterName)) + { + context.RenderChildren(node); + } + + basicWriterScope?.Dispose(); + tagHelperWriterScope?.Dispose(); + context.RenderingConventions = initialRenderingConventions; context.Writer.WriteEndMethodInvocation(endLine: false); diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RedirectedBasicWriterTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RedirectedRuntimeBasicWriterTest.cs similarity index 81% rename from test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RedirectedBasicWriterTest.cs rename to test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RedirectedRuntimeBasicWriterTest.cs index f2f63a21b2..cf075f269e 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RedirectedBasicWriterTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RedirectedRuntimeBasicWriterTest.cs @@ -10,49 +10,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration { public class RedirectedBasicWriterTest { - // In design time this will not include the 'text writer' parameter. - [Fact] - public void WriteCSharpExpression_DesignTime_DoesNormalWrite() - { - // Arrange - var writer = new RedirectedBasicWriter(new DesignTimeBasicWriter(), "test_writer") - { - WriteCSharpExpressionMethod = "Test", - }; - - var context = new CSharpRenderingContext() - { - Options = RazorParserOptions.CreateDefaultOptions(), - Writer = new Legacy.CSharpCodeWriter(), - }; - - context.Options.DesignTimeMode = true; - - var node = new CSharpExpressionIRNode(); - var builder = RazorIRBuilder.Create(node); - builder.Add(new RazorIRToken() - { - Content = "i++", - Kind = RazorIRToken.TokenKind.CSharp, - }); - - // Act - writer.WriteCSharpExpression(context, node); - - // Assert - var csharp = context.Writer.Builder.ToString(); - Assert.Equal( -@"__o = i++; -", - csharp, - ignoreLineEndingDifferences: true); - } - [Fact] public void WriteCSharpExpression_Runtime_SkipsLinePragma_WithoutSource() { // Arrange - var writer = new RedirectedBasicWriter(new DesignTimeBasicWriter(), "test_writer") + var writer = new RedirectedRuntimeBasicWriter("test_writer") { WriteCSharpExpressionMethod = "Test", }; @@ -87,7 +49,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration public void WriteCSharpExpression_Runtime_WritesLinePragma_WithSource() { // Arrange - var writer = new RedirectedBasicWriter(new DesignTimeBasicWriter(), "test_writer") + var writer = new RedirectedRuntimeBasicWriter("test_writer") { WriteCSharpExpressionMethod = "Test", }; @@ -129,7 +91,7 @@ Test(test_writer, i++); public void WriteCSharpExpression_Runtime_WithExtensionNode_WritesPadding() { // Arrange - var writer = new RedirectedBasicWriter(new DesignTimeBasicWriter(), "test_writer") + var writer = new RedirectedRuntimeBasicWriter("test_writer") { WriteCSharpExpressionMethod = "Test", }; @@ -172,7 +134,7 @@ Test(test_writer, i++); public void WriteCSharpExpression_Runtime_WithSource_WritesPadding() { // Arrange - var writer = new RedirectedBasicWriter(new DesignTimeBasicWriter(), "test_writer") + var writer = new RedirectedRuntimeBasicWriter("test_writer") { WriteCSharpExpressionMethod = "Test", }; @@ -220,6 +182,61 @@ Test(test_writer, i++); ignoreLineEndingDifferences: true); } + [Fact] + public void WriteHtmlContent_RendersContentCorrectly() + { + var writer = new RedirectedRuntimeBasicWriter("test_writer"); + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + Options = RazorParserOptions.CreateDefaultOptions(), + }; + + var node = new HtmlContentIRNode() + { + Content = "SomeContent" + }; + + // Act + writer.WriteHtmlContent(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Equal( +@"WriteLiteralTo(test_writer, ""SomeContent""); +", + csharp, + ignoreLineEndingDifferences: true); + } + + [Fact] + public void WriteHtmlContent_LargeStringLiteral_UsesMultipleWrites() + { + var writer = new RedirectedRuntimeBasicWriter("test_writer"); + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + Options = RazorParserOptions.CreateDefaultOptions(), + }; + + var node = new HtmlContentIRNode() + { + Content = new string('*', 2000) + }; + + // Act + writer.WriteHtmlContent(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Equal(string.Format( +@"WriteLiteralTo(test_writer, @""{0}""); +WriteLiteralTo(test_writer, @""{1}""); +", new string('*', 1024), new string('*', 976)), + csharp, + ignoreLineEndingDifferences: true); + } + private class MyExtensionIRNode : ExtensionIRNode { public override IList Children => throw new NotImplementedException(); diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RedirectedTagHelperWriterTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RedirectedRuntimeTagHelperWriterTest.cs similarity index 59% rename from test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RedirectedTagHelperWriterTest.cs rename to test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RedirectedRuntimeTagHelperWriterTest.cs index 7836c87550..2d33d2b1bf 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RedirectedTagHelperWriterTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RedirectedRuntimeTagHelperWriterTest.cs @@ -10,39 +10,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration { public class RedirectedTagHelperWriterTest { - // In design time this will not include the 'text writer' parameter. - [Fact] - public void WriteExecuteTagHelpers_DesignTime_DoesNormalWrite() - { - // Arrange - var writer = new RedirectedTagHelperWriter(new DesignTimeTagHelperWriter(), "test_writer") - { - WriteTagHelperOutputMethod = "Test", - }; - - var context = new CSharpRenderingContext() - { - Options = RazorParserOptions.CreateDefaultOptions(), - Writer = new Legacy.CSharpCodeWriter(), - }; - - context.Options.DesignTimeMode = true; - - var node = new ExecuteTagHelpersIRNode(); - - // Act - writer.WriteExecuteTagHelpers(context, node); - - // Assert - var csharp = context.Writer.Builder.ToString(); - Assert.Empty(csharp); - } - [Fact] public void WriteExecuteTagHelpers_Runtime_RendersWithRedirectWriter() { // Arrange - var writer = new RedirectedTagHelperWriter(new RuntimeTagHelperWriter(), "test_writer") + var writer = new RedirectedRuntimeTagHelperWriter("test_writer") { WriteTagHelperOutputMethod = "Test", }; diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RuntimeBasicWriterTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RuntimeBasicWriterTest.cs index a04b36cbe6..140bd55d05 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RuntimeBasicWriterTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RuntimeBasicWriterTest.cs @@ -316,6 +316,61 @@ if (true) { } ignoreLineEndingDifferences: true); } + [Fact] + public void WriteHtmlContent_RendersContentCorrectly() + { + var writer = new RuntimeBasicWriter(); + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + Options = RazorParserOptions.CreateDefaultOptions(), + }; + + var node = new HtmlContentIRNode() + { + Content = "SomeContent" + }; + + // Act + writer.WriteHtmlContent(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Equal( +@"WriteLiteral(""SomeContent""); +", + csharp, + ignoreLineEndingDifferences: true); + } + + [Fact] + public void WriteHtmlContent_LargeStringLiteral_UsesMultipleWrites() + { + var writer = new RuntimeBasicWriter(); + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + Options = RazorParserOptions.CreateDefaultOptions(), + }; + + var node = new HtmlContentIRNode() + { + Content = new string('*', 2000) + }; + + // Act + writer.WriteHtmlContent(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Equal(string.Format( +@"WriteLiteral(@""{0}""); +WriteLiteral(@""{1}""); +", new string('*', 1024), new string('*', 976)), + csharp, + ignoreLineEndingDifferences: true); + } + private class MyExtensionIRNode : ExtensionIRNode { public override IList Children => throw new NotImplementedException(); diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/TemplateTargetExtensionTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/TemplateTargetExtensionTest.cs index c2999234c1..42569bfc56 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/TemplateTargetExtensionTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/TemplateTargetExtensionTest.cs @@ -25,6 +25,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration BasicWriter = new RuntimeBasicWriter(), TagHelperWriter = new RuntimeTagHelperWriter(), Writer = new CSharpCodeWriter(), + Options = RazorParserOptions.CreateDefaultOptions() }; context.RenderChildren = (n) =>