Beispiel #1
0
        // "+++ <name>" with optional trailing whitespace
        internal virtual bool IsOpeningLine(BlockProcessor blockProcessor)
        {
            StringSlice line = blockProcessor.Line;

            if (line.NextChar() != '+' ||
                line.NextChar() != '+' ||
                line.NextChar() != ' ')
            {
                return(false);
            }

            for (int i = 0; i < _nameLength; i++)
            {
                if (line.NextChar() != _name[i])
                {
                    return(false);
                }
            }

            // Nothing other than whitespace after the name. A multipart block's name may begin with the name of another
            // multipart block, this ensures we don't confuse such blocks.
            line.NextChar();
            line.TrimEnd();

            return(line.CurrentChar == '\0');
        }
Beispiel #2
0
        /// <summary>
        /// Extracts content for a <see cref="Row"/>.
        /// </summary>
        /// <param name="line">The line to extract content from.</param>
        /// <param name="targetRow">The <see cref="Row"/> to extract content for.</param>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="targetRow"/> is <c>null</c>.</exception>
        protected virtual void ExtractContent(StringSlice line, Row targetRow)
        {
            if (targetRow == null)
            {
                throw new ArgumentNullException(nameof(targetRow));
            }

            int numColumns = targetRow.Count;
            int lineStart  = line.Start;

            for (int columnIndex = 0; columnIndex < numColumns;)
            {
                Cell targetCell = targetRow[columnIndex];

                if (targetCell.IsOpen)
                {
                    // Generate slice for cell
                    StringSlice cellLine = line;
                    cellLine.Start = lineStart + targetCell.StartOffset + 1; // Skip '|' or '+', don't trim leading spaces in case we have a block that depends on them
                    cellLine.End   = lineStart + targetCell.EndOffset - 1;   // Don't include ending '|' or '+'
                    cellLine.TrimEnd();

                    // Add slice to cell
                    targetCell.Lines.Add(cellLine);
                }

                columnIndex = targetCell.EndColumnIndex + 1;
            }
        }
Beispiel #3
0
        static public void CanTrimEnd()
        {
            StringSlice firstString = "Trim Me \r";
            StringSlice trimmed     = firstString.TrimEnd();

            Assert.AreEqual("Trim Me", trimmed.ToString());
        }
Beispiel #4
0
        /// <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);
        }
        internal virtual bool LineContainsClosingFence(StringSlice line, int openingFenceCharCount)
        {
            int numFenceChars = 0;

            while (line.CurrentChar == _fenceChar)
            {
                numFenceChars++;
                line.NextChar();
            }
            line.TrimEnd();

            return((_matchingFencesRequired && numFenceChars == openingFenceCharCount ||
                    !_matchingFencesRequired && numFenceChars >= openingFenceCharCount) && // By default (commonmark specs) closing fence must have as many or more fence chars
                   line.CurrentChar == '\0');                                              // No trailing characters other than whitespace
        }
Beispiel #6
0
        // "+++" with optional trailing whitespace
        internal virtual bool IsPartDividerLine(BlockProcessor blockProcessor)
        {
            StringSlice line = blockProcessor.Line;

            if (line.CurrentChar != '+' ||
                line.NextChar() != '+' ||
                line.NextChar() != '+')
            {
                return(false);
            }

            // Nothing other than whitespace after "+++"
            line.NextChar();
            line.TrimEnd();

            return(line.CurrentChar == '\0');
        }
        internal virtual bool LineContainsOpeningFence(StringSlice line, out int numFenceChars)
        {
            numFenceChars = 0;
            char currentChar = line.CurrentChar;

            while (currentChar == _fenceChar)
            {
                numFenceChars++;
                currentChar = line.NextChar();
            }

            if (numFenceChars < 3)
            {
                return(false); // Line must have at least 3 chars. Indices, so (end - start + 1) < 3
            }

            if (_fenceTrailingCharacters == FenceTrailingCharacters.All || currentChar == '\0')
            {
                return(true);
            }

            if (_fenceTrailingCharacters == FenceTrailingCharacters.Whitespace)
            {
                line.TrimEnd();
                return(line.CurrentChar == '\0');
            }

            if (_fenceTrailingCharacters == FenceTrailingCharacters.AllButFenceCharacter)
            {
                currentChar = line.NextChar();
                while (currentChar != '\0')
                {
                    if (currentChar == _fenceChar)
                    {
                        return(false);
                    }
                    currentChar = line.NextChar();
                }
            }

            return(true);
        }
        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);
        }
Beispiel #9
0
        /// <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);
        }
Beispiel #10
0
        /// <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);
        }