From 663a5f22fa5fc894714e1f069972b7249ff24502 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Thu, 25 Sep 2014 12:10:43 -0700 Subject: [PATCH 1/2] Updating CSharpCodeVisitor to generate implicit expressions on a single line Fixing 151 --- .../CSharp/Visitors/CSharpCodeVisitor.cs | 112 ++++++++++----- .../Generator/CSharpRazorCodeGeneratorTest.cs | 5 +- .../CodeGenerator/CS/Output/Await.cs | 45 ++----- .../CodeGenerator/CS/Output/Blocks.cs | 20 +-- .../CS/Output/ConditionalAttributes.cs | 61 ++------- .../CodeGenerator/CS/Output/DesignTime.cs | 7 +- .../CS/Output/ExplicitExpression.cs | 5 +- .../CS/Output/ExpressionsInCode.cs | 10 +- .../CodeGenerator/CS/Output/FunctionsBlock.cs | 5 +- .../CS/Output/FunctionsBlock_Tabs.cs | 5 +- .../CS/Output/Helpers.Instance.cs | 15 +-- .../CodeGenerator/CS/Output/Helpers.cs | 15 +-- .../CS/Output/HelpersMissingCloseParen.cs | 5 +- .../CS/Output/HelpersMissingOpenBrace.cs | 10 +- .../CS/Output/HelpersMissingOpenParen.cs | 10 +- .../CS/Output/ImplicitExpression.cs | 5 +- .../CodeGenerator/CS/Output/Imports.cs | 10 +- .../CS/Output/Inherits.Runtime.cs | 5 +- .../CodeGenerator/CS/Output/InlineBlocks.cs | 5 +- .../CodeGenerator/CS/Output/Instrumented.cs | 20 +-- .../CS/Output/MarkupInCodeBlock.cs | 5 +- .../CodeGenerator/CS/Output/NestedHelpers.cs | 15 +-- .../CodeGenerator/CS/Output/NoLinePragmas.cs | 20 +-- .../CS/Output/RazorComments.DesignTime.cs | 8 +- .../CodeGenerator/CS/Output/RazorComments.cs | 15 +-- .../CodeGenerator/CS/Output/ResolveUrl.cs | 87 ++---------- .../CodeGenerator/CS/Output/Templates.cs | 127 ++++++------------ 27 files changed, 195 insertions(+), 457 deletions(-) diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpCodeVisitor.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpCodeVisitor.cs index ef32591f2..009df6e26 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpCodeVisitor.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpCodeVisitor.cs @@ -149,7 +149,7 @@ protected override void Visit(ExpressionBlockChunk chunk) protected override void Visit(ExpressionChunk chunk) { - CreateExpressionCodeMapping(chunk.Code, chunk); + Writer.Write(chunk.Code); } protected override void Visit(StatementChunk chunk) @@ -343,66 +343,114 @@ public void RenderDesignTimeExpressionBlockChunk(ExpressionBlockChunk chunk) public void RenderRuntimeExpressionBlockChunk(ExpressionBlockChunk chunk) { - var generateInstrumentation = ShouldGenerateInstrumentationForExpressions(); - Span contentSpan = null; + // 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(@

Hello world

)). + // Tracked via https://github.com/aspnet/Razor/issues/153 - if (generateInstrumentation) - { - // 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(@

Hello world

)). - // Tracked via https://github.com/aspnet/Razor/issues/153 - - var block = (Block)chunk.Association; - contentSpan = block.Children - .OfType() - .FirstOrDefault(s => s.Kind == SpanKind.Code || s.Kind == SpanKind.Markup); + var block = (Block)chunk.Association; + var contentSpan = block.Children + .OfType() + .FirstOrDefault(s => s.Kind == SpanKind.Code || s.Kind == SpanKind.Markup); + var generateInstrumentation = ShouldGenerateInstrumentationForExpressions() && + contentSpan != null; + + if (Context.ExpressionRenderingMode == ExpressionRenderingMode.InjectCode) + { + Accept(chunk.Children); + } + else if (Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput) + { if (contentSpan != null) { - Writer.WriteStartInstrumentationContext(Context, contentSpan, isLiteral: false); + RenderRuntimeExpressionBlockChunkWithContentSpan(chunk, contentSpan); + } + else + { + if (!string.IsNullOrEmpty(Context.TargetWriterName)) + { + Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteToMethodName) + .Write(Context.TargetWriterName) + .WriteParameterSeparator(); + } + else + { + Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteMethodName); + } + + Accept(chunk.Children); + + Writer.WriteEndMethodInvocation() + .WriteLine(); } } + } - if (Context.ExpressionRenderingMode == ExpressionRenderingMode.InjectCode) + private void RenderRuntimeExpressionBlockChunkWithContentSpan(ExpressionBlockChunk chunk, Span contentSpan) + { + var generateInstrumentation = ShouldGenerateInstrumentationForExpressions(); + + if (generateInstrumentation) { - Accept(chunk.Children); + Writer.WriteStartInstrumentationContext(Context, contentSpan, isLiteral: false); } - else if (Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput) + + using (var mappingWriter = Writer.BuildLineMapping(chunk.Start, contentSpan.Length, Context.SourceFile)) { - if (!String.IsNullOrEmpty(Context.TargetWriterName)) + mappingWriter.MarkLineMappingStart(); + + var indent = Writer.CurrentIndent; + Writer.ResetIndent(); + + if (!string.IsNullOrEmpty(Context.TargetWriterName)) { - Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteToMethodName) - .Write(Context.TargetWriterName) - .WriteParameterSeparator(); + var generatedStart = Context.Host.GeneratedClassContext.WriteToMethodName.Length + + Context.TargetWriterName.Length + + 3; // 1 for the opening bracket and 2 for the parameter separator + + var padding = _paddingBuilder.BuildExpressionPadding(contentSpan, generatedStart); + + + Writer.Write(padding) + .WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteToMethodName) + .Write(Context.TargetWriterName) + .WriteParameterSeparator(); } else { - Writer.WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteMethodName); + var generatedStart = Context.Host.GeneratedClassContext.WriteMethodName.Length + + 1; // for the opening bracket + var padding = _paddingBuilder.BuildExpressionPadding(contentSpan, generatedStart); + + Writer.Write(padding) + .WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteMethodName); } Accept(chunk.Children); - Writer.WriteEndMethodInvocation() - .WriteLine(); + Writer.WriteEndMethodInvocation(); + Writer.SetIndent(indent); + + mappingWriter.MarkLineMappingEnd(); } - if (contentSpan != null) + if (generateInstrumentation) { Writer.WriteEndInstrumentationContext(Context); } } - public void CreateExpressionCodeMapping(string code, Chunk chunk) + public void CreateStatementCodeMapping(string code, Chunk chunk) { - CreateCodeMapping(_paddingBuilder.BuildExpressionPadding((Span)chunk.Association), code, chunk); + CreateCodeMapping(_paddingBuilder.BuildStatementPadding((Span)chunk.Association), code, chunk); } - public void CreateStatementCodeMapping(string code, Chunk chunk) + public void CreateExpressionCodeMapping(string code, Chunk chunk) { - CreateCodeMapping(_paddingBuilder.BuildStatementPadding((Span)chunk.Association), code, chunk); + CreateCodeMapping(_paddingBuilder.BuildExpressionPadding((Span)chunk.Association), code, chunk); } public void CreateCodeMapping(string padding, string code, Chunk chunk) diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpRazorCodeGeneratorTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpRazorCodeGeneratorTest.cs index f78caa5f6..61a038dc8 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpRazorCodeGeneratorTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpRazorCodeGeneratorTest.cs @@ -145,8 +145,6 @@ public void CSharpCodeGeneratorCorrectlyGeneratesMappingsForRazorCommentsAtDesig BuildLineMapping(238, 11, 899, 45, 2, 24), BuildLineMapping(310, 12, 1036, 51, 45, 3), BuildLineMapping(323, 14, 2, 1112, 56, 6, 1), - BuildLineMapping(328, 14, 1155, 58, 7, 1) - }); } @@ -296,8 +294,7 @@ public void CSharpCodeGeneratorCorrectlyGeneratesDesignTimePragmasMarkupAndExpre BuildLineMapping(113, 7, 2, 1262, 71, 6, 12), BuildLineMapping(129, 8, 1, 1343, 76, 6, 4), BuildLineMapping(142, 8, 1443, 78, 14, 3), - BuildLineMapping(153, 8, 1540, 85, 25, 1), - BuildLineMapping(204, 13, 5, 1725, 95, 6, 3) + BuildLineMapping(204, 13, 5, 1638, 90, 6, 3) }); } diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Await.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Await.cs index 7083ce8d3..d90faf9ad 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Await.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Await.cs @@ -28,27 +28,21 @@ public override async Task ExecuteAsync() "ronous Expression: "); Instrumentation.EndContext(); Instrumentation.BeginContext(192, 11, false); - Write( #line 10 "Await.cshtml" - await Foo() + Write(await Foo()); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(203, 42, true); WriteLiteral("

\r\n

Basic Asynchronous Template: "); Instrumentation.EndContext(); Instrumentation.BeginContext(247, 11, false); - Write( #line 11 "Await.cshtml" - await Foo() + Write(await Foo()); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(259, 43, true); WriteLiteral("

\r\n

Basic Asynchronous Statement: "); @@ -63,14 +57,11 @@ await Foo() WriteLiteral("

\r\n

Basic Asynchronous Statement Nested: "); Instrumentation.EndContext(); Instrumentation.BeginContext(376, 11, false); - Write( #line 13 "Await.cshtml" - await Foo() + Write(await Foo()); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(387, 5, true); WriteLiteral(" "); @@ -85,54 +76,42 @@ await Foo() WriteLiteral("

\r\n

Basic Incomplete Asynchronous Statement: "); Instrumentation.EndContext(); Instrumentation.BeginContext(448, 5, false); - Write( #line 14 "Await.cshtml" - await + Write(await); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(453, 124, true); WriteLiteral("

\r\n\r\n\r\n
\r\n

Advanced Asynchronous Expression Test

" + "\r\n

Advanced Asynchronous Expression: "); Instrumentation.EndContext(); Instrumentation.BeginContext(578, 15, false); - Write( #line 19 "Await.cshtml" - await Foo(1, 2) + Write(await Foo(1, 2)); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(593, 56, true); WriteLiteral("

\r\n

Advanced Asynchronous Expression Extended: "); Instrumentation.EndContext(); Instrumentation.BeginContext(650, 19, false); - Write( #line 20 "Await.cshtml" - await Foo.Bar(1, 2) + Write(await Foo.Bar(1, 2)); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(669, 45, true); WriteLiteral("

\r\n

Advanced Asynchronous Template: "); Instrumentation.EndContext(); Instrumentation.BeginContext(716, 22, false); - Write( #line 21 "Await.cshtml" - await Foo("bob", true) + Write(await Foo("bob", true)); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(739, 46, true); WriteLiteral("

\r\n

Advanced Asynchronous Statement: "); @@ -156,14 +135,11 @@ await Foo.Bar(1, 2) WriteLiteral("

\r\n

Advanced Asynchronous Statement Nested: "); Instrumentation.EndContext(); Instrumentation.BeginContext(966, 27, false); - Write( #line 24 "Await.cshtml" - await Foo(boolValue: false) + Write(await Foo(boolValue: false)); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(993, 5, true); WriteLiteral(" "); @@ -178,14 +154,11 @@ await Foo(boolValue: false) WriteLiteral("

\r\n

Advanced Incomplete Asynchronous Statement: "); Instrumentation.EndContext(); Instrumentation.BeginContext(1057, 19, false); - Write( #line 25 "Await.cshtml" - await ("wrrronggg") + Write(await ("wrrronggg")); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(1076, 16, true); WriteLiteral("

\r\n
"); diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Blocks.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Blocks.cs index 87276c733..cc7a48ebe 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Blocks.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Blocks.cs @@ -34,14 +34,11 @@ public override async Task ExecuteAsync() WriteLiteral("

Hello from C#, #"); Instrumentation.EndContext(); Instrumentation.BeginContext(69, 1, false); - Write( #line 6 "Blocks.cshtml" - i + Write(i); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(71, 6, true); WriteLiteral("

\r\n"); @@ -114,14 +111,11 @@ public override async Task ExecuteAsync() WriteLiteral("

Hello again from C#, #"); Instrumentation.EndContext(); Instrumentation.BeginContext(378, 1, false); - Write( #line 24 "Blocks.cshtml" - j + Write(j); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(380, 6, true); WriteLiteral("

\r\n"); @@ -154,14 +148,11 @@ public override async Task ExecuteAsync() WriteLiteral("

Oh no! An error occurred: "); Instrumentation.EndContext(); Instrumentation.BeginContext(500, 10, false); - Write( #line 30 "Blocks.cshtml" - ex.Message + Write(ex.Message); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(511, 6, true); WriteLiteral("

\r\n"); @@ -176,14 +167,11 @@ public override async Task ExecuteAsync() WriteLiteral("\r\n

i is now "); Instrumentation.EndContext(); Instrumentation.BeginContext(535, 1, false); - Write( #line 33 "Blocks.cshtml" - i + Write(i); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(536, 8, true); WriteLiteral("

\r\n\r\n"); diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ConditionalAttributes.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ConditionalAttributes.cs index e2ab2576e..77e7d58e4 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ConditionalAttributes.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ConditionalAttributes.cs @@ -26,57 +26,27 @@ public override async Task ExecuteAsync() WriteLiteral(" \r\n ( -#line 5 "ConditionalAttributes.cshtml" - cls - -#line default -#line hidden - , 82), false)); + Tuple.Create(Tuple.Create("", 82), Tuple.Create(cls, 82), false)); Instrumentation.BeginContext(87, 11, true); WriteLiteral(" />\r\n ( -#line 6 "ConditionalAttributes.cshtml" - cls - -#line default -#line hidden - , 110), false)); + Tuple.Create(Tuple.Create(" ", 109), Tuple.Create(cls, 110), false)); Instrumentation.BeginContext(115, 11, true); WriteLiteral(" />\r\n ( -#line 7 "ConditionalAttributes.cshtml" - cls - -#line default -#line hidden - , 134), false), Tuple.Create(Tuple.Create(" ", 138), Tuple.Create("foo", 139), true)); + Tuple.Create(Tuple.Create("", 134), Tuple.Create(cls, 134), false), Tuple.Create(Tuple.Create(" ", 138), Tuple.Create("foo", 139), true)); Instrumentation.BeginContext(143, 31, true); WriteLiteral(" />\r\n ( -#line 8 "ConditionalAttributes.cshtml" - ch - -#line default -#line hidden - , 184), false)); + Tuple.Create(Tuple.Create("", 184), Tuple.Create(ch, 184), false)); Instrumentation.BeginContext(188, 31, true); WriteLiteral(" />\r\n ( -#line 9 "ConditionalAttributes.cshtml" - ch - -#line default -#line hidden - , 233), false)); + Tuple.Create(Tuple.Create(" ", 232), Tuple.Create(ch, 233), false)); Instrumentation.BeginContext(237, 11, true); WriteLiteral(" />\r\n \r\n(Href("~/Products/"), 34), false), - Tuple.Create(Tuple.Create("", 45), Tuple.Create( -#line 2 "ResolveUrl.cshtml" - product.id - -#line default -#line hidden - , 45), false)); + Tuple.Create(Tuple.Create("", 45), Tuple.Create(product.id, 45), false)); Instrumentation.BeginContext(57, 1, true); WriteLiteral(">"); Instrumentation.EndContext(); Instrumentation.BeginContext(59, 12, false); - Write( #line 2 "ResolveUrl.cshtml" - product.Name + Write(product.Name); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(71, 8, true); WriteLiteral("\r\n(Href("~/Products/"), 86), false), - Tuple.Create(Tuple.Create("", 97), Tuple.Create( -#line 3 "ResolveUrl.cshtml" - product.id - -#line default -#line hidden - , 97), false), Tuple.Create(Tuple.Create("", 108), Tuple.Create("/Detail", 108), true)); + Tuple.Create(Tuple.Create("", 97), Tuple.Create(product.id, 97), false), Tuple.Create(Tuple.Create("", 108), Tuple.Create("/Detail", 108), true)); Instrumentation.BeginContext(116, 16, true); WriteLiteral(">Details\r\n(Href("~/A+Really(Crazy),Url.Is:This/"), 139), false), - Tuple.Create(Tuple.Create("", 169), Tuple.Create( -#line 4 "ResolveUrl.cshtml" - product.id - -#line default -#line hidden - , 169), false), Tuple.Create(Tuple.Create("", 180), Tuple.Create("/Detail", 180), true)); + Tuple.Create(Tuple.Create("", 169), Tuple.Create(product.id, 169), false), Tuple.Create(Tuple.Create("", 180), Tuple.Create("/Detail", 180), true)); Instrumentation.BeginContext(188, 19, true); WriteLiteral(">Crazy Url!\r\n\r\n"); Instrumentation.EndContext(); @@ -81,48 +60,27 @@ public override async Task ExecuteAsync() WriteLiteral(">Foo\r\n (Href("~/Products/"), 273), false), - Tuple.Create(Tuple.Create("", 284), Tuple.Create( -#line 9 "ResolveUrl.cshtml" - product.id - -#line default -#line hidden - , 284), false)); + Tuple.Create(Tuple.Create("", 284), Tuple.Create(product.id, 284), false)); Instrumentation.BeginContext(296, 1, true); WriteLiteral(">"); Instrumentation.EndContext(); Instrumentation.BeginContext(298, 12, false); - Write( #line 9 "ResolveUrl.cshtml" - product.Name + Write(product.Name); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(310, 16, true); WriteLiteral("\r\n (Href("~/Products/"), 333), false), - Tuple.Create(Tuple.Create("", 344), Tuple.Create( -#line 10 "ResolveUrl.cshtml" - product.id - -#line default -#line hidden - , 344), false), Tuple.Create(Tuple.Create("", 355), Tuple.Create("/Detail", 355), true)); + Tuple.Create(Tuple.Create("", 344), Tuple.Create(product.id, 344), false), Tuple.Create(Tuple.Create("", 355), Tuple.Create("/Detail", 355), true)); Instrumentation.BeginContext(363, 24, true); WriteLiteral(">Details\r\n (Href("~/A+Really(Crazy),Url.Is:This/"), 394), false), - Tuple.Create(Tuple.Create("", 424), Tuple.Create( -#line 11 "ResolveUrl.cshtml" - product.id - -#line default -#line hidden - , 424), false), Tuple.Create(Tuple.Create("", 435), Tuple.Create("/Detail", 435), true)); + Tuple.Create(Tuple.Create("", 424), Tuple.Create(product.id, 424), false), Tuple.Create(Tuple.Create("", 435), Tuple.Create("/Detail", 435), true)); Instrumentation.BeginContext(443, 23, true); WriteLiteral(">Crazy Url!\r\n \r\n"); Instrumentation.EndContext(); @@ -143,48 +101,27 @@ public override async Task ExecuteAsync() WriteLiteralTo(__razor_template_writer, ">Foo\r\n (Href("~/Products/"), 536), false), - Tuple.Create(Tuple.Create("", 547), Tuple.Create( -#line 17 "ResolveUrl.cshtml" - product.id - -#line default -#line hidden - , 547), false)); + Tuple.Create(Tuple.Create("", 547), Tuple.Create(product.id, 547), false)); Instrumentation.BeginContext(559, 1, true); WriteLiteralTo(__razor_template_writer, ">"); Instrumentation.EndContext(); Instrumentation.BeginContext(561, 12, false); - WriteTo(__razor_template_writer, #line 17 "ResolveUrl.cshtml" - product.Name + WriteTo(__razor_template_writer, product.Name); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(573, 12, true); WriteLiteralTo(__razor_template_writer, "\r\n (Href("~/Products/"), 592), false), - Tuple.Create(Tuple.Create("", 603), Tuple.Create( -#line 18 "ResolveUrl.cshtml" - product.id - -#line default -#line hidden - , 603), false), Tuple.Create(Tuple.Create("", 614), Tuple.Create("/Detail", 614), true)); + Tuple.Create(Tuple.Create("", 603), Tuple.Create(product.id, 603), false), Tuple.Create(Tuple.Create("", 614), Tuple.Create("/Detail", 614), true)); Instrumentation.BeginContext(622, 20, true); WriteLiteralTo(__razor_template_writer, ">Details\r\n (Href("~/A+Really(Crazy),Url.Is:This/"), 649), false), - Tuple.Create(Tuple.Create("", 679), Tuple.Create( -#line 19 "ResolveUrl.cshtml" - product.id - -#line default -#line hidden - , 679), false), Tuple.Create(Tuple.Create("", 690), Tuple.Create("/Detail", 690), true)); + Tuple.Create(Tuple.Create("", 679), Tuple.Create(product.id, 679), false), Tuple.Create(Tuple.Create("", 690), Tuple.Create("/Detail", 690), true)); Instrumentation.BeginContext(698, 17, true); WriteLiteralTo(__razor_template_writer, ">Crazy Url!\r\n"); Instrumentation.EndContext(); diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Templates.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Templates.cs index 56b36d94f..a524c9ed4 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Templates.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Templates.cs @@ -41,14 +41,11 @@ public override async Task ExecuteAsync() WriteLiteralTo(__razor_template_writer, "This works "); Instrumentation.EndContext(); Instrumentation.BeginContext(337, 4, false); - WriteTo(__razor_template_writer, #line 12 "Templates.cshtml" - item + WriteTo(__razor_template_writer, item); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(341, 1, true); WriteLiteralTo(__razor_template_writer, "!"); @@ -63,14 +60,11 @@ public override async Task ExecuteAsync() #line hidden Instrumentation.BeginContext(357, 7, false); - Write( #line 13 "Templates.cshtml" - foo("") +Write(foo("")); #line default #line hidden - ); - Instrumentation.EndContext(); #line 13 "Templates.cshtml" @@ -82,104 +76,74 @@ public override async Task ExecuteAsync() WriteLiteral("\r\n\r\n
    \r\n"); Instrumentation.EndContext(); Instrumentation.BeginContext(379, 11, false); - Write( #line 17 "Templates.cshtml" - Repeat(10, - -#line default -#line hidden - item => new Template((__razor_template_writer) => { - Instrumentation.BeginContext(391, 10, true); - WriteLiteralTo(__razor_template_writer, "
  • Item #"); - Instrumentation.EndContext(); - Instrumentation.BeginContext(402, 4, false); - WriteTo(__razor_template_writer, +Write(Repeat(10, item => new Template((__razor_template_writer) => { + Instrumentation.BeginContext(391, 10, true); + WriteLiteralTo(__razor_template_writer, "
  • Item #"); + Instrumentation.EndContext(); + Instrumentation.BeginContext(402, 4, false); #line 17 "Templates.cshtml" - item +WriteTo(__razor_template_writer, item); #line default #line hidden - ); - - Instrumentation.EndContext(); - Instrumentation.BeginContext(406, 5, true); - WriteLiteralTo(__razor_template_writer, "
  • "); - Instrumentation.EndContext(); - } - ) -#line 17 "Templates.cshtml" - ) + Instrumentation.EndContext(); + Instrumentation.BeginContext(406, 5, true); + WriteLiteralTo(__razor_template_writer, ""); + Instrumentation.EndContext(); +} +) +)); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(413, 16, true); WriteLiteral("\r\n
\r\n\r\n

\r\n"); Instrumentation.EndContext(); Instrumentation.BeginContext(430, 16, false); - Write( #line 21 "Templates.cshtml" - Repeat(10, - - -#line default -#line hidden - item => new Template((__razor_template_writer) => { - Instrumentation.BeginContext(448, 14, true); - WriteLiteralTo(__razor_template_writer, " This is line#"); - Instrumentation.EndContext(); - Instrumentation.BeginContext(463, 4, false); - WriteTo(__razor_template_writer, +Write(Repeat(10, + item => new Template((__razor_template_writer) => { + Instrumentation.BeginContext(448, 14, true); + WriteLiteralTo(__razor_template_writer, " This is line#"); + Instrumentation.EndContext(); + Instrumentation.BeginContext(463, 4, false); #line 22 "Templates.cshtml" - item +WriteTo(__razor_template_writer, item); #line default #line hidden - ); - - Instrumentation.EndContext(); - Instrumentation.BeginContext(467, 17, true); - WriteLiteralTo(__razor_template_writer, " of markup
\r\n"); - Instrumentation.EndContext(); - } - ) -#line 23 "Templates.cshtml" + Instrumentation.EndContext(); + Instrumentation.BeginContext(467, 17, true); + WriteLiteralTo(__razor_template_writer, " of markup
\r\n"); + Instrumentation.EndContext(); +} ) +)); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(485, 20, true); WriteLiteral("\r\n

\r\n\r\n
    \r\n "); Instrumentation.EndContext(); Instrumentation.BeginContext(506, 11, false); - Write( #line 27 "Templates.cshtml" - Repeat(10, - -#line default -#line hidden - item => new Template((__razor_template_writer) => { - Instrumentation.BeginContext(518, 20, true); - WriteLiteralTo(__razor_template_writer, "
  • \r\n Item #"); - Instrumentation.EndContext(); - Instrumentation.BeginContext(539, 4, false); - WriteTo(__razor_template_writer, +Write(Repeat(10, item => new Template((__razor_template_writer) => { + Instrumentation.BeginContext(518, 20, true); + WriteLiteralTo(__razor_template_writer, "
  • \r\n Item #"); + Instrumentation.EndContext(); + Instrumentation.BeginContext(539, 4, false); #line 28 "Templates.cshtml" - item +WriteTo(__razor_template_writer, item); #line default #line hidden - ); - - Instrumentation.EndContext(); - Instrumentation.BeginContext(543, 2, true); - WriteLiteralTo(__razor_template_writer, "\r\n"); - Instrumentation.EndContext(); + Instrumentation.EndContext(); + Instrumentation.BeginContext(543, 2, true); + WriteLiteralTo(__razor_template_writer, "\r\n"); + Instrumentation.EndContext(); #line 29 "Templates.cshtml" @@ -192,19 +156,16 @@ public override async Task ExecuteAsync() #line default #line hidden - Instrumentation.BeginContext(574, 93, true); - WriteLiteralTo(__razor_template_writer, "\r\n
      \r\n
    • Child Items... ?
    • \r\n \r\n \r\n
    • Child Items... ?
    • \r\n \r\n \r\n "); - Instrumentation.EndContext(); - } - ) -#line 34 "Templates.cshtml" - ) + Instrumentation.EndContext(); +} +) +)); #line default #line hidden - ); - Instrumentation.EndContext(); Instrumentation.BeginContext(715, 8, true); WriteLiteral("\r\n
    "); From b7b2fd085e65f3bd15493b4eb6beb1b45811b565 Mon Sep 17 00:00:00 2001 From: Pranav K Date: Wed, 1 Oct 2014 12:29:18 -0700 Subject: [PATCH 2/2] Updating per PR comments --- .../CodeBuilder/CSharp/CSharpCodeWriter.cs | 20 +++- .../CSharp/CSharpLineMappingWriter.cs | 83 +++++++++-------- .../CSharp/Visitors/CSharpCodeVisitor.cs | 27 ++---- .../Compiler/CSharpLineMappingWriterTest.cs | 91 +++++++++++++++++++ 4 files changed, 164 insertions(+), 57 deletions(-) create mode 100644 test/Microsoft.AspNet.Razor.Test/Generator/Compiler/CSharpLineMappingWriterTest.cs diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpCodeWriter.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpCodeWriter.cs index 4242e1228..4109d3657 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpCodeWriter.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpCodeWriter.cs @@ -180,9 +180,27 @@ public CSharpCodeWriter WriteReturn(string value, bool endLine) return WriteLine(); } + /// + /// Writes a #line pragma directive for the line number at the specified . + /// + /// The location to generate the line pragma for. + /// The file to generate the line pragma for. + /// The current instance of . + public CSharpCodeWriter WriteLineNumberDirective(SourceLocation location, string file) + { + return WriteLineNumberDirective(location.LineIndex + 1, file); + } + public CSharpCodeWriter WriteLineNumberDirective(int lineNumber, string file) { - return Write("#line ").Write(lineNumber.ToString()).Write(" \"").Write(file).WriteLine("\""); + if (!string.IsNullOrEmpty(LastWrite) && + !LastWrite.EndsWith(Environment.NewLine, StringComparison.Ordinal)) + { + WriteLine(); + } + + var lineNumberAsString = lineNumber.ToString(CultureInfo.InvariantCulture); + return Write("#line ").Write(lineNumberAsString).Write(" \"").Write(file).WriteLine("\""); } public CSharpCodeWriter WriteStartMethodInvocation(string methodName) diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpLineMappingWriter.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpLineMappingWriter.cs index d8a39dc85..8b998fa15 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpLineMappingWriter.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpLineMappingWriter.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.AspNet.Razor.Text; namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp @@ -15,16 +14,21 @@ public class CSharpLineMappingWriter : IDisposable private int _startIndent; private int _generatedContentLength; private bool _writePragmas; + private bool _addLineMapping; - public CSharpLineMappingWriter(CSharpCodeWriter writer, SourceLocation documentLocation, int contentLength) + private CSharpLineMappingWriter([NotNull] CSharpCodeWriter writer, + bool addLineMappings) { _writer = writer; - _documentMapping = new MappingLocation(documentLocation, contentLength); - + _addLineMapping = addLineMappings; _startIndent = _writer.CurrentIndent; - _generatedContentLength = 0; _writer.ResetIndent(); - + } + + public CSharpLineMappingWriter(CSharpCodeWriter writer, SourceLocation documentLocation, int contentLength) + : this(writer, addLineMappings: true) + { + _documentMapping = new MappingLocation(documentLocation, contentLength); _generatedLocation = _writer.GetCurrentSourceLocation(); } @@ -33,17 +37,27 @@ public CSharpLineMappingWriter(CSharpCodeWriter writer, SourceLocation documentL { _writePragmas = true; - // TODO: Should this just be '\n'? - if (!_writer.LastWrite.EndsWith("\n")) - { - _writer.WriteLine(); - } - - _writer.WriteLineNumberDirective(documentLocation.LineIndex + 1, sourceFilename); - + _writer.WriteLineNumberDirective(documentLocation, sourceFilename); _generatedLocation = _writer.GetCurrentSourceLocation(); } + /// + /// Initializes a new instance of used for generation of runtime + /// line mappings. The constructed instance of does not track + /// mappings between the Razor content and the generated content. + /// + /// The to write output to. + /// The of the Razor content being mapping. + /// The input file path. + public CSharpLineMappingWriter([NotNull] CSharpCodeWriter writer, + [NotNull] SourceLocation documentLocation, + [NotNull] string sourceFileName) + : this(writer, addLineMappings: false) + { + _writePragmas = true; + _writer.WriteLineNumberDirective(documentLocation, sourceFileName); + } + public void MarkLineMappingStart() { _generatedLocation = _writer.GetCurrentSourceLocation(); @@ -54,9 +68,9 @@ public void MarkLineMappingEnd() _generatedContentLength = _writer.GenerateCode().Length - _generatedLocation.AbsoluteIndex; } - protected virtual void Dispose(bool disposing) + public void Dispose() { - if(disposing) + if (_addLineMapping) { // Verify that the generated length has not already been calculated if (_generatedContentLength == 0) @@ -73,34 +87,29 @@ protected virtual void Dispose(bool disposing) _writer.LineMappingManager.AddMapping( documentLocation: _documentMapping, generatedLocation: new MappingLocation(_generatedLocation, _generatedContentLength)); + } - 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 ... - bool endsWithNewline = _writer.GenerateCode().EndsWith("\n"); - - // Always write at least 1 empty line to potentially separate code from pragmas. - _writer.WriteLine(); + 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 ... + bool endsWithNewline = _writer.GenerateCode().EndsWith("\n"); - // Check if the previous empty line wasn't enough to separate code from pragmas. - if (!endsWithNewline) - { - _writer.WriteLine(); - } + // Always write at least 1 empty line to potentially separate code from pragmas. + _writer.WriteLine(); - _writer.WriteLineDefaultDirective() - .WriteLineHiddenDirective(); + // Check if the previous empty line wasn't enough to separate code from pragmas. + if (!endsWithNewline) + { + _writer.WriteLine(); } - // Reset indent back to when it was started - _writer.SetIndent(_startIndent); + _writer.WriteLineDefaultDirective() + .WriteLineHiddenDirective(); } - } - public void Dispose() - { - Dispose(disposing: true); + // Reset indent back to when it was started + _writer.SetIndent(_startIndent); } } } diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpCodeVisitor.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpCodeVisitor.cs index 009df6e26..7d4224fb4 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpCodeVisitor.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpCodeVisitor.cs @@ -355,9 +355,6 @@ public void RenderRuntimeExpressionBlockChunk(ExpressionBlockChunk chunk) .OfType() .FirstOrDefault(s => s.Kind == SpanKind.Code || s.Kind == SpanKind.Markup); - var generateInstrumentation = ShouldGenerateInstrumentationForExpressions() && - contentSpan != null; - if (Context.ExpressionRenderingMode == ExpressionRenderingMode.InjectCode) { Accept(chunk.Children); @@ -398,33 +395,28 @@ private void RenderRuntimeExpressionBlockChunkWithContentSpan(ExpressionBlockChu Writer.WriteStartInstrumentationContext(Context, contentSpan, isLiteral: false); } - using (var mappingWriter = Writer.BuildLineMapping(chunk.Start, contentSpan.Length, Context.SourceFile)) + using (var mappingWriter = new CSharpLineMappingWriter(Writer, chunk.Start, Context.SourceFile)) { - mappingWriter.MarkLineMappingStart(); - - var indent = Writer.CurrentIndent; - Writer.ResetIndent(); - if (!string.IsNullOrEmpty(Context.TargetWriterName)) { var generatedStart = Context.Host.GeneratedClassContext.WriteToMethodName.Length + Context.TargetWriterName.Length + - 3; // 1 for the opening bracket and 2 for the parameter separator + 3; // 1 for the opening '(' and 2 for ', ' var padding = _paddingBuilder.BuildExpressionPadding(contentSpan, generatedStart); - + Writer.Write(padding) - .WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteToMethodName) - .Write(Context.TargetWriterName) - .WriteParameterSeparator(); + .WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteToMethodName) + .Write(Context.TargetWriterName) + .WriteParameterSeparator(); } else { var generatedStart = Context.Host.GeneratedClassContext.WriteMethodName.Length + - 1; // for the opening bracket + 1; // for the opening '(' var padding = _paddingBuilder.BuildExpressionPadding(contentSpan, generatedStart); - + Writer.Write(padding) .WriteStartMethodInvocation(Context.Host.GeneratedClassContext.WriteMethodName); } @@ -432,9 +424,6 @@ private void RenderRuntimeExpressionBlockChunkWithContentSpan(ExpressionBlockChu Accept(chunk.Children); Writer.WriteEndMethodInvocation(); - Writer.SetIndent(indent); - - mappingWriter.MarkLineMappingEnd(); } if (generateInstrumentation) diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/Compiler/CSharpLineMappingWriterTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/Compiler/CSharpLineMappingWriterTest.cs new file mode 100644 index 000000000..0fb5c4d35 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Generator/Compiler/CSharpLineMappingWriterTest.cs @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Razor.Text; +using Xunit; + +namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp +{ + public class CSharpLineMappingWriterTest + { + [Fact] + public void WriterConstructedWithContentLength_AddsLineMappings_OnDispose() + { + // Arrange + var location = new SourceLocation(10, 15, 20); + var expected = new LineMapping( + new MappingLocation(location, 30), + new MappingLocation(new SourceLocation(0, 0, 0), 11)); + var writer = new CSharpCodeWriter(); + + // Act + using (var mappingWriter = new CSharpLineMappingWriter(writer, location, 30)) + { + writer.Write("Hello world"); + } + + // Assert + Assert.Equal("Hello world", writer.GenerateCode()); + var mapping = Assert.Single(writer.LineMappingManager.Mappings); + Assert.Equal(expected, mapping); + } + + [Fact] + public void WriterConstructedWithContentLengthAndSourceFile_AddsLineMappingsAndLinePragmas_OnDispose() + { + // Arrange + var location = new SourceLocation(10, 1, 20); + var expected = string.Join(Environment.NewLine, + @"#line 2 ""myfile""", + "Hello world", + "", + "#line default", + "#line hidden", + ""); + var expectedMappings = new LineMapping( + new MappingLocation(location, 30), + new MappingLocation(new SourceLocation(18, 1, 0), 11)); + var writer = new CSharpCodeWriter(); + + // Act + using (var mappingWriter = new CSharpLineMappingWriter(writer, location, 30, "myfile")) + { + writer.Write("Hello world"); + } + + // Assert + Assert.Equal(expected, writer.GenerateCode()); + var mapping = Assert.Single(writer.LineMappingManager.Mappings); + Assert.Equal(expectedMappings, mapping); + } + + [Fact] + public void WriterConstructedWithoutContentLengthAndSourceFile_AddsLinePragmas_OnDispose() + { + // Arrange + var location = new SourceLocation(10, 1, 20); + var expected = string.Join(Environment.NewLine, + @"#line 2 ""myfile""", + "Hello world", + "", + "#line default", + "#line hidden", + ""); + var expectedMappings = new LineMapping( + new MappingLocation(location, 30), + new MappingLocation(new SourceLocation(18, 1, 0), 11)); + var writer = new CSharpCodeWriter(); + + // Act + using (var mappingWriter = new CSharpLineMappingWriter(writer, location, "myfile")) + { + writer.Write("Hello world"); + } + + // Assert + Assert.Equal(expected, writer.GenerateCode()); + Assert.Empty(writer.LineMappingManager.Mappings); + } + } +} \ No newline at end of file