예제 #1
0
        /// <summary>
        /// Parses 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)
        /// </summary>
        /// <param name="p">The p.</param>
        /// <param name="id">The identifier.</param>
        /// <param name="ExtraMode">if set to <c>true</c> [extra mode].</param>
        /// <returns>LinkDefinition.</returns>
        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);
        }
예제 #2
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)));
        }
예제 #3
0
 // Add a link definition
 internal void AddLinkDefinition(LinkDefinition link)
 {
     // Store it
     m_LinkDefinitions[link.id] = link;
 }
예제 #4
0
 /// <summary>
 /// Initializes a new instance of the <see cref="LinkInfo"/> class.
 /// </summary>
 /// <param name="def">The definition.</param>
 /// <param name="link_text">The link text.</param>
 public LinkInfo(LinkDefinition def, string link_text)
 {
     this.def       = def;
     this.link_text = link_text;
 }