Repository: Textualize/textual

Issue:
Markdown renderer does not handle hard line breaks
I have a block of markdown (a so-called poem generated by chatgpt) that doesn't render properly in textual's markdown viewer. Here's a minimal program that shows the problem:
```py
from textual.app import App
from textual.widgets import MarkdownViewer

content = "In the world of social media,  \nWhere trends seem to come and go  \nThere's a symbol that stands out,  \nWith significance that continues to grow.  \n\n(2nd verse)"

class MyApp(App):
    def compose(self):
        yield MarkdownViewer(content)

MyApp().run()
```

By the CommonMark spec, the "<space><space><newline>" should be rendered as a hard line break: https://spec.commonmark.org/0.30/#hard-line-break
.. most markdown renderers, including the one in rich, do this.

```
# Textual Diagnostics

## Versions

| Name    | Value  |
|---------|--------|
| Textual | 0.14.0 |
| Rich    | 13.3.2 |

## Python

| Name           | Value               |
|----------------|---------------------|
| Version        | 3.9.2               |
| Implementation | CPython             |
| Compiler       | GCC 10.2.1 20210110 |
| Executable     | /usr/bin/python3    |

## Operating System

| Name    | Value                                                      |
|---------|------------------------------------------------------------|
| System  | Linux                                                      |
| Release | 6.0.0-0.deb11.2-amd64                                      |
| Version | #1 SMP PREEMPT_DYNAMIC Debian 6.0.3-1~bpo11+1 (2022-10-29) |

## Terminal

| Name                 | Value          |
|----------------------|----------------|
| Terminal Application | *Unknown*      |
| TERM                 | xterm-256color |
| COLORTERM            | truecolor      |
| FORCE_COLOR          | *Not set*      |
| NO_COLOR             | *Not set*      |

## Rich Console options

| Name           | Value               |
|----------------|---------------------|
| size           | width=80, height=24 |
| legacy_windows | False               |
| min_width      | 1                   |
| max_width      | 80                  |
| is_terminal    | False               |
| encoding       | utf-8               |
| max_height     | 24                  |
| justify        | None                |
| overflow       | None                |
| no_wrap        | False               |
| highlight      | None                |
| markup         | None                |
| height         | None                |
```

textual markdown:
![image](https://user-images.githubusercontent.com/1517291/225076819-c4a8a5ed-4a3b-4f0f-a796-80d77706bb16.png)

rich markdown:
![image](https://user-images.githubusercontent.com/1517291/225076962-957a0df2-93b6-4ef3-ad61-4ca9fc49f3d6.png)

gfm:
> In the world of social media,  
> Where trends seem to come and go  
> There's a symbol that stands out,  
> With significance that continues to grow.  
> 
> (2nd verse)



Discussion:
We found the following entries in the [FAQ](https://github.com/textualize/textual/blob/main/FAQ.md) which you may find helpful:


- [Does Textual support images?](https://github.com/textualize/textual/blob/main/FAQ.md#does-textual-support-images)
- [How do I center a widget in a screen?](https://github.com/textualize/textual/blob/main/FAQ.md#how-do-i-center-a-widget-in-a-screen)

Feel free to close this issue if you found an answer in the FAQ. Otherwise, please give us a little time to review.

This is an automated reply, generated by [FAQtory](https://github.com/willmcgugan/faqtory)

Resolving patch:
diff --git a/src/textual/widgets/_markdown.py b/src/textual/widgets/_markdown.py
--- a/src/textual/widgets/_markdown.py
+++ b/src/textual/widgets/_markdown.py
@@ -639,6 +639,8 @@ async def update(self, markdown: str) -> None:
                     for child in token.children:
                         if child.type == "text":
                             content.append(child.content, style_stack[-1])
+                        if child.type == "hardbreak":
+                            content.append("\n")
                         if child.type == "softbreak":
                             content.append(" ")
                         elif child.type == "code_inline":


Test patch: