From 8d2a9e59291df7358b62fc9e51ea21d9cfd110f6 Mon Sep 17 00:00:00 2001 From: Ajay Bhargav Baaskaran Date: Fri, 11 Aug 2017 17:07:43 -0700 Subject: [PATCH] Support tuples for type directive tokens --- .../Legacy/CSharpCodeParser.cs | 41 +++++++++++++++- .../Legacy/CSharpDirectivesTest.cs | 49 +++++++++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpCodeParser.cs b/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpCodeParser.cs index 1580f0f774..5c961c4b93 100644 --- a/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpCodeParser.cs +++ b/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpCodeParser.cs @@ -957,7 +957,34 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy protected bool NamespaceOrTypeName() { - if (Optional(CSharpSymbolType.Identifier) || Optional(CSharpSymbolType.Keyword)) + if (Optional(CSharpSymbolType.LeftParenthesis)) + { + while (!Optional(CSharpSymbolType.RightParenthesis) && !EndOfFile) + { + Optional(CSharpSymbolType.WhiteSpace); + + if (!NamespaceOrTypeName()) + { + return false; + } + + Optional(CSharpSymbolType.WhiteSpace); + Optional(CSharpSymbolType.Identifier); + Optional(CSharpSymbolType.WhiteSpace); + Optional(CSharpSymbolType.Comma); + } + + if (At(CSharpSymbolType.WhiteSpace) && NextIs(CSharpSymbolType.QuestionMark)) + { + // Only accept the whitespace if we are going to consume the next token. + AcceptAndMoveNext(); + } + + Optional(CSharpSymbolType.QuestionMark); // Nullable + + return true; + } + else if (Optional(CSharpSymbolType.Identifier) || Optional(CSharpSymbolType.Keyword)) { if (Optional(CSharpSymbolType.DoubleColon)) { @@ -975,8 +1002,20 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy NamespaceOrTypeName(); } + if (At(CSharpSymbolType.WhiteSpace) && NextIs(CSharpSymbolType.QuestionMark)) + { + // Only accept the whitespace if we are going to consume the next token. + AcceptAndMoveNext(); + } + Optional(CSharpSymbolType.QuestionMark); // Nullable + if (At(CSharpSymbolType.WhiteSpace) && NextIs(CSharpSymbolType.LeftBracket)) + { + // Only accept the whitespace if we are going to consume the next token. + AcceptAndMoveNext(); + } + while (At(CSharpSymbolType.LeftBracket)) { Balance(BalancingModes.None); diff --git a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/CSharpDirectivesTest.cs b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/CSharpDirectivesTest.cs index 9fb722ddfb..2700e7f535 100644 --- a/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/CSharpDirectivesTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Language.Test/Legacy/CSharpDirectivesTest.cs @@ -811,6 +811,55 @@ namespace Microsoft.AspNetCore.Razor.Language.Legacy Factory.Span(SpanKindInternal.Code, expectedType, markup: false).AsDirectiveToken(descriptor.Tokens[0]))); } + [Theory] + [InlineData("(bool, int)")] + [InlineData("(int aa, string bb)?")] + [InlineData("( int? q , bool w )")] + [InlineData("( int ? q, bool ?w ,(long ? [])) ?")] + [InlineData("(List<(int, string)?> aa, string bb)")] + [InlineData("(string ss, (int u, List<(string, int)> k, (Char c, bool b, List l)), global::System.Int32[] a)")] + public void DirectiveDescriptor_AllowsTupleTypes(string expectedType) + { + // Arrange + var descriptor = DirectiveDescriptor.CreateDirective( + "custom", + DirectiveKind.SingleLine, + b => b.AddTypeToken()); + + // Act & Assert + ParseCodeBlockTest( + $"@custom {expectedType}", + new[] { descriptor }, + new DirectiveBlock( + new DirectiveChunkGenerator(descriptor), + Factory.CodeTransition(), + Factory.MetaCode("custom").Accepts(AcceptedCharactersInternal.None), + Factory.Span(SpanKindInternal.Code, " ", markup: false).Accepts(AcceptedCharactersInternal.WhiteSpace), + Factory.Span(SpanKindInternal.Code, expectedType, markup: false).AsDirectiveToken(descriptor.Tokens[0]))); + } + + [Fact] + public void DirectiveDescriptor_AllowsTupleTypes_IgnoresTrailingWhitespace() + { + // Arrange + var descriptor = DirectiveDescriptor.CreateDirective( + "custom", + DirectiveKind.SingleLine, + b => b.AddTypeToken()); + + // Act & Assert + ParseCodeBlockTest( + $"@custom (bool, int?) ", + new[] { descriptor }, + new DirectiveBlock( + new DirectiveChunkGenerator(descriptor), + Factory.CodeTransition(), + Factory.MetaCode("custom").Accepts(AcceptedCharactersInternal.None), + Factory.Span(SpanKindInternal.Code, " ", markup: false).Accepts(AcceptedCharactersInternal.WhiteSpace), + Factory.Span(SpanKindInternal.Code, "(bool, int?)", markup: false).AsDirectiveToken(descriptor.Tokens[0]), + Factory.MetaCode(" ").Accepts(AcceptedCharactersInternal.WhiteSpace))); + } + [Fact] public void DirectiveDescriptor_ErrorsExtraContentAfterDirective() {