CommunityToolkit / Labs-Windows

A safe space to collaborate and engineer solutions from the prototyping stage all the way through polished finalized component for the Windows Community Toolkit.
Other
334 stars 46 forks source link

MarkdownTextBlock MyCodeBlock renders empty RichTextBox as doesn't use StringBuilder for anything #547

Open JoeTomkinson opened 4 months ago

JoeTomkinson commented 4 months ago

Describe the bug

The 'MyCodeBlock' in the pre-release of the MarkDownTextBlock doesnt actually use the lines of text for anything resulting in empty code blocks. I've attached the class snippet below so you can see what I mean.

components/MarkdownTextBlock/src/TextElements/MyCodeBlock.cs

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Markdig.Syntax;

namespace CommunityToolkit.Labs.WinUI.MarkdownTextBlock.TextElements;

internal class MyCodeBlock : IAddChild
{
    private CodeBlock _codeBlock;
    private Paragraph _paragraph;
    private MarkdownConfig _config;

    public TextElement TextElement
    {
        get => _paragraph;
    }

    public MyCodeBlock(CodeBlock codeBlock, MarkdownConfig config)
    {
        _codeBlock = codeBlock;
        _config = config;
        _paragraph = new Paragraph();
        var container = new InlineUIContainer();
        var border = new Border();
        border.Background = (Brush)Application.Current.Resources["ExpanderHeaderBackground"];
        border.Padding = _config.Themes.Padding;
        border.Margin = _config.Themes.InternalMargin;
        border.CornerRadius = _config.Themes.CornerRadius;
        var richTextBlock = new RichTextBlock();

        if (codeBlock is FencedCodeBlock fencedCodeBlock)
        {
//#if !WINAPPSDK
//            var formatter = new ColorCode.RichTextBlockFormatter(Extensions.GetOneDarkProStyle());
//#else
//            var formatter = new ColorCode.RichTextBlockFormatter(Extensions.GetOneDarkProStyle());
//#endif
            var stringBuilder = new StringBuilder();

            // go through all the lines backwards and only add the lines to a stack if we have encountered the first non-empty line
            var lines = fencedCodeBlock.Lines.Lines;
            var stack = new Stack<string>();
            var encounteredFirstNonEmptyLine = false;
            if (lines != null)
            {
                for (var i = lines.Length - 1; i >= 0; i--)
                {
                    var line = lines[i];
                    if (String.IsNullOrWhiteSpace(line.ToString()) && !encounteredFirstNonEmptyLine)
                    {
                        continue;
                    }

                    encounteredFirstNonEmptyLine = true;
                    stack.Push(line.ToString());
                }

                // append all the lines in the stack to the string builder
                while (stack.Count > 0)
                {
                    stringBuilder.AppendLine(stack.Pop());
                }
            }

            //formatter.FormatRichTextBlock(stringBuilder.ToString(), fencedCodeBlock.ToLanguage(), richTextBlock);
        }
        else
        {
            foreach (var line in codeBlock.Lines.Lines)
            {
                var paragraph = new Paragraph();
                var lineString = line.ToString();
                if (!String.IsNullOrWhiteSpace(lineString))
                {
                    paragraph.Inlines.Add(new Run() { Text = lineString });
                }
                richTextBlock.Blocks.Add(paragraph);
            }
        }
        border.Child = richTextBlock;
        container.Child = border;
        _paragraph.Inlines.Add(container);
    }

    public void AddChild(IAddChild child) {}
}

Steps to reproduce

1) Add in the pre-release version of the MarkdownTextBox from Labs-Windows feed.
2) Add MarkdownTextBox to XAML page.
3) Add in markdown text that contains a code snippet.
4) Code block is rendered and it appears empty with no content.

Expected behavior

Can see the lines of code rendered within the code block.

Screenshots

No response

Code Platform

Windows Build Number

Other Windows Build number

No response

App minimum and target SDK version

Other SDK version

No response

Visual Studio Version

2022

Visual Studio Build Number

No response

Device form factor

Desktop

Additional context

No response

Help us help you

Yes, but only if others can assist.

JoeTomkinson commented 4 months ago

There's actually a handful of issues upon looking over it a bit closer, so far I've identified the following:

Newlines are not handled so there's no spacing between paragraphs if there's empty lines in the markdown content.

Bullets are not indented and there's no spacing betwen the bullet and the first word of the run.

Code Block doesn't render the language type and colours as the formatting is commented out, relates directly the the initial problem, easy fix though.

Inadvertant spacing - When pasting in code that then gets rendered to a MarkdownTextblock inside of a chat bubble, it adds a lot of whitespace to the bottom of the code block, not sure why yet still looking into that one.

beeradmoore commented 1 month ago

I have some examples of the spacing and whatnot.

I am using MarkdownTextBlock in a ContentDialog. The markdown part of this looks like,

var markdownTextBlock = new MarkdownTextBlock()
{
    Text = @"DLSS Swapper should find games from your installed game libraries automatically. If your game is not listed there may be a few settings preventing it. Please check:

- Games list filter is not set to ""Hide non-DLSS games""
- Specific game library is enabled in settings

If you have checked these and your game is still not showing up there may be a bug. We would appreciate it if you could report the issue on our GitHub repository so we can make a fix and have your games better detected in the future.",
    Background = new SolidColorBrush(Microsoft.UI.Colors.Transparent),
};

In the old v7 version it would generate a window that would look like,

markdowntextblock-original

After moving to CommunityToolkit.Labs.WinUI.Controls.MarkdownTextBlock, version 0.1.240906-build.1745 it now looks like,

markdowntextblock-new

Looking at the examples I could see they were also using a MarkdownConfig object. After I created an empty one of those and applied to to the Config property I now get.

markdowntextblock-new-config

As @JoeTomkinson said, newlines and bullet spacing is broken.

Looking at the examples again I can see here is a bunch of &nbsp; symbols. So adding those in I get this code,

var markdownTextBlock = new MarkdownTextBlock()
{
    Text = @"DLSS Swapper should find games from your installed game libraries automatically. If your game is not listed there may be a few settings preventing it. Please check:

&nbsp;

- &nbsp; Games list filter is not set to ""Hide non-DLSS games""
- &nbsp; Specific game library is enabled in settings

&nbsp;

If you have checked these and your game is still not showing up there may be a bug. We would appreciate it if you could report the issue on our GitHub repository so we can make a fix and have your games better detected in the future.",
    Background = new SolidColorBrush(Microsoft.UI.Colors.Transparent),
    Config = new MarkdownConfig(),
};

which produces this output,

markdowntextblock-new-config-nbsp

It's certainly more readable, and for my in app markdown I can modify text or swap to a formatted textblock of some sort. But I also pull in release notes from GitHub which would then need the &nbsp; treatment throughout it which may then display incorrectly for older versions of the app, and then display incorrectly again when MarkdownTextBlock is more mature.

Before this goes live it would also be good to get MarkdownConfig set by default to make the migration process easier.

EDIT: I was curious what changelog looks like,

Old:

markdowntextblock-old-changelog

New:

markdowntextblock-new-changelog