Fix C# design time projections for complex TagHelper attributes.

- TagHelper attributes that have expressions intermingled within them (resulting in Block elements) resulted in Spans in the attribute being falsely marked as SpanKind.Markup.
- Updated tests to account for new SpanKind.Code behavior.
- Added complex scenario to validate SpanKind.Code is flowed through to surround attributes.

#387
This commit is contained in:
N. Taylor Mullen 2015-06-24 16:09:47 -07:00
parent a73867eabe
commit 039062c5eb
4 changed files with 118 additions and 27 deletions

View File

@ -254,7 +254,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
// If we're not after an equal then we should treat the value as if it were a minimized attribute.
var attributeValueBuilder = afterEquals ? builder : null;
result.AttributeValueNode =
CreateMarkupAttribute(name, attributeValueBuilder, result.IsBoundNonStringAttribute);
CreateMarkupAttribute(attributeValueBuilder, result.IsBoundNonStringAttribute);
return result;
}
@ -337,17 +337,45 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
var spanBuilder = new SpanBuilder(child);
result.AttributeValueNode =
CreateMarkupAttribute(name, spanBuilder, result.IsBoundNonStringAttribute);
CreateMarkupAttribute(spanBuilder, result.IsBoundNonStringAttribute);
return result;
}
}
result.AttributeValueNode = block;
result.AttributeValueNode = ConvertToMarkupAttributeBlock(block, result.IsBoundNonStringAttribute);
return result;
}
private static Block ConvertToMarkupAttributeBlock(Block block, bool isBoundNonStringAttribute)
{
var blockBuilder = new BlockBuilder
{
ChunkGenerator = block.ChunkGenerator,
Type = block.Type
};
foreach (var child in block.Children)
{
SyntaxTreeNode markupAttributeChild;
if (child.IsBlock)
{
markupAttributeChild = ConvertToMarkupAttributeBlock((Block)child, isBoundNonStringAttribute);
}
else
{
var spanBuilder = new SpanBuilder((Span)child);
markupAttributeChild = CreateMarkupAttribute(spanBuilder, isBoundNonStringAttribute);
}
blockBuilder.Children.Add(markupAttributeChild);
}
return blockBuilder.Build();
}
private static Block RebuildChunkGenerators(Block block)
{
var builder = new BlockBuilder(block);
@ -440,10 +468,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
return nodeStart + firstNonWhitespaceSymbol.Start;
}
private static SyntaxTreeNode CreateMarkupAttribute(
string name,
SpanBuilder builder,
bool isBoundNonStringAttribute)
private static SyntaxTreeNode CreateMarkupAttribute(SpanBuilder builder, bool isBoundNonStringAttribute)
{
Span value = null;

View File

@ -385,6 +385,12 @@ namespace Microsoft.AspNet.Razor.Test.Framework
return Builder.Build();
}
public SpanConstructor As(SpanKind spanKind)
{
Builder.Kind = spanKind;
return this;
}
public SpanConstructor With(ISpanChunkGenerator generator)
{
Builder.ChunkGenerator = generator;

View File

@ -603,6 +603,58 @@ namespace Microsoft.AspNet.Razor.TagHelpers
new MarkupBlock(factory.Markup("Time:"), dateTimeNow))
}))
},
{
"<person age=\"1 + @value + 2\" birthday='(bool)@Bag[\"val\"] ? @@DateTime : @DateTime.Now'/>",
new MarkupBlock(
new MarkupTagHelperBlock("person",
selfClosing: true,
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
{
new KeyValuePair<string, SyntaxTreeNode>(
"age",
new MarkupBlock(
factory.CodeMarkup("1"),
factory.CodeMarkup(" +"),
new MarkupBlock(
factory.CodeMarkup(" "),
new ExpressionBlock(
factory.CodeTransition().As(SpanKind.Code),
factory
.Code("value")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace))),
factory.CodeMarkup(" +"),
factory.CodeMarkup(" 2"))),
new KeyValuePair<string, SyntaxTreeNode>(
"birthday",
new MarkupBlock(
factory.CodeMarkup("(bool)"),
new MarkupBlock(
new ExpressionBlock(
factory.CodeTransition().As(SpanKind.Code),
factory
.Code("Bag[\"val\"]")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace))),
factory.CodeMarkup(" ?"),
new MarkupBlock(
factory.CodeMarkup(" @").Accepts(AcceptedCharacters.None),
factory.CodeMarkup("@")
.With(SpanChunkGenerator.Null)
.Accepts(AcceptedCharacters.None)),
factory.CodeMarkup("DateTime"),
factory.CodeMarkup(" :"),
new MarkupBlock(
factory.CodeMarkup(" "),
new ExpressionBlock(
factory.CodeTransition().As(SpanKind.Code),
factory
.Code("DateTime.Now")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace)))
))
}))
},
{
"<person age=\"12\" birthday=\"DateTime.Now\" name=\"Time: @DateTime.Now\" />",
new MarkupBlock(
@ -675,17 +727,21 @@ namespace Microsoft.AspNet.Razor.TagHelpers
"age",
new MarkupBlock(
new MarkupBlock(
factory.Markup("@").Accepts(AcceptedCharacters.None),
factory.Markup("@")
factory.CodeMarkup("@").Accepts(AcceptedCharacters.None),
factory.CodeMarkup("@")
.With(SpanChunkGenerator.Null)
.Accepts(AcceptedCharacters.None)),
new MarkupBlock(
factory.EmptyHtml(),
factory.EmptyHtml().As(SpanKind.Code),
new ExpressionBlock(
factory.CodeTransition(),
factory.MetaCode("(").Accepts(AcceptedCharacters.None),
factory.CodeTransition().As(SpanKind.Code),
factory.MetaCode("(")
.Accepts(AcceptedCharacters.None)
.As(SpanKind.Code),
factory.Code("11+1").AsExpression(),
factory.MetaCode(")").Accepts(AcceptedCharacters.None))))),
factory.MetaCode(")")
.Accepts(AcceptedCharacters.None)
.As(SpanKind.Code))))),
new KeyValuePair<string, SyntaxTreeNode>(
"birthday",
factory.CodeMarkup("DateTime.Now")),
@ -1653,13 +1709,13 @@ namespace Microsoft.AspNet.Razor.TagHelpers
"bound",
new MarkupBlock(
new MarkupBlock(
factory.Markup(" "),
factory.CodeMarkup(" "),
new ExpressionBlock(
factory.CodeTransition(),
factory.CodeTransition().As(SpanKind.Code),
factory.Code("true")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace))),
factory.Markup(" ")))
factory.CodeMarkup(" ")))
}
})),
new RazorError[0]
@ -1677,13 +1733,17 @@ namespace Microsoft.AspNet.Razor.TagHelpers
"bound",
new MarkupBlock(
new MarkupBlock(
factory.Markup(" "),
factory.CodeMarkup(" "),
new ExpressionBlock(
factory.CodeTransition(),
factory.MetaCode("(").Accepts(AcceptedCharacters.None),
factory.CodeTransition().As(SpanKind.Code),
factory.MetaCode("(")
.Accepts(AcceptedCharacters.None)
.As(SpanKind.Code),
factory.Code("true").AsExpression(),
factory.MetaCode(")").Accepts(AcceptedCharacters.None))),
factory.Markup(" ")))
factory.MetaCode(")")
.Accepts(AcceptedCharacters.None)
.As(SpanKind.Code))),
factory.CodeMarkup(" ")))
}
})),
new RazorError[0]

View File

@ -1131,7 +1131,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
new MarkupBlock(
new MarkupBlock(
new ExpressionBlock(
factory.CodeTransition(),
factory.CodeTransition().As(SpanKind.Code),
factory.Code("DateTime.Now")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace)))))
@ -1153,7 +1153,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
new MarkupBlock(
new MarkupBlock(
new ExpressionBlock(
factory.CodeTransition(),
factory.CodeTransition().As(SpanKind.Code),
factory.Code("DateTime.Now")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace)))))
@ -1174,12 +1174,12 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers
"bound",
new MarkupBlock(
new MarkupBlock(
factory.Markup("@").Accepts(AcceptedCharacters.None),
factory.Markup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharacters.None)),
factory.CodeMarkup("@").Accepts(AcceptedCharacters.None),
factory.CodeMarkup("@").With(SpanChunkGenerator.Null).Accepts(AcceptedCharacters.None)),
new MarkupBlock(
factory.EmptyHtml(),
factory.EmptyHtml().As(SpanKind.Code),
new ExpressionBlock(
factory.CodeTransition(),
factory.CodeTransition().As(SpanKind.Code),
factory.Code("DateTime.Now")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharacters.NonWhiteSpace)))))