// 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 Microsoft.AspNetCore.Razor.Language.Extensions; using Xunit; namespace Microsoft.AspNetCore.Razor.Language.Legacy { public class CSharpSectionTest : CsHtmlMarkupParserTestBase { [Fact] public void ParseSectionBlockCapturesNewlineImmediatelyFollowing() { // Arrange var chunkGenerator = new DirectiveChunkGenerator(SectionDirective.Directive); chunkGenerator.Diagnostics.Add( RazorDiagnosticFactory.CreateParsing_DirectiveExpectsIdentifier( new SourceSpan(new SourceLocation(8, 0, 8), contentLength: Environment.NewLine.Length), SectionDirective.Directive.Directive)); // Act & Assert ParseDocumentTest( "@section" + Environment.NewLine, new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(chunkGenerator, Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, string.Empty, CSharpSymbolType.Unknown) .AsDirectiveToken(DirectiveTokenDescriptor.CreateToken(DirectiveTokenKind.Member))), Factory.Markup(Environment.NewLine))); } [Fact] public void ParseSectionBlockCapturesWhitespaceToEndOfLineInSectionStatementMissingOpenBrace() { // Arrange var chunkGenerator = new DirectiveChunkGenerator(SectionDirective.Directive); chunkGenerator.Diagnostics.Add( RazorDiagnosticFactory.CreateParsing_UnexpectedEOFAfterDirective( new SourceSpan(new SourceLocation(25 + Environment.NewLine.Length, 1, 4), contentLength: 1), SectionDirective.Directive.Directive, "{")); // Act & Assert ParseDocumentTest( "@section Foo " + Environment.NewLine + " ", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(chunkGenerator, Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "Foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " " + Environment.NewLine + " ", markup: false).Accepts(AcceptedCharactersInternal.AllWhiteSpace)), Factory.EmptyHtml())); } [Fact] public void ParseSectionBlockCapturesWhitespaceToEndOfLineInSectionStatementMissingName() { // Arrange var chunkGenerator = new DirectiveChunkGenerator(SectionDirective.Directive); chunkGenerator.Diagnostics.Add( RazorDiagnosticFactory.CreateParsing_DirectiveExpectsIdentifier( new SourceSpan(new SourceLocation(17, 0, 17), contentLength: Environment.NewLine.Length), SectionDirective.Directive.Directive)); // Act & Assert ParseDocumentTest( "@section " + Environment.NewLine + " ", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(chunkGenerator, Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory .Span(SpanKindInternal.Code, string.Empty, CSharpSymbolType.Unknown) .AsDirectiveToken(DirectiveTokenDescriptor.CreateToken(DirectiveTokenKind.Member))), Factory.Markup(Environment.NewLine + " "))); } [Fact] public void ParseSectionBlockIgnoresSectionUnlessAllLowerCase() { ParseDocumentTest( "@Section foo", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new ExpressionBlock( Factory.CodeTransition(), Factory.Code("Section") .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) .Accepts(AcceptedCharactersInternal.NonWhiteSpace)), Factory.Markup(" foo"))); } [Fact] public void ParseSectionBlockReportsErrorAndTerminatesSectionBlockIfKeywordNotFollowedByIdentifierStartCharacter() { // Arrange var chunkGenerator = new DirectiveChunkGenerator(SectionDirective.Directive); chunkGenerator.Diagnostics.Add( RazorDiagnosticFactory.CreateParsing_DirectiveExpectsIdentifier( new SourceSpan(new SourceLocation(9, 0, 9), contentLength: 1), SectionDirective.Directive.Directive)); // Act & Assert ParseDocumentTest( "@section 9 {
Foo
}", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(chunkGenerator, Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace)), Factory.Markup("9 { "), new MarkupTagBlock( Factory.Markup("")), Factory.Markup("Foo"), new MarkupTagBlock( Factory.Markup("
")), Factory.Markup(" }"))); } [Fact] public void ParseSectionBlockReportsErrorAndTerminatesSectionBlockIfNameNotFollowedByOpenBrace() { // Arrange var chunkGenerator = new DirectiveChunkGenerator(SectionDirective.Directive); chunkGenerator.Diagnostics.Add( RazorDiagnosticFactory.CreateParsing_UnexpectedDirectiveLiteral( new SourceSpan(new SourceLocation(12, 0, 12), contentLength: 1), SectionDirective.Directive.Directive, "{")); // Act & Assert ParseDocumentTest( "@section foo-bar {Foo
}", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(chunkGenerator, Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0])), Factory.Markup("-bar { "), new MarkupTagBlock( Factory.Markup("")), Factory.Markup("Foo"), new MarkupTagBlock( Factory.Markup("
")), Factory.Markup(" }"))); } [Fact] public void ParserOutputsErrorOnNestedSections() { // Arrange var erroredChunkGenerator = new DirectiveChunkGenerator(SectionDirective.Directive); erroredChunkGenerator.Diagnostics.Add( RazorDiagnosticFactory.CreateParsing_DirectiveMustAppearAtStartOfLine( new SourceSpan(new SourceLocation(16, 0, 16), contentLength: 7), "section")); erroredChunkGenerator.Diagnostics.Add( RazorDiagnosticFactory.CreateParsing_SectionsCannotBeNested( new SourceSpan(new SourceLocation(15, 0, 15), 8))); // Act & Assert ParseDocumentTest( "@section foo { @section bar {Foo
} }", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(new DirectiveChunkGenerator(SectionDirective.Directive), Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup(" "), new DirectiveBlock(erroredChunkGenerator, Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "bar", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( Factory.Markup("")), Factory.Markup("Foo"), new MarkupTagBlock( Factory.Markup("
")), Factory.Markup(" ")), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.Markup(" ")), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } [Fact] public void ParseSectionBlockHandlesEOFAfterOpenBrace() { // Arrange var chunkGenerator = new DirectiveChunkGenerator(SectionDirective.Directive); chunkGenerator.Diagnostics.Add( RazorDiagnosticFactory.CreateParsing_ExpectedEndOfBlockBeforeEOF( new SourceSpan(new SourceLocation(13, 0, 13), contentLength: 1), SectionDirective.Directive.Directive, "}", "{")); // Act & Assert ParseDocumentTest( "@section foo {", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(chunkGenerator, Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith("}", atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.EmptyHtml())))); } [Theory] [InlineData(" ")] [InlineData("\n")] [InlineData(" abc")] [InlineData(" \n abc")] public void ParseSectionBlockHandlesEOFAfterOpenContent(string postStartBrace) { // Arrange var chunkGenerator = new DirectiveChunkGenerator(SectionDirective.Directive); chunkGenerator.Diagnostics.Add( RazorDiagnosticFactory.CreateParsing_ExpectedEndOfBlockBeforeEOF( new SourceSpan(new SourceLocation(13, 0, 13), contentLength: 1), "section", "}", "{")); // Act & Assert ParseDocumentTest( "@section foo {" + postStartBrace, new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(chunkGenerator, Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith("}", atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup(postStartBrace))))); } [Fact] public void ParseSectionBlockHandlesUnterminatedSection() { // Arrange var chunkGenerator = new DirectiveChunkGenerator(SectionDirective.Directive); chunkGenerator.Diagnostics.Add( RazorDiagnosticFactory.CreateParsing_ExpectedEndOfBlockBeforeEOF( new SourceSpan(new SourceLocation(13, 0, 13), contentLength: 1), "section", "}", "{")); // Act & Assert ParseDocumentTest( "@section foo {Foo{}
", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(chunkGenerator, Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith("}", atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( Factory.Markup("")), // Need to provide the markup span as fragments, since the parser will split the {} into separate symbols. Factory.Markup("Foo", "{", "}"), new MarkupTagBlock( Factory.Markup("
")))))); } [Fact] public void ParseSectionBlockHandlesUnterminatedSectionWithNestedIf() { // Arrange var newLine = Environment.NewLine; var chunkGenerator = new DirectiveChunkGenerator(SectionDirective.Directive); chunkGenerator.Diagnostics.Add( RazorDiagnosticFactory.CreateParsing_ExpectedEndOfBlockBeforeEOF( new SourceSpan(new SourceLocation(13 + newLine.Length, 1, 0), contentLength: 1), "section", "}", "{")); var spaces = " "; // Act & Assert ParseDocumentTest( string.Format( "@section Test{0}{{{0}{1}@if(true){0}{1}{{{0}{1}{1}Hello World
{0}{1}}}", newLine, spaces), new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(chunkGenerator, Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "Test", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, Environment.NewLine, CSharpSymbolType.NewLine).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith("}", atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup(newLine), new StatementBlock( Factory.Code(spaces).AsStatement(), Factory.CodeTransition(), Factory.Code($"if(true){newLine}{spaces}{{{newLine}").AsStatement(), new MarkupBlock( Factory.Markup($"{spaces}{spaces}"), BlockFactory.MarkupTagBlock("", AcceptedCharactersInternal.None), Factory.Markup("Hello World"), BlockFactory.MarkupTagBlock("
", AcceptedCharactersInternal.None), Factory.Markup(newLine).Accepts(AcceptedCharactersInternal.None)), Factory.Code($"{spaces}}}").AsStatement()))))); } [Fact] public void ParseSectionBlockReportsErrorAndAcceptsWhitespaceToEndOfLineIfSectionNotFollowedByOpenBrace() { // Arrange var chunkGenerator = new DirectiveChunkGenerator(SectionDirective.Directive); chunkGenerator.Diagnostics.Add( RazorDiagnosticFactory.CreateParsing_UnexpectedEOFAfterDirective( new SourceSpan(new SourceLocation(18 + Environment.NewLine.Length, 1, 0), contentLength: 1), SectionDirective.Directive.Directive, "{")); // Act & Assert ParseDocumentTest( "@section foo " + Environment.NewLine, new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(chunkGenerator, Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " " + Environment.NewLine, markup: false).Accepts(AcceptedCharactersInternal.AllWhiteSpace)), Factory.EmptyHtml())); } [Fact] public void ParseSectionBlockAcceptsOpenBraceMultipleLinesBelowSectionName() { // Act & Assert ParseDocumentTest( "@section foo " + Environment.NewLine + Environment.NewLine + Environment.NewLine + Environment.NewLine + Environment.NewLine + Environment.NewLine + "{" + Environment.NewLine + "Foo
" + Environment.NewLine + "}", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(new DirectiveChunkGenerator(SectionDirective.Directive), Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " " + string.Format("{0}{0}{0}{0}{0}{0}", Environment.NewLine), markup: false).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup(Environment.NewLine), new MarkupTagBlock( Factory.Markup("")), Factory.Markup("Foo"), new MarkupTagBlock( Factory.Markup("
")), Factory.Markup(Environment.NewLine)), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } [Fact] public void ParseSectionBlockParsesNamedSectionCorrectly() { // Act & Assert ParseDocumentTest( "@section foo {Foo
}", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(new DirectiveChunkGenerator(SectionDirective.Directive), Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( Factory.Markup("")), Factory.Markup("Foo"), new MarkupTagBlock( Factory.Markup("
")), Factory.Markup(" ")), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } [Fact] public void ParseSectionBlockDoesNotRequireSpaceBetweenSectionNameAndOpenBrace() { // Act & Assert ParseDocumentTest( "@section foo{Foo
}", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(new DirectiveChunkGenerator(SectionDirective.Directive), Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( Factory.Markup("")), Factory.Markup("Foo"), new MarkupTagBlock( Factory.Markup("
")), Factory.Markup(" ")), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } [Fact] public void ParseSectionBlockBalancesBraces() { // Act & Assert ParseDocumentTest( "@section foo { }", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(new DirectiveChunkGenerator(SectionDirective.Directive), Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( Factory.Markup("")), Factory.Markup(" ")), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } [Fact] public void ParseSectionBlockAllowsBracesInCSharpExpression() { // Act & Assert ParseDocumentTest( "@section foo { I really want to render a close brace, so here I go: @(\"}\") }", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(new DirectiveChunkGenerator(SectionDirective.Directive), Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup(" I really want to render a close brace, so here I go: "), new ExpressionBlock( Factory.CodeTransition(), Factory.MetaCode("(").Accepts(AcceptedCharactersInternal.None), Factory.Code("\"}\"").AsExpression(), Factory.MetaCode(")").Accepts(AcceptedCharactersInternal.None)), Factory.Markup(" ")), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } [Fact] public void SectionIsCorrectlyTerminatedWhenCloseBraceImmediatelyFollowsCodeBlock() { // Act & Assert ParseDocumentTest( "@section Foo {" + Environment.NewLine + "@if(true) {" + Environment.NewLine + "}" + Environment.NewLine + "}", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(new DirectiveChunkGenerator(SectionDirective.Directive), Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "Foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup(Environment.NewLine), new StatementBlock( Factory.CodeTransition(), Factory.Code($"if(true) {{{Environment.NewLine}}}{Environment.NewLine}").AsStatement() )), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } [Fact] public void SectionIsCorrectlyTerminatedWhenCloseBraceImmediatelyFollowsCodeBlockNoWhitespace() { // Act & Assert ParseDocumentTest( "@section Foo {" + Environment.NewLine + "@if(true) {" + Environment.NewLine + "}}", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(new DirectiveChunkGenerator(SectionDirective.Directive), Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "Foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup(Environment.NewLine), new StatementBlock( Factory.CodeTransition(), Factory.Code($"if(true) {{{Environment.NewLine}}}").AsStatement() )), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } [Fact] public void ParseSectionBlockCorrectlyTerminatesWhenCloseBraceImmediatelyFollowsMarkup() { // Act & Assert ParseDocumentTest( "@section foo {something}", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(new DirectiveChunkGenerator(SectionDirective.Directive), Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "foo", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup("something")), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } [Fact] public void ParseSectionBlockParsesComment() { // Act & Assert ParseDocumentTest( "@section s {}", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(new DirectiveChunkGenerator(SectionDirective.Directive), Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "s", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup("")), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } // This was a user reported bug (codeplex #710), the section parser wasn't handling // comments. [Fact] public void ParseSectionBlockParsesCommentWithDelimiters() { // Act & Assert ParseDocumentTest( "@section s {}", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(new DirectiveChunkGenerator(SectionDirective.Directive), Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "s", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup("")), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } [Fact] public void ParseSectionBlockCommentRecoversFromUnclosedTag() { // Act & Assert ParseDocumentTest( "@section s {" + Environment.NewLine + " \" '-->}", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(new DirectiveChunkGenerator(SectionDirective.Directive), Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "s", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup(Environment.NewLine), new MarkupTagBlock( Factory.Markup(" \" '-->")), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } [Fact] public void ParseSectionBlockParsesXmlProcessingInstruction() { // Act & Assert ParseDocumentTest( "@section s { xml bleh ?>}", new[] { SectionDirective.Directive }, new MarkupBlock( Factory.EmptyHtml(), new DirectiveBlock(new DirectiveChunkGenerator(SectionDirective.Directive), Factory.CodeTransition(), Factory.MetaCode("section").Accepts(AcceptedCharactersInternal.None), Factory.Span(SpanKindInternal.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.WhiteSpace), Factory.Span(SpanKindInternal.Code, "s", CSharpSymbolType.Identifier).AsDirectiveToken(SectionDirective.Directive.Tokens[0]), Factory.Span(SpanKindInternal.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharactersInternal.AllWhiteSpace), Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharactersInternal.None), new MarkupBlock( Factory.Markup(" xml bleh ?>")), Factory.MetaCode("}").Accepts(AcceptedCharactersInternal.None)), Factory.EmptyHtml())); } public static TheoryData SectionWithEscapedTransitionData { get { var factory = new SpanFactory(); return new TheoryData