From da1e4130d2db01f0180d61f720cd1ab5df296b87 Mon Sep 17 00:00:00 2001 From: Ajay Bhargav Baaskaran Date: Tue, 28 Mar 2017 11:30:34 -0700 Subject: [PATCH] Moved from Renderer to Writer --- .../CodeGeneration/DefaultDocumentWriter.cs | 5 + .../CodeGeneration/DesignTimeBasicWriter.cs | 51 +++++- .../DesignTimeCSharpRenderer.cs | 19 +- .../CodeGeneration/RuntimeBasicWriter.cs | 44 ++++- .../CodeGeneration/RuntimeCSharpRenderer.cs | 15 +- .../DesignTimeBasicWriterTest.cs | 171 ++++++++++++++++++ .../CodeGeneration/RuntimeBasicWriterTest.cs | 136 ++++++++++++++ 7 files changed, 431 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DefaultDocumentWriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DefaultDocumentWriter.cs index 093095ed50..defe0adcb5 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DefaultDocumentWriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DefaultDocumentWriter.cs @@ -183,6 +183,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration Context.BasicWriter.WriteCSharpExpression(Context, node); } + public override void VisitCSharpStatement(CSharpStatementIRNode node) + { + Context.BasicWriter.WriteCSharpStatement(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 8824e10b48..589fc4e97d 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeBasicWriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeBasicWriter.cs @@ -2,6 +2,7 @@ // 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 @@ -71,7 +72,55 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration public override void WriteCSharpStatement(CSharpRenderingContext context, CSharpStatementIRNode node) { - throw new NotImplementedException(); + var isWhitespaceStatement = true; + for (var i = 0; i < node.Children.Count; i++) + { + var token = node.Children[i] as RazorIRToken; + if (token == null || !string.IsNullOrWhiteSpace(token.Content)) + { + isWhitespaceStatement = false; + break; + } + } + + IDisposable linePragmaScope = null; + if (node.Source != null) + { + if (!isWhitespaceStatement) + { + linePragmaScope = context.Writer.BuildLinePragma(node.Source.Value); + } + + context.Writer.WritePadding(0, node.Source.Value, context); + } + else if (isWhitespaceStatement) + { + // Don't write whitespace if there is no line mapping for it. + return; + } + + for (var i = 0; i < node.Children.Count; i++) + { + if (node.Children[i] is RazorIRToken token && token.IsCSharp) + { + context.AddLineMappingFor(token); + context.Writer.Write(token.Content); + } + else + { + // There may be something else inside the statement like an extension node. + context.RenderNode(node.Children[i]); + } + } + + if (linePragmaScope != null) + { + linePragmaScope.Dispose(); + } + else + { + context.Writer.WriteLine(); + } } public override void WriteHtmlAttribute(CSharpRenderingContext context, HtmlAttributeIRNode node) diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeCSharpRenderer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeCSharpRenderer.cs index 5cf1779931..5645ae5aa7 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeCSharpRenderer.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/DesignTimeCSharpRenderer.cs @@ -78,13 +78,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration public override void VisitCSharpStatement(CSharpStatementIRNode node) { - var isWhitespaceToken = node.Children.All(child => - child is RazorIRToken token && string.IsNullOrWhiteSpace(token.Content)); + // We can't remove this yet, because it's still used recursively in a few places. + var isWhitespaceStatement = true; + for (var i = 0; i < node.Children.Count; i++) + { + var token = node.Children[i] as RazorIRToken; + if (token == null || !string.IsNullOrWhiteSpace(token.Content)) + { + isWhitespaceStatement = false; + break; + } + } IDisposable linePragmaScope = null; if (node.Source != null) { - if (!isWhitespaceToken) + if (!isWhitespaceStatement) { linePragmaScope = Context.Writer.BuildLinePragma(node.Source.Value); } @@ -92,7 +101,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration var padding = BuildOffsetPadding(0, node.Source.Value, Context); Context.Writer.Write(padding); } - else if (isWhitespaceToken) + else if (isWhitespaceStatement) { // Don't write whitespace if there is no line mapping for it. return; @@ -102,7 +111,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration { if (node.Children[i] is RazorIRToken token && token.IsCSharp) { - Context.AddLineMappingFor(node); + Context.AddLineMappingFor(token); Context.Writer.Write(token.Content); } else diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeBasicWriter.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeBasicWriter.cs index e4d1cda9e9..3204e88c79 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeBasicWriter.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeBasicWriter.cs @@ -2,6 +2,7 @@ // 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 @@ -51,7 +52,48 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration public override void WriteCSharpStatement(CSharpRenderingContext context, CSharpStatementIRNode node) { - throw new NotImplementedException(); + var isWhitespaceStatement = true; + for (var i = 0; i < node.Children.Count; i++) + { + var token = node.Children[i] as RazorIRToken; + if (token == null || !string.IsNullOrWhiteSpace(token.Content)) + { + isWhitespaceStatement = false; + break; + } + } + + if (isWhitespaceStatement) + { + return; + } + + IDisposable linePragmaScope = null; + if (node.Source != null) + { + linePragmaScope = context.Writer.BuildLinePragma(node.Source.Value); + context.Writer.WritePadding(0, node.Source.Value, context); + } + + for (var i = 0; i < node.Children.Count; i++) + { + if (node.Children[i] is RazorIRToken token && token.IsCSharp) + { + context.Writer.Write(token.Content); + } + else + { + // There may be something else inside the statement like an extension node. + context.RenderNode(node.Children[i]); + } + } + + if (linePragmaScope == null) + { + context.Writer.WriteLine(); + } + + linePragmaScope?.Dispose(); } public override void WriteHtmlAttribute(CSharpRenderingContext context, HtmlAttributeIRNode node) diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs index 235c3a5bfa..1f4420f534 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/CodeGeneration/RuntimeCSharpRenderer.cs @@ -210,10 +210,19 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration public override void VisitCSharpStatement(CSharpStatementIRNode node) { - var isWhitespaceToken = node.Children.All(child => - child is RazorIRToken token && string.IsNullOrWhiteSpace(token.Content)); + // We can't remove this yet, because it's still used recursively in a few places. + var isWhitespaceStatement = true; + for (var i = 0; i < node.Children.Count; i++) + { + var token = node.Children[i] as RazorIRToken; + if (token == null || !string.IsNullOrWhiteSpace(token.Content)) + { + isWhitespaceStatement = false; + break; + } + } - if (isWhitespaceToken) + if (isWhitespaceStatement) { return; } diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/DesignTimeBasicWriterTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/DesignTimeBasicWriterTest.cs index edad359711..0f9d129de9 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/DesignTimeBasicWriterTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/DesignTimeBasicWriterTest.cs @@ -161,6 +161,177 @@ __o = i++; @"#line 1 ""test.cshtml"" __o = i++; +#line default +#line hidden +", + csharp, + ignoreLineEndingDifferences: true); + } + + [Fact] + public void WriteCSharpStatement_WhitespaceContent_DoesNothing() + { + // Arrange + var writer = new DesignTimeBasicWriter(); + + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + }; + + var node = new CSharpStatementIRNode(); + RazorIRBuilder.Create(node) + .Add(new RazorIRToken() + { + Kind = RazorIRToken.TokenKind.CSharp, + Content = " \t" + }); + + // Act + writer.WriteCSharpStatement(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Empty(csharp); + } + + [Fact] + public void WriteCSharpStatement_WhitespaceContentWithSource_WritesContent() + { + // Arrange + var writer = new DesignTimeBasicWriter(); + + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + Options = RazorParserOptions.CreateDefaultOptions(), + }; + + var node = new CSharpStatementIRNode() + { + Source = new SourceSpan("test.cshtml", 0, 0, 0, 3), + }; + RazorIRBuilder.Create(node) + .Add(new RazorIRToken() + { + Kind = RazorIRToken.TokenKind.CSharp, + Content = " " + }); + + // Act + writer.WriteCSharpStatement(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Equal( +@" +", + csharp, + ignoreLineEndingDifferences: true); + } + + [Fact] + public void WriteCSharpStatement_SkipsLinePragma_WithoutSource() + { + // Arrange + var writer = new DesignTimeBasicWriter(); + + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + }; + + var node = new CSharpStatementIRNode(); + RazorIRBuilder.Create(node) + .Add(new RazorIRToken() + { + Kind = RazorIRToken.TokenKind.CSharp, + Content = "if (true) { }" + }); + + // Act + writer.WriteCSharpStatement(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Equal( +@"if (true) { } +", + csharp, + ignoreLineEndingDifferences: true); + } + + [Fact] + public void WriteCSharpStatement_WritesLinePragma_WithSource() + { + // Arrange + var writer = new DesignTimeBasicWriter(); + + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + Options = RazorParserOptions.CreateDefaultOptions(), + }; + + var node = new CSharpStatementIRNode() + { + Source = new SourceSpan("test.cshtml", 0, 0, 0, 13), + }; + RazorIRBuilder.Create(node) + .Add(new RazorIRToken() + { + Kind = RazorIRToken.TokenKind.CSharp, + Content = "if (true) { }", + }); + + // Act + writer.WriteCSharpStatement(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Equal( +@"#line 1 ""test.cshtml"" +if (true) { } + +#line default +#line hidden +", + csharp, + ignoreLineEndingDifferences: true); + } + + [Fact] + public void WriteCSharpStatement_WritesPadding_WithSource() + { + // Arrange + var writer = new DesignTimeBasicWriter(); + + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + Options = RazorParserOptions.CreateDefaultOptions(), + }; + + var node = new CSharpStatementIRNode() + { + Source = new SourceSpan("test.cshtml", 0, 0, 0, 17), + }; + RazorIRBuilder.Create(node) + .Add(new RazorIRToken() + { + Kind = RazorIRToken.TokenKind.CSharp, + Content = " if (true) { }", + }); + + // Act + writer.WriteCSharpStatement(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Equal( +@"#line 1 ""test.cshtml"" + if (true) { } + #line default #line hidden ", diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RuntimeBasicWriterTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RuntimeBasicWriterTest.cs index bf61070fcd..a04b36cbe6 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RuntimeBasicWriterTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/CodeGeneration/RuntimeBasicWriterTest.cs @@ -173,6 +173,142 @@ Test(i++); @"#line 1 ""test.cshtml"" Test(i++); +#line default +#line hidden +", + csharp, + ignoreLineEndingDifferences: true); + } + + [Fact] + public void WriteCSharpStatement_WhitespaceContent_DoesNothing() + { + // Arrange + var writer = new RuntimeBasicWriter(); + + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + }; + + var node = new CSharpStatementIRNode(); + RazorIRBuilder.Create(node) + .Add(new RazorIRToken() + { + Kind = RazorIRToken.TokenKind.CSharp, + Content = " \t" + }); + + // Act + writer.WriteCSharpStatement(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Empty(csharp); + } + + [Fact] + public void WriteCSharpStatement_SkipsLinePragma_WithoutSource() + { + // Arrange + var writer = new RuntimeBasicWriter(); + + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + }; + + var node = new CSharpStatementIRNode(); + RazorIRBuilder.Create(node) + .Add(new RazorIRToken() + { + Kind = RazorIRToken.TokenKind.CSharp, + Content = "if (true) { }" + }); + + // Act + writer.WriteCSharpStatement(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Equal( +@"if (true) { } +", + csharp, + ignoreLineEndingDifferences: true); + } + + [Fact] + public void WriteCSharpStatement_WritesLinePragma_WithSource() + { + // Arrange + var writer = new RuntimeBasicWriter(); + + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + Options = RazorParserOptions.CreateDefaultOptions(), + }; + + var node = new CSharpStatementIRNode() + { + Source = new SourceSpan("test.cshtml", 0, 0, 0, 13), + }; + RazorIRBuilder.Create(node) + .Add(new RazorIRToken() + { + Kind = RazorIRToken.TokenKind.CSharp, + Content = "if (true) { }", + }); + + // Act + writer.WriteCSharpStatement(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Equal( +@"#line 1 ""test.cshtml"" +if (true) { } + +#line default +#line hidden +", + csharp, + ignoreLineEndingDifferences: true); + } + + [Fact] + public void WriteCSharpStatement_WritesPadding_WithSource() + { + // Arrange + var writer = new RuntimeBasicWriter(); + + var context = new CSharpRenderingContext() + { + Writer = new Legacy.CSharpCodeWriter(), + Options = RazorParserOptions.CreateDefaultOptions(), + }; + + var node = new CSharpStatementIRNode() + { + Source = new SourceSpan("test.cshtml", 0, 0, 0, 17), + }; + RazorIRBuilder.Create(node) + .Add(new RazorIRToken() + { + Kind = RazorIRToken.TokenKind.CSharp, + Content = " if (true) { }", + }); + + // Act + writer.WriteCSharpStatement(context, node); + + // Assert + var csharp = context.Writer.Builder.ToString(); + Assert.Equal( +@"#line 1 ""test.cshtml"" + if (true) { } + #line default #line hidden ",