// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections.Generic; using System.Web.Razor.Generator; using System.Web.Razor.Parser.SyntaxTree; using System.Web.Razor.Text; using System.Web.Razor.Tokenizer; using System.Web.Razor.Tokenizer.Symbols; namespace System.Web.Razor.Parser { public partial class HtmlMarkupParser : TokenizerBackedParser { //From http://dev.w3.org/html5/spec/Overview.html#elements-0 private ISet _voidElements = new HashSet(StringComparer.OrdinalIgnoreCase) { "area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr" }; public ISet VoidElements { get { return _voidElements; } } protected override ParserBase OtherParser { get { return Context.CodeParser; } } protected override LanguageCharacteristics Language { get { return HtmlLanguageCharacteristics.Instance; } } public override void BuildSpan(SpanBuilder span, SourceLocation start, string content) { span.Kind = SpanKind.Markup; span.CodeGenerator = new MarkupCodeGenerator(); base.BuildSpan(span, start, content); } protected override void OutputSpanBeforeRazorComment() { Output(SpanKind.Markup); } protected void SkipToAndParseCode(HtmlSymbolType type) { SkipToAndParseCode(sym => sym.Type == type); } protected void SkipToAndParseCode(Func condition) { HtmlSymbol last = null; bool startOfLine = false; while (!EndOfFile && !condition(CurrentSymbol)) { if (At(HtmlSymbolType.NewLine)) { if (last != null) { Accept(last); } // Mark the start of a new line startOfLine = true; last = null; AcceptAndMoveNext(); } else if (At(HtmlSymbolType.Transition)) { HtmlSymbol transition = CurrentSymbol; NextToken(); if (At(HtmlSymbolType.Transition)) { if (last != null) { Accept(last); last = null; } Output(SpanKind.Markup); Accept(transition); Span.CodeGenerator = SpanCodeGenerator.Null; Output(SpanKind.Markup); AcceptAndMoveNext(); continue; // while } else { if (!EndOfFile) { PutCurrentBack(); } PutBack(transition); } // Handle whitespace rewriting if (last != null) { if (!Context.DesignTimeMode && last.Type == HtmlSymbolType.WhiteSpace && startOfLine) { // Put the whitespace back too startOfLine = false; PutBack(last); last = null; } else { // Accept last Accept(last); last = null; } } OtherParserBlock(); } else if (At(HtmlSymbolType.RazorCommentTransition)) { if (last != null) { Accept(last); last = null; } AddMarkerSymbolIfNecessary(); Output(SpanKind.Markup); RazorComment(); } else { // As long as we see whitespace, we're still at the "start" of the line startOfLine &= At(HtmlSymbolType.WhiteSpace); // If there's a last token, accept it if (last != null) { Accept(last); last = null; } // Advance last = CurrentSymbol; NextToken(); } } if (last != null) { Accept(last); } } protected static Func IsSpacingToken(bool includeNewLines) { return sym => sym.Type == HtmlSymbolType.WhiteSpace || (includeNewLines && sym.Type == HtmlSymbolType.NewLine); } private void OtherParserBlock() { AddMarkerSymbolIfNecessary(); Output(SpanKind.Markup); using (PushSpanConfig()) { Context.SwitchActiveParser(); Context.CodeParser.ParseBlock(); Context.SwitchActiveParser(); } Initialize(Span); NextToken(); } } }