Add experimental flag to un-special-case data-

This change allows blazor to opt into treating data- attributes the same
way as normal attributes in the parser.
This commit is contained in:
Ryan Nowak 2018-04-05 14:50:40 -07:00
parent d1c0ab587c
commit 4cd24a2b08
9 changed files with 128 additions and 41 deletions

View File

@ -931,7 +931,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{ {
// First, determine if this is a 'data-' attribute (since those can't use conditional attributes) // First, determine if this is a 'data-' attribute (since those can't use conditional attributes)
var name = string.Concat(nameSymbols.Select(s => s.Content)); var name = string.Concat(nameSymbols.Select(s => s.Content));
var attributeCanBeConditional = !name.StartsWith("data-", StringComparison.OrdinalIgnoreCase); var attributeCanBeConditional =
Context.FeatureFlags.EXPERIMENTAL_AllowConditionalDataDashAttributes ||
!name.StartsWith("data-", StringComparison.OrdinalIgnoreCase);
// Accept the whitespace and name // Accept the whitespace and name
Accept(whitespace); Accept(whitespace);

View File

@ -23,6 +23,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Source = new SeekableTextReader(chars, source.FilePath); Source = new SeekableTextReader(chars, source.FilePath);
DesignTimeMode = options.DesignTime; DesignTimeMode = options.DesignTime;
FeatureFlags = options.FeatureFlags;
ParseLeadingDirectives = options.ParseLeadingDirectives; ParseLeadingDirectives = options.ParseLeadingDirectives;
Builder = new SyntaxTreeBuilder(); Builder = new SyntaxTreeBuilder();
ErrorSink = new ErrorSink(); ErrorSink = new ErrorSink();
@ -33,6 +34,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
public ErrorSink ErrorSink { get; set; } public ErrorSink ErrorSink { get; set; }
public RazorParserFeatureFlags FeatureFlags { get; }
public HashSet<string> SeenDirectives { get; } public HashSet<string> SeenDirectives { get; }
public ITextDocument Source { get; } public ITextDocument Source { get; }

View File

@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.Razor.Language
{ {
var allowMinimizedBooleanTagHelperAttributes = false; var allowMinimizedBooleanTagHelperAttributes = false;
var allowHtmlCommentsInTagHelpers = false; var allowHtmlCommentsInTagHelpers = false;
var experimental_AllowConditionalDataDashAttributes = false;
if (version.CompareTo(RazorLanguageVersion.Version_2_1) >= 0) if (version.CompareTo(RazorLanguageVersion.Version_2_1) >= 0)
{ {
@ -17,24 +18,40 @@ namespace Microsoft.AspNetCore.Razor.Language
allowHtmlCommentsInTagHelpers = true; allowHtmlCommentsInTagHelpers = true;
} }
return new DefaultRazorParserFeatureFlags(allowMinimizedBooleanTagHelperAttributes, allowHtmlCommentsInTagHelpers); if (version.CompareTo(RazorLanguageVersion.Experimental) >= 0)
{
experimental_AllowConditionalDataDashAttributes = true;
}
return new DefaultRazorParserFeatureFlags(
allowMinimizedBooleanTagHelperAttributes,
allowHtmlCommentsInTagHelpers,
experimental_AllowConditionalDataDashAttributes);
} }
public abstract bool AllowMinimizedBooleanTagHelperAttributes { get; } public abstract bool AllowMinimizedBooleanTagHelperAttributes { get; }
public abstract bool AllowHtmlCommentsInTagHelpers { get; } public abstract bool AllowHtmlCommentsInTagHelpers { get; }
public abstract bool EXPERIMENTAL_AllowConditionalDataDashAttributes { get; }
private class DefaultRazorParserFeatureFlags : RazorParserFeatureFlags private class DefaultRazorParserFeatureFlags : RazorParserFeatureFlags
{ {
public DefaultRazorParserFeatureFlags(bool allowMinimizedBooleanTagHelperAttributes, bool allowHtmlCommentsInTagHelpers) public DefaultRazorParserFeatureFlags(
bool allowMinimizedBooleanTagHelperAttributes,
bool allowHtmlCommentsInTagHelpers,
bool experimental_AllowConditionalDataDashAttributes)
{ {
AllowMinimizedBooleanTagHelperAttributes = allowMinimizedBooleanTagHelperAttributes; AllowMinimizedBooleanTagHelperAttributes = allowMinimizedBooleanTagHelperAttributes;
AllowHtmlCommentsInTagHelpers = allowHtmlCommentsInTagHelpers; AllowHtmlCommentsInTagHelpers = allowHtmlCommentsInTagHelpers;
EXPERIMENTAL_AllowConditionalDataDashAttributes = experimental_AllowConditionalDataDashAttributes;
} }
public override bool AllowMinimizedBooleanTagHelperAttributes { get; } public override bool AllowMinimizedBooleanTagHelperAttributes { get; }
public override bool AllowHtmlCommentsInTagHelpers { get; } public override bool AllowHtmlCommentsInTagHelpers { get; }
public override bool EXPERIMENTAL_AllowConditionalDataDashAttributes { get; }
} }
} }
} }

View File

@ -1957,7 +1957,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Block expected, Block expected,
params RazorDiagnostic[] expectedErrors) params RazorDiagnostic[] expectedErrors)
{ {
var result = ParseCodeBlock(document, descriptors, designTime: false); var result = ParseCodeBlock(RazorLanguageVersion.Latest, document, descriptors, designTime: false);
EvaluateResults(result, expected, expectedErrors); EvaluateResults(result, expected, expectedErrors);
} }

View File

@ -9,9 +9,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{ {
internal abstract ISet<string> KeywordSet { get; } internal abstract ISet<string> KeywordSet { get; }
internal override RazorSyntaxTree ParseBlock(string document, IEnumerable<DirectiveDescriptor> directives, bool designTime) internal override RazorSyntaxTree ParseBlock(
RazorLanguageVersion version,
string document,
IEnumerable<DirectiveDescriptor> directives,
bool designTime)
{ {
return ParseCodeBlock(document, directives, designTime); return ParseCodeBlock(version, document, directives, designTime);
} }
internal void ImplicitExpressionTest(string input, params RazorDiagnostic[] errors) internal void ImplicitExpressionTest(string input, params RazorDiagnostic[] errors)

View File

@ -559,6 +559,30 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
Assert.Equal(rewritten.Children.Count(), results.Root.Children.Count()); Assert.Equal(rewritten.Children.Count(), results.Root.Children.Count());
} }
[Fact]
public void ConditionalAttributesAreEnabledForDataAttributesWithExperimentalFlag()
{
ParseBlockTest(
RazorLanguageVersion.Experimental,
"<span data-foo='@foo'></span>",
new MarkupBlock(
new MarkupTagBlock(
Factory.Markup("<span"),
new MarkupBlock(
new AttributeBlockChunkGenerator("data-foo", new LocationTagged<string>(" data-foo='", 5, 0, 5), new LocationTagged<string>("'", 20, 0, 20)),
Factory.Markup(" data-foo='").With(SpanChunkGenerator.Null),
new MarkupBlock(new DynamicAttributeBlockChunkGenerator(new LocationTagged<string>(string.Empty, 16, 0, 16), 16, 0, 16),
new ExpressionBlock(
Factory.CodeTransition(),
Factory.Code("foo")
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
.Accepts(AcceptedCharactersInternal.NonWhiteSpace))),
Factory.Markup("'").With(SpanChunkGenerator.Null)),
Factory.Markup(">").Accepts(AcceptedCharactersInternal.None)),
new MarkupTagBlock(
Factory.Markup("</span>").Accepts(AcceptedCharactersInternal.None))));
}
[Fact] [Fact]
public void ConditionalAttributesAreDisabledForDataAttributesInBlock() public void ConditionalAttributesAreDisabledForDataAttributesInBlock()
{ {

View File

@ -1,16 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic; using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language.Legacy namespace Microsoft.AspNetCore.Razor.Language.Legacy
{ {
public abstract class MarkupParserTestBase : CodeParserTestBase public abstract class MarkupParserTestBase : CodeParserTestBase
{ {
internal override RazorSyntaxTree ParseBlock(string document, IEnumerable<DirectiveDescriptor> directives, bool designTime) internal override RazorSyntaxTree ParseBlock(
RazorLanguageVersion version,
string document,
IEnumerable<DirectiveDescriptor> directives,
bool designTime)
{ {
return ParseHtmlBlock(document, directives, designTime); return ParseHtmlBlock(version, document, directives, designTime);
} }
internal virtual void SingleSpanDocumentTest(string document, BlockKindInternal blockKind, SpanKindInternal spanType) internal virtual void SingleSpanDocumentTest(string document, BlockKindInternal blockKind, SpanKindInternal spanType)

View File

@ -3962,7 +3962,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
.Build(), .Build(),
}; };
var featureFlags = new TestRazorParserFeatureFlags(allowMinimizedBooleanTagHelperAttributes: false, allowHtmlCommentsInTagHelper: false); var featureFlags = new TestRazorParserFeatureFlags();
var expectedOutput = new MarkupBlock( var expectedOutput = new MarkupBlock(
new MarkupTagHelperBlock( new MarkupTagHelperBlock(
@ -3994,15 +3994,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private class TestRazorParserFeatureFlags : RazorParserFeatureFlags private class TestRazorParserFeatureFlags : RazorParserFeatureFlags
{ {
public TestRazorParserFeatureFlags(bool allowMinimizedBooleanTagHelperAttributes, bool allowHtmlCommentsInTagHelper) public TestRazorParserFeatureFlags(
bool allowMinimizedBooleanTagHelperAttributes = false,
bool allowHtmlCommentsInTagHelper = false,
bool experimental_AllowConditionalDataDashAttributes = false)
{ {
AllowMinimizedBooleanTagHelperAttributes = allowMinimizedBooleanTagHelperAttributes; AllowMinimizedBooleanTagHelperAttributes = allowMinimizedBooleanTagHelperAttributes;
AllowHtmlCommentsInTagHelpers = allowHtmlCommentsInTagHelper; AllowHtmlCommentsInTagHelpers = allowHtmlCommentsInTagHelper;
EXPERIMENTAL_AllowConditionalDataDashAttributes = experimental_AllowConditionalDataDashAttributes;
} }
public override bool AllowMinimizedBooleanTagHelperAttributes { get; } public override bool AllowMinimizedBooleanTagHelperAttributes { get; }
public override bool AllowHtmlCommentsInTagHelpers { get; } public override bool AllowHtmlCommentsInTagHelpers { get; }
public override bool EXPERIMENTAL_AllowConditionalDataDashAttributes { get; }
} }
} }
} }

View File

@ -32,23 +32,43 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
internal RazorSyntaxTree ParseBlock(string document, bool designTime) internal RazorSyntaxTree ParseBlock(string document, bool designTime)
{ {
return ParseBlock(document, null, designTime); return ParseBlock(RazorLanguageVersion.Latest, document, designTime);
} }
internal abstract RazorSyntaxTree ParseBlock(string document, IEnumerable<DirectiveDescriptor> directives, bool designTime); internal RazorSyntaxTree ParseBlock(RazorLanguageVersion version, string document, bool designTime)
internal virtual RazorSyntaxTree ParseDocument(string document, bool designTime = false)
{ {
return ParseDocument(document, null, designTime); return ParseBlock(version, document, null, designTime);
} }
internal virtual RazorSyntaxTree ParseDocument(string document, IEnumerable<DirectiveDescriptor> directives, bool designTime = false) internal RazorSyntaxTree ParseBlock(string document, IEnumerable<DirectiveDescriptor> directives, bool designTime)
{
return ParseBlock(RazorLanguageVersion.Latest, document, directives, designTime);
}
internal abstract RazorSyntaxTree ParseBlock(RazorLanguageVersion version, string document, IEnumerable<DirectiveDescriptor> directives, bool designTime);
internal RazorSyntaxTree ParseDocument(string document, bool designTime = false)
{
return ParseDocument(RazorLanguageVersion.Latest, document, designTime);
}
internal RazorSyntaxTree ParseDocument(RazorLanguageVersion version, string document, bool designTime = false)
{
return ParseDocument(version, document, null, designTime);
}
internal RazorSyntaxTree ParseDocument(string document, IEnumerable<DirectiveDescriptor> directives, bool designTime = false)
{
return ParseDocument(RazorLanguageVersion.Latest, document, directives, designTime);
}
internal virtual RazorSyntaxTree ParseDocument(RazorLanguageVersion version, string document, IEnumerable<DirectiveDescriptor> directives, bool designTime = false)
{ {
directives = directives ?? Array.Empty<DirectiveDescriptor>(); directives = directives ?? Array.Empty<DirectiveDescriptor>();
var source = TestRazorSourceDocument.Create(document, filePath: null); var source = TestRazorSourceDocument.Create(document, filePath: null);
var options = CreateParserOptions(directives, designTime); var options = CreateParserOptions(version, directives, designTime);
var context = new ParserContext(source, options); var context = new ParserContext(source, options);
var codeParser = new CSharpCodeParser(directives, context); var codeParser = new CSharpCodeParser(directives, context);
@ -73,12 +93,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return syntaxTree; return syntaxTree;
} }
internal virtual RazorSyntaxTree ParseHtmlBlock(string document, IEnumerable<DirectiveDescriptor> directives, bool designTime = false) internal virtual RazorSyntaxTree ParseHtmlBlock(RazorLanguageVersion version, string document, IEnumerable<DirectiveDescriptor> directives, bool designTime = false)
{ {
directives = directives ?? Array.Empty<DirectiveDescriptor>(); directives = directives ?? Array.Empty<DirectiveDescriptor>();
var source = TestRazorSourceDocument.Create(document, filePath: null); var source = TestRazorSourceDocument.Create(document, filePath: null);
var options = CreateParserOptions(directives, designTime); var options = CreateParserOptions(version, directives, designTime);
var context = new ParserContext(source, options); var context = new ParserContext(source, options);
var parser = new HtmlMarkupParser(context); var parser = new HtmlMarkupParser(context);
@ -95,12 +115,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return RazorSyntaxTree.Create(root, source, diagnostics, options); return RazorSyntaxTree.Create(root, source, diagnostics, options);
} }
internal virtual RazorSyntaxTree ParseCodeBlock(string document, bool designTime = false) internal RazorSyntaxTree ParseCodeBlock(string document, bool designTime = false)
{ {
return ParseCodeBlock(document, Enumerable.Empty<DirectiveDescriptor>(), designTime); return ParseCodeBlock(RazorLanguageVersion.Latest, document, Enumerable.Empty<DirectiveDescriptor>(), designTime);
} }
internal virtual RazorSyntaxTree ParseCodeBlock( internal virtual RazorSyntaxTree ParseCodeBlock(
RazorLanguageVersion version,
string document, string document,
IEnumerable<DirectiveDescriptor> directives, IEnumerable<DirectiveDescriptor> directives,
bool designTime) bool designTime)
@ -108,7 +129,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
directives = directives ?? Array.Empty<DirectiveDescriptor>(); directives = directives ?? Array.Empty<DirectiveDescriptor>();
var source = TestRazorSourceDocument.Create(document, filePath: null); var source = TestRazorSourceDocument.Create(document, filePath: null);
var options = CreateParserOptions(directives, designTime); var options = CreateParserOptions(version, directives, designTime);
var context = new ParserContext(source, options); var context = new ParserContext(source, options);
var parser = new CSharpCodeParser(directives, context); var parser = new CSharpCodeParser(directives, context);
@ -152,6 +173,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
ParseBlockTest(document, null, designTime, expectedErrors); ParseBlockTest(document, null, designTime, expectedErrors);
} }
internal virtual void ParseBlockTest(RazorLanguageVersion version, string document, Block expectedRoot)
{
ParseBlockTest(version, document, expectedRoot, false, null);
}
internal virtual void ParseBlockTest(string document, Block expectedRoot) internal virtual void ParseBlockTest(string document, Block expectedRoot)
{ {
ParseBlockTest(document, expectedRoot, false, null); ParseBlockTest(document, expectedRoot, false, null);
@ -182,9 +208,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
ParseBlockTest(document, null, expected, designTime, expectedErrors); ParseBlockTest(document, null, expected, designTime, expectedErrors);
} }
internal virtual void ParseBlockTest(RazorLanguageVersion version, string document, Block expected, bool designTime, params RazorDiagnostic[] expectedErrors)
{
ParseBlockTest(version, document, null, expected, designTime, expectedErrors);
}
internal virtual void ParseBlockTest(string document, IEnumerable<DirectiveDescriptor> directives, Block expected, bool designTime, params RazorDiagnostic[] expectedErrors) internal virtual void ParseBlockTest(string document, IEnumerable<DirectiveDescriptor> directives, Block expected, bool designTime, params RazorDiagnostic[] expectedErrors)
{ {
var result = ParseBlock(document, directives, designTime); ParseBlockTest(RazorLanguageVersion.Latest, document, directives, expected, designTime, expectedErrors);
}
internal virtual void ParseBlockTest(RazorLanguageVersion version, string document, IEnumerable<DirectiveDescriptor> directives, Block expected, bool designTime, params RazorDiagnostic[] expectedErrors)
{
var result = ParseBlock(version, document, directives, designTime);
if (FixupSpans) if (FixupSpans)
{ {
@ -604,24 +640,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
return block.Build(); return block.Build();
} }
private static RazorParserOptions CreateParserOptions(IEnumerable<DirectiveDescriptor> directives, bool designTime) private static RazorParserOptions CreateParserOptions(
RazorLanguageVersion version,
IEnumerable<DirectiveDescriptor> directives,
bool designTime)
{ {
if (designTime) return new DefaultRazorParserOptions(
{ directives.ToArray(),
return RazorParserOptions.CreateDesignTime(ConfigureOptions); designTime,
} parseLeadingDirectives: false,
else version: version);
{
return RazorParserOptions.Create(ConfigureOptions);
}
void ConfigureOptions(RazorParserOptionsBuilder builder)
{
foreach (var directive in directives)
{
builder.Directives.Add(directive);
}
}
} }
private class IgnoreOutputBlock : Block private class IgnoreOutputBlock : Block