diff --git a/src/Microsoft.AspNet.Razor/Parser/HtmlMarkupParser.Block.cs b/src/Microsoft.AspNet.Razor/Parser/HtmlMarkupParser.Block.cs index ef2a330946..005a2ee972 100644 --- a/src/Microsoft.AspNet.Razor/Parser/HtmlMarkupParser.Block.cs +++ b/src/Microsoft.AspNet.Razor/Parser/HtmlMarkupParser.Block.cs @@ -553,8 +553,15 @@ namespace Microsoft.AspNet.Razor.Parser } else { + // Output the attribute name, the equals and optional quote. Ex: foo=" + Output(SpanKind.Markup); + // Not a "conditional" attribute, so just read the value SkipToAndParseCode(sym => IsEndOfAttributeValue(quote, sym)); + + // Output the attribute value (will include everything in-between the attribute's quotes). + Output(SpanKind.Markup); + if (quote != HtmlSymbolType.Unknown) { Optional(quote); diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs index 2ed5f01f98..94594412dc 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs @@ -325,10 +325,17 @@ namespace Microsoft.AspNet.Razor.Test.Generator generatedLineIndex: 15, characterOffsetIndex: 14, contentLength: 17), - BuildLineMapping(documentAbsoluteIndex: 195, + BuildLineMapping(documentAbsoluteIndex: 202, + documentLineIndex: 5, + documentCharacterOffsetIndex: 38, + generatedAbsoluteIndex: 1300, + generatedLineIndex: 40, + generatedCharacterOffsetIndex: 6, + contentLength: 23), + BuildLineMapping(documentAbsoluteIndex: 287, documentLineIndex: 6, - generatedAbsoluteIndex: 1580, - generatedLineIndex: 44, + generatedAbsoluteIndex: 1677, + generatedLineIndex: 49, characterOffsetIndex: 40, contentLength: 4) } diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs index 71cfeaea82..7860db7af0 100644 --- a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs @@ -14,6 +14,165 @@ namespace Microsoft.AspNet.Razor.TagHelpers { public class TagHelperBlockRewriterTest : TagHelperRewritingTestBase { + public static TheoryData DataDashAttributeData_Document + { + get + { + var factory = CreateDefaultSpanFactory(); + var dateTimeNowString = "@DateTime.Now"; + var dateTimeNow = new ExpressionBlock( + factory.CodeTransition(), + factory.Code("DateTime.Now") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)); + + // documentContent, expectedOutput + return new TheoryData + { + { + $"", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair( + "data-required", + new MarkupBlock(dateTimeNow)), + })) + }, + { + "", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair("data-required", factory.Markup("value")), + })) + }, + { + $"", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair( + "data-required", + new MarkupBlock(factory.Markup("prefix "), dateTimeNow)), + })) + }, + { + $"", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair( + "data-required", + new MarkupBlock(dateTimeNow, factory.Markup(" suffix"))), + })) + }, + { + $"", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair( + "data-required", + new MarkupBlock( + factory.Markup("prefix "), + dateTimeNow, + factory.Markup(" suffix"))), + })) + }, + { + $"", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair("pre-attribute", value: null), + new KeyValuePair( + "data-required", + new MarkupBlock( + factory.Markup("prefix "), + dateTimeNow, + factory.Markup(" suffix"))), + new KeyValuePair("post-attribute", value: null), + })) + }, + { + $"", + new MarkupBlock( + new MarkupTagHelperBlock( + "input", + selfClosing: true, + attributes: new List>() + { + new KeyValuePair( + "data-required", + new MarkupBlock( + dateTimeNow, + factory.Markup(" middle "), + dateTimeNow)), + })) + }, + }; + } + } + + public static TheoryData DataDashAttributeData_CSharpBlock + { + get + { + var factory = CreateDefaultSpanFactory(); + var documentData = DataDashAttributeData_Document; + Func, MarkupBlock> buildStatementBlock = (insideBuilder) => + { + return new MarkupBlock( + factory.EmptyHtml(), + new StatementBlock( + factory.CodeTransition(), + factory.MetaCode("{").Accepts(AcceptedCharacters.None), + insideBuilder(), + factory.EmptyCSharp().AsStatement(), + factory.MetaCode("}").Accepts(AcceptedCharacters.None)), + factory.EmptyHtml()); + }; + + foreach (var data in documentData) + { + data[0] = $"@{{{data[0]}}}"; + data[1] = buildStatementBlock(() => data[1] as MarkupBlock); + } + + return documentData; + } + } + + [Theory] + [MemberData(nameof(DataDashAttributeData_Document))] + [MemberData(nameof(DataDashAttributeData_CSharpBlock))] + public void Rewrite_GeneratesExpectedOutputForUnboundDataDashAttributes( + string documentContent, + MarkupBlock expectedOutput) + { + // Act & Assert + RunParseTreeRewriterTest(documentContent, expectedOutput, Enumerable.Empty(), "input"); + } + public static TheoryData MinimizedAttributeData_Document { get diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.CustomAttributeCodeGenerator.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.CustomAttributeCodeGenerator.cs index 5d8925a86e..f5a204d50b 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.CustomAttributeCodeGenerator.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.CustomAttributeCodeGenerator.cs @@ -1,4 +1,4 @@ -#pragma checksum "BasicTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "90382b3c748e0f948dfb6b452b775ba3024b2fe6" +#pragma checksum "BasicTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "d83a512ddca8f28897c27630e252991c84555533" namespace TestOutput { using Microsoft.AspNet.Razor.Runtime.TagHelpers; @@ -25,8 +25,8 @@ namespace TestOutput public override async Task ExecuteAsync() { __tagHelperRunner = __tagHelperRunner ?? new TagHelperRunner(); - Instrumentation.BeginContext(33, 49, true); - WriteLiteral("\r\n
\r\n "); + Instrumentation.BeginContext(33, 71, true); + WriteLiteral("\r\n
\r\n "); Instrumentation.EndContext(); __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", false, "test", async() => { WriteLiteral("\r\n "); @@ -49,6 +49,16 @@ namespace TestOutput __InputTagHelper2 = CreateTagHelper(); __tagHelperExecutionContext.Add(__InputTagHelper2); __InputTagHelper2.Type = __InputTagHelper.Type; + StartTagHelperWritingScope(); + WriteLiteral("2000 + "); +#line 6 "BasicTagHelpers.cshtml" +Write(ViewBag.DefaultInterval); + +#line default +#line hidden + WriteLiteral(" + 1"); + __tagHelperStringValueBuffer = EndTagHelperWritingScope(); + __tagHelperExecutionContext.AddHtmlAttribute("data-interval", Html.Raw(__tagHelperStringValueBuffer.ToString())); __tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext); await WriteTagHelperAsync(__tagHelperExecutionContext); __tagHelperExecutionContext = __tagHelperScopeManager.End(); @@ -78,10 +88,11 @@ namespace TestOutput __PTagHelper = CreateTagHelper(); __tagHelperExecutionContext.Add(__PTagHelper); __tagHelperExecutionContext.AddHtmlAttribute("class", Html.Raw("Hello World")); + __tagHelperExecutionContext.AddHtmlAttribute("data-delay", Html.Raw("1000")); __tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext); await WriteTagHelperAsync(__tagHelperExecutionContext); __tagHelperExecutionContext = __tagHelperScopeManager.End(); - Instrumentation.BeginContext(212, 8, true); + Instrumentation.BeginContext(304, 8, true); WriteLiteral("\r\n
"); Instrumentation.EndContext(); } diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.DesignTime.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.DesignTime.cs index 57b3193d28..299eab8d4a 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.DesignTime.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.DesignTime.cs @@ -37,6 +37,11 @@ namespace TestOutput __InputTagHelper.Type = "text"; __InputTagHelper2 = CreateTagHelper(); __InputTagHelper2.Type = __InputTagHelper.Type; +#line 6 "BasicTagHelpers.cshtml" +__o = ViewBag.DefaultInterval; + +#line default +#line hidden __InputTagHelper = CreateTagHelper(); __InputTagHelper.Type = "checkbox"; __InputTagHelper2 = CreateTagHelper(); diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.cs index b1b35018f7..ad20384e5c 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.cs @@ -1,4 +1,4 @@ -#pragma checksum "BasicTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "90382b3c748e0f948dfb6b452b775ba3024b2fe6" +#pragma checksum "BasicTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "d83a512ddca8f28897c27630e252991c84555533" namespace TestOutput { using Microsoft.AspNet.Razor.Runtime.TagHelpers; @@ -26,8 +26,8 @@ namespace TestOutput public override async Task ExecuteAsync() { __tagHelperRunner = __tagHelperRunner ?? new TagHelperRunner(); - Instrumentation.BeginContext(33, 49, true); - WriteLiteral("\r\n
\r\n "); + Instrumentation.BeginContext(33, 71, true); + WriteLiteral("\r\n
\r\n "); Instrumentation.EndContext(); __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", false, "test", async() => { WriteLiteral("\r\n "); @@ -50,6 +50,16 @@ namespace TestOutput __InputTagHelper2 = CreateTagHelper(); __tagHelperExecutionContext.Add(__InputTagHelper2); __InputTagHelper2.Type = __InputTagHelper.Type; + StartTagHelperWritingScope(); + WriteLiteral("2000 + "); +#line 6 "BasicTagHelpers.cshtml" +Write(ViewBag.DefaultInterval); + +#line default +#line hidden + WriteLiteral(" + 1"); + __tagHelperStringValueBuffer = EndTagHelperWritingScope(); + __tagHelperExecutionContext.AddHtmlAttribute("data-interval", Html.Raw(__tagHelperStringValueBuffer.ToString())); __tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext); await WriteTagHelperAsync(__tagHelperExecutionContext); __tagHelperExecutionContext = __tagHelperScopeManager.End(); @@ -79,10 +89,11 @@ namespace TestOutput __PTagHelper = CreateTagHelper(); __tagHelperExecutionContext.Add(__PTagHelper); __tagHelperExecutionContext.AddHtmlAttribute("class", Html.Raw("Hello World")); + __tagHelperExecutionContext.AddHtmlAttribute("data-delay", Html.Raw("1000")); __tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext); await WriteTagHelperAsync(__tagHelperExecutionContext); __tagHelperExecutionContext = __tagHelperScopeManager.End(); - Instrumentation.BeginContext(212, 8, true); + Instrumentation.BeginContext(304, 8, true); WriteLiteral("\r\n
"); Instrumentation.EndContext(); } diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/BasicTagHelpers.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/BasicTagHelpers.cshtml index 043f080395..30e65e0aae 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/BasicTagHelpers.cshtml +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/BasicTagHelpers.cshtml @@ -1,9 +1,9 @@ @addTagHelper "something, nice" -
-

+

+

- +

\ No newline at end of file