Change HTML in nested C# blocks to properly handle dots.

- Prior to this change adding a `.` after an implicit expression would result in compile errors due to Razor thinking the `.` was part of the C# (normally not the case).
- Added a code generation and unit tests to validate behavior.

#491
This commit is contained in:
N. Taylor Mullen 2015-09-03 16:34:54 -07:00
parent f843aec538
commit d458e8ecb2
7 changed files with 234 additions and 0 deletions

View File

@ -631,6 +631,11 @@ namespace Microsoft.AspNet.Razor.Parser
private void ParseWithOtherParser(Action<ParserBase> parseAction)
{
// When transitioning to the HTML parser we no longer want to act as if we're in a nested C# state.
// For instance, if <div>@hello.</div> is in a nested C# block we don't want the trailing '.' to be handled
// as C#; it should be handled as a period because it's wrapped in markup.
var wasNested = IsNested;
IsNested = false;
using (PushSpanConfig())
{
Context.SwitchActiveParser();
@ -638,6 +643,7 @@ namespace Microsoft.AspNet.Razor.Parser
Context.SwitchActiveParser();
}
Initialize(Span);
IsNested = wasNested;
NextToken();
}
}

View File

@ -52,6 +52,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
}
[Theory]
[InlineData("NestedCSharp")]
[InlineData("NullConditionalExpressions")]
[InlineData("NestedCodeBlocks")]
[InlineData("CodeBlock")]
@ -75,6 +76,55 @@ namespace Microsoft.AspNet.Razor.Test.Generator
RunTest(testType);
}
[Fact]
public void CSharpChunkGeneratorCorrectlyGeneratesMappingsForNestedCSharp()
{
RunTest(
"NestedCSharp",
"NestedCSharp.DesignTime",
designTimeMode: true,
tabTest: TabTest.NoTabs,
expectedDesignTimePragmas: new List<LineMapping>
{
BuildLineMapping(
documentAbsoluteIndex: 2,
documentLineIndex: 0,
generatedAbsoluteIndex: 522,
generatedLineIndex: 22,
characterOffsetIndex: 2,
contentLength: 6),
BuildLineMapping(
documentAbsoluteIndex: 9,
documentLineIndex: 1,
documentCharacterOffsetIndex: 5,
generatedAbsoluteIndex: 598,
generatedLineIndex: 29,
generatedCharacterOffsetIndex: 4,
contentLength: 53),
BuildLineMapping(
documentAbsoluteIndex: 82,
documentLineIndex: 4,
generatedAbsoluteIndex: 730,
generatedLineIndex: 37,
characterOffsetIndex: 13,
contentLength: 16),
BuildLineMapping(
documentAbsoluteIndex: 115,
documentLineIndex: 5,
generatedAbsoluteIndex: 825,
generatedLineIndex: 42,
characterOffsetIndex: 14,
contentLength: 7),
BuildLineMapping(
documentAbsoluteIndex: 122,
documentLineIndex: 6,
generatedAbsoluteIndex: 903,
generatedLineIndex: 49,
characterOffsetIndex: 5,
contentLength: 2),
});
}
[Fact]
public void CSharpChunkGeneratorCorrectlyGeneratesMappingsForNullConditionalOperator()
{

View File

@ -15,6 +15,31 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp
{
public class CSharpBlockTest : CsHtmlCodeParserTestBase
{
[Fact]
public void ParseBlock_NestedCodeBlockWithMarkupSetsDotAsMarkup()
{
ParseBlockTest("if (true) { @if(false) { <div>@something.</div> } }",
new StatementBlock(
Factory.Code("if (true) { ").AsStatement(),
new StatementBlock(
Factory.CodeTransition(),
Factory.Code("if(false) {").AsStatement(),
new MarkupBlock(
Factory.Markup(" "),
BlockFactory.MarkupTagBlock("<div>", AcceptedCharacters.None),
Factory.EmptyHtml(),
new ExpressionBlock(
Factory.CodeTransition(),
Factory.Code("something")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)
.Accepts(AcceptedCharacters.NonWhiteSpace)),
Factory.Markup("."),
BlockFactory.MarkupTagBlock("</div>", AcceptedCharacters.None),
Factory.Markup(" ").Accepts(AcceptedCharacters.None)),
Factory.Code("}").AsStatement()),
Factory.Code(" }").AsStatement()));
}
[Fact]
public void ParseBlockMethodThrowsArgNullExceptionOnNullContext()
{

View File

@ -16,6 +16,34 @@ namespace Microsoft.AspNet.Razor.Test.Parser.Html
{
private static readonly TestFile Nested1000 = TestFile.Create("TestFiles/nested-1000.html");
[Fact]
public void ParseDocument_NestedCodeBlockWithMarkupSetsDotAsMarkup()
{
ParseDocumentTest("@if (true) { @if(false) { <div>@something.</div> } }",
new MarkupBlock(
Factory.EmptyHtml(),
new StatementBlock(
Factory.CodeTransition(),
Factory.Code("if (true) { ").AsStatement(),
new StatementBlock(
Factory.CodeTransition(),
Factory.Code("if(false) {").AsStatement(),
new MarkupBlock(
Factory.Markup(" "),
BlockFactory.MarkupTagBlock("<div>", AcceptedCharacters.None),
Factory.EmptyHtml(),
new ExpressionBlock(
Factory.CodeTransition(),
Factory.Code("something")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: false)
.Accepts(AcceptedCharacters.NonWhiteSpace)),
Factory.Markup("."),
BlockFactory.MarkupTagBlock("</div>", AcceptedCharacters.None),
Factory.Markup(" ").Accepts(AcceptedCharacters.None)),
Factory.Code("}").AsStatement()),
Factory.Code(" }").AsStatement())));
}
[Fact]
public void ParseDocumentMethodThrowsArgNullExceptionOnNullContext()
{

View File

@ -0,0 +1,58 @@
namespace TestOutput
{
using System;
using System.Threading.Tasks;
public class NestedCSharp
{
private static object @__o;
private void @__RazorDesignTimeHelpers__()
{
#pragma warning disable 219
#pragma warning restore 219
}
#line hidden
public NestedCSharp()
{
}
#pragma warning disable 1998
public override async Task ExecuteAsync()
{
#line 1 "NestedCSharp.cshtml"
#line default
#line hidden
#line 2 "NestedCSharp.cshtml"
foreach (var result in (dynamic)Url)
{
#line default
#line hidden
#line 5 "NestedCSharp.cshtml"
__o = result.SomeValue;
#line default
#line hidden
#line 6 "NestedCSharp.cshtml"
}
#line default
#line hidden
#line 7 "NestedCSharp.cshtml"
#line default
#line hidden
}
#pragma warning restore 1998
}
}

View File

@ -0,0 +1,59 @@
#pragma checksum "NestedCSharp.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "2b9e8dcf7c08153c15ac84973938a7c0254f2369"
namespace TestOutput
{
using System;
using System.Threading.Tasks;
public class NestedCSharp
{
#line hidden
public NestedCSharp()
{
}
#pragma warning disable 1998
public override async Task ExecuteAsync()
{
#line 1 "NestedCSharp.cshtml"
#line default
#line hidden
#line 2 "NestedCSharp.cshtml"
foreach (var result in (dynamic)Url)
{
#line default
#line hidden
Instrumentation.BeginContext(54, 27, true);
WriteLiteral(" <div>\r\n ");
Instrumentation.EndContext();
Instrumentation.BeginContext(82, 16, false);
#line 5 "NestedCSharp.cshtml"
Write(result.SomeValue);
#line default
#line hidden
Instrumentation.EndContext();
Instrumentation.BeginContext(98, 19, true);
WriteLiteral(".\r\n </div>\r\n");
Instrumentation.EndContext();
#line 7 "NestedCSharp.cshtml"
}
#line default
#line hidden
#line 7 "NestedCSharp.cshtml"
#line default
#line hidden
}
#pragma warning restore 1998
}
}

View File

@ -0,0 +1,8 @@
@{
@foreach (var result in (dynamic)Url)
{
<div>
@result.SomeValue.
</div>
}
}