Add support for optional directives

This commit is contained in:
Pranav K 2017-02-07 16:55:18 -08:00
parent 6d18c3e85a
commit 2eba53de1b
8 changed files with 74 additions and 8 deletions

View File

@ -1,6 +1,7 @@
// 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;
using System.Collections.Generic; using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Evolution namespace Microsoft.AspNetCore.Razor.Evolution
@ -27,6 +28,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
private readonly List<DirectiveTokenDescriptor> _tokenDescriptors; private readonly List<DirectiveTokenDescriptor> _tokenDescriptors;
private readonly string _name; private readonly string _name;
private readonly DirectiveDescriptorKind _type; private readonly DirectiveDescriptorKind _type;
private bool _optional;
public DefaultDirectiveDescriptorBuilder(string name, DirectiveDescriptorKind type) public DefaultDirectiveDescriptorBuilder(string name, DirectiveDescriptorKind type)
{ {
@ -39,7 +41,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
{ {
var descriptor = new DirectiveTokenDescriptor() var descriptor = new DirectiveTokenDescriptor()
{ {
Kind = DirectiveTokenKind.Type Kind = DirectiveTokenKind.Type,
Optional = _optional,
}; };
_tokenDescriptors.Add(descriptor); _tokenDescriptors.Add(descriptor);
@ -50,7 +53,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
{ {
var descriptor = new DirectiveTokenDescriptor() var descriptor = new DirectiveTokenDescriptor()
{ {
Kind = DirectiveTokenKind.Member Kind = DirectiveTokenKind.Member,
Optional = _optional,
}; };
_tokenDescriptors.Add(descriptor); _tokenDescriptors.Add(descriptor);
@ -61,13 +65,26 @@ namespace Microsoft.AspNetCore.Razor.Evolution
{ {
var descriptor = new DirectiveTokenDescriptor() var descriptor = new DirectiveTokenDescriptor()
{ {
Kind = DirectiveTokenKind.String Kind = DirectiveTokenKind.String,
Optional = _optional,
}; };
_tokenDescriptors.Add(descriptor); _tokenDescriptors.Add(descriptor);
return this; return this;
} }
public IDirectiveDescriptorBuilder BeginOptionals()
{
if (_optional)
{
throw new InvalidOperationException(
Resources.FormatDirectiveDescriptor_BeginOptionalsAlreadyInvoked(nameof(BeginOptionals)));
}
_optional = true;
return this;
}
public DirectiveDescriptor Build() public DirectiveDescriptor Build()
{ {
var descriptor = new DirectiveDescriptor var descriptor = new DirectiveDescriptor

View File

@ -6,5 +6,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
public class DirectiveTokenDescriptor public class DirectiveTokenDescriptor
{ {
public DirectiveTokenKind Kind { get; set; } public DirectiveTokenKind Kind { get; set; }
public bool Optional { get; set; }
} }
} }

View File

@ -23,7 +23,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
} }
return descriptorX != null && return descriptorX != null &&
descriptorX.Kind == descriptorY.Kind; descriptorX.Kind == descriptorY.Kind &&
descriptorX.Optional == descriptorY.Optional;
} }
public int GetHashCode(DirectiveTokenDescriptor descriptor) public int GetHashCode(DirectiveTokenDescriptor descriptor)
@ -33,7 +34,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution
throw new ArgumentNullException(nameof(descriptor)); throw new ArgumentNullException(nameof(descriptor));
} }
return descriptor.Kind.GetHashCode(); var hashCodeCombiner = HashCodeCombiner.Start();
hashCodeCombiner.Add(descriptor.Kind);
hashCodeCombiner.Add(descriptor.Optional ? 1 : 0);
return hashCodeCombiner.CombinedHash;
} }
} }
} }

View File

@ -11,6 +11,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
IDirectiveDescriptorBuilder AddString(); IDirectiveDescriptorBuilder AddString();
IDirectiveDescriptorBuilder BeginOptionals();
DirectiveDescriptor Build(); DirectiveDescriptor Build();
} }
} }

View File

@ -83,7 +83,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
private Dictionary<CSharpKeyword, Action<bool>> _keywordParsers = new Dictionary<CSharpKeyword, Action<bool>>(); private Dictionary<CSharpKeyword, Action<bool>> _keywordParsers = new Dictionary<CSharpKeyword, Action<bool>>();
public CSharpCodeParser(ParserContext context) public CSharpCodeParser(ParserContext context)
: this (directiveDescriptors: Enumerable.Empty<DirectiveDescriptor>(), context: context) : this(directiveDescriptors: Enumerable.Empty<DirectiveDescriptor>(), context: context)
{ {
} }
@ -223,7 +223,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
// No "@" => Jump straight to AfterTransition // No "@" => Jump straight to AfterTransition
AfterTransition(); AfterTransition();
} }
Output(SpanKind.Code); Output(SpanKind.Code);
} }
} }
@ -1507,7 +1507,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
Output(SpanKind.Markup, AcceptedCharacters.WhiteSpace); Output(SpanKind.Markup, AcceptedCharacters.WhiteSpace);
} }
if (EndOfFile) if (tokenDescriptor.Optional && (EndOfFile || At(CSharpSymbolType.NewLine)))
{
break;
}
else if (EndOfFile)
{ {
Context.ErrorSink.OnError( Context.ErrorSink.OnError(
CurrentStart, CurrentStart,

View File

@ -186,6 +186,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution
return GetString("RazorProject_PathMustStartWithForwardSlash"); return GetString("RazorProject_PathMustStartWithForwardSlash");
} }
/// <summary>
/// The method '{0}' has already been invoked.
/// </summary>
internal static string DirectiveDescriptor_BeginOptionalsAlreadyInvoked
{
get { return GetString("DirectiveDescriptor_BeginOptionalsAlreadyInvoked"); }
}
/// <summary>
/// The method '{0}' has already been invoked.
/// </summary>
internal static string FormatDirectiveDescriptor_BeginOptionalsAlreadyInvoked(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("DirectiveDescriptor_BeginOptionalsAlreadyInvoked"), p0);
}
private static string GetString(string name, params string[] formatterNames) private static string GetString(string name, params string[] formatterNames)
{ {
var value = _resourceManager.GetString(name); var value = _resourceManager.GetString(name);

View File

@ -150,4 +150,7 @@
<data name="RazorProject_PathMustStartWithForwardSlash" xml:space="preserve"> <data name="RazorProject_PathMustStartWithForwardSlash" xml:space="preserve">
<value>Path must begin with a forward slash '/'.</value> <value>Path must begin with a forward slash '/'.</value>
</data> </data>
<data name="DirectiveDescriptor_BeginOptionalsAlreadyInvoked" xml:space="preserve">
<value>The method '{0}' has already been invoked.</value>
</data>
</root> </root>

View File

@ -734,6 +734,23 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
.Accepts(AcceptedCharacters.None))); .Accepts(AcceptedCharacters.None)));
} }
[Fact]
public void OptionalDirectiveTokens_AreSkipped()
{
// Arrange
var descriptor = DirectiveDescriptorBuilder.Create("custom").BeginOptionals().AddString().Build();
// Act & Assert
ParseCodeBlockTest(
"@custom ",
new[] { descriptor },
new DirectiveBlock(
new DirectiveChunkGenerator(descriptor),
Factory.CodeTransition(),
Factory.MetaCode("custom").Accepts(AcceptedCharacters.None),
Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace)));
}
internal virtual void ParseCodeBlockTest( internal virtual void ParseCodeBlockTest(
string document, string document,
IEnumerable<DirectiveDescriptor> descriptors, IEnumerable<DirectiveDescriptor> descriptors,