public override bool Match(InlineProcessor processor, ref StringSlice slice) { if (!slice.Match(m_StartTag)) { return(false); } var rawContent = new StringBuilder(); var current = slice.CurrentChar; while (!rawContent.ToString().EndsWith(m_EndTag)) { if (slice.IsEmpty) { return(false); } rawContent.Append(current); current = slice.NextChar(); } processor.Inline = new ProtectedTagsData(rawContent.ToString()); return(true); }
/// <summary> /// Tries to match partial tag from the current slice /// </summary> /// <param name="processor">The processor</param> /// <param name="slice">The slice</param> /// <returns>If a match was found</returns> public override bool Match(Processor processor, ref StringSlice slice) { var tagStart = slice.Start - processor.CurrentTags.StartTag.Length; var index = slice.Start; while (slice[index].IsWhitespace()) { index++; } var match = slice[index]; if (match == TagId) { index++; while (slice[index].IsWhitespace()) { index++; } slice.Start = index; var startIndex = slice.Start; var partialTag = new PartialToken { LineIndent = processor.HasSeenNonSpaceOnLine ? 0 : processor.LineIndent, TagStartPosition = tagStart, ContentStartPosition = startIndex, IsClosed = false }; processor.CurrentToken = partialTag; while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag)) { slice.NextChar(); } if (slice.IsEmpty) { return(false); } var content = new StringSlice(slice.Text, startIndex, slice.Start - 1); content.TrimEnd(); var contentEnd = content.End + 1; partialTag.ContentEndPosition = contentEnd; partialTag.TagEndPosition = slice.Start + processor.CurrentTags.EndTag.Length; partialTag.IsClosed = true; slice.Start += processor.CurrentTags.EndTag.Length; return(true); } return(false); }
/// <summary> /// Tries to open a section tag using the slice /// </summary> /// <param name="processor">The processor</param> /// <param name="slice">The slice</param> /// <returns>The result of the match</returns> public override ParserState TryOpenBlock(Processor processor, ref StringSlice slice) { var tagStart = slice.Start - processor.CurrentTags.StartTag.Length; var index = slice.Start; while (slice[index].IsWhitespace()) { index++; } var match = slice[index]; if (match == OpeningTagDelimiter) { slice.Start = index + 1; // Skip delimiter while (slice.CurrentChar.IsWhitespace()) { slice.NextChar(); } var startIndex = slice.Start; // Take characters until closing tag while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag)) { slice.NextChar(); } var sectionName = slice.ToString(startIndex, slice.Start).TrimEnd(); var contentStartPosition = slice.Start + processor.CurrentTags.EndTag.Length; var sectionTag = new SectionToken { SectionName = sectionName, StartPosition = tagStart, ContentStartPosition = contentStartPosition, Parser = this, IsClosed = false }; processor.CurrentToken = sectionTag; slice.Start = contentStartPosition; return(ParserState.Break); } return(ParserState.Continue); }
/// <summary> /// Try to create a block close tag /// </summary> /// <param name="processor">The processor</param> /// <param name="slice">the slice</param> /// <param name="token">the current block tag</param> /// <returns>If the close was successful</returns> public override bool TryClose(Processor processor, ref StringSlice slice, BlockToken token) { var sectionTag = (SectionToken)token; while (slice.CurrentChar.IsWhitespace()) { slice.NextChar(); } var blockStart = slice.Start - processor.CurrentTags.StartTag.Length; slice.Start = slice.Start + 1; // Skip the slash while (slice.CurrentChar.IsWhitespace()) { slice.NextChar(); } var startIndex = slice.Start; // Take characters until closing tag while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag)) { slice.NextChar(); } var sectionName = slice.ToString(startIndex, slice.Start).TrimEnd(); if (sectionTag.SectionName == sectionName) { var tagEnd = slice.Start + processor.CurrentTags.EndTag.Length; var endTag = new SectionEndToken { SectionName = sectionName, EndPosition = tagEnd, ContentEndPosition = blockStart, IsClosed = true }; processor.CurrentToken = endTag; slice.Start += processor.CurrentTags.EndTag.Length; return(true); } throw new StubbleException($"Cannot close Block '{sectionName}' at {blockStart}. There is already an unclosed Block '{sectionTag.SectionName}'"); }
/// <summary> /// Tries to match a comment tag from the provided slice /// </summary> /// <param name="processor">The processor</param> /// <param name="slice">The slice</param> /// <returns>If a comment tag was matched</returns> public override bool Match(Processor processor, ref StringSlice slice) { var tagStart = slice.Start - processor.CurrentTags.StartTag.Length; var index = slice.Start; while (slice[index].IsWhitespace()) { index++; } var match = slice[index]; if (match == TagId) { slice.Start = index; var startIndex = index + 1; var commentTag = new CommentToken { TagStartPosition = tagStart, ContentStartPosition = startIndex, IsClosed = false }; processor.CurrentToken = commentTag; while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag)) { slice.NextChar(); } if (slice.IsEmpty) { return(false); } commentTag.TagEndPosition = slice.Start + processor.CurrentTags.EndTag.Length; commentTag.ContentEndPosition = slice.Start; commentTag.IsClosed = true; slice.Start += processor.CurrentTags.EndTag.Length; return(true); } return(false); }
private BlockState TryParseTagType16(BlockProcessor state, StringSlice line, int startColumn, int startPosition) { char c; c = line.CurrentChar; if (c == '!') { c = line.NextChar(); if (c == '-' && line.PeekChar() == '-') { return(CreateHtmlBlock(state, HtmlBlockType.Comment, startColumn, startPosition)); // group 2 } if (c.IsAlphaUpper()) { return(CreateHtmlBlock(state, HtmlBlockType.DocumentType, startColumn, startPosition)); // group 4 } if (c == '[' && line.Match("CDATA[", 1)) { return(CreateHtmlBlock(state, HtmlBlockType.CData, startColumn, startPosition)); // group 5 } return(BlockState.None); } if (c == '?') { return(CreateHtmlBlock(state, HtmlBlockType.ProcessingInstruction, startColumn, startPosition)); // group 3 } var hasLeadingClose = c == '/'; if (hasLeadingClose) { c = line.NextChar(); } var tag = new char[10]; var count = 0; for (; count < tag.Length; count++) { if (!c.IsAlphaNumeric()) { break; } tag[count] = char.ToLowerInvariant(c); c = line.NextChar(); } if ( !(c == '>' || (!hasLeadingClose && c == '/' && line.PeekChar() == '>') || c.IsWhitespace() || c == '\0')) { return(BlockState.None); } if (count == 0) { return(BlockState.None); } var tagName = new string(tag, 0, count); var tagIndex = Array.BinarySearch(HtmlTags, tagName, StringComparer.Ordinal); if (tagIndex < 0) { return(BlockState.None); } // Cannot start with </script </pre or </style if ((tagIndex == 49 || tagIndex == 50 || tagIndex == 53)) { if (c == '/' || hasLeadingClose) { return(BlockState.None); } return(CreateHtmlBlock(state, HtmlBlockType.ScriptPreOrStyle, startColumn, startPosition)); } return(CreateHtmlBlock(state, HtmlBlockType.InterruptingBlock, startColumn, startPosition)); }
public override bool Match(Processor processor, ref StringSlice slice) { if (processor is null) { throw new System.ArgumentNullException(nameof(processor)); } var tagStart = slice.Start - processor.CurrentTags.StartTag.Length; var index = slice.Start; while (slice[index].IsWhitespace()) { index++; } var nameStart = index; // Skip whitespace or until end tag while (!slice[index].IsWhitespace() && !slice.Match(processor.CurrentTags.EndTag, index - slice.Start)) { index++; } var name = slice.ToString(nameStart, index); // Skip whitespace or until end tag while (slice[index].IsWhitespace() && !slice.Match(processor.CurrentTags.EndTag, index - slice.Start)) { index++; } if (!_helperMap.TryGetValue(name, out var helperRef)) { return(false); } int contentEnd; var argsList = ImmutableArray <HelperArgument> .Empty; if (helperRef.ArgumentTypes.Length > 1) { var argsStart = index; slice.Start = index; while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag)) { slice.NextChar(); } var args = new StringSlice(slice.Text, argsStart, slice.Start - 1); args.TrimEnd(); contentEnd = args.End + 1; argsList = ParseArguments(new StringSlice(args.Text, args.Start, args.End)); } else { while (!slice.IsEmpty && !slice.Match(processor.CurrentTags.EndTag)) { slice.NextChar(); } contentEnd = slice.Start; } if (!slice.Match(processor.CurrentTags.EndTag)) { throw new StubbleException($"Unclosed Tag at {slice.Start.ToString(CultureInfo.InvariantCulture)}"); } var tag = new HelperToken { TagStartPosition = tagStart, ContentStartPosition = nameStart, Name = name, Args = argsList, ContentEndPosition = contentEnd, TagEndPosition = slice.Start + processor.CurrentTags.EndTag.Length, IsClosed = true }; slice.Start += processor.CurrentTags.EndTag.Length; processor.CurrentToken = tag; processor.HasSeenNonSpaceOnLine = true; return(true); }
private BlockState TryParseTagType16(BlockProcessor state, StringSlice line, int startColumn, int startPosition) { char c; c = line.CurrentChar; if (c == '!') { c = line.NextChar(); if (c == '-' && line.PeekChar() == '-') { return(CreateHtmlBlock(state, HtmlBlockType.Comment, startColumn, startPosition)); // group 2 } if (c.IsAlphaUpper()) { return(CreateHtmlBlock(state, HtmlBlockType.DocumentType, startColumn, startPosition)); // group 4 } if (c == '[' && line.Match("CDATA[", 1)) { return(CreateHtmlBlock(state, HtmlBlockType.CData, startColumn, startPosition)); // group 5 } return(BlockState.None); } if (c == '?') { return(CreateHtmlBlock(state, HtmlBlockType.ProcessingInstruction, startColumn, startPosition)); // group 3 } var hasLeadingClose = c == '/'; if (hasLeadingClose) { c = line.NextChar(); } Span <char> tag = stackalloc char[10]; var count = 0; for (; count < tag.Length; count++) { if (!c.IsAlphaNumeric()) { break; } tag[count] = char.ToLowerInvariant(c); c = line.NextChar(); } if ( !(c == '>' || (!hasLeadingClose && c == '/' && line.PeekChar() == '>') || c.IsWhitespace() || c == '\0')) { return(BlockState.None); } if (count == 0) { return(BlockState.None); } if (!HtmlTags.TryMatchExact(tag.Slice(0, count), out var match)) { return(BlockState.None); } int tagIndex = match.Value; // Cannot start with </script </pre or </style or </textArea if ((tagIndex == 49 || tagIndex == 50 || tagIndex == 53 || tagIndex == 56)) { if (c == '/' || hasLeadingClose) { return(BlockState.None); } return(CreateHtmlBlock(state, HtmlBlockType.ScriptPreOrStyle, startColumn, startPosition)); } return(CreateHtmlBlock(state, HtmlBlockType.InterruptingBlock, startColumn, startPosition)); }
/// <summary> /// Parse a literal tag from the slice /// </summary> /// <param name="processor">The processor</param> /// <param name="slice">The slice</param> /// <returns>The result of the match</returns> public LiteralTagResult Match(Processor processor, ref StringSlice slice) { var c = slice.CurrentChar; var start = slice.Start; var index = slice.Start; var tag = new LiteralToken { ContentStartPosition = index }; processor.CurrentToken = tag; while (c != '\0') { if (slice.Match(processor.CurrentTags.StartTag)) { if (tag.ContentStartPosition == slice.Start) { return(LiteralTagResult.NoContent); } tag.ContentEndPosition = slice.Start; tag.IsWhitespace = new StringSlice(slice.Text, tag.ContentStartPosition, tag.ContentEndPosition - 1).IsEmptyOrWhitespace(); tag.IsClosed = true; return(LiteralTagResult.TagStart); } // If this is whitespace then increase the start pointer by one if (c.IsWhitespace()) { if (!processor.HasSeenNonSpaceOnLine) { processor.LineIndent++; } start++; } else { processor.HasSeenNonSpaceOnLine = true; } if (slice.IsNewLine()) { int endIndex; if (c == '\n') { endIndex = slice.Start + 1; } else { endIndex = slice.Start + 2; } tag.ContentEndPosition = endIndex; tag.IsWhitespace = new StringSlice(slice.Text, tag.ContentStartPosition, tag.ContentEndPosition - 1).IsEmptyOrWhitespace(); tag.IsClosed = true; return(LiteralTagResult.NewLine); } c = slice.NextChar(); } if (tag.ContentStartPosition == slice.Start) { return(LiteralTagResult.NoContent); } tag.ContentEndPosition = slice.Start; tag.IsWhitespace = new StringSlice(slice.Text, tag.ContentStartPosition, tag.ContentEndPosition - 1).IsEmptyOrWhitespace(); tag.IsClosed = true; return(LiteralTagResult.EndOfFile); }
/// <summary> /// Tries to match interpolation tags from the current slice /// </summary> /// <param name="processor">The processor</param> /// <param name="slice">The slice</param> /// <returns>If the match was successful</returns> public override bool Match(Processor processor, ref StringSlice slice) { var tagStart = slice.Start - processor.CurrentTags.StartTag.Length; var index = slice.Start; var escapeResult = true; var isTripleMustache = false; while (slice[index].IsWhitespace()) { index++; } var match = slice[index]; if (match == '&') { escapeResult = false; index++; } else if (match == '{') { escapeResult = false; isTripleMustache = true; index++; } while (slice[index].IsWhitespace()) { index++; } slice.Start = index; var startIndex = index; var endTag = isTripleMustache ? '}' + processor.CurrentTags.EndTag : processor.CurrentTags.EndTag; while (!slice.IsEmpty && !slice.Match(endTag)) { slice.NextChar(); } var content = new StringSlice(slice.Text, startIndex, slice.Start - 1); content.TrimEnd(); var contentEnd = content.End + 1; var tag = new InterpolationToken { EscapeResult = escapeResult, TagStartPosition = tagStart, ContentStartPosition = startIndex, IsClosed = true }; if (!slice.Match(endTag)) { throw new StubbleException($"Unclosed Tag at {slice.Start.ToString()}"); } tag.ContentEndPosition = contentEnd; tag.TagEndPosition = slice.Start + endTag.Length; slice.Start += endTag.Length; processor.CurrentToken = tag; processor.HasSeenNonSpaceOnLine = true; return(true); }
/// <summary> /// Tries to match delimiter tags from the current slice /// </summary> /// <param name="processor">The processor</param> /// <param name="slice">The slice</param> /// <returns>If the match was successful</returns> public override bool Match(Processor processor, ref StringSlice slice) { var tagStart = slice.Start - processor.CurrentTags.StartTag.Length; var index = slice.Start; while (slice[index].IsWhitespace()) { index++; } var match = slice[index]; if (match == openingTagDelimiter[0]) { index++; while (slice[index].IsWhitespace()) { index++; } slice.Start = index; var startIndex = slice.Start; // Take Characters that aren't whitespace while (!slice.CurrentChar.IsWhitespace()) { slice.NextChar(); } var startTag = slice.ToString(startIndex, slice.Start); // Skip Whitespace any while (slice.CurrentChar.IsWhitespace()) { slice.NextChar(); } var endTagStartIndex = slice.Start; // Take characters until end delimiter; var closingTag = closingTagDelimiter[0] + processor.CurrentTags.EndTag; while (!slice.IsEmpty && !slice.Match(closingTag)) { slice.NextChar(); } var endTag = new StringSlice(slice.Text, endTagStartIndex, slice.Start - 1); endTag.TrimEnd(); var contentEnd = endTag.End + 1; var tag = new DelimiterToken { TagStartPosition = tagStart, ContentStartPosition = startIndex, ContentEndPosition = contentEnd, TagEndPosition = slice.Start + closingTag.Length, StartTag = startTag, EndTag = endTag.ToString(), IsClosed = true }; processor.CurrentToken = tag; processor.CurrentTags = new Classes.Tags(tag.StartTag, tag.EndTag); slice.Start += closingTag.Length; return(true); } return(false); }