protected CodeBlockInfo ParseBlockStart(bool isTopLevel, bool captureTransition) { // Capture the transition token, if any, into a span Span transitionSpan = null; if (HaveContent && captureTransition) { transitionSpan = TransitionSpan.Create(Context, hidden: false, acceptedCharacters: AcceptedCharacters.None); Context.ResetBuffers(); } SourceLocation start = CurrentLocation; string identifier = Context.AcceptIdentifier(); Span initialSpan = null; if (isTopLevel) { initialSpan = CodeSpan.Create(Context); Context.ResetBuffers(); } CodeBlockInfo block = new CodeBlockInfo(identifier, start, isTopLevel, transitionSpan, initialSpan); return(block); }
private void ParseEndPsuedoTag(Stack <TagInfo> tags, TagInfo tag, bool inDocument) { Debug.Assert(tag.IsEndTag, "ParseEndPsuedoTag requires an end tag"); // Collect what we've seen so far, since the "</text>" is not a markup span, it's a transition span Span prev = null; if (HaveContent) { prev = MarkupSpan.Create(Context); Context.ResetBuffers(); } // Accept the "</text>" Context.Expect("<"); Context.AcceptWhiteSpace(includeNewLines: true); Context.Expect("/text"); bool complete = CurrentCharacter == '>'; if (!complete) { OnError(tag.Start, RazorResources.ParseError_TextTagCannotContainAttributes); } else { Context.AcceptCurrent(); } // Remove the tag UpdateTagStack(tags, tag, !inDocument); if (tags.Count == 0) { // That was the top-level tag, output the markup then a transition span for the </text> if (prev != null) { Output(prev); } End(TransitionSpan.Create(Context, hidden: false, acceptedCharacters: complete ? AcceptedCharacters.None : AcceptedCharacters.Any)); } else { // Wasn't top-level, so resume the original span Context.ResumeSpan(prev); } }
/// <summary> /// Starts a block of the specified type /// </summary> /// <remarks> /// The current contents of the buffer will be outputted as a transition token AFTER starting the block if outputCurrentBufferAsTransition is true /// </remarks> /// <param name="blockType">The type of the block to start</param> public IDisposable StartBlock(BlockType blockType, bool outputCurrentBufferAsTransition) { AssertOnOwnerTask(); if (blockType == BlockType.Template) { HandleNestingCheck(RazorResources.ParseError_InlineMarkup_Blocks_Cannot_Be_Nested, ref _nestedAnonymousSections); } else if (blockType == BlockType.Section) { HandleNestingCheck(FormatForLanguage(RazorResources.ParseError_Sections_Cannot_Be_Nested, RazorResources.SectionExample_VB, RazorResources.SectionExample_CS), ref _nestedNamedSections); if (_nestedHelpers > 0) { OnError(CurrentLocation, RazorResources.ParseError_Helpers_Cannot_Contain_Sections); } } else if (blockType == BlockType.Helper) { HandleNestingCheck(RazorResources.ParseError_Helpers_Cannot_Be_Nested, ref _nestedHelpers); } // TODO: Remove this super-hacky whitespace rewriting if (!DesignTimeMode && !WhiteSpaceIsImportantToAncestorBlock && blockType == BlockType.Statement && (_blockStack.Count == 0 || _blockStack.Peek() != BlockType.Statement) && _nextSpanToOutput != null) { HandleWhitespaceRewriting(); } else { FlushNextOutputSpan(); } _blockStack.Push(blockType); Visitor.VisitStartBlock(blockType); if (outputCurrentBufferAsTransition && HaveContent) { OutputSpan(TransitionSpan.Create(this, hidden: false, acceptedCharacters: AcceptedCharacters.None)); ResetBuffers(); } return(new DisposableAction(EndBlock)); }
protected void ParseComment() { using (StartBlock(BlockType.Comment)) { SourceLocation startLocation = CurrentLocation; Context.Expect(RazorParser.StartCommentSequence[0]); End(TransitionSpan.Create(Context, hidden: false, acceptedCharacters: AcceptedCharacters.None)); Context.Expect(RazorParser.StartCommentSequence[1]); End(MetaCodeSpan.Create(Context, hidden: false, acceptedCharacters: AcceptedCharacters.None)); bool inComment = true; while (inComment && !EndOfFile) { Context.AcceptUntil(RazorParser.EndCommentSequence[0]); if (Context.Peek(RazorParser.EndCommentSequence, caseSensitive: true)) { inComment = false; } else { Context.AcceptCurrent(); } } End(CommentSpan.Create); if (EndOfFile) { OnError(startLocation, RazorResources.ParseError_RazorComment_Not_Terminated); } else { Context.Expect(RazorParser.EndCommentSequence[0]); End(MetaCodeSpan.Create(Context, hidden: false, acceptedCharacters: AcceptedCharacters.None)); Context.Expect(RazorParser.EndCommentSequence[1]); End(TransitionSpan.Create(Context, hidden: false, acceptedCharacters: AcceptedCharacters.None)); } } }
private bool ParseStartPsuedoTag(Stack <TagInfo> tags, TagInfo tag) { Debug.Assert(!tag.IsEndTag, "ParseStartPsuedoTag requires a start tag"); // Output what we've seen so far, since the "<text>" is not a markup token, it's a transition token if (HaveContent) { End(MarkupSpan.Create); } // Accept the "<text>" Context.Expect("<"); Context.AcceptWhiteSpace(includeNewLines: true); Context.Expect("text"); bool isValid = false; using (Context.StartTemporaryBuffer()) { Context.AcceptWhiteSpace(includeNewLines: true); if (CurrentCharacter == '/' || CurrentCharacter == '>') { isValid = true; Context.AcceptTemporaryBuffer(); } } bool transitionComplete = false; bool isEmpty = false; if (!isValid) { OnError(tag.Start, RazorResources.ParseError_TextTagCannotContainAttributes); } else { isEmpty = CurrentCharacter == '/'; Context.AcceptCurrent(); if (isEmpty) { if (CurrentCharacter != '>') { OnError(CurrentLocation, RazorResources.ParseError_SlashInEmptyTagMustBeFollowedByCloseAngle); } else { transitionComplete = true; Context.AcceptCurrent(); } } else { transitionComplete = true; } } // Output the transition End(TransitionSpan.Create(Context, hidden: false, acceptedCharacters: transitionComplete ? AcceptedCharacters.None : AcceptedCharacters.Any)); // Push it on to the stack and continue if (!isEmpty) { tags.Push(tag); } return(isEmpty); }
public override void ParseBlock() { if (Context == null) { throw new InvalidOperationException(RazorResources.Parser_Context_Not_Set); } using (StartBlock(BlockType.Markup)) { // An HTML block starts with a start tag and ends with the matching end tag. For example, each of the following lines are HTML blocks: // <li>foo</li> // <li>foo<b>bar</b></li> // <text>This block uses the <pre><text></pre> pseudo-tag which is not emitted, but is used for balancing tags</text> // Or, if it starts with a ":", then it is part of the "@:" single-line markup construct. The ":" is discarded and the rest of the line is markup // For example, each of the following lines are HTML blocks: // :this is all markup, except for the initial ':' // :you <b>can</b> put tags in here <em>and</em> they don't have to be <img src="foo.jpg"> balanced! SpanFactory spanFactory = MarkupSpan.Create; Context.AcceptWhiteSpace(includeNewLines: true); if (CurrentCharacter == RazorParser.TransitionCharacter) { if (HaveContent) { End(MarkupSpan.Create); } Context.AcceptCurrent(); Debug.Assert(HaveContent); End(TransitionSpan.Create(Context, hidden: false, acceptedCharacters: AcceptedCharacters.None)); if (CurrentCharacter == RazorParser.TransitionCharacter) { // Second "@" is for VB // TODO: Refactor!!! Context.AcceptCurrent(); End(MetaCodeSpan.Create); } } bool complete = false; if (CurrentCharacter == ':') { // Parse a single line of markup spanFactory = SingleLineMarkupSpan.Create; Context.WhiteSpaceIsImportantToAncestorBlock = true; complete = !ParseSingleLineBlock(); Context.WhiteSpaceIsImportantToAncestorBlock = false; } else if (CurrentCharacter == '<') { complete = !ParseTagBlock(false); } else { // First non-whitespace character in a block must be a start tag or ':' OnError(CurrentLocation, RazorResources.ParseError_MarkupBlock_Must_Start_With_Tag); return; } // Output anything that's left over // If we have content ==> Output // If the previous span can't grow ==> Output UNLESS we're "complete" if ((!complete && !Context.PreviousSpanCanGrow) || HaveContent) { Span span = spanFactory(Context); span.AcceptedCharacters = complete ? AcceptedCharacters.None : AcceptedCharacters.Any; End(span); } } }