Add support for data bound symbols as attribute names.
- Took the HTML5 spec approach of disallowing specific characters and accepting all others. - Added several parser and code generation tests to cover both `TagHelper` and non-`TagHelper` variations of symbol bound attribute names. #137
This commit is contained in:
parent
fb0d235301
commit
7239eb015c
|
|
@ -464,7 +464,7 @@ namespace Microsoft.AspNet.Razor.Parser
|
|||
// Read the 'name' (i.e. read until the '=' or whitespace/newline)
|
||||
var name = Enumerable.Empty<HtmlSymbol>();
|
||||
var whitespaceAfterAttributeName = Enumerable.Empty<HtmlSymbol>();
|
||||
if (At(HtmlSymbolType.Text))
|
||||
if (IsValidAttributeNameSymbol(CurrentSymbol))
|
||||
{
|
||||
name = ReadWhile(sym =>
|
||||
sym.Type != HtmlSymbolType.WhiteSpace &&
|
||||
|
|
@ -1136,5 +1136,28 @@ namespace Microsoft.AspNet.Razor.Parser
|
|||
}
|
||||
Output(SpanKind.Markup);
|
||||
}
|
||||
|
||||
internal static bool IsValidAttributeNameSymbol(HtmlSymbol symbol)
|
||||
{
|
||||
if (symbol == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// These restrictions cover most of the spec defined: http://www.w3.org/TR/html5/syntax.html#attributes-0
|
||||
// However, it's not all of it. For instance we don't special case control characters or allow OpenAngle.
|
||||
// It also doesn't try to exclude Razor specific features such as the @ transition. This is based on the
|
||||
// expectation that the parser handles such scenarios prior to falling through to name resolution.
|
||||
var symbolType = symbol.Type;
|
||||
return symbolType != HtmlSymbolType.WhiteSpace &&
|
||||
symbolType != HtmlSymbolType.NewLine &&
|
||||
symbolType != HtmlSymbolType.CloseAngle &&
|
||||
symbolType != HtmlSymbolType.OpenAngle &&
|
||||
symbolType != HtmlSymbolType.ForwardSlash &&
|
||||
symbolType != HtmlSymbolType.DoubleQuote &&
|
||||
symbolType != HtmlSymbolType.SingleQuote &&
|
||||
symbolType != HtmlSymbolType.Equals &&
|
||||
symbolType != HtmlSymbolType.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,13 +186,21 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
|
||||
builder.Accept(symbol);
|
||||
}
|
||||
else if (name == null && symbol.Type == HtmlSymbolType.Text)
|
||||
else if (name == null && HtmlMarkupParser.IsValidAttributeNameSymbol(symbol))
|
||||
{
|
||||
// We've captured all leading whitespace prior to the attribute name.
|
||||
// We're now at: " |asp-for='...'" or " |asp-for=..."
|
||||
// The goal here is to capture the attribute name.
|
||||
|
||||
name = symbol.Content;
|
||||
var symbolContents = htmlSymbols
|
||||
.Skip(i) // Skip prefix
|
||||
.TakeWhile(nameSymbol => HtmlMarkupParser.IsValidAttributeNameSymbol(nameSymbol))
|
||||
.Select(nameSymbol => nameSymbol.Content);
|
||||
|
||||
// Move the indexer past the attribute name symbols.
|
||||
i += symbolContents.Count() - 1;
|
||||
|
||||
name = string.Concat(symbolContents);
|
||||
attributeValueStartLocation = SourceLocation.Advance(attributeValueStartLocation, name);
|
||||
}
|
||||
else if (symbol.Type == HtmlSymbolType.Equals)
|
||||
|
|
@ -322,10 +330,15 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal
|
|||
return TryParseSpan(childSpan, descriptors, errorSink);
|
||||
}
|
||||
|
||||
var textSymbol = childSpan.Symbols.FirstHtmlSymbolAs(HtmlSymbolType.Text);
|
||||
var name = textSymbol != null ? textSymbol.Content : null;
|
||||
var nameSymbols = childSpan
|
||||
.Symbols
|
||||
.OfType<HtmlSymbol>()
|
||||
.SkipWhile(symbol => !HtmlMarkupParser.IsValidAttributeNameSymbol(symbol)) // Skip prefix
|
||||
.TakeWhile(nameSymbol => HtmlMarkupParser.IsValidAttributeNameSymbol(nameSymbol))
|
||||
.Select(nameSymbol => nameSymbol.Content);
|
||||
|
||||
if (name == null)
|
||||
var name = string.Concat(nameSymbols);
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
errorSink.OnError(
|
||||
childSpan.Start,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#if DNXCORE50
|
||||
|
|
@ -20,6 +21,62 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
private static IEnumerable<TagHelperDescriptor> PrefixedPAndInputTagHelperDescriptors { get; }
|
||||
= BuildPAndInputTagHelperDescriptors(prefix: "THS");
|
||||
|
||||
private static IEnumerable<TagHelperDescriptor> SymbolBoundTagHelperDescriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "*",
|
||||
TypeName = "CatchAllTagHelper",
|
||||
AssemblyName = "SomeAssembly",
|
||||
Attributes = new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "[item]",
|
||||
PropertyName = "ListItems",
|
||||
TypeName = typeof(List<string>).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "[(item)]",
|
||||
PropertyName = "ArrayItems",
|
||||
TypeName = typeof(string[]).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "(click)",
|
||||
PropertyName = "Event1",
|
||||
TypeName = typeof(Action).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "(^click)",
|
||||
PropertyName = "Event2",
|
||||
TypeName = typeof(Action).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "*something",
|
||||
PropertyName = "StringProperty1",
|
||||
TypeName = typeof(string).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "#local",
|
||||
PropertyName = "StringProperty2",
|
||||
TypeName = typeof(string).FullName
|
||||
},
|
||||
},
|
||||
RequiredAttributes = new[] { "bound" },
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<TagHelperDescriptor> MinimizedTagHelpers_Descriptors
|
||||
{
|
||||
get
|
||||
|
|
@ -1518,6 +1575,53 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
contentLength: 15),
|
||||
}
|
||||
},
|
||||
{
|
||||
"SymbolBoundAttributes",
|
||||
"SymbolBoundAttributes.DesignTime",
|
||||
SymbolBoundTagHelperDescriptors,
|
||||
new[]
|
||||
{
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 14,
|
||||
documentLineIndex: 0,
|
||||
generatedAbsoluteIndex: 487,
|
||||
generatedLineIndex: 15,
|
||||
characterOffsetIndex: 14,
|
||||
contentLength: 9),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 296,
|
||||
documentLineIndex: 11,
|
||||
documentCharacterOffsetIndex: 18,
|
||||
generatedAbsoluteIndex: 1013,
|
||||
generatedLineIndex: 34,
|
||||
generatedCharacterOffsetIndex: 32,
|
||||
contentLength: 5),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 345,
|
||||
documentLineIndex: 12,
|
||||
documentCharacterOffsetIndex: 20,
|
||||
generatedAbsoluteIndex: 1199,
|
||||
generatedLineIndex: 40,
|
||||
generatedCharacterOffsetIndex: 33,
|
||||
contentLength: 5),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 399,
|
||||
documentLineIndex: 13,
|
||||
documentCharacterOffsetIndex: 23,
|
||||
generatedAbsoluteIndex: 1381,
|
||||
generatedLineIndex: 46,
|
||||
generatedCharacterOffsetIndex: 29,
|
||||
contentLength: 13),
|
||||
BuildLineMapping(
|
||||
documentAbsoluteIndex: 481,
|
||||
documentLineIndex: 14,
|
||||
documentCharacterOffsetIndex: 24,
|
||||
generatedAbsoluteIndex: 1571,
|
||||
generatedLineIndex: 52,
|
||||
generatedCharacterOffsetIndex: 29,
|
||||
contentLength: 13),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1567,6 +1671,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator
|
|||
{ "DynamicAttributeTagHelpers", null, DynamicAttributeTagHelpers_Descriptors },
|
||||
{ "TransitionsInTagHelperAttributes", null, DefaultPAndInputTagHelperDescriptors },
|
||||
{ "NestedScriptTagTagHelpers", null, DefaultPAndInputTagHelperDescriptors },
|
||||
{ "SymbolBoundAttributes", null, SymbolBoundTagHelperDescriptors },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,174 @@ namespace Microsoft.AspNet.Razor.Test.Parser.Html
|
|||
{
|
||||
public class HtmlAttributeTest : CsHtmlMarkupParserTestBase
|
||||
{
|
||||
public static TheoryData SymbolBoundAttributeNames
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<string>
|
||||
{
|
||||
"[item]",
|
||||
"[(item,",
|
||||
"(click)",
|
||||
"(^click)",
|
||||
"*something",
|
||||
"#local",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(SymbolBoundAttributeNames))]
|
||||
public void SymbolBoundAttributes_BeforeEqualWhitespace(string attributeName)
|
||||
{
|
||||
// Arrange
|
||||
var attributeNameLength = attributeName.Length;
|
||||
var newlineLength = Environment.NewLine.Length;
|
||||
var prefixLocation1 = new SourceLocation(
|
||||
absoluteIndex: 2,
|
||||
lineIndex: 0,
|
||||
characterIndex: 2);
|
||||
var suffixLocation1 = new SourceLocation(
|
||||
absoluteIndex: 8 + newlineLength + attributeNameLength,
|
||||
lineIndex: 1,
|
||||
characterIndex: 5 + attributeNameLength);
|
||||
var valueLocation1 = new SourceLocation(
|
||||
absoluteIndex: 5 + attributeNameLength + newlineLength,
|
||||
lineIndex: 1,
|
||||
characterIndex: 2 + attributeNameLength);
|
||||
var prefixLocation2 = SourceLocation.Advance(suffixLocation1, "'");
|
||||
var suffixLocation2 = new SourceLocation(
|
||||
absoluteIndex: 15 + attributeNameLength * 2 + newlineLength * 2,
|
||||
lineIndex: 2,
|
||||
characterIndex: 4);
|
||||
var valueLocation2 = new SourceLocation(
|
||||
absoluteIndex: 12 + attributeNameLength * 2 + newlineLength * 2,
|
||||
lineIndex: 2,
|
||||
characterIndex: 1);
|
||||
|
||||
// Act & Assert
|
||||
ParseBlockTest(
|
||||
$"<a {attributeName}{Environment.NewLine}='Foo'\t{attributeName}={Environment.NewLine}'Bar' />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagBlock(
|
||||
Factory.Markup("<a"),
|
||||
new MarkupBlock(
|
||||
new AttributeBlockChunkGenerator(
|
||||
attributeName,
|
||||
prefix: new LocationTagged<string>(
|
||||
$" {attributeName}{Environment.NewLine}='", prefixLocation1),
|
||||
suffix: new LocationTagged<string>("'", suffixLocation1)),
|
||||
Factory.Markup($" {attributeName}{Environment.NewLine}='").With(SpanChunkGenerator.Null),
|
||||
Factory.Markup("Foo").With(
|
||||
new LiteralAttributeChunkGenerator(
|
||||
prefix: new LocationTagged<string>(string.Empty, valueLocation1),
|
||||
value: new LocationTagged<string>("Foo", valueLocation1))),
|
||||
Factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||
new MarkupBlock(
|
||||
new AttributeBlockChunkGenerator(
|
||||
attributeName,
|
||||
prefix: new LocationTagged<string>(
|
||||
$"\t{attributeName}={Environment.NewLine}'", prefixLocation2),
|
||||
suffix: new LocationTagged<string>("'", suffixLocation2)),
|
||||
Factory.Markup($"\t{attributeName}={Environment.NewLine}'").With(SpanChunkGenerator.Null),
|
||||
Factory.Markup("Bar").With(
|
||||
new LiteralAttributeChunkGenerator(
|
||||
prefix: new LocationTagged<string>(string.Empty, valueLocation2),
|
||||
value: new LocationTagged<string>("Bar", valueLocation2))),
|
||||
Factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||
Factory.Markup(" />").Accepts(AcceptedCharacters.None))));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(SymbolBoundAttributeNames))]
|
||||
public void SymbolBoundAttributes_Whitespace(string attributeName)
|
||||
{
|
||||
// Arrange
|
||||
var attributeNameLength = attributeName.Length;
|
||||
var newlineLength = Environment.NewLine.Length;
|
||||
var prefixLocation1 = new SourceLocation(
|
||||
absoluteIndex: 2,
|
||||
lineIndex: 0,
|
||||
characterIndex: 2);
|
||||
var suffixLocation1 = new SourceLocation(
|
||||
absoluteIndex: 10 + newlineLength + attributeNameLength,
|
||||
lineIndex: 1,
|
||||
characterIndex: 5 + attributeNameLength + newlineLength);
|
||||
var valueLocation1 = new SourceLocation(
|
||||
absoluteIndex: 7 + attributeNameLength + newlineLength,
|
||||
lineIndex: 1,
|
||||
characterIndex: 4 + attributeNameLength);
|
||||
var prefixLocation2 = SourceLocation.Advance(suffixLocation1, "'");
|
||||
var suffixLocation2 = new SourceLocation(
|
||||
absoluteIndex: 17 + attributeNameLength * 2 + newlineLength * 2,
|
||||
lineIndex: 2,
|
||||
characterIndex: 5 + attributeNameLength);
|
||||
var valueLocation2 = new SourceLocation(
|
||||
absoluteIndex: 14 + attributeNameLength * 2 + newlineLength * 2,
|
||||
lineIndex: 2,
|
||||
characterIndex: 2 + attributeNameLength);
|
||||
|
||||
// Act & Assert
|
||||
ParseBlockTest(
|
||||
$"<a {Environment.NewLine} {attributeName}='Foo'\t{Environment.NewLine}{attributeName}='Bar' />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagBlock(
|
||||
Factory.Markup("<a"),
|
||||
new MarkupBlock(
|
||||
new AttributeBlockChunkGenerator(
|
||||
attributeName,
|
||||
prefix: new LocationTagged<string>(
|
||||
$" {Environment.NewLine} {attributeName}='", prefixLocation1),
|
||||
suffix: new LocationTagged<string>("'", suffixLocation1)),
|
||||
Factory.Markup($" {Environment.NewLine} {attributeName}='").With(SpanChunkGenerator.Null),
|
||||
Factory.Markup("Foo").With(
|
||||
new LiteralAttributeChunkGenerator(
|
||||
prefix: new LocationTagged<string>(string.Empty, valueLocation1),
|
||||
value: new LocationTagged<string>("Foo", valueLocation1))),
|
||||
Factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||
new MarkupBlock(
|
||||
new AttributeBlockChunkGenerator(
|
||||
attributeName,
|
||||
prefix: new LocationTagged<string>(
|
||||
$"\t{Environment.NewLine}{attributeName}='", prefixLocation2),
|
||||
suffix: new LocationTagged<string>("'", suffixLocation2)),
|
||||
Factory.Markup($"\t{Environment.NewLine}{attributeName}='").With(SpanChunkGenerator.Null),
|
||||
Factory.Markup("Bar").With(
|
||||
new LiteralAttributeChunkGenerator(
|
||||
prefix: new LocationTagged<string>(string.Empty, valueLocation2),
|
||||
value: new LocationTagged<string>("Bar", valueLocation2))),
|
||||
Factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||
Factory.Markup(" />").Accepts(AcceptedCharacters.None))));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(SymbolBoundAttributeNames))]
|
||||
public void SymbolBoundAttributes(string attributeName)
|
||||
{
|
||||
// Arrange
|
||||
var attributeNameLength = attributeName.Length;
|
||||
var suffixLocation = 8 + attributeNameLength;
|
||||
var valueLocation = 5 + attributeNameLength;
|
||||
|
||||
// Act & Assert
|
||||
ParseBlockTest($"<a {attributeName}='Foo' />",
|
||||
new MarkupBlock(
|
||||
new MarkupTagBlock(
|
||||
Factory.Markup("<a"),
|
||||
new MarkupBlock(
|
||||
new AttributeBlockChunkGenerator(
|
||||
attributeName,
|
||||
prefix: new LocationTagged<string>($" {attributeName}='", 2, 0, 2),
|
||||
suffix: new LocationTagged<string>("'", suffixLocation, 0, suffixLocation)),
|
||||
Factory.Markup($" {attributeName}='").With(SpanChunkGenerator.Null),
|
||||
Factory.Markup("Foo").With(
|
||||
new LiteralAttributeChunkGenerator(
|
||||
prefix: new LocationTagged<string>(string.Empty, valueLocation, 0, valueLocation),
|
||||
value: new LocationTagged<string>("Foo", valueLocation, 0, valueLocation))),
|
||||
Factory.Markup("'").With(SpanChunkGenerator.Null)),
|
||||
Factory.Markup(" />").Accepts(AcceptedCharacters.None))));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SimpleLiteralAttribute()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -18,6 +18,154 @@ namespace Microsoft.AspNet.Razor.TagHelpers
|
|||
{
|
||||
public class TagHelperBlockRewriterTest : TagHelperRewritingTestBase
|
||||
{
|
||||
public static TheoryData SymbolBoundAttributeData
|
||||
{
|
||||
get
|
||||
{
|
||||
var factory = CreateDefaultSpanFactory();
|
||||
|
||||
return new TheoryData<string, MarkupBlock>
|
||||
{
|
||||
{
|
||||
"<ul bound [item]='items'></ul>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("ul",
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("[item]", factory.CodeMarkup("items"))
|
||||
}))
|
||||
},
|
||||
{
|
||||
"<ul bound [(item)]='items'></ul>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("ul",
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("[(item)]", factory.CodeMarkup("items"))
|
||||
}))
|
||||
},
|
||||
{
|
||||
"<button bound (click)='doSomething()'>Click Me</button>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("button",
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>(
|
||||
"(click)",
|
||||
factory.CodeMarkup("doSomething()"))
|
||||
},
|
||||
children: factory.Markup("Click Me")))
|
||||
},
|
||||
{
|
||||
"<button bound (^click)='doSomething()'>Click Me</button>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("button",
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>(
|
||||
"(^click)",
|
||||
factory.CodeMarkup("doSomething()"))
|
||||
},
|
||||
children: factory.Markup("Click Me")))
|
||||
},
|
||||
{
|
||||
"<template bound *something='value'></template>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("template",
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("*something", factory.Markup("value"))
|
||||
}))
|
||||
},
|
||||
{
|
||||
"<div bound #localminimized></div>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("div",
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("#localminimized", null)
|
||||
}))
|
||||
},
|
||||
{
|
||||
"<div bound #local='value'></div>",
|
||||
new MarkupBlock(
|
||||
new MarkupTagHelperBlock("div",
|
||||
attributes: new List<KeyValuePair<string, SyntaxTreeNode>>
|
||||
{
|
||||
new KeyValuePair<string, SyntaxTreeNode>("bound", null),
|
||||
new KeyValuePair<string, SyntaxTreeNode>("#local", factory.Markup("value"))
|
||||
}))
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(SymbolBoundAttributeData))]
|
||||
public void Rewrite_CanHandleSymbolBoundAttributes(string documentContent, MarkupBlock expectedOutput)
|
||||
{
|
||||
// Arrange
|
||||
var descriptors = new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "*",
|
||||
TypeName = "CatchAllTagHelper",
|
||||
AssemblyName = "SomeAssembly",
|
||||
Attributes = new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "[item]",
|
||||
PropertyName = "ListItems",
|
||||
TypeName = typeof(List<string>).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "[(item)]",
|
||||
PropertyName = "ArrayItems",
|
||||
TypeName = typeof(string[]).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "(click)",
|
||||
PropertyName = "Event1",
|
||||
TypeName = typeof(Action).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "(^click)",
|
||||
PropertyName = "Event2",
|
||||
TypeName = typeof(Action).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "*something",
|
||||
PropertyName = "StringProperty1",
|
||||
TypeName = typeof(string).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "#local",
|
||||
PropertyName = "StringProperty2",
|
||||
TypeName = typeof(string).FullName
|
||||
},
|
||||
},
|
||||
RequiredAttributes = new[] { "bound" },
|
||||
},
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||
|
||||
// Act & Assert
|
||||
EvaluateData(descriptorProvider, documentContent, expectedOutput, expectedErrors: new RazorError[0]);
|
||||
}
|
||||
|
||||
public static TheoryData WithoutEndTagElementData
|
||||
{
|
||||
get
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
namespace TestOutput
|
||||
{
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class SymbolBoundAttributes
|
||||
{
|
||||
private static object @__o;
|
||||
private void @__RazorDesignTimeHelpers__()
|
||||
{
|
||||
#pragma warning disable 219
|
||||
string __tagHelperDirectiveSyntaxHelper = null;
|
||||
__tagHelperDirectiveSyntaxHelper =
|
||||
#line 1 "SymbolBoundAttributes.cshtml"
|
||||
"*, nice"
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
;
|
||||
#pragma warning restore 219
|
||||
}
|
||||
#line hidden
|
||||
private CatchAllTagHelper __CatchAllTagHelper = null;
|
||||
#line hidden
|
||||
public SymbolBoundAttributes()
|
||||
{
|
||||
}
|
||||
|
||||
#pragma warning disable 1998
|
||||
public override async Task ExecuteAsync()
|
||||
{
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
#line 12 "SymbolBoundAttributes.cshtml"
|
||||
__CatchAllTagHelper.ListItems = items;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
#line 13 "SymbolBoundAttributes.cshtml"
|
||||
__CatchAllTagHelper.ArrayItems = items;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
#line 14 "SymbolBoundAttributes.cshtml"
|
||||
__CatchAllTagHelper.Event1 = doSomething();
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
#line 15 "SymbolBoundAttributes.cshtml"
|
||||
__CatchAllTagHelper.Event2 = doSomething();
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__CatchAllTagHelper.StringProperty1 = "value";
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__CatchAllTagHelper.StringProperty2 = "value";
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
#pragma checksum "SymbolBoundAttributes.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "65ef0c8f673481f5ab85bd4936f91f31e84c490c"
|
||||
namespace TestOutput
|
||||
{
|
||||
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class SymbolBoundAttributes
|
||||
{
|
||||
#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 CatchAllTagHelper __CatchAllTagHelper = null;
|
||||
#line hidden
|
||||
public SymbolBoundAttributes()
|
||||
{
|
||||
}
|
||||
|
||||
#pragma warning disable 1998
|
||||
public override async Task ExecuteAsync()
|
||||
{
|
||||
__tagHelperRunner = __tagHelperRunner ?? new TagHelperRunner();
|
||||
Instrumentation.BeginContext(25, 253, true);
|
||||
WriteLiteral("\r\n<ul [item]=\"items\"></ul>\r\n<ul [(item)]=\"items\"></ul>\r\n<button (click)=\"doSometh" +
|
||||
"ing()\">Click Me</button>\r\n<button (^click)=\"doSomething()\">Click Me</button>\r\n<t" +
|
||||
"emplate *something=\"value\">\r\n</template>\r\n<div #local></div>\r\n<div #local=\"value" +
|
||||
"\"></div>\r\n\r\n");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("ul", TagMode.StartTagAndEndTag, "test", async() => {
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__tagHelperExecutionContext.Add(__CatchAllTagHelper);
|
||||
__tagHelperExecutionContext.AddMinimizedHtmlAttribute("bound");
|
||||
#line 12 "SymbolBoundAttributes.cshtml"
|
||||
__CatchAllTagHelper.ListItems = items;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("[item]", __CatchAllTagHelper.ListItems);
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("[item]", Html.Raw("items"));
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.BeginContext(278, 45, false);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(323, 2, true);
|
||||
WriteLiteral("\r\n");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("ul", TagMode.StartTagAndEndTag, "test", async() => {
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__tagHelperExecutionContext.Add(__CatchAllTagHelper);
|
||||
__tagHelperExecutionContext.AddMinimizedHtmlAttribute("bound");
|
||||
#line 13 "SymbolBoundAttributes.cshtml"
|
||||
__CatchAllTagHelper.ArrayItems = items;
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("[(item)]", __CatchAllTagHelper.ArrayItems);
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("[(item)]", Html.Raw("items"));
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.BeginContext(325, 49, false);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(374, 2, true);
|
||||
WriteLiteral("\r\n");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("button", TagMode.StartTagAndEndTag, "test", async() => {
|
||||
Instrumentation.BeginContext(438, 8, true);
|
||||
WriteLiteral("Click Me");
|
||||
Instrumentation.EndContext();
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__tagHelperExecutionContext.Add(__CatchAllTagHelper);
|
||||
__tagHelperExecutionContext.AddMinimizedHtmlAttribute("bound");
|
||||
#line 14 "SymbolBoundAttributes.cshtml"
|
||||
__CatchAllTagHelper.Event1 = doSomething();
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("(click)", __CatchAllTagHelper.Event1);
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("(click)", Html.Raw("doSomething()"));
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.BeginContext(376, 79, false);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(455, 2, true);
|
||||
WriteLiteral("\r\n");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("button", TagMode.StartTagAndEndTag, "test", async() => {
|
||||
Instrumentation.BeginContext(521, 8, true);
|
||||
WriteLiteral("Click Me");
|
||||
Instrumentation.EndContext();
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__tagHelperExecutionContext.Add(__CatchAllTagHelper);
|
||||
__tagHelperExecutionContext.AddMinimizedHtmlAttribute("bound");
|
||||
#line 15 "SymbolBoundAttributes.cshtml"
|
||||
__CatchAllTagHelper.Event2 = doSomething();
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("(^click)", __CatchAllTagHelper.Event2);
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("(^click)", Html.Raw("doSomething()"));
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.BeginContext(457, 81, false);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(538, 2, true);
|
||||
WriteLiteral("\r\n");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("template", TagMode.StartTagAndEndTag, "test", async() => {
|
||||
Instrumentation.BeginContext(594, 2, true);
|
||||
WriteLiteral("\r\n");
|
||||
Instrumentation.EndContext();
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__tagHelperExecutionContext.Add(__CatchAllTagHelper);
|
||||
__tagHelperExecutionContext.AddMinimizedHtmlAttribute("bound");
|
||||
__CatchAllTagHelper.StringProperty1 = "value";
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("*something", __CatchAllTagHelper.StringProperty1);
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("*something", Html.Raw("value"));
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.BeginContext(540, 67, false);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(607, 2, true);
|
||||
WriteLiteral("\r\n");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("div", TagMode.StartTagAndEndTag, "test", async() => {
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__tagHelperExecutionContext.Add(__CatchAllTagHelper);
|
||||
__tagHelperExecutionContext.AddMinimizedHtmlAttribute("bound");
|
||||
__tagHelperExecutionContext.AddMinimizedHtmlAttribute("#localminimized");
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.BeginContext(609, 33, false);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
Instrumentation.BeginContext(642, 2, true);
|
||||
WriteLiteral("\r\n");
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("div", TagMode.StartTagAndEndTag, "test", async() => {
|
||||
}
|
||||
, StartTagHelperWritingScope, EndTagHelperWritingScope);
|
||||
__CatchAllTagHelper = CreateTagHelper<CatchAllTagHelper>();
|
||||
__tagHelperExecutionContext.Add(__CatchAllTagHelper);
|
||||
__tagHelperExecutionContext.AddMinimizedHtmlAttribute("bound");
|
||||
__CatchAllTagHelper.StringProperty2 = "value";
|
||||
__tagHelperExecutionContext.AddTagHelperAttribute("#local", __CatchAllTagHelper.StringProperty2);
|
||||
__tagHelperExecutionContext.AddHtmlAttribute("#local", Html.Raw("value"));
|
||||
__tagHelperExecutionContext.Output = await __tagHelperRunner.RunAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.BeginContext(644, 47, false);
|
||||
await WriteTagHelperAsync(__tagHelperExecutionContext);
|
||||
Instrumentation.EndContext();
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
@addTagHelper "*, nice"
|
||||
|
||||
<ul [item]="items"></ul>
|
||||
<ul [(item)]="items"></ul>
|
||||
<button (click)="doSomething()">Click Me</button>
|
||||
<button (^click)="doSomething()">Click Me</button>
|
||||
<template *something="value">
|
||||
</template>
|
||||
<div #local></div>
|
||||
<div #local="value"></div>
|
||||
|
||||
<ul bound [item]="items" [item]="items"></ul>
|
||||
<ul bound [(item)]="items" [(item)]="items"></ul>
|
||||
<button bound (click)="doSomething()" (click)="doSomething()">Click Me</button>
|
||||
<button bound (^click)="doSomething()" (^click)="doSomething()">Click Me</button>
|
||||
<template bound *something="value" *something="value">
|
||||
</template>
|
||||
<div bound #localminimized></div>
|
||||
<div bound #local="value" #local="value"></div>
|
||||
Loading…
Reference in New Issue