public void NoTitle() { string str = "[id]: url.com"; r = LinkDefinition.ParseLinkDefinition(str, false); Assert.IsNotNull(r); Assert.AreEqual(r.id, "id"); Assert.AreEqual(r.url, "url.com"); Assert.AreEqual(r.title, null); }
public void MultiLine() { string str = "[id]:\n\t http://www.site.com \n\t (my title)"; r = LinkDefinition.ParseLinkDefinition(str, false); Assert.IsNotNull(r); Assert.AreEqual(r.id, "id"); Assert.AreEqual(r.url, "http://www.site.com"); Assert.AreEqual(r.title, "my title"); }
public void DoubleQuoteTitle() { string str = "[id]: url.com \"my title\""; r = LinkDefinition.ParseLinkDefinition(str, false); Assert.IsNotNull(r); Assert.AreEqual(r.id, "id"); Assert.AreEqual(r.url, "url.com"); Assert.AreEqual(r.title, "my title"); }
public void AngleBracketedUrl() { string str = "[id]: <url.com> (my title)"; r = LinkDefinition.ParseLinkDefinition(str, false); Assert.IsNotNull(r); Assert.AreEqual(r.id, "id"); Assert.AreEqual(r.url, "url.com"); Assert.AreEqual(r.title, "my title"); }
// 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; }
// 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))); }
// Add a link definition internal void AddLinkDefinition(LinkDefinition link) { // Store it m_LinkDefinitions[link.id] = link; }
// 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); }
// Add a link definition internal void AddLinkDefinition(LinkDefinition link) { // Store it m_LinkDefinitions[link.id]=link; }
public void Setup() { r=null; }
public void ParenthesizedTitle() { string str = "[id]: url.com (my title)"; r = LinkDefinition.ParseLinkDefinition(str, false); Assert.IsNotNull(r); Assert.AreEqual(r.id, "id"); Assert.AreEqual(r.url, "url.com"); Assert.AreEqual(r.title, "my title"); }