in Clients/Xamarin.Interactive.Client/CommonMark/MarkdownFormatter.cs [156:312]
void WriteSingleBlock (Block block)
{
switch (block.Tag) {
case BlockTag.Document:
WriteBlock (block.FirstChild);
break;
case BlockTag.ReferenceDefinition:
// FIXME: these are completely broken in CommonMark.NET as it
// only persists reference definitions on the document (root)
// node, which means all context is lost regarding _where_ in
// the document a reference definition exists. The block here
// is completely empty. Additionally, it upper-cases the keys
// the reference dictionary, which means they also cannot be
// round-tripped correctly :(
//
// However - we can still write out _mostly_ semantically
// identical (e.g. functional) markdown since CommonMark.NET's
// link inlines will have the resolved URLs.
break;
case BlockTag.BlockQuote:
writer.PushLinePrefix ("> ");
WriteBlock (block.FirstChild);
writer.PopLinePrefix ();
break;
case BlockTag.Paragraph:
WriteInline (block.InlineContent);
break;
case BlockTag.AtxHeading:
writer.WriteLiteral ('#', block.Heading.Level);
writer.WriteLiteral (' ');
WriteInline (block.InlineContent);
break;
case BlockTag.SetextHeading:
var startPosition = writer.Position;
WriteInline (block.InlineContent);
var inlineLength = writer.Position - startPosition;
var underlineLength = (int)block.Heading.SetextUnderlineLength;
if (underlineLength < 1)
underlineLength = inlineLength;
writer.WriteLineLiteral ();
writer.WriteLiteral (block.Heading.Level == 1 ? '=' : '-', underlineLength);
break;
case BlockTag.ThematicBreak:
var breakChar = settings.ThematicBreakChar;
var listData = block.Parent?.ListData;
if (listData != null && listData.BulletChar == breakChar) {
if (breakChar == '*')
breakChar = '-';
else if (breakChar == '-')
breakChar = '*';
}
writer.WriteLiteral (breakChar, settings.ActualThematicBreakWidth);
break;
case BlockTag.FencedCode:
case BlockTag.YamlBlock:
var content = block.StringContent.ToString ();
var fenceOffset = block.FencedCodeData.FenceOffset;
var fenceChar = block.FencedCodeData.FenceChar;
var fenceSize = Math.Max (3, MaxConsecutiveCharCount (content, fenceChar) + 1);
writer.WriteLiteral (' ', fenceOffset);
writer.WriteLiteral (fenceChar, fenceSize);
if (!string.IsNullOrEmpty (block.FencedCodeData.Info))
writer.WriteLiteral (block.FencedCodeData.Info);
writer.WriteLineLiteral ();
if (fenceOffset > 0)
writer.PushLinePrefix (new string (' ', block.FencedCodeData.FenceOffset));
writer.WriteLiteral (content);
if (fenceOffset > 0)
writer.PopLinePrefix ();
writer.WriteLiteral (' ', fenceOffset);
writer.WriteLiteral (fenceChar, fenceSize);
break;
case BlockTag.IndentedCode:
writer.PushLinePrefix (" ");
// FIXME: RemoveTrailingBlankLines appears to be broken.
// TrimEnd works, but that's not exactly what we want.
// Workaround is to convert to a string first, which is
// is a bit of a perf hit.
//
// block.StringContent.RemoveTrailingBlankLines ();
// block.StringContent.WriteTo (writer);
writer.WriteLiteral (block.StringContent.ToString ().TrimEnd ('\n', '\r'));
writer.PopLinePrefix ();
break;
case BlockTag.HtmlBlock:
// FIXME: see comment for IndentedCode case
writer.WriteLiteral (block.StringContent.ToString ().TrimEnd ('\n', '\r'));
break;
case BlockTag.List:
WriteBlock (block.FirstChild);
break;
case BlockTag.ListItem:
// FIXME: we could compute the ordered list information in the
// BlockTag.List case above and pass via WriteBlock, but I wanted
// to keep it simple. Could be an ever-so-slight perf improvement.
var orderedIndex = block.ListData.Start;
char orderedDelimiter;
switch (block.ListData.Delimiter) {
case ListDelimiter.Period:
orderedDelimiter = '.';
break;
case ListDelimiter.Parenthesis:
orderedDelimiter = ')';
break;
default:
throw new NotImplementedException (
$"{nameof (ListDelimiter)}.{block.ListData.Delimiter}");
}
string marker;
switch (block.ListData.ListType) {
case ListType.Bullet:
marker = block.ListData.BulletChar.ToString ();
break;
case ListType.Ordered:
marker = (orderedIndex++).ToString (CultureInfo.InvariantCulture)
+ orderedDelimiter;
break;
default:
throw new NotImplementedException (
$"{nameof (ListType)}.{block.ListData.ListType}");
}
writer.WriteLiteral (' ', block.ListData.MarkerOffset);
writer.WriteLiteral (marker);
writer.WriteLiteral (' ', block.ListData.Padding - marker.Length);
var padding = block.ListData.Padding + block.ListData.MarkerOffset;
if (padding > 0)
writer.PushLinePrefix (new string (' ', padding), false);
WriteBlock (block.FirstChild);
if (padding > 0)
writer.PopLinePrefix ();
break;
default:
throw new NotImplementedException ($"{block.Tag}");
}
}