diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/BalancingModes.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/BalancingModes.cs index a5adf88a2ac..898eb9133a2 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/BalancingModes.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/BalancingModes.cs @@ -12,6 +12,7 @@ internal enum BalancingModes BacktrackOnFailure = 1, NoErrorOnFailure = 2, AllowCommentsAndTemplates = 4, - AllowEmbeddedTransitions = 8 + AllowEmbeddedTransitions = 8, + StopAtEndOfLine = 16, } } diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpCodeParser.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpCodeParser.cs index 0093c8b91cf..43e1d0efa16 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpCodeParser.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.Language/Legacy/CSharpCodeParser.cs @@ -1356,10 +1356,9 @@ private void ParseExtensibleDirective(in SyntaxListBuilder buil case DirectiveTokenKind.Attribute: if (At(SyntaxKind.LeftBracket)) { - AcceptUntil(SyntaxKind.RightBracket, SyntaxKind.NewLine); - if (At(SyntaxKind.RightBracket)) + if (Balance(directiveBuilder, BalancingModes.NoErrorOnFailure | BalancingModes.StopAtEndOfLine)) { - AcceptAndMoveNext(); + TryAccept(SyntaxKind.RightBracket); } } else @@ -2446,7 +2445,9 @@ private bool Balance(SyntaxListBuilder builder, BalancingModes { var startPosition = CurrentStart.AbsoluteIndex; var nesting = 1; - if (!EndOfFile) + var stopAtEndOfLine = (mode & BalancingModes.StopAtEndOfLine) == BalancingModes.StopAtEndOfLine; + if (!EndOfFile && + !(stopAtEndOfLine && At(SyntaxKind.NewLine))) { var tokens = new List(); do @@ -2475,7 +2476,7 @@ private bool Balance(SyntaxListBuilder builder, BalancingModes tokens.Add(CurrentToken); } } - while (nesting > 0 && NextToken()); + while (nesting > 0 && NextToken() && !(stopAtEndOfLine && At(SyntaxKind.NewLine))); if (nesting > 0) { diff --git a/src/Razor/test/RazorLanguage.Test/Legacy/RazorDirectivesTest.cs b/src/Razor/test/RazorLanguage.Test/Legacy/RazorDirectivesTest.cs index 95bb2fe742e..b4ca1a19fbc 100644 --- a/src/Razor/test/RazorLanguage.Test/Legacy/RazorDirectivesTest.cs +++ b/src/Razor/test/RazorLanguage.Test/Legacy/RazorDirectivesTest.cs @@ -924,6 +924,22 @@ @custom [DllImport(""user32.dll"", SetLastError=false, ExactSpelling=false)] new[] { descriptor }); } + [Fact] + public void DirectiveDescriptor_AttributeToken_BalancesBrackets() + { + // Arrange + var descriptor = DirectiveDescriptor.CreateDirective( + "custom", + DirectiveKind.SingleLine, + b => b.AddAttributeToken()); + + // Act & Assert + ParseDocumentTest(@" +@custom [SomeCustom(new int[] { 1, 2, 3 } +", + new[] { descriptor }); + } + [Fact] public void DirectiveDescriptor_AttributeToken_ErrorsIfDoesNotStartWithOpenBracket() { diff --git a/src/Razor/test/RazorLanguage.Test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_BalancesBrackets.cspans.txt b/src/Razor/test/RazorLanguage.Test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_BalancesBrackets.cspans.txt new file mode 100644 index 00000000000..7ec1b9246bb --- /dev/null +++ b/src/Razor/test/RazorLanguage.Test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_BalancesBrackets.cspans.txt @@ -0,0 +1,7 @@ +Markup span at (0:0,0 [2] ) (Accepts:Any) - Parent: Markup block at (0:0,0 [45] ) +Transition span at (2:1,0 [1] ) (Accepts:None) - Parent: Directive block at (2:1,0 [43] ) +MetaCode span at (3:1,1 [6] ) (Accepts:None) - Parent: Directive block at (2:1,0 [43] ) +Code span at (9:1,7 [1] ) (Accepts:Whitespace) - Parent: Directive block at (2:1,0 [43] ) +Code span at (10:1,8 [33] ) (Accepts:NonWhitespace) - Parent: Directive block at (2:1,0 [43] ) +Markup span at (43:1,41 [2] ) (Accepts:Whitespace) - Parent: Directive block at (2:1,0 [43] ) +Markup span at (45:2,0 [0] ) (Accepts:Any) - Parent: Markup block at (0:0,0 [45] ) diff --git a/src/Razor/test/RazorLanguage.Test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_BalancesBrackets.stree.txt b/src/Razor/test/RazorLanguage.Test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_BalancesBrackets.stree.txt new file mode 100644 index 00000000000..164579b7390 --- /dev/null +++ b/src/Razor/test/RazorLanguage.Test/TestFiles/ParserTests/RazorDirectivesTest/DirectiveDescriptor_AttributeToken_BalancesBrackets.stree.txt @@ -0,0 +1,39 @@ +RazorDocument - [0..45)::45 - [LF@custom [SomeCustom(new int[] { 1, 2, 3 }LF] + MarkupBlock - [0..45)::45 + MarkupTextLiteral - [0..2)::2 - [LF] - Gen - SpanEditHandler;Accepts:Any + NewLine;[LF]; + CSharpCodeBlock - [2..45)::43 + RazorDirective - [2..45)::43 - Directive:{custom;SingleLine;Unrestricted} + CSharpTransition - [2..3)::1 - Gen - SpanEditHandler;Accepts:None + Transition;[@]; + RazorDirectiveBody - [3..45)::42 + RazorMetaCode - [3..9)::6 - Gen - SpanEditHandler;Accepts:None + Identifier;[custom]; + CSharpCodeBlock - [9..45)::36 + CSharpStatementLiteral - [9..10)::1 - [ ] - Gen - SpanEditHandler;Accepts:Whitespace + Whitespace;[ ]; + CSharpStatementLiteral - [10..43)::33 - [[SomeCustom(new int[] { 1, 2, 3 }] - Gen - DirectiveTokenEditHandler;Accepts:NonWhitespace + LeftBracket;[[]; + Identifier;[SomeCustom]; + LeftParenthesis;[(]; + Keyword;[new]; + Whitespace;[ ]; + Keyword;[int]; + LeftBracket;[[]; + RightBracket;[]]; + Whitespace;[ ]; + LeftBrace;[{]; + Whitespace;[ ]; + IntegerLiteral;[1]; + Comma;[,]; + Whitespace;[ ]; + IntegerLiteral;[2]; + Comma;[,]; + Whitespace;[ ]; + IntegerLiteral;[3]; + Whitespace;[ ]; + RightBrace;[}]; + MarkupEphemeralTextLiteral - [43..45)::2 - [LF] - Gen - SpanEditHandler;Accepts:Whitespace + NewLine;[LF]; + MarkupTextLiteral - [45..45)::0 - [] - Gen - SpanEditHandler;Accepts:Any + Marker;[];