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)
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(whitespace);

View File

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

View File

@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.Razor.Language
{
var allowMinimizedBooleanTagHelperAttributes = false;
var allowHtmlCommentsInTagHelpers = false;
var experimental_AllowConditionalDataDashAttributes = false;
if (version.CompareTo(RazorLanguageVersion.Version_2_1) >= 0)
{
@ -17,24 +18,40 @@ namespace Microsoft.AspNetCore.Razor.Language
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 AllowHtmlCommentsInTagHelpers { get; }
public abstract bool EXPERIMENTAL_AllowConditionalDataDashAttributes { get; }
private class DefaultRazorParserFeatureFlags : RazorParserFeatureFlags
{
public DefaultRazorParserFeatureFlags(bool allowMinimizedBooleanTagHelperAttributes, bool allowHtmlCommentsInTagHelpers)
public DefaultRazorParserFeatureFlags(
bool allowMinimizedBooleanTagHelperAttributes,
bool allowHtmlCommentsInTagHelpers,
bool experimental_AllowConditionalDataDashAttributes)
{
AllowMinimizedBooleanTagHelperAttributes = allowMinimizedBooleanTagHelperAttributes;
AllowHtmlCommentsInTagHelpers = allowHtmlCommentsInTagHelpers;
EXPERIMENTAL_AllowConditionalDataDashAttributes = experimental_AllowConditionalDataDashAttributes;
}
public override bool AllowMinimizedBooleanTagHelperAttributes { 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,
params RazorDiagnostic[] expectedErrors)
{
var result = ParseCodeBlock(document, descriptors, designTime: false);
var result = ParseCodeBlock(RazorLanguageVersion.Latest, document, descriptors, designTime: false);
EvaluateResults(result, expected, expectedErrors);
}

View File

@ -9,9 +9,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
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)

View File

@ -559,6 +559,30 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
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]
public void ConditionalAttributesAreDisabledForDataAttributesInBlock()
{

View File

@ -1,16 +1,19 @@
// 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.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Language.Legacy
{
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)

View File

@ -3962,7 +3962,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
.Build(),
};
var featureFlags = new TestRazorParserFeatureFlags(allowMinimizedBooleanTagHelperAttributes: false, allowHtmlCommentsInTagHelper: false);
var featureFlags = new TestRazorParserFeatureFlags();
var expectedOutput = new MarkupBlock(
new MarkupTagHelperBlock(
@ -3994,15 +3994,21 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
private class TestRazorParserFeatureFlags : RazorParserFeatureFlags
{
public TestRazorParserFeatureFlags(bool allowMinimizedBooleanTagHelperAttributes, bool allowHtmlCommentsInTagHelper)
public TestRazorParserFeatureFlags(
bool allowMinimizedBooleanTagHelperAttributes = false,
bool allowHtmlCommentsInTagHelper = false,
bool experimental_AllowConditionalDataDashAttributes = false)
{
AllowMinimizedBooleanTagHelperAttributes = allowMinimizedBooleanTagHelperAttributes;
AllowHtmlCommentsInTagHelpers = allowHtmlCommentsInTagHelper;
EXPERIMENTAL_AllowConditionalDataDashAttributes = experimental_AllowConditionalDataDashAttributes;
}
public override bool AllowMinimizedBooleanTagHelperAttributes { 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)
{
return ParseBlock(document, null, designTime);
return ParseBlock(RazorLanguageVersion.Latest, document, designTime);
}
internal abstract RazorSyntaxTree ParseBlock(string document, IEnumerable<DirectiveDescriptor> directives, bool designTime);
internal virtual RazorSyntaxTree ParseDocument(string document, bool designTime = false)
internal RazorSyntaxTree ParseBlock(RazorLanguageVersion version, string document, bool designTime)
{
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>();
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 codeParser = new CSharpCodeParser(directives, context);
@ -73,12 +93,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
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>();
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 parser = new HtmlMarkupParser(context);
@ -95,12 +115,13 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
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(
RazorLanguageVersion version,
string document,
IEnumerable<DirectiveDescriptor> directives,
bool designTime)
@ -108,7 +129,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
directives = directives ?? Array.Empty<DirectiveDescriptor>();
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 parser = new CSharpCodeParser(directives, context);
@ -152,6 +173,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
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)
{
ParseBlockTest(document, expectedRoot, false, null);
@ -182,9 +208,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
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)
{
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)
{
@ -604,24 +640,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy
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 RazorParserOptions.CreateDesignTime(ConfigureOptions);
}
else
{
return RazorParserOptions.Create(ConfigureOptions);
}
void ConfigureOptions(RazorParserOptionsBuilder builder)
{
foreach (var directive in directives)
{
builder.Directives.Add(directive);
}
}
return new DefaultRazorParserOptions(
directives.ToArray(),
designTime,
parseLeadingDirectives: false,
version: version);
}
private class IgnoreOutputBlock : Block