diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/DefaultDirectiveIRPass.cs b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultDirectiveIRPass.cs new file mode 100644 index 0000000000..06d02202eb --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultDirectiveIRPass.cs @@ -0,0 +1,61 @@ +// 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.Linq; +using Microsoft.AspNetCore.Razor.Evolution.Intermediate; +using Microsoft.AspNetCore.Razor.Evolution.Legacy; + +namespace Microsoft.AspNetCore.Razor.Evolution +{ + internal class DefaultDirectiveIRPass : IRazorIRPass + { + public RazorEngine Engine { get; set; } + + public int Order => 150; + + public DocumentIRNode Execute(RazorCodeDocument codeDocument, DocumentIRNode irDocument) + { + var walker = new DirectiveWalker(); + walker.VisitDefault(irDocument); + + return irDocument; + } + + private class DirectiveWalker : RazorIRNodeWalker + { + private ClassDeclarationIRNode _classNode; + + public override void VisitClass(ClassDeclarationIRNode node) + { + if (_classNode == null) + { + _classNode = node; + } + + VisitDefault(node); + } + + public override void VisitDirective(DirectiveIRNode node) + { + if (string.Equals(node.Name, CSharpCodeParser.FunctionsDirectiveDescriptor.Name, StringComparison.Ordinal)) + { + foreach (var child in node.Children.Except(node.Tokens)) + { + child.Parent = _classNode; + _classNode.Children.Add(child); + } + } + else if (string.Equals(node.Name, CSharpCodeParser.InheritsDirectiveDescriptor.Name, StringComparison.Ordinal)) + { + var token = node.Tokens.FirstOrDefault(); + + if (token != null) + { + _classNode.BaseType = token.Content; + } + } + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/DefaultDirectiveSyntaxTreePass.cs b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultDirectiveSyntaxTreePass.cs new file mode 100644 index 0000000000..dddd26bfac --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultDirectiveSyntaxTreePass.cs @@ -0,0 +1,62 @@ +// 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.Linq; +using Microsoft.AspNetCore.Razor.Evolution.Legacy; + +namespace Microsoft.AspNetCore.Razor.Evolution +{ + internal class DefaultDirectiveSyntaxTreePass : IRazorSyntaxTreePass + { + public RazorEngine Engine { get; set; } + + public int Order => 75; + + public RazorSyntaxTree Execute(RazorCodeDocument codeDocument, RazorSyntaxTree syntaxTree) + { + var errorSink = new ErrorSink(); + var sectionVerifier = new NestedSectionVerifier(); + sectionVerifier.Verify(syntaxTree, errorSink); + + if (errorSink.Errors.Count > 0) + { + var combinedErrors = syntaxTree.Diagnostics.Concat(errorSink.Errors).ToList(); + syntaxTree = RazorSyntaxTree.Create(syntaxTree.Root, combinedErrors, syntaxTree.Options); + } + + return syntaxTree; + } + + private class NestedSectionVerifier : ParserVisitor + { + private int _nestedLevel; + private ErrorSink _errorSink; + + public void Verify(RazorSyntaxTree tree, ErrorSink errorSink) + { + _errorSink = errorSink; + tree.Root.Accept(this); + } + + public override void VisitStartDirectiveBlock(DirectiveChunkGenerator chunkGenerator, Block block) + { + if (_nestedLevel > 0) + { + var directiveStart = block.Children.First(child => !child.IsBlock && ((Span)child).Kind == SpanKind.Transition).Start; + var errorLength = /* @ */ 1 + CSharpCodeParser.SectionDirectiveDescriptor.Name.Length; + _errorSink.OnError( + directiveStart, + LegacyResources.FormatParseError_Sections_Cannot_Be_Nested(LegacyResources.SectionExample_CS), + errorLength); + } + + _nestedLevel++; + } + + public override void VisitEndDirectiveBlock(DirectiveChunkGenerator chunkGenerator, Block block) + { + _nestedLevel--; + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorIRLoweringPhase.cs b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorIRLoweringPhase.cs index 45fd8473c5..7d503773ec 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorIRLoweringPhase.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorIRLoweringPhase.cs @@ -153,18 +153,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution }); } - public override void VisitTypeMemberSpan(TypeMemberChunkGenerator chunkGenerator, Span span) - { - var functionsNode = new CSharpStatementIRNode() - { - Content = span.Content, - SourceRange = new MappingLocation(span.Start, span.Length), - Parent = Class, - }; - - Class.Children.Add(functionsNode); - } - public override void VisitStatementSpan(StatementChunkGenerator chunkGenerator, Span span) { Builder.Add(new CSharpStatementIRNode() diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/CSharpCodeParser.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/CSharpCodeParser.cs index f6e7593bd9..37f933e23b 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/CSharpCodeParser.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/CSharpCodeParser.cs @@ -14,6 +14,30 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy private static readonly Func IsValidStatementSpacingSymbol = IsSpacingToken(includeNewLines: true, includeComments: true); + internal static readonly DirectiveDescriptor SectionDirectiveDescriptor = + DirectiveDescriptorBuilder + .CreateRazorBlock(SyntaxConstants.CSharp.SectionKeyword) + .AddMember() + .Build(); + + internal static readonly DirectiveDescriptor FunctionsDirectiveDescriptor = + DirectiveDescriptorBuilder + .CreateCodeBlock(SyntaxConstants.CSharp.FunctionsKeyword) + .Build(); + + internal static readonly DirectiveDescriptor InheritsDirectiveDescriptor = + DirectiveDescriptorBuilder + .Create(SyntaxConstants.CSharp.InheritsKeyword) + .AddType() + .Build(); + + internal static readonly IEnumerable DefaultDirectiveDescriptors = new[] + { + SectionDirectiveDescriptor, + FunctionsDirectiveDescriptor, + InheritsDirectiveDescriptor, + }; + internal static ISet DefaultKeywords = new HashSet() { SyntaxConstants.CSharp.TagHelperPrefixKeyword, @@ -35,6 +59,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy "class", }; + private readonly ISet CurrentKeywords = new HashSet(DefaultKeywords); + private Dictionary _directiveParsers = new Dictionary(StringComparer.Ordinal); private Dictionary> _keywordParsers = new Dictionary>(); @@ -263,7 +289,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy Span.ChunkGenerator = new ExpressionChunkGenerator(); Span.EditHandler = new ImplicitExpressionEditHandler( Language.TokenizeString, - DefaultKeywords, + CurrentKeywords, acceptTrailingDot: IsNested) { AcceptedCharacters = AcceptedCharacters.NonWhiteSpace @@ -611,7 +637,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy private void SectionBlock(string left, string right, bool caseSensitive) { - ParseWithOtherParser(p => p.ParseSection(Tuple.Create(left, right), caseSensitive)); + ParseWithOtherParser(p => p.ParseRazorBlock(Tuple.Create(left, right), caseSensitive)); } private void NestedBlock() @@ -1411,17 +1437,18 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy private void SetupDirectives(IEnumerable directiveDescriptors) { - foreach (var directiveDescriptor in directiveDescriptors) + var allDirectives = directiveDescriptors.Concat(DefaultDirectiveDescriptors).ToList(); + + for (var i = 0; i < allDirectives.Count; i++) { + var directiveDescriptor = allDirectives[i]; + CurrentKeywords.Add(directiveDescriptor.Name); MapDirectives(() => HandleDirective(directiveDescriptor), directiveDescriptor.Name); } MapDirectives(TagHelperPrefixDirective, SyntaxConstants.CSharp.TagHelperPrefixKeyword); MapDirectives(AddTagHelperDirective, SyntaxConstants.CSharp.AddTagHelperKeyword); MapDirectives(RemoveTagHelperDirective, SyntaxConstants.CSharp.RemoveTagHelperKeyword); - MapDirectives(InheritsDirective, SyntaxConstants.CSharp.InheritsKeyword); - MapDirectives(FunctionsDirective, SyntaxConstants.CSharp.FunctionsKeyword); - MapDirectives(SectionDirective, SyntaxConstants.CSharp.SectionKeyword); } private void HandleDirective(DirectiveDescriptor descriptor) @@ -1449,14 +1476,27 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy Output(SpanKind.Markup, AcceptedCharacters.WhiteSpace); } + if (EndOfFile) + { + Context.ErrorSink.OnError( + CurrentLocation, + LegacyResources.FormatUnexpectedEOFAfterDirective(descriptor.Name, tokenDescriptor.Kind.ToString().ToLowerInvariant()), + length: 1); + return; + } + var outputKind = SpanKind.Markup; switch (tokenDescriptor.Kind) { case DirectiveTokenKind.Type: if (!NamespaceOrTypeName()) { - // Error logged for invalid type name, continue onto next piece. - continue; + Context.ErrorSink.OnError( + CurrentLocation, + LegacyResources.FormatDirectiveExpectsTypeName(descriptor.Name), + CurrentSymbol.Content.Length); + + return; } outputKind = SpanKind.Code; @@ -1520,10 +1560,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy CurrentSymbol.Content.Length); } - Output(SpanKind.Markup, AcceptedCharacters.AllWhiteSpace); + Output(SpanKind.Markup, AcceptedCharacters.WhiteSpace); break; case DirectiveDescriptorKind.RazorBlock: - Output(SpanKind.Markup, AcceptedCharacters.WhiteSpace); + AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); + Output(SpanKind.Markup, AcceptedCharacters.AllWhiteSpace); ParseDirectiveBlock(descriptor, parseChildren: (startingBraceLocation) => { @@ -1534,7 +1575,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy IsNested = false; using (PushSpanConfig()) { - HtmlParser.ParseSection(Tuple.Create("{", "}"), caseSensitive: true); + HtmlParser.ParseRazorBlock(Tuple.Create("{", "}"), caseSensitive: true); } Initialize(Span); IsNested = wasNested; @@ -1542,7 +1583,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy }); break; case DirectiveDescriptorKind.CodeBlock: - Output(SpanKind.Markup, AcceptedCharacters.WhiteSpace); + AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); + Output(SpanKind.Markup, AcceptedCharacters.AllWhiteSpace); ParseDirectiveBlock(descriptor, parseChildren: (startingBraceLocation) => { @@ -1588,7 +1630,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy editHandler.AutoCompleteString = "}"; Context.ErrorSink.OnError( startingBraceLocation, - LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF(descriptor.Name, "{", "}"), + LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF(descriptor.Name, "}", "{"), length: 1 /* } */); } else @@ -1622,186 +1664,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy lookupText => new RemoveTagHelperChunkGenerator(lookupText)); } - protected virtual void SectionDirective() - { - var nested = Context.Builder.ActiveBlocks.Any(block => block.Type == BlockType.Section); - var errorReported = false; - - // Set the block and span type - Context.Builder.CurrentBlock.Type = BlockType.Section; - - // Verify we're on "section" and accept - AssertDirective(SyntaxConstants.CSharp.SectionKeyword); - var startLocation = CurrentLocation; - AcceptAndMoveNext(); - - if (nested) - { - Context.ErrorSink.OnError( - startLocation, - LegacyResources.FormatParseError_Sections_Cannot_Be_Nested(LegacyResources.SectionExample_CS), - Span.GetContent().Value.Length); - errorReported = true; - } - - var whitespace = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: false)); - - // Get the section name - var sectionName = string.Empty; - if (!Required(CSharpSymbolType.Identifier, - errorIfNotFound: true, - errorBase: LegacyResources.FormatParseError_Unexpected_Character_At_Section_Name_Start)) - { - if (!errorReported) - { - errorReported = true; - } - - PutCurrentBack(); - PutBack(whitespace); - AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: false)); - } - else - { - Accept(whitespace); - sectionName = CurrentSymbol.Content; - AcceptAndMoveNext(); - } - Context.Builder.CurrentBlock.ChunkGenerator = new SectionChunkGenerator(sectionName); - - var errorLocation = CurrentLocation; - whitespace = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: false)); - - // Get the starting brace - var sawStartingBrace = At(CSharpSymbolType.LeftBrace); - if (!sawStartingBrace) - { - if (!errorReported) - { - errorReported = true; - Context.ErrorSink.OnError( - errorLocation, - LegacyResources.ParseError_MissingOpenBraceAfterSection, - length: 1 /* { */); - } - - PutCurrentBack(); - PutBack(whitespace); - AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: false)); - Optional(CSharpSymbolType.NewLine); - Output(SpanKind.MetaCode); - CompleteBlock(); - return; - } - else - { - Accept(whitespace); - } - - var startingBraceLocation = CurrentLocation; - - // Set up edit handler - var editHandler = new AutoCompleteEditHandler(Language.TokenizeString, autoCompleteAtEndOfSpan: true); - - Span.EditHandler = editHandler; - Span.Accept(CurrentSymbol); - - // Output Metacode then switch to section parser - Output(SpanKind.MetaCode); - SectionBlock("{", "}", caseSensitive: true); - - Span.ChunkGenerator = SpanChunkGenerator.Null; - // Check for the terminating "}" - if (!Optional(CSharpSymbolType.RightBrace)) - { - editHandler.AutoCompleteString = "}"; - Context.ErrorSink.OnError( - startingBraceLocation, - LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF( - SyntaxConstants.CSharp.SectionKeyword, - Language.GetSample(CSharpSymbolType.RightBrace), - Language.GetSample(CSharpSymbolType.LeftBrace)), - length: 1 /* } */); - } - else - { - Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; - } - CompleteBlock(insertMarkerIfNecessary: false, captureWhitespaceToEndOfLine: true); - Output(SpanKind.MetaCode); - return; - } - - protected virtual void FunctionsDirective() - { - // Set the block type - Context.Builder.CurrentBlock.Type = BlockType.Functions; - - // Verify we're on "functions" and accept - AssertDirective(SyntaxConstants.CSharp.FunctionsKeyword); - var block = new Block(CurrentSymbol); - AcceptAndMoveNext(); - - AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: false)); - - if (!At(CSharpSymbolType.LeftBrace)) - { - Context.ErrorSink.OnError( - CurrentLocation, - LegacyResources.FormatParseError_Expected_X(Language.GetSample(CSharpSymbolType.LeftBrace)), - length: 1 /* { */); - CompleteBlock(); - Output(SpanKind.MetaCode); - return; - } - else - { - Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; - } - - // Capture start point and continue - var blockStart = CurrentLocation; - AcceptAndMoveNext(); - - // Output what we've seen and continue - Output(SpanKind.MetaCode); - - var editHandler = new AutoCompleteEditHandler(Language.TokenizeString); - Span.EditHandler = editHandler; - - Balance(BalancingModes.NoErrorOnFailure, CSharpSymbolType.LeftBrace, CSharpSymbolType.RightBrace, blockStart); - Span.ChunkGenerator = new TypeMemberChunkGenerator(); - if (!At(CSharpSymbolType.RightBrace)) - { - editHandler.AutoCompleteString = "}"; - Context.ErrorSink.OnError( - blockStart, - LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF(block.Name, "}", "{"), - length: 1 /* } */); - CompleteBlock(); - Output(SpanKind.Code); - } - else - { - Output(SpanKind.Code); - Assert(CSharpSymbolType.RightBrace); - Span.ChunkGenerator = SpanChunkGenerator.Null; - Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; - AcceptAndMoveNext(); - CompleteBlock(); - Output(SpanKind.MetaCode); - } - } - - protected virtual void InheritsDirective() - { - // Verify we're on the right keyword and accept - AssertDirective(SyntaxConstants.CSharp.InheritsKeyword); - AcceptAndMoveNext(); - - InheritsDirectiveCore(); - } - [Conditional("DEBUG")] protected void AssertDirective(string directive) { @@ -1809,13 +1671,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy Debug.Assert(string.Equals(CurrentSymbol.Content, directive, StringComparison.Ordinal)); } - protected void InheritsDirectiveCore() - { - BaseTypeDirective( - LegacyResources.ParseError_InheritsKeyword_Must_Be_Followed_By_TypeName, - baseType => new SetBaseTypeChunkGenerator(baseType)); - } - protected void BaseTypeDirective(string noTypeNameError, Func createChunkGenerator) { var keywordStartLocation = Span.Start; diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/HtmlMarkupParser.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/HtmlMarkupParser.cs index 82753dfb6e..017eec80e7 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/HtmlMarkupParser.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/HtmlMarkupParser.cs @@ -1577,7 +1577,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy return false; } - public void ParseSection(Tuple nestingSequences, bool caseSensitive) + public void ParseRazorBlock(Tuple nestingSequences, bool caseSensitive) { if (Context == null) { diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/ParserVisitor.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/ParserVisitor.cs index 5f45e68119..f4db4a49d9 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/ParserVisitor.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/ParserVisitor.cs @@ -69,10 +69,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy { } - public virtual void VisitTypeMemberSpan(TypeMemberChunkGenerator chunkGenerator, Span span) - { - } - public virtual void VisitMarkupSpan(MarkupChunkGenerator chunkGenerator, Span span) { } diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/SectionChunkGenerator.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/SectionChunkGenerator.cs deleted file mode 100644 index 5f6cb83527..0000000000 --- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/SectionChunkGenerator.cs +++ /dev/null @@ -1,54 +0,0 @@ -// 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; - -namespace Microsoft.AspNetCore.Razor.Evolution.Legacy -{ - internal class SectionChunkGenerator : ParentChunkGenerator - { - public SectionChunkGenerator(string sectionName) - { - SectionName = sectionName; - } - - public string SectionName { get; } - - public override void AcceptStart(ParserVisitor visitor, Block block) - { - } - - public override void AcceptEnd(ParserVisitor visitor, Block block) - { - } - - public override void GenerateStartParentChunk(Block target, ChunkGeneratorContext context) - { - //var chunk = context.ChunkTreeBuilder.StartParentChunk(target); - - //chunk.Name = SectionName; - } - - public override void GenerateEndParentChunk(Block target, ChunkGeneratorContext context) - { - //context.ChunkTreeBuilder.EndParentChunk(); - } - - public override bool Equals(object obj) - { - var other = obj as SectionChunkGenerator; - return base.Equals(other) && - string.Equals(SectionName, other.SectionName, StringComparison.Ordinal); - } - - public override int GetHashCode() - { - return SectionName == null ? 0 : StringComparer.Ordinal.GetHashCode(SectionName); - } - - public override string ToString() - { - return "Section:" + SectionName; - } - } -} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/SetBaseTypeChunkGenerator.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/SetBaseTypeChunkGenerator.cs deleted file mode 100644 index 3aca881a08..0000000000 --- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/SetBaseTypeChunkGenerator.cs +++ /dev/null @@ -1,42 +0,0 @@ -// 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; - -namespace Microsoft.AspNetCore.Razor.Evolution.Legacy -{ - internal class SetBaseTypeChunkGenerator : SpanChunkGenerator - { - public SetBaseTypeChunkGenerator(string baseType) - { - BaseType = baseType; - } - - public string BaseType { get; } - - public override void Accept(ParserVisitor visitor, Span span) - { - } - - public override void GenerateChunk(Span target, ChunkGeneratorContext context) - { - } - - public override string ToString() - { - return "Base:" + BaseType; - } - - public override bool Equals(object obj) - { - var other = obj as SetBaseTypeChunkGenerator; - return other != null && - string.Equals(BaseType, other.BaseType, StringComparison.Ordinal); - } - - public override int GetHashCode() - { - return BaseType.GetHashCode(); - } - } -} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TypeMemberChunkGenerator.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TypeMemberChunkGenerator.cs deleted file mode 100644 index 1fcd3599c0..0000000000 --- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TypeMemberChunkGenerator.cs +++ /dev/null @@ -1,23 +0,0 @@ -// 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. - -namespace Microsoft.AspNetCore.Razor.Evolution.Legacy -{ - internal class TypeMemberChunkGenerator : SpanChunkGenerator - { - public override void GenerateChunk(Span target, ChunkGeneratorContext context) - { - //context.ChunkTreeBuilder.AddTypeMemberChunk(target.Content, target); - } - - public override void Accept(ParserVisitor visitor, Span span) - { - visitor.VisitTypeMemberSpan(this, span); - } - - public override string ToString() - { - return "TypeMember"; - } - } -} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/LegacyResources.resx b/src/Microsoft.AspNetCore.Razor.Evolution/LegacyResources.resx index 1c7605f288..12a1b2c3e8 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/LegacyResources.resx +++ b/src/Microsoft.AspNetCore.Razor.Evolution/LegacyResources.resx @@ -182,6 +182,9 @@ The '{0}' directive expects an identifier. + + The '{0}' directive expects a type name. + "EndBlock" was called without a matching call to "StartBlock". diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Properties/LegacyResources.Designer.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Properties/LegacyResources.Designer.cs index fe56041a01..5bd5c4c7e8 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/Properties/LegacyResources.Designer.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Properties/LegacyResources.Designer.cs @@ -346,6 +346,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution return string.Format(CultureInfo.CurrentCulture, GetString("DirectiveExpectsIdentifier"), p0); } + /// + /// The '{0}' directive expects a type name. + /// + internal static string DirectiveExpectsTypeName + { + get { return GetString("DirectiveExpectsTypeName"); } + } + + /// + /// The '{0}' directive expects a type name. + /// + internal static string FormatDirectiveExpectsTypeName(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("DirectiveExpectsTypeName"), p0); + } + /// /// "EndBlock" was called without a matching call to "StartBlock". /// diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/RazorEngine.cs b/src/Microsoft.AspNetCore.Razor.Evolution/RazorEngine.cs index 79bd6f541d..c720d43812 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/RazorEngine.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/RazorEngine.cs @@ -35,8 +35,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution builder.Phases.Add(new DefaultRazorIRLoweringPhase()); builder.Phases.Add(new DefaultRazorIRPhase()); + // Syntax Tree passes + builder.Features.Add(new DefaultDirectiveSyntaxTreePass()); builder.Features.Add(new TagHelperBinderSyntaxTreePass()); builder.Features.Add(new HtmlNodeOptimizationPass()); + + // IR Passes + builder.Features.Add(new DefaultDirectiveIRPass()); } public abstract IReadOnlyList Features { get; } diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultDirectiveIRPassTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultDirectiveIRPassTest.cs new file mode 100644 index 0000000000..8df52b874d --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultDirectiveIRPassTest.cs @@ -0,0 +1,95 @@ +// 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 Microsoft.AspNetCore.Razor.Evolution.Intermediate; +using Xunit; +using static Microsoft.AspNetCore.Razor.Evolution.Intermediate.RazorIRAssert; + +namespace Microsoft.AspNetCore.Razor.Evolution +{ + public class DefaultDirectiveIRPassTest + { + [Fact] + public void Execute_MutatesIRDocument() + { + // Arrange + var content = +@"@inherits Hello +@functions { + var value = true; +}"; + var originalIRDocument = Lower(content); + var pass = new DefaultDirectiveIRPass(); + + // Act + var irDocument = pass.Execute(codeDocument: null, irDocument: originalIRDocument); + + // Assert + Assert.Same(originalIRDocument, irDocument); + } + + [Fact] + public void Execute_Inherits_SetsClassDeclarationBaseType() + { + // Arrange + var content = "@inherits Hello"; + var originalIRDocument = Lower(content); + var pass = new DefaultDirectiveIRPass(); + + // Act + var irDocument = pass.Execute(codeDocument: null, irDocument: originalIRDocument); + + // Assert + var @namespace = SingleChild(irDocument); + var @class = SingleChild(@namespace); + Assert.Equal(@class.BaseType, "Hello"); + } + + [Fact] + public void Execute_Functions_ExistsAtClassDeclarationAndMethodLevel() + { + // Arrange + var content = "@functions { var value = true; }"; + var originalIRDocument = Lower(content); + var pass = new DefaultDirectiveIRPass(); + + // Act + var irDocument = pass.Execute(codeDocument: null, irDocument: originalIRDocument); + + // Assert + var @namespace = SingleChild(irDocument); + var @class = SingleChild(@namespace); + Children(@class, + node => Assert.IsType(node), + node => CSharpStatement(" var value = true; ", node)); + var method = (RazorMethodDeclarationIRNode)@class.Children[0]; + Children(method, + node => Html(string.Empty, node), + node => Directive("functions", node, + directiveChild => CSharpStatement(" var value = true; ", directiveChild)), + node => Html(string.Empty, node)); + } + + private static DocumentIRNode Lower(string content) + { + var sourceDocument = TestRazorSourceDocument.Create(content); + var codeDocument = RazorCodeDocument.Create(sourceDocument); + var engine = RazorEngine.Create(); + + for (var i = 0; i < engine.Phases.Count; i++) + { + var phase = engine.Phases[i]; + phase.Execute(codeDocument); + + if (phase is IRazorIRLoweringPhase) + { + break; + } + } + + var irDocument = codeDocument.GetIRDocument(); + Assert.NotNull(irDocument); + return irDocument; + } + } +} diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultDirectiveSyntaxTreePassTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultDirectiveSyntaxTreePassTest.cs new file mode 100644 index 0000000000..3369d93c74 --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultDirectiveSyntaxTreePassTest.cs @@ -0,0 +1,119 @@ +// 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 Microsoft.AspNetCore.Razor.Evolution.Legacy; +using Xunit; + +namespace Microsoft.AspNetCore.Razor.Evolution +{ + public class DefaultDirectiveSyntaxTreePassTest + { + [Fact] + public void Execute_DoesNotRecreateSyntaxTreeWhenNoErrors() + { + // Arrange + var engine = RazorEngine.Create(); + var pass = new DefaultDirectiveSyntaxTreePass() + { + Engine = engine, + }; + var content = + @" +@section Foo { +}"; + var sourceDocument = TestRazorSourceDocument.Create(content); + var codeDocument = RazorCodeDocument.Create(sourceDocument); + var originalTree = RazorSyntaxTree.Parse(sourceDocument); + + // Act + var outputTree = pass.Execute(codeDocument, originalTree); + + // Assert + Assert.Empty(originalTree.Diagnostics); + Assert.Same(originalTree, outputTree); + Assert.Empty(outputTree.Diagnostics); + } + + [Fact] + public void Execute_LogsErrorsForNestedSections() + { + // Arrange + var expectedErrors = new[] { + new RazorError( + LegacyResources.FormatParseError_Sections_Cannot_Be_Nested(LegacyResources.SectionExample_CS), + new SourceLocation(22, 2, 4), + length: 8), + new RazorError( + LegacyResources.FormatParseError_Sections_Cannot_Be_Nested(LegacyResources.SectionExample_CS), + new SourceLocation(49, 4, 4), + length: 8), + }; + var engine = RazorEngine.Create(); + var pass = new DefaultDirectiveSyntaxTreePass() + { + Engine = engine, + }; + var content = + @" +@section Foo { + @section Bar { + } + @section Baz { + } +}"; + var sourceDocument = TestRazorSourceDocument.Create(content); + var codeDocument = RazorCodeDocument.Create(sourceDocument); + var originalTree = RazorSyntaxTree.Parse(sourceDocument); + + // Act + var outputTree = pass.Execute(codeDocument, originalTree); + + // Assert + Assert.Empty(originalTree.Diagnostics); + Assert.NotSame(originalTree, outputTree); + Assert.Equal(expectedErrors, outputTree.Diagnostics); + } + + [Fact] + public void Execute_CombinesErrorsWhenNestedSections() + { + // Arrange + var expectedErrors = new[] { + new RazorError("Test Error", SourceLocation.Zero, 3), + new RazorError( + LegacyResources.FormatParseError_Sections_Cannot_Be_Nested(LegacyResources.SectionExample_CS), + new SourceLocation(22, 2, 4), + length: 8), + new RazorError( + LegacyResources.FormatParseError_Sections_Cannot_Be_Nested(LegacyResources.SectionExample_CS), + new SourceLocation(49, 4, 4), + length: 8), + }; + var engine = RazorEngine.Create(); + var pass = new DefaultDirectiveSyntaxTreePass() + { + Engine = engine, + }; + var content = + @" +@section Foo { + @section Bar { + } + @section Baz { + } +}"; + var sourceDocument = TestRazorSourceDocument.Create(content); + var codeDocument = RazorCodeDocument.Create(sourceDocument); + var originalTree = RazorSyntaxTree.Parse(sourceDocument); + var erroredOriginalTree = RazorSyntaxTree.Create(originalTree.Root, new[] { expectedErrors[0] }, originalTree.Options); + + // Act + var outputTree = pass.Execute(codeDocument, erroredOriginalTree); + + // Assert + Assert.Empty(originalTree.Diagnostics); + Assert.NotSame(originalTree, outputTree); + Assert.Equal(expectedErrors, outputTree.Diagnostics); + } + } +} diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/DefaultRazorIRLoweringPhaseIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/DefaultRazorIRLoweringPhaseIntegrationTest.cs index b1281fa359..183bfbdfeb 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/DefaultRazorIRLoweringPhaseIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/DefaultRazorIRLoweringPhaseIntegrationTest.cs @@ -157,7 +157,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate var phase = engine.Phases[i]; phase.Execute(codeDocument); - if (phase is IRazorIRLoweringPhase) + if (phase is IRazorIRPhase) { break; } diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/RazorIRAssert.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/RazorIRAssert.cs index 62306fafaf..f5ac1b207c 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/RazorIRAssert.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/RazorIRAssert.cs @@ -73,6 +73,34 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate } } + public static void CSharpStatement(string expected, RazorIRNode node) + { + try + { + var statement = Assert.IsType(node); + Assert.Equal(expected, statement.Content); + } + catch (XunitException e) + { + throw new IRAssertException(node, node.Children, e.Message, e); + } + } + + public static void Directive(string expectedName, RazorIRNode node, params Action[] childValidators) + { + try + { + var directive = Assert.IsType(node); + Assert.Equal(expectedName, directive.Name); + } + catch (XunitException e) + { + throw new IRAssertException(node, node.Children, e.Message, e); + } + + Children(node, childValidators); + } + public static void Using(string expected, RazorIRNode node) { try diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/BlockTypes.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/BlockTypes.cs index 6f9c4789cc..eb3138590f 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/BlockTypes.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/BlockTypes.cs @@ -47,26 +47,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy } } - internal class FunctionsBlock : Block - { - private const BlockType ThisBlockType = BlockType.Functions; - - public FunctionsBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList children) - : base(ThisBlockType, children, chunkGenerator) - { - } - - public FunctionsBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children) - : this(chunkGenerator, (IReadOnlyList)children) - { - } - - public FunctionsBlock(params SyntaxTreeNode[] children) - : this(ParentChunkGenerator.Null, children) - { - } - } - internal class ExpressionBlock : Block { private const BlockType ThisBlockType = BlockType.Expression; @@ -188,31 +168,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy } } - internal class SectionBlock : Block - { - private const BlockType ThisBlockType = BlockType.Section; - - public SectionBlock(IParentChunkGenerator chunkGenerator, IReadOnlyList children) - : base(ThisBlockType, children, chunkGenerator) - { - } - - public SectionBlock(IParentChunkGenerator chunkGenerator, params SyntaxTreeNode[] children) - : this(chunkGenerator, (IReadOnlyList)children) - { - } - - public SectionBlock(params SyntaxTreeNode[] children) - : this(ParentChunkGenerator.Null, children) - { - } - - public SectionBlock(IReadOnlyList children) - : this(ParentChunkGenerator.Null, children) - { - } - } - internal class TemplateBlock : Block { private const BlockType ThisBlockType = BlockType.Template; diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpAutoCompleteTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpAutoCompleteTest.cs index 5ec29dcaa7..8741508333 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpAutoCompleteTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpAutoCompleteTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Linq; using Xunit; namespace Microsoft.AspNetCore.Razor.Evolution.Legacy @@ -13,19 +14,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy { ParseBlockTest( "@functions{", - new FunctionsBlock( - Factory.CodeTransition("@") - .Accepts(AcceptedCharacters.None), - Factory.MetaCode("functions{") - .Accepts(AcceptedCharacters.None), - Factory.EmptyCSharp() - .AsFunctionsBody() - .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) - { - AutoCompleteString = "}" - })), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.FunctionsDirectiveDescriptor), + Factory.CodeTransition(), + Factory.MetaCode("functions").Accepts(AcceptedCharacters.None), + Factory.MetaCode("{").AutoCompleteWith("}", atEndOfSpan: true).Accepts(AcceptedCharacters.None)), new RazorError( - LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF("functions", "}", "{"), + LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF(CSharpCodeParser.FunctionsDirectiveDescriptor.Name, "}", "{"), new SourceLocation(10, 0, 10), length: 1)); } @@ -34,12 +28,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy public void SectionDirectiveAutoCompleteAtEOF() { ParseBlockTest("@section Header {", - new SectionBlock(new SectionChunkGenerator("Header"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section Header {") - .AutoCompleteWith("}", atEndOfSpan: true) - .Accepts(AcceptedCharacters.Any), - new MarkupBlock()), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "Header", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith("}", atEndOfSpan: true).Accepts(AcceptedCharacters.None), + new MarkupBlock( + Factory.EmptyHtml())), new RazorError( LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF("section", "}", "{"), new SourceLocation(16, 0, 16), @@ -67,23 +66,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy [Fact] public void FunctionsDirectiveAutoCompleteAtStartOfFile() { - ParseBlockTest("@functions{" + Environment.NewLine - + "foo", - new FunctionsBlock( - Factory.CodeTransition("@") - .Accepts(AcceptedCharacters.None), - Factory.MetaCode("functions{") - .Accepts(AcceptedCharacters.None), - Factory.Code(Environment.NewLine + "foo") - .AsFunctionsBody() - .With(new AutoCompleteEditHandler(CSharpLanguageCharacteristics.Instance.TokenizeString) - { - AutoCompleteString = "}" - })), - new RazorError( - LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF("functions", "}", "{"), - new SourceLocation(10, 0, 10), - length: 1)); + ParseBlockTest("@functions{" + Environment.NewLine + "foo", + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.FunctionsDirectiveDescriptor), + Factory.CodeTransition(), + Factory.MetaCode("functions").Accepts(AcceptedCharacters.None), + Factory.MetaCode("{").AutoCompleteWith("}", atEndOfSpan: true).Accepts(AcceptedCharacters.None), + Factory.Code(Environment.NewLine + "foo").AsStatement()), + new RazorError( + LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF("functions", "}", "{"), + new SourceLocation(10, 0, 10), + length: 1)); } [Fact] @@ -91,11 +83,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy { ParseBlockTest("@section Header {" + Environment.NewLine + "

Foo

", - new SectionBlock(new SectionChunkGenerator("Header"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section Header {") - .AutoCompleteWith("}", atEndOfSpan: true) - .Accepts(AcceptedCharacters.Any), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "Header", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith("}", atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(Environment.NewLine), new MarkupTagBlock( diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpDirectivesTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpDirectivesTest.cs index 0f4e0b443f..88c0ed3a04 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpDirectivesTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpDirectivesTest.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Xunit; namespace Microsoft.AspNetCore.Razor.Evolution.Legacy @@ -148,7 +149,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy Factory.Span(SpanKind.Markup, "Header", markup: false) .With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0])) .Accepts(AcceptedCharacters.NonWhiteSpace), - Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.AllWhiteSpace), Factory.MetaCode("{") .AutoCompleteWith(null, atEndOfSpan: true) .Accepts(AcceptedCharacters.None), @@ -181,7 +182,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy Factory.Span(SpanKind.Markup, "Name", markup: false) .With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0])) .Accepts(AcceptedCharacters.NonWhiteSpace), - Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.AllWhiteSpace), Factory.MetaCode("{") .AutoCompleteWith(null, atEndOfSpan: true) .Accepts(AcceptedCharacters.None), @@ -218,7 +219,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy .Accepts(AcceptedCharacters.NonWhiteSpace), Factory.Span(SpanKind.Markup, " ", markup: false) - .Accepts(AcceptedCharacters.AllWhiteSpace))); + .Accepts(AcceptedCharacters.WhiteSpace))); } [Fact] @@ -288,7 +289,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy .With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0])) .Accepts(AcceptedCharacters.NonWhiteSpace), - Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.AllWhiteSpace)), + Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace)), expectedErorr); } @@ -315,7 +316,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy .With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0])) .Accepts(AcceptedCharacters.NonWhiteSpace), - Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace)), + Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.AllWhiteSpace)), expectedErorr); } @@ -350,7 +351,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy // Arrange var descriptor = DirectiveDescriptorBuilder.CreateCodeBlock("custom").AddString().Build(); var expectedErorr = new RazorError( - LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF("custom", "{", "}"), + LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF("custom", "}", "{"), new SourceLocation(14, 0, 14), length: 1); @@ -366,7 +367,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy Factory.Span(SpanKind.Markup, "Hello", markup: false) .With(new DirectiveTokenChunkGenerator(descriptor.Tokens[0])) .Accepts(AcceptedCharacters.NonWhiteSpace), - Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Markup, " ", markup: false).Accepts(AcceptedCharacters.AllWhiteSpace), Factory.MetaCode("{") .AutoCompleteWith("}", atEndOfSpan: true) .Accepts(AcceptedCharacters.None)), @@ -682,101 +683,93 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy public void InheritsDirective() { ParseBlockTest("@inherits System.Web.WebPages.WebPage", - new DirectiveBlock( + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.InheritsDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode(SyntaxConstants.CSharp.InheritsKeyword + " ") - .Accepts(AcceptedCharacters.None), - Factory.Code("System.Web.WebPages.WebPage") - .AsBaseType("System.Web.WebPages.WebPage"))); + Factory.MetaCode("inherits").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "System.Web.WebPages.WebPage", markup: false) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.InheritsDirectiveDescriptor.Tokens.First())))); } [Fact] public void InheritsDirectiveSupportsArrays() { ParseBlockTest("@inherits string[[]][]", - new DirectiveBlock( + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.InheritsDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode(SyntaxConstants.CSharp.InheritsKeyword + " ") - .Accepts(AcceptedCharacters.None), - Factory.Code("string[[]][]") - .AsBaseType("string[[]][]"))); + Factory.MetaCode("inherits").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "string[[]][]", markup: false) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.InheritsDirectiveDescriptor.Tokens.First())))); } [Fact] public void InheritsDirectiveSupportsNestedGenerics() { ParseBlockTest("@inherits System.Web.Mvc.WebViewPage>", - new DirectiveBlock( + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.InheritsDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode(SyntaxConstants.CSharp.InheritsKeyword + " ") - .Accepts(AcceptedCharacters.None), - Factory.Code("System.Web.Mvc.WebViewPage>") - .AsBaseType("System.Web.Mvc.WebViewPage>"))); + Factory.MetaCode("inherits").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "System.Web.Mvc.WebViewPage>", markup: false) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.InheritsDirectiveDescriptor.Tokens.First())))); } [Fact] public void InheritsDirectiveSupportsTypeKeywords() { ParseBlockTest("@inherits string", - new DirectiveBlock( + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.InheritsDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode(SyntaxConstants.CSharp.InheritsKeyword + " ") - .Accepts(AcceptedCharacters.None), - Factory.Code("string") - .AsBaseType("string"))); - } - - [Fact] - public void InheritsDirectiveSupportsVSTemplateTokens() - { - ParseBlockTest("@inherits $rootnamespace$.MyBase", - new DirectiveBlock( - Factory.CodeTransition(), - Factory.MetaCode(SyntaxConstants.CSharp.InheritsKeyword + " ") - .Accepts(AcceptedCharacters.None), - Factory.Code("$rootnamespace$.MyBase") - .AsBaseType("$rootnamespace$.MyBase"))); + Factory.MetaCode("inherits").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "string", markup: false) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.InheritsDirectiveDescriptor.Tokens.First())))); } [Fact] public void FunctionsDirective() { ParseBlockTest("@functions { foo(); bar(); }", - new FunctionsBlock( + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.FunctionsDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode(SyntaxConstants.CSharp.FunctionsKeyword + " {") - .Accepts(AcceptedCharacters.None), - Factory.Code(" foo(); bar(); ") - .AsFunctionsBody() - .AutoCompleteWith(autoCompleteString: null), - Factory.MetaCode("}") - .Accepts(AcceptedCharacters.None))); + Factory.MetaCode("functions").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), + Factory.Code(" foo(); bar(); ").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None))); } [Fact] public void EmptyFunctionsDirective() { ParseBlockTest("@functions { }", - new FunctionsBlock( + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.FunctionsDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode(SyntaxConstants.CSharp.FunctionsKeyword + " {") - .Accepts(AcceptedCharacters.None), - Factory.Code(" ") - .AsFunctionsBody() - .AutoCompleteWith(autoCompleteString: null), - Factory.MetaCode("}") - .Accepts(AcceptedCharacters.None))); + Factory.MetaCode("functions").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), + Factory.Code(" ").AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None))); } [Fact] public void SectionDirective() { ParseBlockTest("@section Header {

F{o}o

}", - new SectionBlock(new SectionChunkGenerator("Header"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section Header {") - .AutoCompleteWith(null, atEndOfSpan: true) - .Accepts(AcceptedCharacters.Any), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "Header", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpErrorTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpErrorTest.cs index 07182d5d90..0deae1bf89 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpErrorTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpErrorTest.cs @@ -299,15 +299,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy public void ParseBlockReportsErrorIfClassBlockUnterminatedAtEOF() { ParseBlockTest("functions { var foo = bar; if(foo != null) { bar(); } ", - new FunctionsBlock( - Factory.MetaCode("functions {").Accepts(AcceptedCharacters.None), - Factory.Code(" var foo = bar; if(foo != null) { bar(); } ") - .AsFunctionsBody() - .AutoCompleteWith("}")), - new RazorError( - LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF("functions", '}', '{'), - new SourceLocation(10, 0, 10), - length: 1)); + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.FunctionsDirectiveDescriptor), + Factory.MetaCode("functions").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith("}", atEndOfSpan: true).Accepts(AcceptedCharacters.None), + Factory.Code(" var foo = bar; if(foo != null) { bar(); } ").AsStatement()), + new RazorError( + LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF("functions", '}', '{'), + new SourceLocation(10, 0, 10), + length: 1)); } [Fact] diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpSectionTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpSectionTest.cs index e6085fe97f..fd1ad152b0 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpSectionTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/CSharpSectionTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Linq; using Xunit; namespace Microsoft.AspNetCore.Razor.Evolution.Legacy @@ -14,49 +15,52 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest("@section" + Environment.NewLine, new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator(string.Empty), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section" + Environment.NewLine))), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None)), + Factory.Markup(Environment.NewLine)), new RazorError( - LegacyResources.FormatParseError_Unexpected_Character_At_Section_Name_Start( - LegacyResources.ErrorComponent_EndOfFile), - new SourceLocation(8 + Environment.NewLine.Length, 1, 0), - length: 1)); + LegacyResources.FormatDirectiveExpectsIdentifier(CSharpCodeParser.SectionDirectiveDescriptor.Name), + new SourceLocation(8, 0, 8), + length: Environment.NewLine.Length)); } [Fact] public void ParseSectionBlockCapturesWhitespaceToEndOfLineInSectionStatementMissingOpenBrace() { - ParseDocumentTest("@section Foo " + Environment.NewLine - + " ", + ParseDocumentTest("@section Foo " + Environment.NewLine + " ", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("Foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section Foo " + Environment.NewLine)), - Factory.Markup(" ")), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "Foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " " + Environment.NewLine + " ", markup: false).Accepts(AcceptedCharacters.AllWhiteSpace)), + Factory.EmptyHtml()), new RazorError( - LegacyResources.ParseError_MissingOpenBraceAfterSection, - new SourceLocation(12, 0, 12), + LegacyResources.FormatUnexpectedEOFAfterDirective(CSharpCodeParser.SectionDirectiveDescriptor.Name, "{"), + new SourceLocation(25 + Environment.NewLine.Length, 0, 25 + Environment.NewLine.Length), length: 1)); } [Fact] public void ParseSectionBlockCapturesWhitespaceToEndOfLineInSectionStatementMissingName() { - ParseDocumentTest("@section " + Environment.NewLine - + " ", + ParseDocumentTest("@section " + Environment.NewLine + " ", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator(string.Empty), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section " + Environment.NewLine)), - Factory.Markup(" ")), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace)), + Factory.Markup(Environment.NewLine + " ")), new RazorError( - LegacyResources.FormatParseError_Unexpected_Character_At_Section_Name_Start( - LegacyResources.ErrorComponent_EndOfFile), - new SourceLocation(21 + Environment.NewLine.Length, 1, 4), - length: 1)); + LegacyResources.FormatDirectiveExpectsIdentifier(CSharpCodeParser.SectionDirectiveDescriptor.Name), + new SourceLocation(17, 0, 17), + length: Environment.NewLine.Length)); } [Fact] @@ -79,19 +83,19 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest("@section 9 {

Foo

}", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator(string.Empty), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section ")), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace)), Factory.Markup("9 { "), new MarkupTagBlock( Factory.Markup("

")), Factory.Markup("Foo"), new MarkupTagBlock( Factory.Markup("

")), - Factory.Markup(" }")), + Factory.Markup(" }")), new RazorError( - LegacyResources.FormatParseError_Unexpected_Character_At_Section_Name_Start( - LegacyResources.FormatErrorComponent_Character("9")), + LegacyResources.FormatDirectiveExpectsIdentifier(CSharpCodeParser.SectionDirectiveDescriptor.Name), new SourceLocation(9, 0, 9), length: 1)); } @@ -102,9 +106,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest("@section foo-bar {

Foo

}", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section foo")), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First()))), Factory.Markup("-bar { "), new MarkupTagBlock( Factory.Markup("

")), @@ -113,7 +121,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy Factory.Markup("

")), Factory.Markup(" }")), new RazorError( - LegacyResources.ParseError_MissingOpenBraceAfterSection, + LegacyResources.FormatUnexpectedDirectiveLiteral(CSharpCodeParser.SectionDirectiveDescriptor.Name, "{"), new SourceLocation(12, 0, 12), length: 1)); } @@ -124,16 +132,26 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest("@section foo { @section bar {

Foo

} }", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section foo {") - .AutoCompleteWith(null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(" "), - new SectionBlock(new SectionChunkGenerator("bar"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section bar {") - .AutoCompleteWith(null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "bar", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( @@ -148,8 +166,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy Factory.EmptyHtml()), new RazorError( LegacyResources.FormatParseError_Sections_Cannot_Be_Nested(LegacyResources.SectionExample_CS), - new SourceLocation(16, 0, 16), - 7)); + new SourceLocation(15, 0, 15), + 8)); } [Fact] @@ -158,13 +176,19 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest("@section foo {", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section foo {") - .AutoCompleteWith("}", atEndOfSpan: true), - new MarkupBlock())), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith("}", atEndOfSpan: true).Accepts(AcceptedCharacters.None), + new MarkupBlock( + Factory.EmptyHtml()))), new RazorError( - LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF("section", "}", "{"), + LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF(CSharpCodeParser.SectionDirectiveDescriptor.Name, "}", "{"), new SourceLocation(13, 0, 13), length: 1)); } @@ -179,10 +203,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest("@section foo {" + postStartBrace, new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section foo {") - .AutoCompleteWith("}", atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith("}", atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(postStartBrace)))), new RazorError( @@ -197,10 +226,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest("@section foo {

Foo{}

", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section foo {") - .AutoCompleteWith("}", atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith("}", atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( @@ -227,10 +261,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy spaces), new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("Test"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode($"section Test{newLine}{{") - .AutoCompleteWith("}", atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "Test", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, Environment.NewLine, CSharpSymbolType.NewLine).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith("}", atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(newLine), new StatementBlock( @@ -256,12 +295,18 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest("@section foo " + Environment.NewLine, new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section foo " + Environment.NewLine))), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " " + Environment.NewLine, markup: false).Accepts(AcceptedCharacters.AllWhiteSpace)), + Factory.EmptyHtml()), new RazorError( - LegacyResources.ParseError_MissingOpenBraceAfterSection, - new SourceLocation(12, 0, 12), + LegacyResources.FormatUnexpectedEOFAfterDirective(CSharpCodeParser.SectionDirectiveDescriptor.Name, "{"), + new SourceLocation(18 + Environment.NewLine.Length, 0, 18 + Environment.NewLine.Length), length: 1)); } @@ -279,10 +324,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy + "}", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode(string.Format("section foo {0}{0}{0}{0}{0}{0}{{", Environment.NewLine)) - .AutoCompleteWith(null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " " + string.Format("{0}{0}{0}{0}{0}{0}", Environment.NewLine), markup: false).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(Environment.NewLine), new MarkupTagBlock( @@ -301,10 +351,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest("@section foo {

Foo

}", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section foo {") - .AutoCompleteWith(null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( @@ -323,10 +378,14 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest("@section foo{

Foo

}", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section foo{") - .AutoCompleteWith(null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( @@ -345,10 +404,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest("@section foo { }", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section foo {") - .AutoCompleteWith(null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(" "), new MarkupTagBlock( @@ -367,10 +431,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest("@section foo { I really want to render a close brace, so here I go: @(\"}\") }", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section foo {") - .AutoCompleteWith(null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(" I really want to render a close brace, so here I go: "), new ExpressionBlock( @@ -392,10 +461,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy + "}", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("Foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section Foo {") - .AutoCompleteWith(null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "Foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(Environment.NewLine), new StatementBlock( @@ -414,10 +488,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy + "}}", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("Foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section Foo {") - .AutoCompleteWith(null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "Foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(Environment.NewLine), new StatementBlock( @@ -434,10 +513,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest("@section foo {something}", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section foo {") - .AutoCompleteWith(null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup("something")), Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), @@ -450,10 +534,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest("@section s {}", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("s"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section s {") - .AutoCompleteWith(null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "s", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup("")), Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), @@ -468,10 +557,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest("@section s {}", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("s"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section s {") - .AutoCompleteWith(null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "s", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup("")), Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), @@ -485,10 +579,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy "@section s {" + Environment.NewLine + " \" '-->}", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("s"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section s {") - .AutoCompleteWith(null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "s", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(Environment.NewLine), new MarkupTagBlock( @@ -505,10 +604,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy "@section s { }", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("s"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section s {") - .AutoCompleteWith(null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "s", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(" ")), Factory.MetaCode("}").Accepts(AcceptedCharacters.None)), @@ -527,10 +631,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy "@section s {}", new MarkupBlock( factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("s"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), factory.CodeTransition(), - factory.MetaCode("section s {") - .AutoCompleteWith(null, atEndOfSpan: true), + factory.MetaCode("section").Accepts(AcceptedCharacters.None), + factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + factory.Span(SpanKind.Code, "s", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( new MarkupTagBlock( factory.Markup("}", new MarkupBlock( factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("s"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), factory.CodeTransition(), - factory.MetaCode("section s {") - .AutoCompleteWith(null, atEndOfSpan: true), + factory.MetaCode("section").Accepts(AcceptedCharacters.None), + factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + factory.Span(SpanKind.Code, "s", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( new MarkupTagBlock( factory.Markup(", string, int>.Baz", - new DirectiveBlock( - Factory.MetaCode("inherits ").Accepts(AcceptedCharacters.None), - Factory.Code("Foo.Bar, string, int>.Baz") - .AsBaseType("Foo.Bar, string, int>.Baz") - )); + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.InheritsDirectiveDescriptor), + Factory.MetaCode("inherits").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "Foo.Bar, string, int>.Baz", markup: false) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.InheritsDirectiveDescriptor.Tokens.First())))); } [Fact] public void InheritsBlockOutputsErrorIfInheritsNotFollowedByTypeButAcceptsEntireLineAsCode() { - ParseBlockTest("inherits " + Environment.NewLine - + "foo", - new DirectiveBlock( - Factory.MetaCode("inherits ").Accepts(AcceptedCharacters.None), - Factory.Code(" " + Environment.NewLine) - .AsBaseType(string.Empty) - ), - new RazorError(LegacyResources.ParseError_InheritsKeyword_Must_Be_Followed_By_TypeName, 0, 0, 0, 8)); + ParseBlockTest("inherits " + Environment.NewLine + "foo", + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.InheritsDirectiveDescriptor), + Factory.MetaCode("inherits").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace)), + new RazorError(LegacyResources.FormatDirectiveExpectsTypeName(CSharpCodeParser.InheritsDirectiveDescriptor.Name), 24, 0, 24, 2)); } [Fact] @@ -143,29 +141,27 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy { const string code = " foo(); \"bar}baz\" "; ParseBlockTest("functions {" + code + "} zoop", - new FunctionsBlock( - Factory.MetaCode("functions {").Accepts(AcceptedCharacters.None), - Factory.Code(code) - .AsFunctionsBody() - .AutoCompleteWith(autoCompleteString: null), - Factory.MetaCode("}").Accepts(AcceptedCharacters.None) - )); + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.FunctionsDirectiveDescriptor), + Factory.MetaCode("functions").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), + Factory.Code(code).AsStatement(), + Factory.MetaCode("}").Accepts(AcceptedCharacters.None))); } [Fact] public void ParseBlockDoesNoErrorRecoveryForFunctionsBlock() { ParseBlockTest("functions { { { { { } zoop", - new FunctionsBlock( - Factory.MetaCode("functions {").Accepts(AcceptedCharacters.None), - Factory.Code(" { { { { } zoop") - .AsFunctionsBody() - .AutoCompleteWith("}") - ), - new RazorError( - LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF("functions", "}", "{"), - new SourceLocation(10, 0, 10), - length: 1)); + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.FunctionsDirectiveDescriptor), + Factory.MetaCode("functions").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith("}", atEndOfSpan: true).Accepts(AcceptedCharacters.None), + Factory.Code(" { { { { } zoop").AsStatement()), + new RazorError( + LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF("functions", "}", "{"), + new SourceLocation(10, 0, 10), + length: 1)); } [Fact] diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/HtmlDocumentTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/HtmlDocumentTest.cs index 8f352580c7..c397aecd94 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/HtmlDocumentTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Legacy/HtmlDocumentTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Linq; using Xunit; namespace Microsoft.AspNetCore.Razor.Evolution.Legacy @@ -108,10 +109,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy + "}", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("Foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section Foo {") - .AutoCompleteWith(null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "Foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(Environment.NewLine + " "), BlockFactory.MarkupTagBlock(""), @@ -468,10 +474,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy ParseDocumentTest(@"@section Foo { }", new MarkupBlock( Factory.EmptyHtml(), - new SectionBlock(new SectionChunkGenerator("Foo"), + new DirectiveBlock(new DirectiveChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor), Factory.CodeTransition(), - Factory.MetaCode("section Foo {") - .AutoCompleteWith(autoCompleteString: null, atEndOfSpan: true), + Factory.MetaCode("section").Accepts(AcceptedCharacters.None), + Factory.Span(SpanKind.Code, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.WhiteSpace), + Factory.Span(SpanKind.Code, "Foo", CSharpSymbolType.Identifier) + .Accepts(AcceptedCharacters.NonWhiteSpace) + .With(new DirectiveTokenChunkGenerator(CSharpCodeParser.SectionDirectiveDescriptor.Tokens.First())), + Factory.Span(SpanKind.Markup, " ", CSharpSymbolType.WhiteSpace).Accepts(AcceptedCharacters.AllWhiteSpace), + Factory.MetaCode("{").AutoCompleteWith(null, atEndOfSpan: true).Accepts(AcceptedCharacters.None), new MarkupBlock( Factory.Markup(" "), BlockFactory.MarkupTagBlock("