private void ParseDirectiveBlock(RazorDirectiveDescriptor descriptor, Action <SourceLocation> parseChildren) { if (EndOfFile) { Context.OnError( CurrentLocation, $"Unexpected end of file following directive {descriptor.Name}. Expected '{{'", CurrentSymbol.Content.Length); } else if (!At(CSharpSymbolType.LeftBrace)) { Context.OnError( CurrentLocation, $"Unexpected literal '{CurrentSymbol.Content.Length}' following directive {descriptor.Name}. Expected '{{'", CurrentSymbol.Content.Length); } else { var editHandler = new AutoCompleteEditHandler(Language.TokenizeString, autoCompleteAtEndOfSpan: true); Span.EditHandler = editHandler; var startingBraceLocation = CurrentLocation; AcceptAndMoveNext(); Span.ChunkGenerator = SpanChunkGenerator.Null; Output(SpanKind.MetaCode, AcceptedCharacters.None); parseChildren(startingBraceLocation); Span.ChunkGenerator = SpanChunkGenerator.Null; if (!Optional(CSharpSymbolType.RightBrace)) { editHandler.AutoCompleteString = "}"; Context.OnError( startingBraceLocation, // TODO: This is RazorResources.FormatParseError_Expected_EndOfBlock_Before_EOF string.Format( "The {0} block is missing a closing \"{1}\" character. Make sure you have a matching \"{1}\" character for all the \"{2}\" characters within this block, and that none of the \"{1}\" characters are being interpreted as markup.", descriptor.Name, Language.GetSample(CSharpSymbolType.RightBrace), Language.GetSample(CSharpSymbolType.LeftBrace)), length: 1 /* } */); } else { Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; } CompleteBlock(insertMarkerIfNecessary: false, captureWhitespaceToEndOfLine: true); Span.ChunkGenerator = SpanChunkGenerator.Null; Output(SpanKind.MetaCode, AcceptedCharacters.None); } }
protected void HandleDirective(RazorDirectiveDescriptor descriptor) { Context.CurrentBlock.Type = BlockType.Directive; Context.CurrentBlock.ChunkGenerator = new RazorDirectiveChunkGenerator(descriptor); AssertDirective(descriptor.Name); AcceptAndMoveNext(); Output(SpanKind.MetaCode, AcceptedCharacters.None); for (var i = 0; i < descriptor.Tokens.Count; i++) { var tokenDescriptor = descriptor.Tokens[i]; AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true)); if (tokenDescriptor.Type == RazorDirectiveTokenType.Member || tokenDescriptor.Type == RazorDirectiveTokenType.Type) { Output(SpanKind.Code, AcceptedCharacters.WhiteSpace); } else { Output(SpanKind.Markup, AcceptedCharacters.WhiteSpace); } var outputKind = SpanKind.Markup; switch (tokenDescriptor.Type) { case RazorDirectiveTokenType.Type: if (!NamespaceOrTypeName()) { continue; } outputKind = SpanKind.Code; break; case RazorDirectiveTokenType.Member: if (At(CSharpSymbolType.Identifier)) { AcceptAndMoveNext(); } else { Context.OnError( CurrentLocation, $"TODO: Directive {descriptor.Name} expects an identifier.", CurrentSymbol.Content.Length); continue; } outputKind = SpanKind.Code; break; case RazorDirectiveTokenType.String: AcceptAndMoveNext(); break; case RazorDirectiveTokenType.Literal: if (string.Equals(CurrentSymbol.Content, tokenDescriptor.Value, StringComparison.Ordinal)) { AcceptAndMoveNext(); } else if (!tokenDescriptor.Optional) { Context.OnError( CurrentLocation, $"TODO: Expected literal '{tokenDescriptor.Value}'.", CurrentSymbol.Content.Length); } break; } Span.ChunkGenerator = new RazorDirectiveTokenChunkGenerator(tokenDescriptor); Output(outputKind, AcceptedCharacters.NonWhiteSpace); } AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true)); switch (descriptor.Type) { case RazorDirectiveDescriptorType.SingleLine: if (At(CSharpSymbolType.NewLine)) { AcceptAndMoveNext(); } else if (!EndOfFile) { Context.OnError( CurrentLocation, $"Unexpected literal following directive {descriptor.Name}.", CurrentSymbol.Content.Length); } break; case RazorDirectiveDescriptorType.RazorBlock: ParseDirectiveBlock(descriptor, parseChildren: (startingBraceLocation) => { // When transitioning to the HTML parser we no longer want to act as if we're in a nested C# state. // For instance, if <div>@hello.</div> is in a nested C# block we don't want the trailing '.' to be handled // as C#; it should be handled as a period because it's wrapped in markup. var wasNested = IsNested; IsNested = false; using (PushSpanConfig()) { Context.SwitchActiveParser(); Context.MarkupParser.ParseSection(Tuple.Create("{", "}"), caseSensitive: true); Context.SwitchActiveParser(); } Initialize(Span); IsNested = wasNested; NextToken(); }); break; case RazorDirectiveDescriptorType.CodeBlock: ParseDirectiveBlock(descriptor, parseChildren: (startingBraceLocation) => { Balance(BalancingModes.NoErrorOnFailure, CSharpSymbolType.LeftBrace, CSharpSymbolType.RightBrace, startingBraceLocation); Span.ChunkGenerator = new StatementChunkGenerator(); Output(SpanKind.Code); }); break; } }