diff --git a/src/Microsoft.AspNet.Razor/CodeGenerators/CSharpTagHelperCodeRenderer.cs b/src/Microsoft.AspNet.Razor/CodeGenerators/CSharpTagHelperCodeRenderer.cs
index 0ee81fc78c..a75a9d0d33 100644
--- a/src/Microsoft.AspNet.Razor/CodeGenerators/CSharpTagHelperCodeRenderer.cs
+++ b/src/Microsoft.AspNet.Razor/CodeGenerators/CSharpTagHelperCodeRenderer.cs
@@ -404,25 +404,15 @@ namespace Microsoft.AspNet.Razor.CodeGenerators
private void RenderUnboundAttribute(string attributeName, Chunk attributeValueChunk)
{
- string textValue = null;
- var isPlainTextValue = false;
-
- // A null attribute value means the HTML attribute is minimized.
- if (attributeValueChunk != null)
- {
- isPlainTextValue = TryGetPlainTextValue(attributeValueChunk, out textValue);
-
- // HTML attributes are always strings. So if this value is not plain text i.e. if the value contains
- // C# code, then we need to buffer it.
- if (!isPlainTextValue)
- {
- BuildBufferedWritingScope(attributeValueChunk, htmlEncodeValues: true);
- }
- }
-
- // Execution contexts are a runtime feature, therefore no need to add anything to them.
+ // Render children to provide IntelliSense at design time. No need for the execution context logic, it's
+ // a runtime feature.
if (_designTimeMode)
{
+ if (attributeValueChunk != null)
+ {
+ _bodyVisitor.Accept(attributeValueChunk);
+ }
+
return;
}
@@ -438,27 +428,60 @@ namespace Microsoft.AspNet.Razor.CodeGenerators
}
else
{
- _writer
- .WriteStartInstanceMethodInvocation(
- ExecutionContextVariableName,
- _tagHelperContext.ExecutionContextAddHtmlAttributeMethodName)
- .WriteStringLiteral(attributeName)
- .WriteParameterSeparator()
- .WriteStartMethodInvocation(_tagHelperContext.MarkAsHtmlEncodedMethodName);
+ string textValue = null;
+ var isPlainTextValue = TryGetPlainTextValue(attributeValueChunk, out textValue);
- // If it's a plain text value then we need to surround the value with quotes.
if (isPlainTextValue)
{
- _writer.WriteStringLiteral(textValue);
+ // If it's a plain text value then we need to surround the value with quotes.
+ _writer
+ .WriteStartInstanceMethodInvocation(
+ ExecutionContextVariableName,
+ _tagHelperContext.ExecutionContextAddHtmlAttributeMethodName)
+ .WriteStringLiteral(attributeName)
+ .WriteParameterSeparator()
+ .WriteStartMethodInvocation(_tagHelperContext.MarkAsHtmlEncodedMethodName)
+ .WriteStringLiteral(textValue)
+ .WriteEndMethodInvocation(endLine: false)
+ .WriteEndMethodInvocation();
+ }
+ else if (IsDynamicAttributeValue(attributeValueChunk))
+ {
+ // Dynamic attribute value should be run through the conditional attribute removal system. It's
+ // unbound and contains C#.
+
+ _writer
+ .WriteStartMethodInvocation(_tagHelperContext.AddHtmlAttributeValuesMethodName)
+ .WriteStringLiteral(attributeName)
+ .WriteParameterSeparator()
+ .Write(ExecutionContextVariableName);
+
+ _bodyVisitor.Accept(attributeValueChunk);
+
+ _writer.WriteEndMethodInvocation();
}
else
{
- RenderBufferedAttributeValueAccessor(_writer);
- }
+ // HTML attributes are always strings. This attribute contains C# but is not dynamic. This occurs
+ // when the attribute is a data-* attribute.
- _writer
- .WriteEndMethodInvocation(endLine: false)
- .WriteEndMethodInvocation();
+ // Attribute value is not plain text, must be buffered to determine its final value.
+ BuildBufferedWritingScope(attributeValueChunk, htmlEncodeValues: true);
+
+ _writer
+ .WriteStartInstanceMethodInvocation(
+ ExecutionContextVariableName,
+ _tagHelperContext.ExecutionContextAddHtmlAttributeMethodName)
+ .WriteStringLiteral(attributeName)
+ .WriteParameterSeparator()
+ .WriteStartMethodInvocation(_tagHelperContext.MarkAsHtmlEncodedMethodName);
+
+ RenderBufferedAttributeValueAccessor(_writer);
+
+ _writer
+ .WriteEndMethodInvocation(endLine: false)
+ .WriteEndMethodInvocation();
+ }
}
}
@@ -616,6 +639,17 @@ namespace Microsoft.AspNet.Razor.CodeGenerators
}
}
+ private static bool IsDynamicAttributeValue(Chunk attributeValueChunk)
+ {
+ var parentChunk = attributeValueChunk as ParentChunk;
+ if (parentChunk != null)
+ {
+ return parentChunk.Children.Any(child => child is DynamicCodeAttributeChunk);
+ }
+
+ return false;
+ }
+
private static bool TryGetPlainTextValue(Chunk chunk, out string plainText)
{
var parentChunk = chunk as ParentChunk;
diff --git a/src/Microsoft.AspNet.Razor/CodeGenerators/GeneratedTagHelperContext.cs b/src/Microsoft.AspNet.Razor/CodeGenerators/GeneratedTagHelperContext.cs
index d50d4901da..79380fddfb 100644
--- a/src/Microsoft.AspNet.Razor/CodeGenerators/GeneratedTagHelperContext.cs
+++ b/src/Microsoft.AspNet.Razor/CodeGenerators/GeneratedTagHelperContext.cs
@@ -13,6 +13,7 @@ namespace Microsoft.AspNet.Razor.CodeGenerators
///
public GeneratedTagHelperContext()
{
+ AddHtmlAttributeValuesMethodName = "AddHtmlAttributeValues";
CreateTagHelperMethodName = "CreateTagHelper";
RunnerRunAsyncMethodName = "RunAsync";
ScopeManagerBeginMethodName = "Begin";
@@ -34,6 +35,20 @@ namespace Microsoft.AspNet.Razor.CodeGenerators
WriteTagHelperToAsyncMethodName = "WriteTagHelperToAsync";
}
+ ///
+ /// The name of the method used to add unbound, complex tag helper attributes to TagHelperExecutionContexts.
+ ///
+ ///
+ /// Method signature should be
+ ///
+ /// public void AddHtmlAttributeValues(
+ /// string attributeName,
+ /// TagHelperExecutionContext executionContext,
+ /// params Microsoft.AspNet.Mvc.Razor.AttributeValue[] values)
+ ///
+ ///
+ public string AddHtmlAttributeValuesMethodName { get; set; }
+
///
/// The name of the method used to create a tag helper.
///
diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockRewriter.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockRewriter.cs
index 5dc2bee774..94b4c234aa 100644
--- a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockRewriter.cs
+++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockRewriter.cs
@@ -324,7 +324,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
// We need to rebuild the chunk generators of the builder and its children (this is needed to
// ensure we don't do special attribute chunk generation since this is a tag helper).
- block = RebuildChunkGenerators(builder.Build());
+ block = RebuildChunkGenerators(builder.Build(), result.IsBoundAttribute);
// If there's only 1 child at this point its value could be a simple markup span (treated differently than
// block level elements for attributes).
@@ -376,10 +376,27 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
return blockBuilder.Build();
}
- private static Block RebuildChunkGenerators(Block block)
+ private static Block RebuildChunkGenerators(Block block, bool isBound)
{
var builder = new BlockBuilder(block);
+ // Don't want to rebuild unbound dynamic attributes. They need to run through the conditional attribute
+ // removal system at runtime. A conditional attribute at the parse tree rewriting level is defined by
+ // having at least 1 child with a DynamicAttributeBlockChunkGenerator.
+ if (!isBound &&
+ block.Children.Any(
+ child => child.IsBlock &&
+ ((Block)child).ChunkGenerator is DynamicAttributeBlockChunkGenerator))
+ {
+ // The parent chunk generator must be removed because it's normally responsible for conditionally
+ // generating the attribute prefix (class=") and suffix ("). The prefix and suffix concepts aren't
+ // applicable for the TagHelper use case since the attributes are put into a dictionary like object as
+ // name value pairs.
+ builder.ChunkGenerator = ParentChunkGenerator.Null;
+
+ return builder.Build();
+ }
+
var isDynamic = builder.ChunkGenerator is DynamicAttributeBlockChunkGenerator;
// We don't want any attribute specific logic here, null out the block chunk generator.
@@ -395,7 +412,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
if (child.IsBlock)
{
// The child is a block, recurse down into the block to rebuild its children
- builder.Children[i] = RebuildChunkGenerators((Block)child);
+ builder.Children[i] = RebuildChunkGenerators((Block)child, isBound);
}
else
{
diff --git a/test/Microsoft.AspNet.Razor.Test/CodeGenerators/CSharpTagHelperRenderingTest.cs b/test/Microsoft.AspNet.Razor.Test/CodeGenerators/CSharpTagHelperRenderingTest.cs
index 472787e151..e456e5774d 100644
--- a/test/Microsoft.AspNet.Razor.Test/CodeGenerators/CSharpTagHelperRenderingTest.cs
+++ b/test/Microsoft.AspNet.Razor.Test/CodeGenerators/CSharpTagHelperRenderingTest.cs
@@ -63,6 +63,29 @@ namespace Microsoft.AspNet.Razor.Test.Generator
}
}
+ private static IEnumerable DynamicAttributeTagHelpers_Descriptors
+ {
+ get
+ {
+ return new[]
+ {
+ new TagHelperDescriptor(
+ tagName: "input",
+ typeName: "InputTagHelper",
+ assemblyName: "SomeAssembly",
+ attributes: new[]
+ {
+ new TagHelperAttributeDescriptor(
+ "bound",
+ "Bound",
+ typeof(string).FullName,
+ isIndexer: false,
+ designTimeDescriptor: null)
+ }),
+ };
+ }
+ }
+
private static IEnumerable DuplicateTargetTagHelperDescriptors
{
get
@@ -922,6 +945,261 @@ namespace Microsoft.AspNet.Razor.Test.Generator
contentLength: 1),
}
},
+ {
+ "DynamicAttributeTagHelpers",
+ "DynamicAttributeTagHelpers.DesignTime",
+ DynamicAttributeTagHelpers_Descriptors,
+ new List
+ {
+ BuildLineMapping(
+ documentAbsoluteIndex: 14,
+ documentLineIndex: 0,
+ generatedAbsoluteIndex: 497,
+ generatedLineIndex: 15,
+ characterOffsetIndex: 14,
+ contentLength: 17),
+ BuildLineMapping(
+ documentAbsoluteIndex: 59,
+ documentLineIndex: 2,
+ documentCharacterOffsetIndex: 24,
+ generatedAbsoluteIndex: 1002,
+ generatedLineIndex: 34,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 12),
+ BuildLineMapping(
+ documentAbsoluteIndex: 96,
+ documentLineIndex: 4,
+ documentCharacterOffsetIndex: 17,
+ generatedAbsoluteIndex: 1160,
+ generatedLineIndex: 40,
+ generatedCharacterOffsetIndex: 0,
+ contentLength: 12),
+ BuildLineMapping(
+ documentAbsoluteIndex: 109,
+ documentLineIndex: 4,
+ documentCharacterOffsetIndex: 30,
+ generatedAbsoluteIndex: 1258,
+ generatedLineIndex: 46,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 12),
+ BuildLineMapping(
+ documentAbsoluteIndex: 121,
+ documentLineIndex: 4,
+ documentCharacterOffsetIndex: 42,
+ generatedAbsoluteIndex: 1349,
+ generatedLineIndex: 51,
+ generatedCharacterOffsetIndex: 0,
+ contentLength: 10),
+ BuildLineMapping(
+ documentAbsoluteIndex: 132,
+ documentLineIndex: 4,
+ documentCharacterOffsetIndex: 53,
+ generatedAbsoluteIndex: 1445,
+ generatedLineIndex: 57,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 5),
+ BuildLineMapping(
+ documentAbsoluteIndex: 137,
+ documentLineIndex: 4,
+ documentCharacterOffsetIndex: 58,
+ generatedAbsoluteIndex: 1529,
+ generatedLineIndex: 62,
+ generatedCharacterOffsetIndex: 0,
+ contentLength: 2),
+ BuildLineMapping(
+ documentAbsoluteIndex: 176,
+ documentLineIndex: 6,
+ documentCharacterOffsetIndex: 22,
+ generatedAbsoluteIndex: 1684,
+ generatedLineIndex: 69,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 12),
+ BuildLineMapping(
+ documentAbsoluteIndex: 214,
+ documentLineIndex: 6,
+ documentCharacterOffsetIndex: 60,
+ generatedAbsoluteIndex: 1833,
+ generatedLineIndex: 75,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 12),
+ BuildLineMapping(
+ documentAbsoluteIndex: 256,
+ documentLineIndex: 8,
+ documentCharacterOffsetIndex: 15,
+ generatedAbsoluteIndex: 1997,
+ generatedLineIndex: 81,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 13),
+ BuildLineMapping(
+ documentAbsoluteIndex: 271,
+ documentLineIndex: 8,
+ documentCharacterOffsetIndex: 30,
+ generatedAbsoluteIndex: 2089,
+ generatedLineIndex: 86,
+ generatedCharacterOffsetIndex: 0,
+ contentLength: 12),
+ BuildLineMapping(
+ documentAbsoluteIndex: 284,
+ documentLineIndex: 8,
+ documentCharacterOffsetIndex: 43,
+ generatedAbsoluteIndex: 2187,
+ generatedLineIndex: 92,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 12),
+ BuildLineMapping(
+ documentAbsoluteIndex: 296,
+ documentLineIndex: 8,
+ documentCharacterOffsetIndex: 55,
+ generatedAbsoluteIndex: 2278,
+ generatedLineIndex: 97,
+ generatedCharacterOffsetIndex: 0,
+ contentLength: 10),
+ BuildLineMapping(
+ documentAbsoluteIndex: 307,
+ documentLineIndex: 8,
+ documentCharacterOffsetIndex: 66,
+ generatedAbsoluteIndex: 2374,
+ generatedLineIndex: 103,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 5),
+ BuildLineMapping(
+ documentAbsoluteIndex: 312,
+ documentLineIndex: 8,
+ documentCharacterOffsetIndex: 71,
+ generatedAbsoluteIndex: 2458,
+ generatedLineIndex: 108,
+ generatedCharacterOffsetIndex: 0,
+ contentLength: 2),
+ BuildLineMapping(
+ documentAbsoluteIndex: 316,
+ documentLineIndex: 8,
+ documentCharacterOffsetIndex: 75,
+ generatedAbsoluteIndex: 2546,
+ generatedLineIndex: 114,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 12),
+ BuildLineMapping(
+ documentAbsoluteIndex: 348,
+ documentLineIndex: 9,
+ documentCharacterOffsetIndex: 17,
+ generatedAbsoluteIndex: 2696,
+ generatedLineIndex: 120,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 13),
+ BuildLineMapping(
+ documentAbsoluteIndex: 363,
+ documentLineIndex: 9,
+ documentCharacterOffsetIndex: 32,
+ generatedAbsoluteIndex: 2789,
+ generatedLineIndex: 125,
+ generatedCharacterOffsetIndex: 0,
+ contentLength: 12),
+ BuildLineMapping(
+ documentAbsoluteIndex: 376,
+ documentLineIndex: 9,
+ documentCharacterOffsetIndex: 45,
+ generatedAbsoluteIndex: 2888,
+ generatedLineIndex: 131,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 12),
+ BuildLineMapping(
+ documentAbsoluteIndex: 388,
+ documentLineIndex: 9,
+ documentCharacterOffsetIndex: 57,
+ generatedAbsoluteIndex: 2980,
+ generatedLineIndex: 136,
+ generatedCharacterOffsetIndex: 0,
+ contentLength: 10),
+ BuildLineMapping(
+ documentAbsoluteIndex: 399,
+ documentLineIndex: 9,
+ documentCharacterOffsetIndex: 68,
+ generatedAbsoluteIndex: 3077,
+ generatedLineIndex: 142,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 5),
+ BuildLineMapping(
+ documentAbsoluteIndex: 404,
+ documentLineIndex: 9,
+ documentCharacterOffsetIndex: 73,
+ generatedAbsoluteIndex: 3162,
+ generatedLineIndex: 147,
+ generatedCharacterOffsetIndex: 0,
+ contentLength: 2),
+ BuildLineMapping(
+ documentAbsoluteIndex: 408,
+ documentLineIndex: 9,
+ documentCharacterOffsetIndex: 77,
+ generatedAbsoluteIndex: 3251,
+ generatedLineIndex: 153,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 12),
+ BuildLineMapping(
+ documentAbsoluteIndex: 445,
+ documentLineIndex: 11,
+ documentCharacterOffsetIndex: 17,
+ generatedAbsoluteIndex: 3416,
+ generatedLineIndex: 159,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 13),
+ BuildLineMapping(
+ documentAbsoluteIndex: 460,
+ documentLineIndex: 11,
+ documentCharacterOffsetIndex: 32,
+ generatedAbsoluteIndex: 3515,
+ generatedLineIndex: 164,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 12),
+ BuildLineMapping(
+ documentAbsoluteIndex: 492,
+ documentLineIndex: 11,
+ documentCharacterOffsetIndex: 64,
+ generatedAbsoluteIndex: 3613,
+ generatedLineIndex: 169,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 12),
+ BuildLineMapping(
+ documentAbsoluteIndex: 529,
+ documentLineIndex: 13,
+ documentCharacterOffsetIndex: 17,
+ generatedAbsoluteIndex: 3772,
+ generatedLineIndex: 175,
+ generatedCharacterOffsetIndex: 0,
+ contentLength: 12),
+ BuildLineMapping(
+ documentAbsoluteIndex: 542,
+ documentLineIndex: 13,
+ documentCharacterOffsetIndex: 30,
+ generatedAbsoluteIndex: 3871,
+ generatedLineIndex: 181,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 12),
+ BuildLineMapping(
+ documentAbsoluteIndex: 554,
+ documentLineIndex: 13,
+ documentCharacterOffsetIndex: 42,
+ generatedAbsoluteIndex: 3963,
+ generatedLineIndex: 186,
+ generatedCharacterOffsetIndex: 0,
+ contentLength: 10),
+ BuildLineMapping(
+ documentAbsoluteIndex: 565,
+ documentLineIndex: 13,
+ documentCharacterOffsetIndex: 53,
+ generatedAbsoluteIndex: 4060,
+ generatedLineIndex: 192,
+ generatedCharacterOffsetIndex: 6,
+ contentLength: 5),
+ BuildLineMapping(
+ documentAbsoluteIndex: 570,
+ documentLineIndex: 13,
+ documentCharacterOffsetIndex: 58,
+ generatedAbsoluteIndex: 4145,
+ generatedLineIndex: 197,
+ generatedCharacterOffsetIndex: 0,
+ contentLength: 2),
+ }
+ },
};
}
}
@@ -965,6 +1243,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
PrefixedAttributeTagHelperDescriptors.Reverse()
},
{ "DuplicateAttributeTagHelpers", null, DefaultPAndInputTagHelperDescriptors },
+ { "DynamicAttributeTagHelpers", null, DynamicAttributeTagHelpers_Descriptors },
};
}
}
diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs
index f75491aa81..95e2262733 100644
--- a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs
+++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperBlockRewriterTest.cs
@@ -32,17 +32,15 @@ namespace Microsoft.AspNet.Razor.TagHelpers
{
return new MarkupBlock(
new MarkupBlock(
+ new DynamicAttributeBlockChunkGenerator(
+ new LocationTagged(
+ string.Empty,
+ new SourceLocation(10, 0, 10)),
+ new SourceLocation(10, 0, 10)),
new StatementBlock(
factory.CodeTransition(),
factory.Code("do {" + extraCode).AsStatement())));
};
- var dateTimeNow = new MarkupBlock(
- new MarkupBlock(
- new ExpressionBlock(
- factory.CodeTransition(),
- factory.Code("DateTime.Now")
- .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
- .Accepts(AcceptedCharacters.NonWhiteSpace))));
return new TheoryData
{
@@ -301,7 +299,20 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new MarkupTagHelperBlock("p",
new List>
{
- new KeyValuePair("class", dateTimeNow)
+ new KeyValuePair(
+ "class",
+ new MarkupBlock(
+ new MarkupBlock(
+ new DynamicAttributeBlockChunkGenerator(
+ new LocationTagged(
+ string.Empty,
+ new SourceLocation(9, 0, 9)),
+ new SourceLocation(9, 0, 9)),
+ new ExpressionBlock(
+ factory.CodeTransition(),
+ factory.Code("DateTime.Now")
+ .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
+ .Accepts(AcceptedCharacters.NonWhiteSpace)))))
})),
new []
{
@@ -803,12 +814,6 @@ namespace Microsoft.AspNet.Razor.TagHelpers
var blockFactory = new BlockFactory(factory);
var malformedErrorFormat = "Found a malformed '{0}' tag helper. Tag helpers must have a start and " +
"end tag or be self closing.";
- var dateTimeNow = new MarkupBlock(
- new ExpressionBlock(
- factory.CodeTransition(),
- factory.Code("DateTime.Now")
- .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
- .Accepts(AcceptedCharacters.NonWhiteSpace)));
yield return new object[]
{
@@ -818,7 +823,20 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new List>
{
new KeyValuePair("class", factory.Markup("foo")),
- new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)),
+ new KeyValuePair(
+ "dynamic",
+ new MarkupBlock(
+ new MarkupBlock(
+ new DynamicAttributeBlockChunkGenerator(
+ new LocationTagged(
+ string.Empty,
+ new SourceLocation(21, 0, 21)),
+ new SourceLocation(21, 0, 21)),
+ new ExpressionBlock(
+ factory.CodeTransition(),
+ factory.Code("DateTime.Now")
+ .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
+ .Accepts(AcceptedCharacters.NonWhiteSpace))))),
new KeyValuePair("style", factory.Markup("color:red;"))
},
new MarkupTagHelperBlock("strong")),
@@ -991,25 +1009,45 @@ namespace Microsoft.AspNet.Razor.TagHelpers
var factory = CreateDefaultSpanFactory();
var blockFactory = new BlockFactory(factory);
var dateTimeNowString = "@DateTime.Now";
- var dateTimeNow = new MarkupBlock(
- new ExpressionBlock(
- factory.CodeTransition(),
- factory.Code("DateTime.Now")
- .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
- .Accepts(AcceptedCharacters.NonWhiteSpace)));
+ var dateTimeNow = new Func(index =>
+ new MarkupBlock(
+ new MarkupBlock(
+ new DynamicAttributeBlockChunkGenerator(
+ new LocationTagged(
+ string.Empty,
+ new SourceLocation(index, 0, index)),
+ new SourceLocation(index, 0, index)),
+ new ExpressionBlock(
+ factory.CodeTransition(),
+ factory.Code("DateTime.Now")
+ .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
+ .Accepts(AcceptedCharacters.NonWhiteSpace)))));
var doWhileString = "@do { var foo = bar; Foo foo++; } while (foo);";
- var doWhile = new MarkupBlock(
- new StatementBlock(
- factory.CodeTransition(),
- factory.Code("do { var foo = bar;").AsStatement(),
- new MarkupBlock(
- new MarkupTagBlock(
- factory.MarkupTransition("")),
- factory.Markup("Foo").Accepts(AcceptedCharacters.None),
- new MarkupTagBlock(
- factory.MarkupTransition("")),
- factory.CodeMarkup(" ").With(new StatementChunkGenerator()).Accepts(AcceptedCharacters.None)),
- factory.Code("foo++; } while (foo);").AsStatement().Accepts(AcceptedCharacters.None)));
+ var doWhile = new Func(index =>
+ new MarkupBlock(
+ new MarkupBlock(
+ new DynamicAttributeBlockChunkGenerator(
+ new LocationTagged(
+ string.Empty,
+ new SourceLocation(index, 0, index)),
+ new SourceLocation(index, 0, index)),
+ new StatementBlock(
+ factory.CodeTransition(),
+ factory.Code("do { var foo = bar;").AsStatement(),
+ new MarkupBlock(
+ new MarkupTagBlock(
+ factory.MarkupTransition("")),
+ factory.Markup("Foo").Accepts(AcceptedCharacters.None),
+ new MarkupTagBlock(
+ factory.MarkupTransition("")),
+ factory
+ .CodeMarkup(" ")
+ .With(new StatementChunkGenerator())
+ .Accepts(AcceptedCharacters.None)),
+ factory
+ .Code("foo++; } while (foo);")
+ .AsStatement()
+ .Accepts(AcceptedCharacters.None)))));
var currentFormattedString = "";
yield return new object[]
@@ -1019,8 +1057,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new MarkupTagHelperBlock("p",
new List>
{
- new KeyValuePair("class", new MarkupBlock(dateTimeNow)),
- new KeyValuePair("style", new MarkupBlock(dateTimeNow))
+ new KeyValuePair("class", dateTimeNow(10)),
+ new KeyValuePair("style", dateTimeNow(32))
}))
};
yield return new object[]
@@ -1030,8 +1068,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new MarkupTagHelperBlock("p",
new List>
{
- new KeyValuePair("class", new MarkupBlock(doWhile)),
- new KeyValuePair("style", new MarkupBlock(doWhile))
+ new KeyValuePair("class", doWhile(10)),
+ new KeyValuePair("style", doWhile(83))
}))
};
@@ -1043,8 +1081,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new MarkupTagHelperBlock("p",
new List>
{
- new KeyValuePair("class", new MarkupBlock(dateTimeNow)),
- new KeyValuePair("style", new MarkupBlock(dateTimeNow))
+ new KeyValuePair("class", dateTimeNow(10)),
+ new KeyValuePair("style", dateTimeNow(32))
},
factory.Markup("Hello World")))
};
@@ -1055,8 +1093,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new MarkupTagHelperBlock("p",
new List>
{
- new KeyValuePair("class", new MarkupBlock(doWhile)),
- new KeyValuePair("style", new MarkupBlock(doWhile))
+ new KeyValuePair("class", doWhile(10)),
+ new KeyValuePair("style", doWhile(83))
},
factory.Markup("Hello World")))
};
@@ -1069,14 +1107,14 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new MarkupTagHelperBlock("p",
new List>
{
- new KeyValuePair("class", new MarkupBlock(dateTimeNow))
+ new KeyValuePair("class", dateTimeNow(10))
},
factory.Markup("Hello")),
factory.Markup(" "),
new MarkupTagHelperBlock("p",
new List>
{
- new KeyValuePair("style", new MarkupBlock(dateTimeNow))
+ new KeyValuePair("style", dateTimeNow(45))
},
factory.Markup("World")))
};
@@ -1087,14 +1125,14 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new MarkupTagHelperBlock("p",
new List>
{
- new KeyValuePair("class", new MarkupBlock(doWhile))
+ new KeyValuePair("class", doWhile(10))
},
factory.Markup("Hello")),
factory.Markup(" "),
new MarkupTagHelperBlock("p",
new List>
{
- new KeyValuePair("style", new MarkupBlock(doWhile))
+ new KeyValuePair("style", doWhile(96))
},
factory.Markup("World")))
};
@@ -1108,8 +1146,8 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new MarkupTagHelperBlock("p",
new List>
{
- new KeyValuePair("class", new MarkupBlock(dateTimeNow)),
- new KeyValuePair("style", new MarkupBlock(dateTimeNow))
+ new KeyValuePair("class", dateTimeNow(10)),
+ new KeyValuePair("style", dateTimeNow(32))
},
factory.Markup("Hello World "),
new MarkupTagBlock(
@@ -1977,12 +2015,19 @@ namespace Microsoft.AspNet.Razor.TagHelpers
{
var factory = CreateDefaultSpanFactory();
var blockFactory = new BlockFactory(factory);
- var dateTimeNow = new MarkupBlock(
- new ExpressionBlock(
- factory.CodeTransition(),
- factory.Code("DateTime.Now")
- .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
- .Accepts(AcceptedCharacters.NonWhiteSpace)));
+ var dateTimeNow = new Func(index =>
+ new MarkupBlock(
+ new MarkupBlock(
+ new DynamicAttributeBlockChunkGenerator(
+ new LocationTagged(
+ string.Empty,
+ new SourceLocation(index, 0, index)),
+ new SourceLocation(index, 0, index)),
+ new ExpressionBlock(
+ factory.CodeTransition(),
+ factory.Code("DateTime.Now")
+ .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
+ .Accepts(AcceptedCharacters.NonWhiteSpace)))));
yield return new object[]
{
@@ -1992,7 +2037,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new List>
{
new KeyValuePair("class", factory.Markup("foo")),
- new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)),
+ new KeyValuePair("dynamic", dateTimeNow(21)),
new KeyValuePair("style", factory.Markup("color:red;"))
}))
};
@@ -2004,7 +2049,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new List>
{
new KeyValuePair("class", factory.Markup("foo")),
- new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)),
+ new KeyValuePair("dynamic", dateTimeNow(21)),
new KeyValuePair("style", factory.Markup("color:red;"))
},
factory.Markup("Hello World")))
@@ -2017,7 +2062,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new List>
{
new KeyValuePair("class", factory.Markup("foo")),
- new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)),
+ new KeyValuePair("dynamic", dateTimeNow(21)),
new KeyValuePair(
"style",
new MarkupBlock(
@@ -2037,7 +2082,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new List>
{
new KeyValuePair("class", factory.Markup("foo")),
- new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow))
+ new KeyValuePair("dynamic", dateTimeNow(21))
},
factory.Markup("Hello")),
factory.Markup(" "),
@@ -2045,7 +2090,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new List>
{
new KeyValuePair("style", factory.Markup("color:red;")),
- new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow))
+ new KeyValuePair("dynamic", dateTimeNow(73))
},
factory.Markup("World")))
};
@@ -2057,7 +2102,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new List>
{
new KeyValuePair("class", factory.Markup("foo")),
- new KeyValuePair("dynamic", new MarkupBlock(dateTimeNow)),
+ new KeyValuePair("dynamic", dateTimeNow(21)),
new KeyValuePair("style", factory.Markup("color:red;"))
},
factory.Markup("Hello World "),
@@ -2394,15 +2439,27 @@ namespace Microsoft.AspNet.Razor.TagHelpers
var stringType = typeof(string).FullName;
var intType = typeof(int).FullName;
var expressionString = "@DateTime.Now + 1";
- var expression = new MarkupBlock(
+ var expression = new Func(index =>
new MarkupBlock(
- new ExpressionBlock(
- factory.CodeTransition(),
- factory.Code("DateTime.Now")
- .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
- .Accepts(AcceptedCharacters.NonWhiteSpace))),
- factory.Markup(" +"),
- factory.Markup(" 1"));
+ new MarkupBlock(
+ new DynamicAttributeBlockChunkGenerator(
+ new LocationTagged(
+ string.Empty,
+ new SourceLocation(index, 0, index)),
+ new SourceLocation(index, 0, index)),
+ new ExpressionBlock(
+ factory.CodeTransition(),
+ factory.Code("DateTime.Now")
+ .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
+ .Accepts(AcceptedCharacters.NonWhiteSpace))),
+ factory.Markup(" +")
+ .With(new LiteralAttributeChunkGenerator(
+ prefix: new LocationTagged(" ", index + 13, 0, index + 13),
+ value: new LocationTagged("+", index + 14, 0, index + 14))),
+ factory.Markup(" 1")
+ .With(new LiteralAttributeChunkGenerator(
+ prefix: new LocationTagged(" ", index + 15, 0, index + 15),
+ value: new LocationTagged("1", index + 16, 0, index + 16)))));
// documentContent, expectedOutput, expectedErrors
return new TheoryData
@@ -2933,7 +2990,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
selfClosing: true,
attributes: new List>()
{
- new KeyValuePair("class", expression),
+ new KeyValuePair("class", expression(14)),
new KeyValuePair("bound-required-int", null),
})),
new[]
@@ -2950,7 +3007,7 @@ namespace Microsoft.AspNet.Razor.TagHelpers
selfClosing: false,
attributes: new List>()
{
- new KeyValuePair("class", expression),
+ new KeyValuePair("class", expression(10)),
new KeyValuePair("bound-int", null),
})),
new[]
@@ -2968,9 +3025,9 @@ namespace Microsoft.AspNet.Razor.TagHelpers
attributes: new List>()
{
new KeyValuePair("bound-required-int", null),
- new KeyValuePair("class", expression),
+ new KeyValuePair("class", expression(36)),
new KeyValuePair("bound-required-string", null),
- new KeyValuePair("class", expression),
+ new KeyValuePair("class", expression(86)),
new KeyValuePair("unbound-required", null),
})),
new[]
@@ -2995,9 +3052,9 @@ namespace Microsoft.AspNet.Razor.TagHelpers
attributes: new List>()
{
new KeyValuePair("bound-int", null),
- new KeyValuePair("class", expression),
+ new KeyValuePair("class", expression(23)),
new KeyValuePair("bound-string", null),
- new KeyValuePair("class", expression),
+ new KeyValuePair("class", expression(64)),
new KeyValuePair("bound-string", null),
})),
new[]
@@ -3029,10 +3086,85 @@ namespace Microsoft.AspNet.Razor.TagHelpers
factory.MetaCode("}").Accepts(AcceptedCharacters.None)),
factory.EmptyHtml());
};
+ Action updateDynamicChunkGenerators = (block) =>
+ {
+ var tagHelperBlock = block.Children.First() as MarkupTagHelperBlock;
+
+ for (var i = 0; i < tagHelperBlock.Attributes.Count; i++)
+ {
+ var attribute = tagHelperBlock.Attributes[i];
+ var holderBlock = attribute.Value as Block;
+
+ if (holderBlock == null)
+ {
+ continue;
+ }
+
+ var valueBlock = holderBlock.Children.FirstOrDefault() as Block;
+ if (valueBlock != null)
+ {
+ var chunkGenerator = valueBlock.ChunkGenerator as DynamicAttributeBlockChunkGenerator;
+
+ if (chunkGenerator != null)
+ {
+ var blockBuilder = new BlockBuilder(holderBlock);
+ var expressionBlockBuilder = new BlockBuilder(valueBlock);
+ var newChunkGenerator = new DynamicAttributeBlockChunkGenerator(
+ new LocationTagged(
+ chunkGenerator.Prefix.Value,
+ new SourceLocation(
+ chunkGenerator.Prefix.Location.AbsoluteIndex + 2,
+ chunkGenerator.Prefix.Location.LineIndex,
+ chunkGenerator.Prefix.Location.CharacterIndex + 2)),
+ new SourceLocation(
+ chunkGenerator.ValueStart.AbsoluteIndex + 2,
+ chunkGenerator.ValueStart.LineIndex,
+ chunkGenerator.ValueStart.CharacterIndex + 2));
+
+ expressionBlockBuilder.ChunkGenerator = newChunkGenerator;
+ blockBuilder.Children[0] = expressionBlockBuilder.Build();
+
+ for (var j = 1; j < blockBuilder.Children.Count; j++)
+ {
+ var span = blockBuilder.Children[j] as Span;
+ if (span != null)
+ {
+ var literalChunkGenerator =
+ span.ChunkGenerator as LiteralAttributeChunkGenerator;
+
+ var spanBuilder = new SpanBuilder(span);
+ spanBuilder.ChunkGenerator = new LiteralAttributeChunkGenerator(
+ prefix: new LocationTagged(
+ literalChunkGenerator.Prefix.Value,
+ new SourceLocation(
+ literalChunkGenerator.Prefix.Location.AbsoluteIndex + 2,
+ literalChunkGenerator.Prefix.Location.LineIndex,
+ literalChunkGenerator.Prefix.Location.CharacterIndex + 2)),
+ value: new LocationTagged(
+ literalChunkGenerator.Value.Value,
+ new SourceLocation(
+ literalChunkGenerator.Value.Location.AbsoluteIndex + 2,
+ literalChunkGenerator.Value.Location.LineIndex,
+ literalChunkGenerator.Value.Location.CharacterIndex + 2)));
+
+ blockBuilder.Children[j] = spanBuilder.Build();
+ }
+ }
+
+ tagHelperBlock.Attributes[i] = new KeyValuePair(
+ attribute.Key,
+ blockBuilder.Build());
+ }
+ }
+ }
+ };
foreach (var data in documentData)
{
data[0] = $"@{{{data[0]}}}";
+
+ updateDynamicChunkGenerators(data[1] as MarkupBlock);
+
data[1] = buildStatementBlock(() => data[1] as MarkupBlock);
var errors = data[2] as RazorError[];
diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs
index 2b23559c32..0be0ea587c 100644
--- a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs
+++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs
@@ -23,13 +23,19 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
{
var factory = CreateDefaultSpanFactory();
var blockFactory = new BlockFactory(factory);
- var dateTimeNow = new MarkupBlock(
- new MarkupBlock(
- new ExpressionBlock(
- factory.CodeTransition(),
- factory.Code("DateTime.Now")
- .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
- .Accepts(AcceptedCharacters.NonWhiteSpace))));
+ var dateTimeNow = new Func(index =>
+ new MarkupBlock(
+ new MarkupBlock(
+ new DynamicAttributeBlockChunkGenerator(
+ new LocationTagged(
+ string.Empty,
+ new SourceLocation(index, 0, index)),
+ new SourceLocation(index, 0, index)),
+ new ExpressionBlock(
+ factory.CodeTransition(),
+ factory.Code("DateTime.Now")
+ .AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
+ .Accepts(AcceptedCharacters.NonWhiteSpace)))));
// documentContent, expectedOutput
return new TheoryData
@@ -73,7 +79,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
selfClosing: true,
attributes: new List>
{
- new KeyValuePair("class", dateTimeNow)
+ new KeyValuePair("class", dateTimeNow(10))
}))
},
{
@@ -94,7 +100,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
"p",
attributes: new List>
{
- new KeyValuePair("class", dateTimeNow)
+ new KeyValuePair("class", dateTimeNow(10))
},
children: factory.Markup("words and spaces")))
},
@@ -135,7 +141,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
selfClosing: true,
attributes: new List>
{
- new KeyValuePair("catchAll", dateTimeNow)
+ new KeyValuePair("catchAll", dateTimeNow(18))
}))
},
{
@@ -156,7 +162,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
"strong",
attributes: new List>
{
- new KeyValuePair("catchAll", dateTimeNow)
+ new KeyValuePair("catchAll", dateTimeNow(18))
},
children: factory.Markup("words and spaces")))
},
@@ -217,7 +223,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
selfClosing: true,
attributes: new List>
{
- new KeyValuePair("notRequired", dateTimeNow),
+ new KeyValuePair("notRequired", dateTimeNow(16)),
new KeyValuePair("class", factory.Markup("btn"))
}))
},
@@ -253,7 +259,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
selfClosing: true,
attributes: new List>
{
- new KeyValuePair("style", dateTimeNow),
+ new KeyValuePair("style", dateTimeNow(12)),
new KeyValuePair("class", factory.Markup("btn"))
}))
},
@@ -276,8 +282,8 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
"div",
attributes: new List>
{
- new KeyValuePair("style", dateTimeNow),
- new KeyValuePair("class", dateTimeNow)
+ new KeyValuePair("style", dateTimeNow(12)),
+ new KeyValuePair("class", dateTimeNow(34))
},
children: factory.Markup("words and spaces")))
},
@@ -376,9 +382,9 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
"div",
attributes: new List>
{
- new KeyValuePair("style", dateTimeNow),
- new KeyValuePair("class", dateTimeNow),
- new KeyValuePair("catchAll", dateTimeNow)
+ new KeyValuePair("style", dateTimeNow(12)),
+ new KeyValuePair("class", dateTimeNow(34)),
+ new KeyValuePair("catchAll", dateTimeNow(59))
},
children: factory.Markup("words and spaces")))
},
diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/ComplexTagHelpers.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/ComplexTagHelpers.cs
index bc2d154ba0..4194474902 100644
--- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/ComplexTagHelpers.cs
+++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/ComplexTagHelpers.cs
@@ -239,15 +239,8 @@ if(true) {
, StartTagHelperWritingScope, EndTagHelperWritingScope);
__PTagHelper = CreateTagHelper();
__tagHelperExecutionContext.Add(__PTagHelper);
- StartTagHelperWritingScope();
- WriteLiteral("Current Time: ");
-#line 8 "ComplexTagHelpers.cshtml"
-Write(DateTime.Now);
-
-#line default
-#line hidden
- __tagHelperStringValueBuffer = EndTagHelperWritingScope();
- __tagHelperExecutionContext.AddHtmlAttribute("time", Html.Raw(__tagHelperStringValueBuffer.ToString()));
+ AddHtmlAttributeValues("time", __tagHelperExecutionContext, Tuple.Create(Tuple.Create("", 148), Tuple.Create("Current", 148), true), Tuple.Create(Tuple.Create(" ", 155), Tuple.Create("Time:", 156), true),
+ Tuple.Create(Tuple.Create(" ", 161), Tuple.Create(DateTime.Now, 162), false));
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
Instrumentation.BeginContext(139, 531, false);
await WriteTagHelperAsync(__tagHelperExecutionContext);
diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/DynamicAttributeTagHelpers.DesignTime.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/DynamicAttributeTagHelpers.DesignTime.cs
new file mode 100644
index 0000000000..3f154a58d5
--- /dev/null
+++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/DynamicAttributeTagHelpers.DesignTime.cs
@@ -0,0 +1,206 @@
+namespace TestOutput
+{
+ using Microsoft.AspNet.Razor.Runtime.TagHelpers;
+ using System;
+ using System.Threading.Tasks;
+
+ public class DynamicAttributeTagHelpers
+ {
+ private static object @__o;
+ private void @__RazorDesignTimeHelpers__()
+ {
+ #pragma warning disable 219
+ string __tagHelperDirectiveSyntaxHelper = null;
+ __tagHelperDirectiveSyntaxHelper =
+#line 1 "DynamicAttributeTagHelpers.cshtml"
+ "something, nice"
+
+#line default
+#line hidden
+ ;
+ #pragma warning restore 219
+ }
+ #line hidden
+ private InputTagHelper __InputTagHelper = null;
+ #line hidden
+ public DynamicAttributeTagHelpers()
+ {
+ }
+
+ #pragma warning disable 1998
+ public override async Task ExecuteAsync()
+ {
+ __InputTagHelper = CreateTagHelper();
+#line 3 "DynamicAttributeTagHelpers.cshtml"
+__o = DateTime.Now;
+
+#line default
+#line hidden
+ __InputTagHelper = CreateTagHelper();
+#line 5 "DynamicAttributeTagHelpers.cshtml"
+if (true) {
+
+#line default
+#line hidden
+
+#line 5 "DynamicAttributeTagHelpers.cshtml"
+__o = string.Empty;
+
+#line default
+#line hidden
+#line 5 "DynamicAttributeTagHelpers.cshtml"
+ } else {
+
+#line default
+#line hidden
+
+#line 5 "DynamicAttributeTagHelpers.cshtml"
+__o = false;
+
+#line default
+#line hidden
+#line 5 "DynamicAttributeTagHelpers.cshtml"
+ }
+
+#line default
+#line hidden
+
+ __InputTagHelper = CreateTagHelper();
+#line 7 "DynamicAttributeTagHelpers.cshtml"
+__o = DateTime.Now;
+
+#line default
+#line hidden
+ __InputTagHelper.Bound = string.Empty;
+#line 7 "DynamicAttributeTagHelpers.cshtml"
+__o = DateTime.Now;
+
+#line default
+#line hidden
+ __InputTagHelper = CreateTagHelper();
+#line 9 "DynamicAttributeTagHelpers.cshtml"
+__o = long.MinValue;
+
+#line default
+#line hidden
+#line 9 "DynamicAttributeTagHelpers.cshtml"
+if (true) {
+
+#line default
+#line hidden
+
+#line 9 "DynamicAttributeTagHelpers.cshtml"
+__o = string.Empty;
+
+#line default
+#line hidden
+#line 9 "DynamicAttributeTagHelpers.cshtml"
+ } else {
+
+#line default
+#line hidden
+
+#line 9 "DynamicAttributeTagHelpers.cshtml"
+__o = false;
+
+#line default
+#line hidden
+#line 9 "DynamicAttributeTagHelpers.cshtml"
+ }
+
+#line default
+#line hidden
+
+#line 9 "DynamicAttributeTagHelpers.cshtml"
+__o = int.MaxValue;
+
+#line default
+#line hidden
+ __InputTagHelper.Bound = string.Empty;
+#line 10 "DynamicAttributeTagHelpers.cshtml"
+__o = long.MinValue;
+
+#line default
+#line hidden
+#line 10 "DynamicAttributeTagHelpers.cshtml"
+if (true) {
+
+#line default
+#line hidden
+
+#line 10 "DynamicAttributeTagHelpers.cshtml"
+__o = string.Empty;
+
+#line default
+#line hidden
+#line 10 "DynamicAttributeTagHelpers.cshtml"
+ } else {
+
+#line default
+#line hidden
+
+#line 10 "DynamicAttributeTagHelpers.cshtml"
+__o = false;
+
+#line default
+#line hidden
+#line 10 "DynamicAttributeTagHelpers.cshtml"
+ }
+
+#line default
+#line hidden
+
+#line 10 "DynamicAttributeTagHelpers.cshtml"
+__o = int.MaxValue;
+
+#line default
+#line hidden
+ __InputTagHelper = CreateTagHelper();
+#line 12 "DynamicAttributeTagHelpers.cshtml"
+__o = long.MinValue;
+
+#line default
+#line hidden
+#line 12 "DynamicAttributeTagHelpers.cshtml"
+__o = DateTime.Now;
+
+#line default
+#line hidden
+#line 12 "DynamicAttributeTagHelpers.cshtml"
+__o = int.MaxValue;
+
+#line default
+#line hidden
+ __InputTagHelper = CreateTagHelper();
+#line 14 "DynamicAttributeTagHelpers.cshtml"
+if (true) {
+
+#line default
+#line hidden
+
+#line 14 "DynamicAttributeTagHelpers.cshtml"
+__o = string.Empty;
+
+#line default
+#line hidden
+#line 14 "DynamicAttributeTagHelpers.cshtml"
+ } else {
+
+#line default
+#line hidden
+
+#line 14 "DynamicAttributeTagHelpers.cshtml"
+__o = false;
+
+#line default
+#line hidden
+#line 14 "DynamicAttributeTagHelpers.cshtml"
+ }
+
+#line default
+#line hidden
+
+ }
+ #pragma warning restore 1998
+ }
+}
diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/DynamicAttributeTagHelpers.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/DynamicAttributeTagHelpers.cs
new file mode 100644
index 0000000000..c2712ca754
--- /dev/null
+++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/DynamicAttributeTagHelpers.cs
@@ -0,0 +1,281 @@
+#pragma checksum "DynamicAttributeTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "782463195265ee647cc2fc63fd5095a80090845b"
+namespace TestOutput
+{
+ using Microsoft.AspNet.Razor.Runtime.TagHelpers;
+ using System;
+ using System.Threading.Tasks;
+
+ public class DynamicAttributeTagHelpers
+ {
+ #line hidden
+ #pragma warning disable 0414
+ private TagHelperContent __tagHelperStringValueBuffer = null;
+ #pragma warning restore 0414
+ private TagHelperExecutionContext __tagHelperExecutionContext = null;
+ private TagHelperRunner __tagHelperRunner = null;
+ private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager();
+ private InputTagHelper __InputTagHelper = null;
+ #line hidden
+ public DynamicAttributeTagHelpers()
+ {
+ }
+
+ #pragma warning disable 1998
+ public override async Task ExecuteAsync()
+ {
+ __tagHelperRunner = __tagHelperRunner ?? new TagHelperRunner();
+ Instrumentation.BeginContext(33, 2, true);
+ WriteLiteral("\r\n");
+ Instrumentation.EndContext();
+ __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
+ }
+ , StartTagHelperWritingScope, EndTagHelperWritingScope);
+ __InputTagHelper = CreateTagHelper();
+ __tagHelperExecutionContext.Add(__InputTagHelper);
+ AddHtmlAttributeValues("unbound", __tagHelperExecutionContext, Tuple.Create(Tuple.Create("", 51), Tuple.Create("prefix", 51), true),
+ Tuple.Create(Tuple.Create(" ", 57), Tuple.Create(DateTime.Now, 58), false));
+ __tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
+ Instrumentation.BeginContext(35, 40, false);
+ await WriteTagHelperAsync(__tagHelperExecutionContext);
+ Instrumentation.EndContext();
+ __tagHelperExecutionContext = __tagHelperScopeManager.End();
+ Instrumentation.BeginContext(75, 4, true);
+ WriteLiteral("\r\n\r\n");
+ Instrumentation.EndContext();
+ __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
+ }
+ , StartTagHelperWritingScope, EndTagHelperWritingScope);
+ __InputTagHelper = CreateTagHelper();
+ __tagHelperExecutionContext.Add(__InputTagHelper);
+ AddHtmlAttributeValues("unbound", __tagHelperExecutionContext,
+ Tuple.Create(Tuple.Create("", 95), Tuple.Create(new Template((__razor_attribute_value_writer) => {
+#line 5 "DynamicAttributeTagHelpers.cshtml"
+if (true) {
+
+#line default
+#line hidden
+
+ Instrumentation.BeginContext(109, 12, false);
+#line 5 "DynamicAttributeTagHelpers.cshtml"
+WriteTo(__razor_attribute_value_writer, string.Empty);
+
+#line default
+#line hidden
+ Instrumentation.EndContext();
+#line 5 "DynamicAttributeTagHelpers.cshtml"
+ } else {
+
+#line default
+#line hidden
+
+ Instrumentation.BeginContext(132, 5, false);
+#line 5 "DynamicAttributeTagHelpers.cshtml"
+WriteTo(__razor_attribute_value_writer, false);
+
+#line default
+#line hidden
+ Instrumentation.EndContext();
+#line 5 "DynamicAttributeTagHelpers.cshtml"
+ }
+
+#line default
+#line hidden
+
+ }
+ ), 95), false), Tuple.Create(Tuple.Create(" ", 139), Tuple.Create("suffix", 140), true));
+ __tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
+ Instrumentation.BeginContext(79, 71, false);
+ await WriteTagHelperAsync(__tagHelperExecutionContext);
+ Instrumentation.EndContext();
+ __tagHelperExecutionContext = __tagHelperScopeManager.End();
+ Instrumentation.BeginContext(150, 4, true);
+ WriteLiteral("\r\n\r\n");
+ Instrumentation.EndContext();
+ __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
+ }
+ , StartTagHelperWritingScope, EndTagHelperWritingScope);
+ __InputTagHelper = CreateTagHelper();
+ __tagHelperExecutionContext.Add(__InputTagHelper);
+ StartTagHelperWritingScope();
+ WriteLiteral("prefix ");
+#line 7 "DynamicAttributeTagHelpers.cshtml"
+WriteLiteral(DateTime.Now);
+
+#line default
+#line hidden
+ WriteLiteral(" suffix");
+ __tagHelperStringValueBuffer = EndTagHelperWritingScope();
+ __InputTagHelper.Bound = __tagHelperStringValueBuffer.ToString();
+ __tagHelperExecutionContext.AddTagHelperAttribute("bound", __InputTagHelper.Bound);
+ AddHtmlAttributeValues("unbound", __tagHelperExecutionContext, Tuple.Create(Tuple.Create("", 206), Tuple.Create("prefix", 206), true),
+ Tuple.Create(Tuple.Create(" ", 212), Tuple.Create(DateTime.Now, 213), false), Tuple.Create(Tuple.Create(" ", 226), Tuple.Create("suffix", 227), true));
+ __tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
+ Instrumentation.BeginContext(154, 83, false);
+ await WriteTagHelperAsync(__tagHelperExecutionContext);
+ Instrumentation.EndContext();
+ __tagHelperExecutionContext = __tagHelperScopeManager.End();
+ Instrumentation.BeginContext(237, 4, true);
+ WriteLiteral("\r\n\r\n");
+ Instrumentation.EndContext();
+ __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
+ }
+ , StartTagHelperWritingScope, EndTagHelperWritingScope);
+ __InputTagHelper = CreateTagHelper();
+ __tagHelperExecutionContext.Add(__InputTagHelper);
+ StartTagHelperWritingScope();
+#line 9 "DynamicAttributeTagHelpers.cshtml"
+WriteLiteral(long.MinValue);
+
+#line default
+#line hidden
+ WriteLiteral(" ");
+#line 9 "DynamicAttributeTagHelpers.cshtml"
+if (true) {
+
+#line default
+#line hidden
+
+#line 9 "DynamicAttributeTagHelpers.cshtml"
+WriteLiteral(string.Empty);
+
+#line default
+#line hidden
+#line 9 "DynamicAttributeTagHelpers.cshtml"
+ } else {
+
+#line default
+#line hidden
+
+#line 9 "DynamicAttributeTagHelpers.cshtml"
+WriteLiteral(false);
+
+#line default
+#line hidden
+#line 9 "DynamicAttributeTagHelpers.cshtml"
+ }
+
+#line default
+#line hidden
+
+ WriteLiteral(" ");
+#line 9 "DynamicAttributeTagHelpers.cshtml"
+WriteLiteral(int.MaxValue);
+
+#line default
+#line hidden
+ __tagHelperStringValueBuffer = EndTagHelperWritingScope();
+ __InputTagHelper.Bound = __tagHelperStringValueBuffer.ToString();
+ __tagHelperExecutionContext.AddTagHelperAttribute("bound", __InputTagHelper.Bound);
+ AddHtmlAttributeValues("unbound", __tagHelperExecutionContext,
+ Tuple.Create(Tuple.Create("", 347), Tuple.Create(long.MinValue, 347), false),
+ Tuple.Create(Tuple.Create(" ", 361), Tuple.Create(new Template((__razor_attribute_value_writer) => {
+#line 10 "DynamicAttributeTagHelpers.cshtml"
+if (true) {
+
+#line default
+#line hidden
+
+ Instrumentation.BeginContext(376, 12, false);
+#line 10 "DynamicAttributeTagHelpers.cshtml"
+WriteTo(__razor_attribute_value_writer, string.Empty);
+
+#line default
+#line hidden
+ Instrumentation.EndContext();
+#line 10 "DynamicAttributeTagHelpers.cshtml"
+ } else {
+
+#line default
+#line hidden
+
+ Instrumentation.BeginContext(399, 5, false);
+#line 10 "DynamicAttributeTagHelpers.cshtml"
+WriteTo(__razor_attribute_value_writer, false);
+
+#line default
+#line hidden
+ Instrumentation.EndContext();
+#line 10 "DynamicAttributeTagHelpers.cshtml"
+ }
+
+#line default
+#line hidden
+
+ }
+ ), 362), false),
+ Tuple.Create(Tuple.Create(" ", 406), Tuple.Create(int.MaxValue, 407), false));
+ __tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
+ Instrumentation.BeginContext(241, 183, false);
+ await WriteTagHelperAsync(__tagHelperExecutionContext);
+ Instrumentation.EndContext();
+ __tagHelperExecutionContext = __tagHelperScopeManager.End();
+ Instrumentation.BeginContext(424, 4, true);
+ WriteLiteral("\r\n\r\n");
+ Instrumentation.EndContext();
+ __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
+ }
+ , StartTagHelperWritingScope, EndTagHelperWritingScope);
+ __InputTagHelper = CreateTagHelper();
+ __tagHelperExecutionContext.Add(__InputTagHelper);
+ AddHtmlAttributeValues("unbound", __tagHelperExecutionContext,
+ Tuple.Create(Tuple.Create("", 444), Tuple.Create(long.MinValue, 444), false),
+ Tuple.Create(Tuple.Create(" ", 458), Tuple.Create(DateTime.Now, 459), false), Tuple.Create(Tuple.Create(" ", 472), Tuple.Create("static", 473), true), Tuple.Create(Tuple.Create(" ", 479), Tuple.Create("content", 483), true),
+ Tuple.Create(Tuple.Create(" ", 490), Tuple.Create(int.MaxValue, 491), false));
+ __tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
+ Instrumentation.BeginContext(428, 80, false);
+ await WriteTagHelperAsync(__tagHelperExecutionContext);
+ Instrumentation.EndContext();
+ __tagHelperExecutionContext = __tagHelperScopeManager.End();
+ Instrumentation.BeginContext(508, 4, true);
+ WriteLiteral("\r\n\r\n");
+ Instrumentation.EndContext();
+ __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", true, "test", async() => {
+ }
+ , StartTagHelperWritingScope, EndTagHelperWritingScope);
+ __InputTagHelper = CreateTagHelper();
+ __tagHelperExecutionContext.Add(__InputTagHelper);
+ AddHtmlAttributeValues("unbound", __tagHelperExecutionContext,
+ Tuple.Create(Tuple.Create("", 528), Tuple.Create(new Template((__razor_attribute_value_writer) => {
+#line 14 "DynamicAttributeTagHelpers.cshtml"
+if (true) {
+
+#line default
+#line hidden
+
+ Instrumentation.BeginContext(542, 12, false);
+#line 14 "DynamicAttributeTagHelpers.cshtml"
+WriteTo(__razor_attribute_value_writer, string.Empty);
+
+#line default
+#line hidden
+ Instrumentation.EndContext();
+#line 14 "DynamicAttributeTagHelpers.cshtml"
+ } else {
+
+#line default
+#line hidden
+
+ Instrumentation.BeginContext(565, 5, false);
+#line 14 "DynamicAttributeTagHelpers.cshtml"
+WriteTo(__razor_attribute_value_writer, false);
+
+#line default
+#line hidden
+ Instrumentation.EndContext();
+#line 14 "DynamicAttributeTagHelpers.cshtml"
+ }
+
+#line default
+#line hidden
+
+ }
+ ), 528), false));
+ __tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
+ Instrumentation.BeginContext(512, 64, false);
+ await WriteTagHelperAsync(__tagHelperExecutionContext);
+ Instrumentation.EndContext();
+ __tagHelperExecutionContext = __tagHelperScopeManager.End();
+ }
+ #pragma warning restore 1998
+ }
+}
diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/TagHelpersInSection.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/TagHelpersInSection.cs
index e23dab4f1f..db2d98010a 100644
--- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/TagHelpersInSection.cs
+++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Output/TagHelpersInSection.cs
@@ -83,15 +83,8 @@ WriteLiteral(DateTime.Now);
__tagHelperStringValueBuffer = EndTagHelperWritingScope();
__MyTagHelper.BoundProperty = __tagHelperStringValueBuffer.ToString();
__tagHelperExecutionContext.AddTagHelperAttribute("boundproperty", __MyTagHelper.BoundProperty);
- StartTagHelperWritingScope();
- WriteLiteral("Current Time: ");
-#line 9 "TagHelpersInSection.cshtml"
-Write(DateTime.Now);
-
-#line default
-#line hidden
- __tagHelperStringValueBuffer = EndTagHelperWritingScope();
- __tagHelperExecutionContext.AddHtmlAttribute("unboundproperty", Html.Raw(__tagHelperStringValueBuffer.ToString()));
+ AddHtmlAttributeValues("unboundproperty", __tagHelperExecutionContext, Tuple.Create(Tuple.Create("", 188), Tuple.Create("Current", 188), true), Tuple.Create(Tuple.Create(" ", 195), Tuple.Create("Time:", 196), true),
+ Tuple.Create(Tuple.Create(" ", 201), Tuple.Create(DateTime.Now, 202), false));
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
Instrumentation.BeginContext(114, 245, false);
await WriteTagHelperToAsync(__razor_template_writer, __tagHelperExecutionContext);
diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Source/DynamicAttributeTagHelpers.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Source/DynamicAttributeTagHelpers.cshtml
new file mode 100644
index 0000000000..8cf97d3f5d
--- /dev/null
+++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/Source/DynamicAttributeTagHelpers.cshtml
@@ -0,0 +1,14 @@
+@addTagHelper "something, nice"
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file