Exemplo n.º 1
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            if (slice.Start != 0 && !slice.PeekCharExtra(-1).IsWhitespace())
            {
                return(false);
            }
            string match;

            if (!_textMatchHelper.TryMatch(slice.Text, slice.Start, slice.Length, out match))
            {
                return(false);
            }

            // Move the cursor to the character after the matched string
            slice.Start += match.Length;

            var builder = new StringBuilder();

            while (slice.CurrentChar.IsDigit() && !slice.IsEmpty)
            {
                builder.Append(slice.CurrentChar);
                slice.Start++;
            }

            var index = builder.Length > 0 ? int.Parse(builder.ToString()) : 0;

            if (index == 0)
            {
                //If we failed to parse index, abort
                slice.Start -= builder.Length;
                slice.Start -= match.Length;
                return(false);
            }

            builder.Clear();

            if (slice.CurrentChar == '(')
            {
                slice.Start++;

                while (slice.CurrentChar != ')' && !slice.IsEmpty)
                {
                    builder.Append(slice.CurrentChar);
                    slice.Start++;
                }

                if (!slice.IsEmpty)
                {
                    slice.Start++;
                }
            }

            var extra = builder.ToString();

            processor.Inline = new EntityLinkInline(match, index, extra);

            return(true);
        }
Exemplo n.º 2
0
        private bool IsCurrentCharEscaped(StringSlice slice)
        {
            var previous = slice.PeekCharExtra(-1);
            var counter  = 0;

            while (previous == m_escapeChar)
            {
                counter++;
                previous = slice.PeekCharExtra(-1 * (counter + 1));
            }

            if (counter % 2 == 1)
            {
                return(true);
            }

            return(false);
        }
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            if (!slice.PeekCharExtra(-1).IsWhiteSpaceOrZero())
            {
                return(false); //require whitespace/nothing before
            }

            var key     = String.Empty;
            var issue   = String.Empty;
            var current = slice.CurrentChar;

            //read as many uppercase characters as required - project key
            while (current.IsAlphaUpper())
            {
                key    += current;
                current = slice.NextChar();
            }

            //require a '-' between key and issue number
            if (!current.Equals('-'))
            {
                return(false);
            }

            current = slice.NextChar();

            //read as many numbers as required - issue number
            while (current.IsDigit())
            {
                issue  += current;
                current = slice.NextChar();
            }

            if (!current.IsWhiteSpaceOrZero()) //must be followed by whitespace
            {
                return(false);
            }

            int line;
            int column;

            processor.Inline = new JiraLink() //create the link at the relevant position
            {
                Span =
                {
                    Start = processor.GetSourcePosition(slice.Start, out line, out column)
                },
                Line   = line,
                Column = column,
                Issue  = issue,
                Key    = key
            };

            processor.Inline.Span.End = processor.Inline.Span.Start + issue.Length + key.Length + 1; //+1 for the '-'

            return(true);
        }
Exemplo n.º 4
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            // Allow preceding whitespace or `(`
            var pc = slice.PeekCharExtra(-1);

            if (!pc.IsWhiteSpaceOrZero() && pc != '(')
            {
                return(false);
            }

            var current = slice.CurrentChar;

            var startKey = slice.Start;
            var endKey   = slice.Start;

            //read as many uppercase characters as required - project key
            while (current.IsAlphaUpper())
            {
                endKey  = slice.Start;
                current = slice.NextChar();
            }

            //require a '-' between key and issue number
            if (!current.Equals('-'))
            {
                return(false);
            }

            current = slice.NextChar(); // skip -

            //read as many numbers as required - issue number
            if (!current.IsDigit())
            {
                return(false);
            }

            var startIssue = slice.Start;
            var endIssue   = slice.Start;

            while (current.IsDigit())
            {
                endIssue = slice.Start;
                current  = slice.NextChar();
            }

            if (!current.IsWhiteSpaceOrZero() && current != ')') //can be followed only by a whitespace or `)`
            {
                return(false);
            }

            var jiraLink = new JiraLink() //create the link at the relevant position
            {
                Span =
                {
                    Start = processor.GetSourcePosition(slice.Start, out int line, out int column)
                },
Exemplo n.º 5
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            string match;

            // Previous char must be a space
            if (!slice.PeekCharExtra(-1).IsWhiteSpaceOrZero())
            {
                return(false);
            }

            // Try to match an existing emoji
            var startPosition = slice.Start;

            if (!textMatchHelper.TryMatch(slice.Text, slice.Start, slice.Length, out match))
            {
                return(false);
            }

            // If we have a smiley, we decode it to emoji
            string emoji;

            if (!SmileyToEmoji.TryGetValue(match, out emoji))
            {
                emoji = match;
            }

            // Decode the eomji to unicode
            string unicode;

            if (!EmojiToUnicode.TryGetValue(emoji, out unicode))
            {
                // Should not happen but in case
                return(false);
            }

            // Move the cursor to the character after the matched string
            slice.Start += match.Length;

            // Push the EmojiInline
            int line;
            int column;

            processor.Inline = new EmojiInline(unicode)
            {
                Span =
                {
                    Start = processor.GetSourcePosition(startPosition, out line, out column),
                },
                Line   = line,
                Column = column,
                Match  = match
            };
            processor.Inline.Span.End = processor.Inline.Span.Start + match.Length - 1;

            return(true);
        }
Exemplo n.º 6
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            bool matchFound;
            char previous;

            matchFound = false;

            previous = slice.PeekCharExtra(-1);

            if (previous.IsWhiteSpaceOrZero() || previous == '(' || previous == '[')
            {
                char current;
                int  start;
                int  end;

                slice.NextChar(); // skip the # starting character

                current = slice.CurrentChar;
                start   = slice.Start;
                end     = start;

                // read as many digits as we can find, incrementing the slice length as we go
                while (current.IsDigit())
                {
                    end     = slice.Start;
                    current = slice.NextChar();
                }

                // once we've ran out of digits, check to see what the next character is
                // to make sure this is a valid issue and nothing something random like #001Alpha
                if (current.IsWhiteSpaceOrZero() || current == ')' || current == ']')
                {
                    int inlineStart;

                    inlineStart = processor.GetSourcePosition(slice.Start, out int line, out int column);

                    // and if we got here, then we've got a valid reference, so create our AST node
                    // and assign it to the processor
                    processor.Inline = new MantisLink
                    {
                        Span =
                        {
                            Start = inlineStart,
                            End   = inlineStart + (end - start) + 1    // add one to the length to account for the # starting character
                        },
                        Line        = line,
                        Column      = column,
                        IssueNumber = new StringSlice(slice.Text, start, end)
                    };

                    matchFound = true;
                }
            }

            return(matchFound);
        }
Exemplo n.º 7
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            // Hard line breaks are for separating inline content within a block. Neither syntax for hard line breaks works at the end of a paragraph or other block element:
            if (!processor.Block !.IsParagraphBlock)
            {
                return(false);
            }

            var startPosition         = slice.Start;
            var hasDoubleSpacesBefore = slice.PeekCharExtra(-1).IsSpace() && slice.PeekCharExtra(-2).IsSpace();
            var newLine = NewLine.LineFeed;

            if (processor.TrackTrivia)
            {
                if (slice.CurrentChar == '\r')
                {
                    if (slice.PeekChar() == '\n')
                    {
                        newLine = NewLine.CarriageReturnLineFeed;
                        slice.SkipChar(); // Skip \n
                    }
                    else
                    {
                        newLine = NewLine.CarriageReturn;
                    }
                }
                else
                {
                    newLine = NewLine.LineFeed;
                }
            }
            else
            {
                if (slice.CurrentChar == '\r' && slice.PeekChar() == '\n')
                {
                    slice.SkipChar(); // Skip \n
                }
            }
            slice.SkipChar(); // Skip \r or \n

            processor.Inline = new LineBreakInline
            {
                Span    = { Start = processor.GetSourcePosition(startPosition, out int line, out int column) },
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            // Previous char must be a whitespace or a punctuation
            var previousChar = slice.PeekCharExtra(-1);

            if (!previousChar.IsWhiteSpaceOrZero() && ValidPreviousCharacters.IndexOf(previousChar) == -1)
            {
                return(false);
            }

            var pendingEmphasis = _listOfCharCache.Get();

            try
            {
                // Check that an autolink is possible in the current context
                if (!IsAutoLinkValidInCurrentContext(processor, pendingEmphasis))
                {
                    return(false);
                }

                var startPosition = slice.Start;
                // Parse URL
                var match = AutoEmailBare.Match(slice.Text, slice.Start, slice.Length);
                if (!match.Success)
                {
                    return(false);
                }

                // Remove <...>
                if (slice.CurrentChar == '<')
                {
                    if (slice[match.Length] != '>')
                    {
                        return(false);
                    }
                    // Remove trailing >
                    slice.Start++;
                }

                var email = match.Groups[1].Value;
                slice.Start += match.Length;

                var inline = new LinkInline()
                {
                    Span =
                    {
                        Start = processor.GetSourcePosition(startPosition, out var line, out var column),
                    },
                    Line       = line,
                    Column     = column,
                    Url        = "mailto:" + email,
                    IsClosed   = true,
                    IsAutoLink = true,
                };
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            // [!clientId:NameOfComponent(- or |)] # comment
            // ```
            // init: function(component) {}
            // click: function(component) {}
            // mouseover: function(component){}
            // ... other event handlers etc ...
            // ```
            bool match              = false;
            int  start              = slice.Start;
            int  end                = slice.Start;
            char openSquareBracket  = slice.PeekCharExtra(-1);
            char shouldBeWhitespace = slice.PeekCharExtra(-2);

            if (shouldBeWhitespace.IsWhiteSpaceOrZero() && openSquareBracket == '[')
            {
                slice.NextChar();
                StringSlice clientId              = ReadUntil(slice, ':', out end);
                StringSlice nameOfComponent       = ReadUntil(slice, ']', out end);
                string      nameOfComponentString = nameOfComponent.ToString();
                //bool block = true;
                //if (nameOfComponentString.EndsWith("|"))
                //{
                //    nameOfComponentString = nameOfComponentString.Truncate(1);
                //    block = false;
                //}
                if (ComponentResolver.IsValidComponentName(nameOfComponentString, out LeafInline component))
                {
                    int inlineStart = processor.GetSourcePosition(slice.Start, out int line, out int column);
                    component.Span.Start = inlineStart;
                    component.Span.End   = inlineStart + (end - start) + 1;
                    processor.Inline     = component;

                    match = true;
                }
            }

            return(match);
        }
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            var  _isMatch = false;
            int  _start, _end;
            var  _url = "";
            char _current;
            var  _fullSlice = slice.ToString().ToLower().TrimStart(OpeningCharacters);

            if (!slice.PeekCharExtra(-1).IsWhiteSpaceOrZero())
            {
                return(_isMatch);
            }

            if (_configuration.BaseUrls.Where(u => _fullSlice.StartsWith(u)).Count() == 0)
            {
                return(_isMatch);
            }

            _start   = slice.Start;
            _end     = _start;
            _current = slice.NextChar();

            while (_current != ']')
            {
                _url    += _current;
                _end     = slice.Start;
                _current = slice.NextChar();
            }

            if (_current == ']')
            {
                slice.NextChar();

                var _inlineStart = processor.GetSourcePosition(slice.Start, out int _line, out int _column);

                processor.Inline = new EmbeddedGist
                {
                    Span =
                    {
                        Start = _inlineStart,
                        End   = _inlineStart + (_end - _start) + 1
                    },
                    Line   = _line,
                    Column = _column,
                    Url    = _url
                };

                _isMatch = true;
            }

            return(_isMatch);
        }
Exemplo n.º 11
0
        private bool MatchXrefShortcut(InlineProcessor processor, ref StringSlice slice)
        {
            if (!slice.CurrentChar.IsAlpha())
            {
                return(false);
            }

            var saved = slice;
            int line;
            int column;

            var c    = slice.CurrentChar;
            var href = StringBuilderCache.Local();

            while (!c.IsZero())
            {
                //Meet line ends or whitespaces
                if (c.IsWhiteSpaceOrZero() || StopCharacters.Contains(c))
                {
                    break;
                }

                var nextChar = slice.PeekCharExtra(1);
                if (ContinuableCharacters.Contains(c) && (nextChar.IsWhiteSpaceOrZero() || StopCharacters.Contains(nextChar) || ContinuableCharacters.Contains(nextChar)))
                {
                    break;
                }

                href.Append(c);
                c = slice.NextChar();
            }


            var xrefInline = new XrefInline
            {
                Href   = href.ToString(),
                Span   = new SourceSpan(processor.GetSourcePosition(saved.Start, out line, out column), processor.GetSourcePosition(slice.Start - 1)),
                Line   = line,
                Column = column
            };

            var htmlAttributes = xrefInline.GetAttributes();

            var sourceContent = href.Insert(0, '@');

            htmlAttributes.AddPropertyIfNotExist("data-throw-if-not-resolved", "False");
            htmlAttributes.AddPropertyIfNotExist("data-raw-source", sourceContent.ToString());
            processor.Inline = xrefInline;

            return(true);
        }
Exemplo n.º 12
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            bool matchFound;
            char previous;

            matchFound = false;
            previous   = slice.PeekCharExtra(-1);

            if (previous.IsWhiteSpaceOrZero() || previous == '(' || previous == '[')
            {
                char current;
                int  start;
                int  end;

                slice.NextChar();

                current = slice.CurrentChar;
                start   = slice.Start;
                end     = start;

                while (current.IsDigit())
                {
                    end     = slice.Start;
                    current = slice.NextChar();
                }

                if (current.IsWhiteSpaceOrZero() || current == ')' || current == ']')
                {
                    int inlineStart;

                    inlineStart = processor.GetSourcePosition(slice.Start, out int line, out int column);

                    processor.Inline = new Mention
                    {
                        Span =
                        {
                            Start = inlineStart,
                            End   = inlineStart + (end - start) + 1
                        },
                        Line     = line,
                        Column   = column,
                        Username = new StringSlice(slice.Text, start, end)
                    };

                    matchFound = true;
                }
            }

            return(matchFound);
        }
Exemplo n.º 13
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            var previous = slice.PeekCharExtra(-1);

            //if (!previous.IsWhiteSpaceOrZero()) return false;

            slice.NextChar();

            if (slice.CurrentChar != '>')
            {
                return(false);
            }

            var current = slice.NextChar();
            var start   = slice.Start;
            var end     = start;

            while (current.IsDigit())
            {
                end     = slice.Start;
                current = slice.NextChar();
            }

            if (!current.IsWhiteSpaceOrZero() && current.IsDigit())
            {
                return(false);
            }
            var inlineStart = processor.GetSourcePosition(slice.Start, out var line, out var column);

            if (!int.TryParse(new StringSlice(slice.Text, start, end).ToString(), out var parsedId))
            {
                return(false);
            }

            processor.Inline = new LinkToParsedModel
            {
                Span =
                {
                    Start = inlineStart,
                    End   = inlineStart + (end - start) + 1
                },
                Line   = line,
                Column = column,
                Id     = parsedId,
            };

            return(true);
        }
Exemplo n.º 14
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            var c = slice.PeekCharExtra(-1);

            if (c == '\\')
            {
                return(false);
            }

            c = slice.NextChar();

            if (c == '\'' || c == '"')
            {
                return(MatchXrefShortcutWithQuote(processor, ref slice));
            }
            else
            {
                return(MatchXrefShortcut(processor, ref slice));
            }
        }
Exemplo n.º 15
0
        private bool MatchStart(ref StringSlice slice)
        {
            var pc = slice.PeekCharExtra(-1);

            if (pc == '\\')
            {
                return(false);
            }

            var c     = slice.CurrentChar;
            var index = 0;

            while (c != '\0' && index < StartString.Length && Char.ToLower(c) == StartString[index])
            {
                c = slice.NextChar();
                index++;
            }

            return(index == StartString.Length);
        }
Exemplo n.º 16
0
        public override Boolean Match(InlineProcessor processor, ref StringSlice slice)
        {
            var startPosition = processor.GetSourcePosition(slice.Start, out int line, out int column);
            var last          = slice.PeekCharExtra(-1);
            var current       = slice.CurrentChar;

            if (current != '[' || last == '\\')
            {
                return(false);
            }

            // Link reference
            if (LinkHelper.TryParseLabel(ref slice, out String label, out SourceSpan labelSpan))
            {
                if (processor.Document.ContainsLinkReferenceDefinition(label))
                {
                    return(false);
                }
            }

            // Explicit link
            if (slice.CurrentChar.IsOneOf('[', '(', ':'))
            {
                return(false);
            }

            // Shorthand
            processor.Inline = new LinkInline(label, "")
            {
                LabelSpan = processor.GetSourcePositionFromLocalSpan(labelSpan),
                IsImage   = false,
                Span      = new SourceSpan(
                    startPosition,
                    processor.GetSourcePosition(slice.Start - 1)),
                Line     = line,
                Column   = column,
                IsClosed = true,
            }.AppendChild(new LiteralInline(label));

            return(true);
        }
Exemplo n.º 17
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            // Previous char must be a space
            if (!slice.PeekCharExtra(-1).IsWhiteSpaceOrZero())
            {
                return(false);
            }

            // Try to match an emoji
            if (!_emojiPrefixTree.TryMatchLongest(slice.Text, slice.Start, slice.Length, out KeyValuePair <string, string> match))
            {
                return(false);
            }

            // Push the EmojiInline
            processor.Inline = new EmojiInline(match.Value)
            {
                Span =
                {
                    Start = processor.GetSourcePosition(slice.Start, out int line, out int column),
                },
        /// <summary>
        /// <para>Parses the next line in the JSON block.</para>
        /// <para>Markdig parses markdown line by line, unfortunately, JSON.NET does not expose any efficient method for parsing JSON over several method calls. The commonly used
        /// JsonSerializer.Deserialize is a static method that doesn't store any state. Deserialization using it must occur within a single call.</para>
        ///
        /// <para>To test if JSON is complete (all objects closed), on each <see cref="BlockParser.TryContinue(BlockProcessor, Block)"/> call, we could append the line to a
        /// string stored on the block and attempt to deserialize the string. Such an approach is inefficient, potentially allocating tons of strings.
        /// On top of that we'd have to use exceptions as control flow.</para>
        ///
        /// <para>Alternatively, we could attempt to deserialize the current line's <see cref="StringSlice.Text"/> value, starting from the point where the JSON begins and
        /// ignoring text after the JSON (referred to as additional text by JSON.NET). The issue with this approach is that we may not know what type of object
        /// to deserialize to. We'd have to create an inefficient intermediate JObject.</para>
        ///
        /// <para>This method is a fairly efficient approach. On each <see cref="BlockParser.TryContinue(BlockProcessor, Block)"/> call, it iterates through each character to
        /// determine whether the JSON is complete. It does this according to the <a href="https://www.json.org/">JSON specification</a>. Basically, braces have no semantic
        /// meaning if they occur within strings, otherwise, { opens an object and } closes an object. This method simply tallies braces till opening
        /// and closing braces are balanced.</para>
        ///
        /// <para>It does not catch syntactic errors. Also, if braces aren't balanced, it could well end up iterating through all characters
        /// in a document. Nonetheless, it is more efficient than other methods.</para>
        /// </summary>
        /// <param name="line">The line to parse.</param>
        /// <param name="proxyJsonBlock"></param>
        /// <returns>The state of this block after parsing <paramref name="line"/>.</returns>
        internal virtual BlockState ParseLine(StringSlice line, TProxy proxyJsonBlock)
        {
            char previousChar = line.PeekCharExtra(-1);
            char currentChar  = line.CurrentChar;

            while (currentChar != '\0')
            {
                if (!proxyJsonBlock.WithinString)
                {
                    if (currentChar == '{')
                    {
                        proxyJsonBlock.NumOpenObjects++;
                    }
                    else if (currentChar == '}')
                    {
                        // Braces balanced
                        if (--proxyJsonBlock.NumOpenObjects == 0)
                        {
                            proxyJsonBlock.UpdateSpanEnd(line.End);

                            return(BlockState.Break);
                        }
                    }
                    else if (previousChar != '\\' && currentChar == '"')
                    {
                        proxyJsonBlock.WithinString = true;
                    }
                }
                else if (previousChar != '\\' && currentChar == '"')
                {
                    proxyJsonBlock.WithinString = false;
                }

                previousChar = currentChar;
                currentChar  = line.NextChar(); // \0 if out of range
            }

            return(BlockState.Continue);
        }
Exemplo n.º 19
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            // Allow preceding whitespace or `(`
            var pc = slice.PeekCharExtra(-1);

            if (!pc.IsWhiteSpaceOrZero() && pc != '(')
            {
                return(false);
            }

            var current = slice.CurrentChar;

            var startKey = slice.Start;
            var endKey   = slice.Start;

            //read as many uppercase characters as required - project key
            while (current.IsAlphaUpper())
            {
                endKey  = slice.Start;
                current = slice.NextChar();
            }

            //require a '-' between key and issue number
            if (!current.Equals('-'))
            {
                return(false);
            }

            current = slice.NextChar(); // skip -

            //read as many numbers as required - issue number
            if (!current.IsDigit())
            {
                return(false);
            }

            var startIssue = slice.Start;
            var endIssue   = slice.Start;

            while (current.IsDigit())
            {
                endIssue = slice.Start;
                current  = slice.NextChar();
            }

            if (!current.IsWhiteSpaceOrZero() && current != ')') //can be followed only by a whitespace or `)`
            {
                return(false);
            }

            int line;
            int column;

            var jiraLink = new JiraLink() //create the link at the relevant position
            {
                Span =
                {
                    Start = processor.GetSourcePosition(slice.Start, out line, out column)
                },
                Line       = line,
                Column     = column,
                Issue      = new StringSlice(slice.Text, startIssue, endIssue),
                ProjectKey = new StringSlice(slice.Text, startKey, endKey),
            };

            jiraLink.Span.End = jiraLink.Span.Start + (endIssue - startKey);

            // Builds the Url
            var builder = new StringBuilder();

            builder.Append(_baseUrl).Append('/').Append(jiraLink.ProjectKey).Append('-').Append(jiraLink.Issue);
            jiraLink.Url = builder.ToString();

            // Builds the Label
            builder.Length = 0;
            builder.Append(jiraLink.ProjectKey).Append('-').Append(jiraLink.Issue);
            jiraLink.AppendChild(new LiteralInline(builder.ToString()));

            if (_options.OpenInNewWindow)
            {
                jiraLink.GetAttributes().AddProperty("target", "blank");
            }

            processor.Inline = jiraLink;

            return(true);
        }
Exemplo n.º 20
0
        /// <summary>
        /// Tries to extra from the current position of a slice an HTML attributes {...}
        /// </summary>
        /// <param name="slice">The slice to parse.</param>
        /// <param name="attributes">The output attributes or null if not found or invalid</param>
        /// <returns><c>true</c> if parsing the HTML attributes was successful</returns>
        public static bool TryParse(ref StringSlice slice, out HtmlAttributes attributes)
        {
            attributes = null;
            if (slice.PeekCharExtra(-1) == '{')
            {
                return(false);
            }

            var line = slice;

            string        id      = null;
            List <string> classes = null;
            List <KeyValuePair <string, string> > properties = null;

            bool isValid = false;
            var  c       = line.NextChar();

            while (true)
            {
                if (c == '}')
                {
                    isValid = true;
                    line.NextChar(); // skip }
                    break;
                }

                if (c == '\0')
                {
                    break;
                }

                bool isClass = c == '.';
                if (c == '#' || isClass)
                {
                    c = line.NextChar(); // Skip #
                    var start = line.Start;
                    // Get all non-whitespace characters following a #
                    // But stop if we found a } or \0
                    while (c != '}' && c != '\0' && !c.IsWhitespace())
                    {
                        c = line.NextChar();
                    }
                    var end = line.Start - 1;
                    if (end == start)
                    {
                        break;
                    }
                    var text = slice.Text.Substring(start, end - start + 1);
                    if (isClass)
                    {
                        if (classes == null)
                        {
                            classes = new List <string>();
                        }
                        classes.Add(text);
                    }
                    else
                    {
                        id = text;
                    }
                    continue;
                }

                if (!c.IsWhitespace())
                {
                    // Parse the attribute name
                    if (!IsStartAttributeName(c))
                    {
                        break;
                    }
                    var startName = line.Start;
                    while (true)
                    {
                        c = line.NextChar();
                        if (!(c.IsAlphaNumeric() || c == '_' || c == ':' || c == '.' || c == '-'))
                        {
                            break;
                        }
                    }
                    var name = slice.Text.Substring(startName, line.Start - startName);

                    var hasSpace = c.IsSpaceOrTab();

                    // Skip any whitespaces
                    line.TrimStart();
                    c = line.CurrentChar;

                    // Handle boolean properties that are not followed by =
                    if ((hasSpace && (c == '.' || c == '#' || IsStartAttributeName(c))) || c == '}')
                    {
                        if (properties == null)
                        {
                            properties = new List <KeyValuePair <string, string> >();
                        }
                        // Add a null value for the property
                        properties.Add(new KeyValuePair <string, string>(name, null));
                        continue;
                    }

                    // Else we expect a regular property
                    if (line.CurrentChar != '=')
                    {
                        break;
                    }

                    // Go to next char, skip any spaces
                    line.NextChar();
                    line.TrimStart();

                    int startValue = -1;
                    int endValue   = -1;

                    c = line.CurrentChar;
                    // Parse a quoted string
                    if (c == '\'' || c == '"')
                    {
                        char openingStringChar = c;
                        startValue = line.Start + 1;
                        while (true)
                        {
                            c = line.NextChar();
                            if (c == '\0')
                            {
                                return(false);
                            }
                            if (c == openingStringChar)
                            {
                                break;
                            }
                        }
                        endValue = line.Start - 1;
                        c        = line.NextChar(); // Skip closing opening string char
                    }
                    else
                    {
                        // Parse until we match a space or a special html character
                        startValue = line.Start;
                        while (true)
                        {
                            if (c == '\0')
                            {
                                return(false);
                            }
                            if (c.IsWhitespace() || c == '}')
                            {
                                break;
                            }
                            c = line.NextChar();
                        }
                        endValue = line.Start - 1;
                        if (endValue == startValue)
                        {
                            break;
                        }
                    }

                    var value = slice.Text.Substring(startValue, endValue - startValue + 1);

                    if (properties == null)
                    {
                        properties = new List <KeyValuePair <string, string> >();
                    }
                    properties.Add(new KeyValuePair <string, string>(name, value));
                    continue;
                }

                c = line.NextChar();
            }

            if (isValid)
            {
                attributes = new HtmlAttributes()
                {
                    Id         = id,
                    Classes    = classes,
                    Properties = properties
                };

                // Assign back the current processor of the line to
                slice = line;
            }
            return(isValid);
        }
Exemplo n.º 21
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            // Previous char must be a whitespace or a punctuation
            var previousChar = slice.PeekCharExtra(-1);

            if (!previousChar.IsAsciiPunctuation() && !previousChar.IsWhiteSpaceOrZero())
            {
                return(false);
            }

            List <char> pendingEmphasis;

            // Check that an autolink is possible in the current context
            if (!IsAutoLinkValidInCurrentContext(processor, out pendingEmphasis))
            {
                return(false);
            }

            var startPosition = slice.Start;

            var c = slice.CurrentChar;

            // Precheck URL
            switch (c)
            {
            case 'h':
                if (!slice.MatchLowercase("ttp://", 1) && !slice.MatchLowercase("ttps://", 1))
                {
                    return(false);
                }
                break;

            case 'f':
                if (!slice.MatchLowercase("tp://", 1))
                {
                    return(false);
                }
                break;

            case 'm':
                if (!slice.MatchLowercase("ailto:", 1))
                {
                    return(false);
                }
                break;

            case 'w':
                if (!slice.MatchLowercase("ww.", 1) || previousChar == '/')     // We won't match http:/www. or /www.xxx
                {
                    return(false);
                }
                break;
            }

            // Parse URL
            string link;

            if (!LinkHelper.TryParseUrl(ref slice, out link))
            {
                return(false);
            }


            // If we have any pending emphasis, remove any pending emphasis characters from the end of the link
            if (pendingEmphasis != null)
            {
                for (int i = link.Length - 1; i >= 0; i--)
                {
                    if (pendingEmphasis.Contains(link[i]))
                    {
                        slice.Start--;
                    }
                    else
                    {
                        if (i < link.Length - 1)
                        {
                            link = link.Substring(0, i + 1);
                        }
                        break;
                    }
                }
            }

            // Post-check URL
            switch (c)
            {
            case 'h':
                if (string.Equals(link, "http://", StringComparison.OrdinalIgnoreCase) ||
                    string.Equals(link, "https://", StringComparison.OrdinalIgnoreCase))
                {
                    return(false);
                }
                break;

            case 'f':
                if (string.Equals(link, "ftp://", StringComparison.OrdinalIgnoreCase))
                {
                    return(false);
                }
                break;

            case 'm':
                if (string.Equals(link, "mailto:", StringComparison.OrdinalIgnoreCase) || !link.Contains("@"))
                {
                    return(false);
                }
                break;

            case 'w':
                // We require at least two .
                if (link.Length <= "www.x.y".Length || link.IndexOf(".", 4, StringComparison.Ordinal) < 0)
                {
                    return(false);
                }
                break;
            }

            int line;
            int column;
            var inline = new LinkInline()
            {
                Span =
                {
                    Start = processor.GetSourcePosition(startPosition, out line, out column),
                },
                Line     = line,
                Column   = column,
                Url      = c == 'w' ? "http://" + link : link,
                IsClosed = true,
            };

            inline.Span.End = inline.Span.Start + link.Length - 1;
            inline.UrlSpan  = inline.Span;
            inline.AppendChild(new LiteralInline()
            {
                Span     = inline.Span,
                Line     = line,
                Column   = column,
                Content  = new StringSlice(slice.Text, startPosition, startPosition + link.Length - 1),
                IsClosed = true
            });
            processor.Inline = inline;

            return(true);
        }
Exemplo n.º 22
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            // Previous char must be a whitespace or a punctuation
            var previousChar = slice.PeekCharExtra(-1);

            if (!previousChar.IsWhiteSpaceOrZero() && Options.ValidPreviousCharacters.IndexOf(previousChar) == -1)
            {
                return(false);
            }

            List <char> pendingEmphasis = _listOfCharCache.Get();

            try
            {
                // Check that an autolink is possible in the current context
                if (!IsAutoLinkValidInCurrentContext(processor, pendingEmphasis))
                {
                    return(false);
                }

                var startPosition = slice.Start;
                int domainOffset  = 0;

                var c = slice.CurrentChar;
                // Precheck URL
                switch (c)
                {
                case 'h':
                    if (slice.MatchLowercase("ttp://", 1))
                    {
                        domainOffset = 7;     // http://
                    }
                    else if (slice.MatchLowercase("ttps://", 1))
                    {
                        domainOffset = 8;     // https://
                    }
                    else
                    {
                        return(false);
                    }
                    break;

                case 'f':
                    if (!slice.MatchLowercase("tp://", 1))
                    {
                        return(false);
                    }
                    domainOffset = 6;     // ftp://
                    break;

                case 'm':
                    if (!slice.MatchLowercase("ailto:", 1))
                    {
                        return(false);
                    }
                    break;

                case 't':
                    if (!slice.MatchLowercase("el:", 1))
                    {
                        return(false);
                    }
                    domainOffset = 4;
                    break;

                case 'w':
                    if (!slice.MatchLowercase("ww.", 1))     // We won't match http:/www. or /www.xxx
                    {
                        return(false);
                    }
                    domainOffset = 4;     // www.
                    break;
                }

                // Parse URL
                if (!LinkHelper.TryParseUrl(ref slice, out string?link, out _, true))
                {
                    return(false);
                }


                // If we have any pending emphasis, remove any pending emphasis characters from the end of the link
                if (pendingEmphasis.Count > 0)
                {
                    for (int i = link.Length - 1; i >= 0; i--)
                    {
                        if (pendingEmphasis.Contains(link[i]))
                        {
                            slice.Start--;
                        }
                        else
                        {
                            if (i < link.Length - 1)
                            {
                                link = link.Substring(0, i + 1);
                            }
                            break;
                        }
                    }
                }

                // Post-check URL
                switch (c)
                {
                case 'h':
                    if (string.Equals(link, "http://", StringComparison.OrdinalIgnoreCase) ||
                        string.Equals(link, "https://", StringComparison.OrdinalIgnoreCase))
                    {
                        return(false);
                    }
                    break;

                case 'f':
                    if (string.Equals(link, "ftp://", StringComparison.OrdinalIgnoreCase))
                    {
                        return(false);
                    }
                    break;

                case 't':
                    if (string.Equals(link, "tel", StringComparison.OrdinalIgnoreCase))
                    {
                        return(false);
                    }
                    break;

                case 'm':
                    int atIndex = link.IndexOf('@');
                    if (atIndex == -1 ||
                        atIndex == 7)     // mailto:@ - no email part
                    {
                        return(false);
                    }
                    domainOffset = atIndex + 1;
                    break;
                }

                // Do not need to check if a telephone number is a valid domain
                if (c != 't' && !LinkHelper.IsValidDomain(link, domainOffset))
                {
                    return(false);
                }

                var inline = new LinkInline()
                {
                    Span =
                    {
                        Start = processor.GetSourcePosition(startPosition, out int line, out int column),
                    },
                    Line       = line,
                    Column     = column,
                    Url        = c == 'w' ? ((Options.UseHttpsForWWWLinks ? "https://" : "http://") + link) : link,
                    IsClosed   = true,
                    IsAutoLink = true,
                };
Exemplo n.º 23
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();
            }

            bool openPrevIsPunctuation;
            bool openPrevIsWhiteSpace;

            pc.CheckUnicodeCategory(out openPrevIsWhiteSpace, out openPrevIsPunctuation);

            // Check that opening $/$$ is correct, using the different heuristics than for emphasis delimiters
            // If a $/$$ is not preceded by a whitespace or punctuation, or followed by a digit
            // 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)
                        {
                            while (c == match)
                            {
                                closeDollars++;
                                c = slice.NextChar();
                            }
                        }

                        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)
            {
                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)
                {
                    return(false);
                }

                end = slice.Start - 1;

                // Create a new MathInline
                int line;
                int column;
                var inline = new MathInline()
                {
                    Span           = new SourceSpan(processor.GetSourcePosition(startPosition, out line, out 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);
        }
Exemplo n.º 24
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);
        }
Exemplo n.º 25
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            int  start   = slice.Start;
            char current = slice.CurrentChar;

            if (slice.PeekCharExtra(-1) != open)
            {
                if (current == open)
                {
                    current = slice.NextChar();
                }
                else
                {
                    while (current.IsWhiteSpaceOrZero())
                    {
                        current = slice.NextChar();
                        if (slice.Start > slice.End)
                        {
                            return(false);
                        }
                    }
                    if (current != open)
                    {
                        return(false);
                    }
                }
            }

            int startName = slice.Start;
            int endName   = slice.Start;

            // Read the reference name.
            while (current != close)
            {
                endName = slice.Start;
                if ((endName - startName) > maxLength)
                {
                    return(false);
                }
                current = slice.NextChar();
                if (slice.Start > slice.End)
                {
                    // Abort if we've reached the end of the slice.
                    return(false);
                }
            }

            current = slice.NextChar(); // skip the closing ]

            // If the next char is a '(', then this is an actual link.
            if (current == '(')
            {
                return(false);
            }

            string reference = new StringSlice(slice.Text, startName, endName).ToString();
            var    inline    = new ReferenceInline(reference)
            {
                Span =
                {
                    Start = processor.GetSourcePosition(slice.Start, out int line, out int column)
                },
Exemplo n.º 26
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            // We are matching the following characters:
            //
            // '    ‘ ’     &lsquo; &rsquo;     'left-single-quote', 'right-single-quote'
            // ''   “ ”     &ldquo; &rdquo;     'left-double-quote', 'right-double-quote'
            // "    “ ”     &ldquo; &rdquo;     'left-double-quote', 'right-double-quote'
            // << >>    « »     &laquo; &raquo;     'left-angle-quote', 'right-angle-quote'
            // ...  …   &hellip;    'ellipsis'

            // Special case: &ndash; and &mdash; are handle as a PostProcess step to avoid conflicts with pipetables header separator row
            // --   –   &ndash;     'ndash'
            // ---  —   &mdash;     'mdash'

            var pc          = slice.PeekCharExtra(-1);
            var c           = slice.CurrentChar;
            var openingChar = c;

            var startingPosition = slice.Start;

            // undefined first
            var type = (SmartyPantType)0;

            switch (c)
            {
            case '\'':
                type = SmartyPantType.Quote;     // We will resolve them at the end of parsing all inlines
                if (slice.PeekChar(1) == '\'')
                {
                    slice.NextChar();
                    type = SmartyPantType.DoubleQuote;     // We will resolve them at the end of parsing all inlines
                }
                break;

            case '"':
                type = SmartyPantType.DoubleQuote;
                break;

            case '<':
                if (slice.NextChar() == '<')
                {
                    type = SmartyPantType.LeftAngleQuote;
                }
                break;

            case '>':
                if (slice.NextChar() == '>')
                {
                    type = SmartyPantType.RightAngleQuote;
                }
                break;

            case '.':
                if (slice.NextChar() == '.' && slice.NextChar() == '.')
                {
                    type = SmartyPantType.Ellipsis;
                }
                break;

            case '-':
                if (slice.NextChar() == '-')
                {
                    var quotePants = GetOrCreateState(processor);
                    quotePants.HasDash = true;
                    return(false);
                }
                break;
            }

            // If it is not matched, early exit
            if (type == 0)
            {
                return(false);
            }

            // Skip char
            c = slice.NextChar();

            bool canOpen;
            bool canClose;

            CharHelper.CheckOpenCloseDelimiter(pc, c, false, out canOpen, out canClose);

            bool postProcess = false;

            switch (type)
            {
            case SmartyPantType.Quote:
                postProcess = true;
                if (canOpen && !canClose)
                {
                    type = SmartyPantType.LeftQuote;
                }
                else if (!canOpen && canClose)
                {
                    type = SmartyPantType.RightQuote;
                }
                else
                {
                    return(false);
                }
                break;

            case SmartyPantType.DoubleQuote:
                postProcess = true;
                if (canOpen && !canClose)
                {
                    type = SmartyPantType.LeftDoubleQuote;
                }
                else if (!canOpen && canClose)
                {
                    type = SmartyPantType.RightDoubleQuote;
                }
                else
                {
                    return(false);
                }
                break;

            case SmartyPantType.LeftAngleQuote:
                postProcess = true;
                if (!canOpen || canClose)
                {
                    return(false);
                }
                break;

            case SmartyPantType.RightAngleQuote:
                postProcess = true;
                if (canOpen || !canClose)
                {
                    return(false);
                }
                break;

            case SmartyPantType.Ellipsis:
                if (canOpen || !canClose)
                {
                    return(false);
                }
                break;
            }

            // Create the SmartyPant inline
            int line;
            int column;
            var pant = new SmartyPant()
            {
                Span             = { Start = processor.GetSourcePosition(startingPosition, out line, out column) },
                Line             = line,
                Column           = column,
                OpeningCharacter = openingChar,
                Type             = type
            };

            pant.Span.End = pant.Span.Start + slice.Start - startingPosition - 1;

            // We will check in a post-process step for balanced open/close quotes
            if (postProcess)
            {
                var quotePants = GetOrCreateState(processor);

                // Register only if we don't have yet any quotes
                if (quotePants.Count == 0)
                {
                    processor.Block.ProcessInlinesEnd += BlockOnProcessInlinesEnd;
                }
                quotePants.Add(pant);
            }

            processor.Inline = pant;
            return(true);
        }
Exemplo n.º 27
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();
            }

            bool canOpen;
            bool canClose;

            // Check that opening $/$$ is correct, using the same heuristics than for emphasis delimiters
            CharHelper.CheckOpenCloseDelimiter(pc, c, false, out canOpen, out canClose);
            if (!canOpen)
            {
                return(false);
            }

            bool isMatching   = false;
            int  closeDollars = 0;

            var start = slice.Start;

            pc = match;
            while (c != '\0')
            {
                // Don't process sticks if we have a '\' as a previous char
                if (pc != '\\')
                {
                    while (c == match)
                    {
                        closeDollars++;
                        c = slice.NextChar();
                    }

                    if (closeDollars >= openDollars)
                    {
                        break;
                    }
                    pc = match;
                }

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

            if (closeDollars >= openDollars)
            {
                // Check that closing $/$$ is correct
                CharHelper.CheckOpenCloseDelimiter(pc, c, false, out canOpen, out canClose);
                if (!canClose || c.IsDigit())
                {
                    return(false);
                }

                // Create a new MathInline
                int line;
                int column;
                var inline = new MathInline()
                {
                    Span           = new SourceSpan(processor.GetSourcePosition(startPosition, out line, out 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 = inline.Content.End - openDollars;

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

            return(isMatching);
        }
Exemplo n.º 28
0
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            int openSticks = 0;
            var match      = slice.CurrentChar;

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

            var startPosition = slice.Start;

            // Match the opened sticks
            var c = slice.CurrentChar;

            while (c == match)
            {
                openSticks++;
                c = slice.NextChar();
            }

            bool isMatching = false;

            var builder     = processor.StringBuilders.Get();
            int closeSticks = 0;

            // 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, with leading and trailing spaces and line endings removed, and whitespace collapsed to single spaces.
            var pc = ' ';

            int newLinesFound = 0;

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

                if (c != match && (c != ' ' || pc != ' '))
                {
                    builder.Append(c);
                }
                else
                {
                    while (c == match)
                    {
                        closeSticks++;
                        pc = c;
                        c  = slice.NextChar();
                    }

                    if (openSticks == closeSticks)
                    {
                        break;
                    }
                }

                if (closeSticks > 0)
                {
                    builder.Append(match, closeSticks);
                    closeSticks = 0;
                }
                else
                {
                    pc = c;
                    c  = slice.NextChar();
                }
            }

            if (closeSticks == openSticks)
            {
                // Remove trailing space
                if (builder.Length > 0)
                {
                    if (builder[builder.Length - 1].IsWhitespace())
                    {
                        builder.Length--;
                    }
                }
                int line;
                int column;
                processor.Inline = new CodeInline()
                {
                    Delimiter = match,
                    Content   = builder.ToString(),
                    Span      = new SourceSpan(processor.GetSourcePosition(startPosition, out line, out column), processor.GetSourcePosition(slice.Start - 1)),
                    Line      = line,
                    Column    = column
                };
                isMatching = true;
            }

            // Release the builder if not used
            processor.StringBuilders.Release(builder);
            return(isMatching);
        }
Exemplo n.º 29
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 = 0;
            char c;

            do
            {
                delimiterCount++;
                c = slice.NextChar();
            } while (c == delimiterChar);


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

            // 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);
        }
        public override bool Match(InlineProcessor processor, ref StringSlice slice)
        {
            var match = slice.CurrentChar;

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

            var startPosition = slice.Start;
            int openSticks = 0, closeSticks = 0;
            var c = slice.CurrentChar;

            while (c == match)
            {
                openSticks++;
                c = slice.NextChar();
            }

            var builder = processor.StringBuilders.Get();

            while (c != '\0')
            {
                // If we find a close brace, that's the end of our
                // macro. Otherwise, append to the builder.
                if (c == '}')
                {
                    do
                    {
                        closeSticks++;
                        c = slice.NextChar();
                    } while (c == match);

                    if (openSticks == closeSticks)
                    {
                        break;
                    }

                    builder.Append(match, closeSticks);
                    closeSticks = 0;
                }
                else
                {
                    builder.Append(c);
                    c = slice.NextChar();
                }
            }

            bool isMatching = false;

            if (closeSticks == openSticks)
            {
                processor.Inline = new ConfluenceMacro {
                    Delimiter = match,
                    Content   = builder.ToString(),
                    Span      = new SourceSpan(
                        processor.GetSourcePosition(startPosition, out int line, out int column),
                        processor.GetSourcePosition(slice.Start - 1)
                        ),
                    Line   = line,
                    Column = column
                };
                isMatching = true;
            }
            processor.StringBuilders.Release(builder);

            return(isMatching);
        }
    }