예제 #1
0
        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}");
            }
        }