in Clients/Xamarin.Interactive.Client/CommonMark/MarkdownFormatter.cs [320:500]
void WriteSingleInline (ref Inline inline, char [] escapeChars)
{
switch (inline.Tag) {
case InlineTag.Emphasis:
case InlineTag.Strong:
var delimeterChar = inline.Emphasis.DelimiterCharacter;
if (delimeterChar == char.MinValue)
delimeterChar = '_';
var delimeter = new string (
delimeterChar,
inline.Tag == InlineTag.Emphasis ? 1 : 2);
writer.WriteLiteral (delimeter);
WriteInline (inline.FirstChild);
writer.WriteLiteral (delimeter);
break;
case InlineTag.Strikethrough:
writer.WriteLiteral ("~~");
WriteInline (inline.FirstChild);
writer.WriteLiteral ("~~");
break;
case InlineTag.Placeholder:
writer.WriteLiteral ('[');
writer.WriteEscaped (inline.TargetUrl);
writer.WriteLiteral (']');
break;
case InlineTag.Link:
case InlineTag.Image:
// for writing out autolinks, we use this potentially stripped
// URL so we can round-trip email address autolinks.
var targetUrl = inline.TargetUrl;
if (targetUrl.StartsWith ("mailto:", StringComparison.Ordinal))
targetUrl = targetUrl.Substring (7);
// translate links where the target URL and the content are
// the same (and no title is specified) into <autolinks>
if (inline.Tag == InlineTag.Link &&
string.IsNullOrEmpty (inline.LiteralContent) &&
inline.FirstChild != null &&
inline.FirstChild.Tag == InlineTag.String &&
inline.FirstChild.NextSibling == null &&
inline.FirstChild.LiteralContent == targetUrl) {
writer.WriteLiteral ('<');
writer.WriteEscaped (targetUrl);
writer.WriteLiteral ('>');
break;
}
// the only difference between a link and an image is ^!
if (inline.Tag == InlineTag.Image)
writer.WriteLiteral ('!');
writer.WriteLiteral ('[');
WriteInline (inline.FirstChild, '[', ']');
writer.WriteLiteral (']');
writer.WriteLiteral ('(');
writer.WriteEscaped (inline.TargetUrl, '(', ')');
if (!string.IsNullOrEmpty (inline.LiteralContent)) {
writer.WriteLiteral (" \"");
writer.WriteEscaped (inline.LiteralContent, '"', '(', ')');
writer.WriteLiteral ('"');
}
writer.WriteLiteral (')');
break;
case InlineTag.Code:
if (string.IsNullOrEmpty (inline.LiteralContent)) {
writer.WriteLiteral ("` `");
break;
}
var contentLength = inline.LiteralContent.Length;
var wrapSize = MaxConsecutiveCharCount (inline.LiteralContent, '`') + 1;
writer.WriteLiteral ('`', wrapSize);
if (contentLength > 0 && inline.LiteralContent [0] == '`')
writer.WriteLiteral (' ');
writer.WriteLiteral (inline.LiteralContent);
if (contentLength > 0 && inline.LiteralContent [contentLength - 1] == '`')
writer.WriteLiteral (' ');
writer.WriteLiteral ('`', wrapSize);
break;
case InlineTag.RawHtml:
writer.WriteLiteral (inline.LiteralContent);
break;
case InlineTag.LineBreak:
writer.WriteLiteral ('\\');
writer.WriteLineLiteral ();
break;
case InlineTag.SoftBreak:
writer.WriteLineLiteral ();
var next = inline.NextSibling;
if (next != null &&
next.Tag == InlineTag.String &&
!string.IsNullOrEmpty (next.LiteralContent)) {
switch (next.LiteralContent [0]) {
case '#':
case '-':
case '>':
case '*':
case '=':
writer.WriteLiteral (' ', 4);
break;
}
}
break;
case InlineTag.String:
var content = inline.LiteralContent;
if (string.IsNullOrEmpty (content))
break;
// CommonMark.NET and the CommonMark JS reference parser appear
// to yield single-character string inlines for characters that
// need escaping. Therefore, we only attempt to escape these
// strings, and not the first character on a string of an arbitray
// length.
switch (content) {
case "*":
case "_":
case ">":
case "-":
case "#":
case "\\":
case "[":
case "`":
case "<":
writer.WriteEscaped (content [0]);
break;
case "!":
if (inline.NextSibling != null &&
inline.NextSibling.Tag == InlineTag.Link)
writer.WriteEscaped ('!');
else
writer.WriteLiteral ('!');
break;
default:
// match this inline and a next for escaped ordered list
// syntax: ^\d+[\.\)]$ - if the two sibling inlines match,
// we want to escape the list delimeter.
if (inline.NextSibling != null && content.Length < 9 && (
inline.NextSibling.LiteralContent == "." ||
inline.NextSibling.LiteralContent == ")")) {
var allDigits = true;
for (int i = 0; i < content.Length; i++) {
if (!char.IsDigit (content [i])) {
allDigits = false;
break;
}
}
if (allDigits) {
writer.WriteLiteral (content);
// this will skip over the delimeter inline on the
// next outer loop iteration since we're handling it
// here (escaping it).
inline = inline.NextSibling;
writer.WriteEscaped (inline.LiteralContent [0]);
break;
}
}
writer.WriteEscaped (content, escapeChars);
break;
}
break;
default:
throw new NotImplementedException ($"{inline.Tag}");
}
}