Example #1
0
        // Process '*', '**' or '_', '__'
        // This is horrible and probably much better done through regex, but I'm stubborn.
        // For normal cases this routine works as expected.  For unusual cases (eg: overlapped
        // strong and emphasis blocks), the behaviour is probably not the same as the original
        // markdown scanner.

        /*
         *      public Token ProcessEmphasisOld(ref Token prev_single, ref Token prev_double)
         *      {
         *              // Check whitespace before/after
         *              bool bSpaceBefore = !bof && IsLineSpace(CharAtOffset(-1));
         *              bool bSpaceAfter = IsLineSpace(CharAtOffset(1));
         *
         *              // Ignore if surrounded by whitespace
         *              if (bSpaceBefore && bSpaceAfter)
         *              {
         *                      return null;
         *              }
         *
         *              // Save the current character and skip it
         *              char ch = current;
         *              Skip(1);
         *
         *              // Do we have a previous matching single star?
         *              if (!bSpaceBefore && prev_single != null)
         *              {
         *                      // Yes, match them...
         *                      prev_single.type = TokenType.open_em;
         *                      prev_single = null;
         *                      return CreateToken(TokenType.close_em, position - 1, 1);
         *              }
         *
         *              // Is this a double star/under
         *              if (current == ch)
         *              {
         *                      // Skip second character
         *                      Skip(1);
         *
         *                      // Space after?
         *                      bSpaceAfter = IsLineSpace(current);
         *
         *                      // Space both sides?
         *                      if (bSpaceBefore && bSpaceAfter)
         *                      {
         *                              // Ignore it
         *                              return CreateToken(TokenType.Text, position - 2, 2);
         *                      }
         *
         *                      // Do we have a previous matching double
         *                      if (!bSpaceBefore && prev_double != null)
         *                      {
         *                              // Yes, match them
         *                              prev_double.type = TokenType.open_strong;
         *                              prev_double = null;
         *                              return CreateToken(TokenType.close_strong, position - 2, 2);
         *                      }
         *
         *                      if (!bSpaceAfter)
         *                      {
         *                              // Opening double star
         *                              prev_double = CreateToken(TokenType.Text, position - 2, 2);
         *                              return prev_double;
         *                      }
         *
         *                      // Ignore it
         *                      return CreateToken(TokenType.Text, position - 2, 2);
         *              }
         *
         *              // If there's a space before, we can open em
         *              if (!bSpaceAfter)
         *              {
         *                      // Opening single star
         *                      prev_single = CreateToken(TokenType.Text, position - 1, 1);
         *                      return prev_single;
         *              }
         *
         *              // Ignore
         *              Skip(-1);
         *              return null;
         *      }
         */

        // Process auto links eg: <google.com>
        Token ProcessAutoLink()
        {
            if (DisableLinks)
            {
                return(null);
            }

            // Skip the angle bracket and remember the start
            SkipForward(1);
            Mark();

            bool ExtraMode = m_Markdown.ExtraMode;

            // Allow anything up to the closing angle, watch for escapable characters
            while (!eof)
            {
                char ch = current;

                // No whitespace allowed
                if (char.IsWhiteSpace(ch))
                {
                    break;
                }

                // End found?
                if (ch == '>')
                {
                    string url = Utils.UnescapeString(Extract(), ExtraMode);

                    LinkInfo li = null;
                    if (Utils.IsEmailAddress(url))
                    {
                        string link_text;
                        if (url.StartsWith("mailto:"))
                        {
                            link_text = url.Substring(7);
                        }
                        else
                        {
                            link_text = url;
                            url       = "mailto:" + url;
                        }

                        li = new LinkInfo(new LinkDefinition("auto", url, null), link_text);
                    }
                    else if (Utils.IsWebAddress(url))
                    {
                        li = new LinkInfo(new LinkDefinition("auto", url, null), url);
                    }

                    if (li != null)
                    {
                        SkipForward(1);
                        return(CreateToken(TokenType.link, li));
                    }

                    return(null);
                }

                this.SkipEscapableChar(ExtraMode);
            }

            // Didn't work
            return(null);
        }
Example #2
0
        // Parse just the link target
        // For reference link definition, this is the bit after "[id]: thisbit"
        // For inline link, this is the bit in the parens: [link text](thisbit)
        internal static LinkDefinition ParseLinkTarget(StringScanner p, string id, bool ExtraMode)
        {
            // Skip whitespace
            p.SkipWhitespace();

            // End of string?
            if (p.eol)
            {
                return(null);
            }

            // Create the link definition
            var r = new LinkDefinition(id);

            // Is the url enclosed in angle brackets
            if (p.SkipChar('<'))
            {
                // Extract the url
                p.Mark();

                // Find end of the url
                while (p.current != '>')
                {
                    if (p.eof)
                    {
                        return(null);
                    }
                    p.SkipEscapableChar(ExtraMode);
                }

                string url = p.Extract();
                if (!p.SkipChar('>'))
                {
                    return(null);
                }

                // Unescape it
                r.url = Utils.UnescapeString(url.Trim(), ExtraMode);

                // Skip whitespace
                p.SkipWhitespace();
            }
            else
            {
                // Find end of the url
                p.Mark();
                int paren_depth = 1;
                while (!p.eol)
                {
                    char ch = p.current;
                    if (char.IsWhiteSpace(ch))
                    {
                        break;
                    }
                    if (id == null)
                    {
                        if (ch == '(')
                        {
                            paren_depth++;
                        }
                        else if (ch == ')')
                        {
                            paren_depth--;
                            if (paren_depth == 0)
                            {
                                break;
                            }
                        }
                    }

                    p.SkipEscapableChar(ExtraMode);
                }

                r.url = Utils.UnescapeString(p.Extract().Trim(), ExtraMode);
            }

            p.SkipLinespace();

            // End of inline target
            if (p.DoesMatch(')'))
            {
                return(r);
            }

            bool bOnNewLine = p.eol;
            int  posLineEnd = p.position;

            if (p.eol)
            {
                p.SkipEol();
                p.SkipLinespace();
            }

            // Work out what the title is delimited with
            char delim;

            switch (p.current)
            {
            case '\'':
            case '\"':
                delim = p.current;
                break;

            case '(':
                delim = ')';
                break;

            default:
                if (bOnNewLine)
                {
                    p.position = posLineEnd;
                    return(r);
                }
                else
                {
                    return(null);
                }
            }

            // Skip the opening title delimiter
            p.SkipForward(1);

            // Find the end of the title
            p.Mark();
            while (true)
            {
                if (p.eol)
                {
                    return(null);
                }

                if (p.current == delim)
                {
                    if (delim != ')')
                    {
                        int savepos = p.position;

                        // Check for embedded quotes in title

                        // Skip the quote and any trailing whitespace
                        p.SkipForward(1);
                        p.SkipLinespace();

                        // Next we expect either the end of the line for a link definition
                        // or the close bracket for an inline link
                        if ((id == null && p.current != ')') ||
                            (id != null && !p.eol))
                        {
                            continue;
                        }

                        p.position = savepos;
                    }

                    // End of title
                    break;
                }

                p.SkipEscapableChar(ExtraMode);
            }

            // Store the title
            r.title = Utils.UnescapeString(p.Extract(), ExtraMode);

            // Skip closing quote
            p.SkipForward(1);

            // Done!
            return(r);
        }
Example #3
0
        // Process [link] and ![image] directives
        Token ProcessLinkOrImageOrFootnote()
        {
            // Link or image?
            TokenType token_type = SkipChar('!') ? TokenType.img : TokenType.link;

            // Opening '['
            if (!SkipChar('['))
            {
                return(null);
            }

            // Is it a foonote?
            var savepos = position;

            if (m_Markdown.ExtraMode && token_type == TokenType.link && SkipChar('^'))
            {
                SkipLinespace();

                // Parse it
                string id;
                if (SkipFootnoteID(out id) && SkipChar(']'))
                {
                    // Look it up and create footnote reference token
                    int footnote_index = m_Markdown.ClaimFootnote(id);
                    if (footnote_index >= 0)
                    {
                        // Yes it's a footnote
                        return(CreateToken(TokenType.footnote, new FootnoteReference(footnote_index, id)));
                    }
                }

                // Rewind
                position = savepos;
            }

            if (DisableLinks && token_type == TokenType.link)
            {
                return(null);
            }

            bool ExtraMode = m_Markdown.ExtraMode;

            // Find the closing square bracket, allowing for nesting, watching for
            // escapable characters
            Mark();
            int depth = 1;

            while (!eof)
            {
                char ch = current;
                if (ch == '[')
                {
                    depth++;
                }
                else if (ch == ']')
                {
                    depth--;
                    if (depth == 0)
                    {
                        break;
                    }
                }

                this.SkipEscapableChar(ExtraMode);
            }

            // Quit if end
            if (eof)
            {
                return(null);
            }

            // Get the link text and unescape it
            string link_text = Utils.UnescapeString(Extract(), ExtraMode);

            // The closing ']'
            SkipForward(1);

            // Save position in case we need to rewind
            savepos = position;

            // Inline links must follow immediately
            if (SkipChar('('))
            {
                // Extract the url and title
                var link_def = LinkDefinition.ParseLinkTarget(this, null, m_Markdown.ExtraMode);
                if (link_def == null)
                {
                    return(null);
                }

                // Closing ')'
                SkipWhitespace();
                if (!SkipChar(')'))
                {
                    return(null);
                }

                // Create the token
                return(CreateToken(token_type, new LinkInfo(link_def, link_text)));
            }

            // Optional space or tab
            if (!SkipChar(' '))
            {
                SkipChar('\t');
            }

            // If there's line end, we're allow it and as must line space as we want
            // before the link id.
            if (eol)
            {
                SkipEol();
                SkipLinespace();
            }

            // Reference link?
            string link_id = null;

            if (current == '[')
            {
                // Skip the opening '['
                SkipForward(1);

                // Find the start/end of the id
                Mark();
                if (!Find(']'))
                {
                    return(null);
                }

                // Extract the id
                link_id = Extract();

                // Skip closing ']'
                SkipForward(1);
            }
            else
            {
                // Rewind to just after the closing ']'
                position = savepos;
            }

            // Link id not specified?
            if (string.IsNullOrEmpty(link_id))
            {
                // Use the link text (implicit reference link)
                link_id = Utils.NormalizeLineEnds(link_text);

                // If the link text has carriage returns, normalize
                // to spaces
                if (!object.ReferenceEquals(link_id, link_text))
                {
                    while (link_id.Contains(" \n"))
                    {
                        link_id = link_id.Replace(" \n", "\n");
                    }
                    link_id = link_id.Replace("\n", " ");
                }
            }

            // Find the link definition abort if not defined
            var def = m_Markdown.GetLinkDefinition(link_id);

            if (def == null)
            {
                return(null);
            }

            // Create a token
            return(CreateToken(token_type, new LinkInfo(def, link_text)));
        }