Add support for optional directives
This commit is contained in:
parent
6d18c3e85a
commit
2eba53de1b
|
|
@ -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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
|
|
@ -27,6 +28,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
private readonly List<DirectiveTokenDescriptor> _tokenDescriptors;
|
||||
private readonly string _name;
|
||||
private readonly DirectiveDescriptorKind _type;
|
||||
private bool _optional;
|
||||
|
||||
public DefaultDirectiveDescriptorBuilder(string name, DirectiveDescriptorKind type)
|
||||
{
|
||||
|
|
@ -39,7 +41,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
var descriptor = new DirectiveTokenDescriptor()
|
||||
{
|
||||
Kind = DirectiveTokenKind.Type
|
||||
Kind = DirectiveTokenKind.Type,
|
||||
Optional = _optional,
|
||||
};
|
||||
_tokenDescriptors.Add(descriptor);
|
||||
|
||||
|
|
@ -50,7 +53,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
var descriptor = new DirectiveTokenDescriptor()
|
||||
{
|
||||
Kind = DirectiveTokenKind.Member
|
||||
Kind = DirectiveTokenKind.Member,
|
||||
Optional = _optional,
|
||||
};
|
||||
_tokenDescriptors.Add(descriptor);
|
||||
|
||||
|
|
@ -61,13 +65,26 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
var descriptor = new DirectiveTokenDescriptor()
|
||||
{
|
||||
Kind = DirectiveTokenKind.String
|
||||
Kind = DirectiveTokenKind.String,
|
||||
Optional = _optional,
|
||||
};
|
||||
_tokenDescriptors.Add(descriptor);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public IDirectiveDescriptorBuilder BeginOptionals()
|
||||
{
|
||||
if (_optional)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
Resources.FormatDirectiveDescriptor_BeginOptionalsAlreadyInvoked(nameof(BeginOptionals)));
|
||||
}
|
||||
|
||||
_optional = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DirectiveDescriptor Build()
|
||||
{
|
||||
var descriptor = new DirectiveDescriptor
|
||||
|
|
|
|||
|
|
@ -6,5 +6,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
public class DirectiveTokenDescriptor
|
||||
{
|
||||
public DirectiveTokenKind Kind { get; set; }
|
||||
|
||||
public bool Optional { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
}
|
||||
|
||||
return descriptorX != null &&
|
||||
descriptorX.Kind == descriptorY.Kind;
|
||||
descriptorX.Kind == descriptorY.Kind &&
|
||||
descriptorX.Optional == descriptorY.Optional;
|
||||
}
|
||||
|
||||
public int GetHashCode(DirectiveTokenDescriptor descriptor)
|
||||
|
|
@ -33,7 +34,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
|
||||
IDirectiveDescriptorBuilder AddString();
|
||||
|
||||
IDirectiveDescriptorBuilder BeginOptionals();
|
||||
|
||||
DirectiveDescriptor Build();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
private Dictionary<CSharpKeyword, Action<bool>> _keywordParsers = new Dictionary<CSharpKeyword, Action<bool>>();
|
||||
|
||||
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
|
||||
AfterTransition();
|
||||
}
|
||||
|
||||
|
||||
Output(SpanKind.Code);
|
||||
}
|
||||
}
|
||||
|
|
@ -1507,7 +1507,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
Output(SpanKind.Markup, AcceptedCharacters.WhiteSpace);
|
||||
}
|
||||
|
||||
if (EndOfFile)
|
||||
if (tokenDescriptor.Optional && (EndOfFile || At(CSharpSymbolType.NewLine)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (EndOfFile)
|
||||
{
|
||||
Context.ErrorSink.OnError(
|
||||
CurrentStart,
|
||||
|
|
|
|||
|
|
@ -186,6 +186,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
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)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -150,4 +150,7 @@
|
|||
<data name="RazorProject_PathMustStartWithForwardSlash" xml:space="preserve">
|
||||
<value>Path must begin with a forward slash '/'.</value>
|
||||
</data>
|
||||
<data name="DirectiveDescriptor_BeginOptionalsAlreadyInvoked" xml:space="preserve">
|
||||
<value>The method '{0}' has already been invoked.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -734,6 +734,23 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
.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(
|
||||
string document,
|
||||
IEnumerable<DirectiveDescriptor> descriptors,
|
||||
|
|
|
|||
Loading…
Reference in New Issue