Example #1
0
        /// <summary>
        /// Parses a column header equivalent to the regexp: <code>\s*:\s*[delimiterChar]+\s*:\s*</code>
        /// </summary>
        /// <param name="slice">The text slice.</param>
        /// <param name="delimiterChar">The delimiter character (either `-` or `=`). If `\0`, it will detect the character (either `-` or `=`)</param>
        /// <param name="align">The alignment of the column.</param>
        /// <returns>
        ///   <c>true</c> if parsing was successfull
        /// </returns>
        public static bool ParseColumnHeaderDetect(ref StringSlice slice, ref char delimiterChar, out TableColumnAlign?align)
        {
            align = null;

            slice.TrimStart();
            var  c        = slice.CurrentChar;
            bool hasLeft  = false;
            bool hasRight = false;

            if (c == ':')
            {
                hasLeft = true;
                slice.NextChar();
            }

            slice.TrimStart();
            c = slice.CurrentChar;

            // if we want to automatically detect
            if (delimiterChar == '\0')
            {
                if (c == '=' || c == '-')
                {
                    delimiterChar = c;
                }
                else
                {
                    return(false);
                }
            }

            // We expect at least one `-` delimiter char
            if (slice.CountAndSkipChar(delimiterChar) == 0)
            {
                return(false);
            }

            slice.TrimStart();
            c = slice.CurrentChar;

            if (c == ':')
            {
                hasRight = true;
                slice.NextChar();
            }
            slice.TrimStart();

            align = hasLeft && hasRight
                ? TableColumnAlign.Center
                : hasRight ? TableColumnAlign.Right : hasLeft ? TableColumnAlign.Left : (TableColumnAlign?)null;

            return(true);
        }
Example #2
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            // First, some definitions.
            // A delimiter run is a sequence of one or more delimiter characters that is not preceded or followed by the same delimiter character
            // The amount of delimiter characters in the delimiter run may exceed emphasisDesc.MaximumCount, as that is handeled in `ProcessEmphasis`

            var delimiterChar = slice.CurrentChar;
            var emphasisDesc  = emphasisMap[delimiterChar];

            char pc = (char)0;

            if (processor.Inline is HtmlEntityInline htmlEntityInline)
            {
                if (htmlEntityInline.Transcoded.Length > 0)
                {
                    pc = htmlEntityInline.Transcoded[htmlEntityInline.Transcoded.End];
                }
            }
            if (pc == 0)
            {
                pc = slice.PeekCharExtra(-1);
                if (pc == delimiterChar && slice.PeekCharExtra(-2) != '\\')
                {
                    // If we get here, we determined that either:
                    // a) there weren't enough delimiters in the delimiter run to satisfy the MinimumCount condition
                    // b) the previous character couldn't open/close
                    return(false);
                }
            }
            var startPosition = slice.Start;

            int delimiterCount = slice.CountAndSkipChar(delimiterChar);

            // If the emphasis doesn't have the minimum required character
            if (delimiterCount < emphasisDesc.MinimumCount)
            {
                return(false);
            }

            char c = slice.CurrentChar;

            // The following character is actually an entity, we need to decode it
            if (HtmlEntityParser.TryParse(ref slice, out string htmlString, out int htmlLength))
            {
                c = htmlString[0];
            }

            // Calculate Open-Close for current character
            CharHelper.CheckOpenCloseDelimiter(pc, c, emphasisDesc.EnableWithinWord, out bool canOpen, out bool canClose);

            // We have potentially an open or close emphasis
            if (canOpen || canClose)
            {
                var delimiterType = DelimiterType.Undefined;
                if (canOpen)
                {
                    delimiterType |= DelimiterType.Open;
                }
                if (canClose)
                {
                    delimiterType |= DelimiterType.Close;
                }

                var delimiter = new EmphasisDelimiterInline(this, emphasisDesc)
                {
                    DelimiterCount = delimiterCount,
                    Type           = delimiterType,
                    Span           = new SourceSpan(processor.GetSourcePosition(startPosition, out int line, out int column), processor.GetSourcePosition(slice.Start - 1)),
                    Column         = column,
                    Line           = line,
                };

                processor.Inline = delimiter;
                return(true);
            }

            // We don't have an emphasis
            return(false);
        }
Example #3
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            var match = slice.CurrentChar;

            if (slice.PeekCharExtra(-1) == match)
            {
                return(false);
            }

            var startPosition = slice.Start;

            // Match the opened sticks
            int openSticks   = slice.CountAndSkipChar(match);
            int contentStart = slice.Start;
            int closeSticks  = 0;

            char c = slice.CurrentChar;

            var builder = StringBuilderCache.Local();

            // A backtick string is a string of one or more backtick characters (`) that is neither preceded nor followed by a backtick.
            // A code span begins with a backtick string and ends with a backtick string of equal length.
            // The contents of the code span are the characters between the two backtick strings, normalized in the following ways:

            // 1. line endings are converted to spaces.

            // 2. If the resulting string both begins AND ends with a space character, but does not consist entirely
            // of space characters, a single space character is removed from the front and back.
            // This allows you to include code that begins or ends with backtick characters, which must be separated by
            // whitespace from the opening or closing backtick strings.

            bool allSpace   = true;
            var  contentEnd = -1;

            while (c != '\0')
            {
                // Transform '\n' into a single space
                if (c == '\n')
                {
                    c = ' ';
                }

                if (c == match)
                {
                    contentEnd  = slice.Start;
                    closeSticks = slice.CountAndSkipChar(match);

                    if (openSticks == closeSticks)
                    {
                        break;
                    }

                    allSpace = false;
                    builder.Append(match, closeSticks);
                    c = slice.CurrentChar;
                }
                else
                {
                    builder.Append(c);
                    if (c != ' ')
                    {
                        allSpace = false;
                    }
                    c = slice.NextChar();
                }
            }

            bool isMatching = false;

            if (closeSticks == openSticks)
            {
                string content;

                // Remove one space from front and back if the string is not all spaces
                if (!allSpace && builder.Length > 2 && builder[0] == ' ' && builder[builder.Length - 1] == ' ')
                {
                    content = builder.ToString(1, builder.Length - 2);
                }
                else
                {
                    content = builder.ToString();
                }

                int delimiterCount = Math.Min(openSticks, closeSticks);
                var spanStart      = processor.GetSourcePosition(startPosition, out int line, out int column);
                var spanEnd        = processor.GetSourcePosition(slice.Start - 1);
                processor.Inline = new CodeInline()
                {
                    Delimiter         = match,
                    Content           = content,
                    ContentWithTrivia = new StringSlice(slice.Text, contentStart, contentEnd - 1),
                    Span           = new SourceSpan(spanStart, spanEnd),
                    Line           = line,
                    Column         = column,
                    DelimiterCount = delimiterCount,
                };
                isMatching = true;
            }

            return(isMatching);
        }
Example #4
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            var match = slice.CurrentChar;
            var pc    = slice.PeekCharExtra(-1);

            if (pc == match)
            {
                return(false);
            }

            var startPosition = slice.Start;

            // Match the opened $ or $$
            int openDollars = 1; // we have at least a $
            var c           = slice.NextChar();

            if (c == match)
            {
                openDollars++;
                c = slice.NextChar();
            }

            pc.CheckUnicodeCategory(out bool openPrevIsWhiteSpace, out bool openPrevIsPunctuation);
            c.CheckUnicodeCategory(out bool openNextIsWhiteSpace, out _);

            // Check that opening $/$$ is correct, using the different heuristics than for emphasis delimiters
            // If a $/$$ is not preceded by a whitespace or punctuation, this is a not a math block
            if ((!openPrevIsWhiteSpace && !openPrevIsPunctuation))
            {
                return(false);
            }

            bool isMatching   = false;
            int  closeDollars = 0;

            // Eat any leading spaces
            while (c.IsSpaceOrTab())
            {
                c = slice.NextChar();
            }

            var start = slice.Start;
            var end   = 0;

            pc = match;
            var lastWhiteSpace = -1;

            while (c != '\0')
            {
                // Don't allow newline in an inline math expression
                if (c == '\r' || c == '\n')
                {
                    return(false);
                }

                // Don't process sticks if we have a '\' as a previous char
                if (pc != '\\')
                {
                    // Record continuous whitespaces at the end
                    if (c.IsSpaceOrTab())
                    {
                        if (lastWhiteSpace < 0)
                        {
                            lastWhiteSpace = slice.Start;
                        }
                    }
                    else
                    {
                        bool hasClosingDollars = c == match;
                        if (hasClosingDollars)
                        {
                            closeDollars += slice.CountAndSkipChar(match);
                            c             = slice.CurrentChar;
                        }

                        if (closeDollars >= openDollars)
                        {
                            break;
                        }

                        lastWhiteSpace = -1;
                        if (hasClosingDollars)
                        {
                            pc = match;
                            continue;
                        }
                    }
                }

                if (closeDollars > 0)
                {
                    closeDollars = 0;
                }
                else
                {
                    pc = c;
                    c  = slice.NextChar();
                }
            }

            if (closeDollars >= openDollars)
            {
                pc.CheckUnicodeCategory(out bool closePrevIsWhiteSpace, out _);
                c.CheckUnicodeCategory(out bool closeNextIsWhiteSpace, out bool closeNextIsPunctuation);

                // A closing $/$$ should be followed by at least a punctuation or a whitespace
                // and if the character after an opening $/$$ was a whitespace, it should be
                // a whitespace as well for the character preceding the closing of $/$$
                if ((!closeNextIsPunctuation && !closeNextIsWhiteSpace) || (openNextIsWhiteSpace != closePrevIsWhiteSpace))
                {
                    return(false);
                }

                if (closePrevIsWhiteSpace && lastWhiteSpace > 0)
                {
                    end = lastWhiteSpace + openDollars - 1;
                }
                else
                {
                    end = slice.Start - 1;
                }

                // Create a new MathInline
                var inline = new MathInline()
                {
                    Span           = new SourceSpan(processor.GetSourcePosition(startPosition, out int line, out int column), processor.GetSourcePosition(slice.End)),
                    Line           = line,
                    Column         = column,
                    Delimiter      = match,
                    DelimiterCount = openDollars,
                    Content        = slice
                };
                inline.Content.Start = start;
                // We substract the end to the number of opening $ to keep inside the block the additionals $
                inline.Content.End = end - openDollars;

                // Add the default class if necessary
                if (DefaultClass != null)
                {
                    inline.GetAttributes().AddClass(DefaultClass);
                }
                processor.Inline = inline;
                isMatching       = true;
            }

            return(isMatching);
        }
    }