// 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); }
internal string MakeID(string str, int start, int len) { // Parse the string into a list of tokens Tokenize(str, start, len); StringBuilder sb = new StringBuilder(); foreach (var t in m_Tokens) { switch (t.type) { case TokenType.Text: sb.Append(str, t.startOffset, t.length); break; case TokenType.link: LinkInfo li = (LinkInfo)t.data; sb.Append(li.link_text); break; } FreeToken(t); } // Now clean it using the same rules as pandoc base.Reset(sb.ToString()); // Skip everything up to the first letter while (!eof) { if (Char.IsLetter(current)) { break; } SkipForward(1); } // Process all characters sb.Length = 0; while (!eof) { char ch = current; if (char.IsLetterOrDigit(ch) || ch == '_' || ch == '-' || ch == '.') { sb.Append(Char.ToLower(ch)); } else if (ch == ' ') { sb.Append("-"); } else if (IsLineEnd(ch)) { sb.Append("-"); SkipEol(); continue; } SkipForward(1); } return(sb.ToString()); }
// Render a list of tokens to a destinatino string builder. private void Render(StringBuilder sb, string str) { foreach (Token t in m_Tokens) { switch (t.type) { case TokenType.Text: // Append encoded text m_Markdown.HtmlEncode(sb, str, t.startOffset, t.length); break; case TokenType.HtmlTag: // Append html as is Utils.SmartHtmlEncodeAmps(sb, str, t.startOffset, t.length); break; case TokenType.Html: case TokenType.opening_mark: case TokenType.closing_mark: case TokenType.internal_mark: // Append html as is sb.Append(str, t.startOffset, t.length); break; case TokenType.br: sb.Append("<br />\n"); break; case TokenType.open_em: sb.Append("<em>"); break; case TokenType.close_em: sb.Append("</em>"); break; case TokenType.open_strong: sb.Append("<strong>"); break; case TokenType.close_strong: sb.Append("</strong>"); break; case TokenType.code_span: sb.Append("<code>"); m_Markdown.HtmlEncode(sb, str, t.startOffset, t.length); sb.Append("</code>"); break; case TokenType.link: { LinkInfo li = (LinkInfo)t.data; var sf = new SpanFormatter(m_Markdown); sf.DisableLinks = true; li.def.RenderLink(m_Markdown, sb, sf.Format(li.link_text)); break; } case TokenType.img: { LinkInfo li = (LinkInfo)t.data; li.def.RenderImg(m_Markdown, sb, li.link_text); break; } case TokenType.footnote: { FootnoteReference r = (FootnoteReference)t.data; sb.Append("<sup id=\"fnref:"); sb.Append(r.id); sb.Append("\"><a href=\"#fn:"); sb.Append(r.id); sb.Append("\" rel=\"footnote\">"); sb.Append(r.index + 1); sb.Append("</a></sup>"); break; } case TokenType.abbreviation: { Abbreviation a = (Abbreviation)t.data; sb.Append("<abbr"); if (!String.IsNullOrEmpty(a.Title)) { sb.Append(" title=\""); m_Markdown.HtmlEncode(sb, a.Title, 0, a.Title.Length); sb.Append("\""); } sb.Append(">"); m_Markdown.HtmlEncode(sb, a.Abbr, 0, a.Abbr.Length); sb.Append("</abbr>"); break; } } FreeToken(t); } }