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. // 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; var attributeValueBuilder = afterEquals ? builder : null;
result.AttributeValueNode = result.AttributeValueNode =
CreateMarkupAttribute(name, attributeValueBuilder, result.IsBoundNonStringAttribute); CreateMarkupAttribute(attributeValueBuilder, result.IsBoundNonStringAttribute);
return result; return result;
} }
@ -337,17 +337,45 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
var spanBuilder = new SpanBuilder(child); var spanBuilder = new SpanBuilder(child);
result.AttributeValueNode = result.AttributeValueNode =
CreateMarkupAttribute(name, spanBuilder, result.IsBoundNonStringAttribute); CreateMarkupAttribute(spanBuilder, result.IsBoundNonStringAttribute);
return result; return result;
} }
} }
result.AttributeValueNode = block; result.AttributeValueNode = ConvertToMarkupAttributeBlock(block, result.IsBoundNonStringAttribute);
return result; 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) private static Block RebuildChunkGenerators(Block block)
{ {
var builder = new BlockBuilder(block); var builder = new BlockBuilder(block);
@ -440,10 +468,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
return nodeStart + firstNonWhitespaceSymbol.Start; return nodeStart + firstNonWhitespaceSymbol.Start;
} }
private static SyntaxTreeNode CreateMarkupAttribute( private static SyntaxTreeNode CreateMarkupAttribute(SpanBuilder builder, bool isBoundNonStringAttribute)
string name,
SpanBuilder builder,
bool isBoundNonStringAttribute)
{ {
Span value = null; Span value = null;

View File

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

View File

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

View File

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