Repository: Textualize/rich

Issue:
[BUG] Rich markdown printed to console only shows a single link in a column/ table cell
- [x] I've checked [docs](https://rich.readthedocs.io/en/latest/introduction.html) and [closed issues](https://github.com/Textualize/rich/issues?q=is%3Aissue+is%3Aclosed) for possible solutions.
- [x] I can't find my issue in the [FAQ](https://github.com/Textualize/rich/blob/master/FAQ.md).

**Describe the bug**

I use Rich to format a pandas data frame with multiple links in some column to markdown and show it in the terminal. However, when there are more than a single link in a column rich only shows the last one?

```python
from rich.console import Console
from rich.markdown import Markdown

import pandas as pd

md_table = pd.DataFrame(
    {
        "links": ["[page1](https://www.example.com/page1), [page2](https://www.example.com/page2)"]
    }
).to_markdown()

console = Console()
console.print(Markdown(md_table))
```

Output:
```bash
      links  
 ━━━━━━━━━━━ 
  0   page2  
```

Expected
```
      links  
 ━━━━━━━━━━━ 
  0   page1, page2  
```

If I write the markdown to a file I get the expected table with multiple links...

**Platform**

platform="Darwin"
rich==13.5.2


</details>



Discussion:
Thank you for your issue. Give us a little time to review it.

PS. You might want to check the [FAQ](https://github.com/textualize/rich/blob/master/FAQ.md) if you haven't done so already.

This is an automated reply, generated by [FAQtory](https://github.com/willmcgugan/faqtory)
Could you post the Markdown it generates?
```
|    | links                                                                          |
|---:|:-------------------------------------------------------------------------------|
|  0 | [page1](https://www.example.com/page1), [page2](https://www.example.com/page2) |
```
I actually discovered that the problem is that if there is a link in a table, only the link (and in case of multiple links the last one) is rendered.

For instance:

```python
from rich.console import Console
from rich.markdown import Markdown

import pandas as pd

md_table = pd.DataFrame(
    {
        "links": ["some text [page1](https://www.example.com/page1 )"]
    }
).to_markdown()

console = Console()
console.print(Markdown(md_table)) 
```

will result in:
```bash
      links  
 ━━━━━━━━━━━ 
  0   page1  
```

Thus, swallowing "some text" that was also present in this table cell. Not sure if this gives a clue about what might be wrong?
This may also be present in Textual.
> This may also be present in Textual.

If I'm reading the issue correctly, it seems to be working fine in Textual (as seen via Frogmouth):

![Screenshot 2023-08-31 at 13 13 57](https://github.com/Textualize/rich/assets/28237/3a6e2c59-6b87-4816-8a8b-134b7c36c065)

viewing this Markdown:

```markdown
| One                                                        | Two       | Three                                 |
|------------------------------------------------------------|-----------|---------------------------------------|
| Some test [and then a link](https://textualize.io/)        | Just Text | [Just a link](https://textualize.io/) |
| Text [then a link](https://blog.davep.org/) then more text | Just Text | Some final text                       |
```

Good to know. We must be doing something different in Rich.
Oh nice. Wondering: Can I use `textual` to render my Markdown before I hand it over to `console.print()`until this bug is fixed? I haven't used `textual` before but don't mind this extra dependency for the moment...
You can certainly use Textual, but it works differently from `console.print`. It creates an interactive window inside the terminal.
Consider this markdown file:

```md
[page 1](https://mathspp.com)

| links |
| - |
| asdfasdf  [page 1](https://mathspp.com) |
```

Running `python -m rich.markdown file.md` will show the first link just fine but the one on the table doesn't:

```
page 1 (https://mathspp.com)


  links
 ━━━━━━━
  )
```

Putting text on the right of the table link makes the link go away and only displays that text:

<details>
<summary <code>file.md</code> with text on the right. </summary>

```md
[page 1](https://mathspp.com)

| links |
| - |
| asdfasdf  [page 1](https://mathspp.com) right |
```
</details>
<details>
<summary> Output of <code>python -m rich.markdown file.md</code> </summary>

```
page 1 (https://mathspp.com)


  links
 ━━━━━━━━
   right
```

</details>

---

Running with `--hyperlinks` does a slightly better job, but it still drops things.

### With text on the right

<details>
<summary <code>file.md</code> with text on the right. </summary>

```md
[page 1](https://mathspp.com)

| links |
| - |
| asdfasdf  [page 1](https://mathspp.com) some text |
```

</details>
<details>
<summary> Output of <code>python -m rich.markdown file.md -y</code> </summary>
(The “page 1” has the proper link in the terminal)

```
page 1


  links
 ━━━━━━━━━━━━
   some text
```
</details>

---

### Without text on the right

<details>
<summary <code>file.md</code> without text on the right. </summary>

```md
[page 1](https://mathspp.com)

| links |
| - |
| asdfasdf  [page 1](https://mathspp.com) |
```
</details>
<details>
<summary> Output of <code>python -m rich.markdown file.md -y</code> </summary>
(The output has the proper links in the terminal)

```
page 1


  links
 ━━━━━━━━
  page 1
```
</details>
Turns out we're seeing this interesting interaction with a bunch of inline styles in markdown.

The markdown below gets rendered into the output shown after that:

```md
| stuff |
| - |
| asdfasdf  [page 1](https://mathspp.com) some text |
| This is `some code` here |
| This is `some code` |
| Can I set this to _italics_? |
```

```
  stuff
 ━━━━━━━━━━━━
   some text
   here
  some code
  ?
```
I've tracked the issue down to `rich/markdown.py::TableDataElement.on_text`, which should append to its content instead of setting it directly.

Resolving patch:
diff --git a/rich/markdown.py b/rich/markdown.py
--- a/rich/markdown.py
+++ b/rich/markdown.py
@@ -314,7 +314,7 @@ class TableDataElement(MarkdownElement):
 
     @classmethod
     def create(cls, markdown: "Markdown", token: Token) -> "MarkdownElement":
-        style = str(token.attrs.get("style" "")) or ""
+        style = str(token.attrs.get("style")) or ""
 
         justify: JustifyMethod
         if "text-align:right" in style:
@@ -330,15 +330,13 @@ def create(cls, markdown: "Markdown", token: Token) -> "MarkdownElement":
         return cls(justify=justify)
 
     def __init__(self, justify: JustifyMethod) -> None:
-        self.content: TextType = ""
+        self.content: Text = Text("", justify=justify)
         self.justify = justify
 
     def on_text(self, context: "MarkdownContext", text: TextType) -> None:
-        plain = text.plain if isinstance(text, Text) else text
-        style = text.style if isinstance(text, Text) else ""
-        self.content = Text(
-            plain, justify=self.justify, style=context.style_stack.current
-        )
+        text = Text(text) if isinstance(text, str) else text
+        text.stylize(context.current_style)
+        self.content.append_text(text)
 
 
 class ListElement(MarkdownElement):


Test patch:
diff --git a/tests/test_markdown.py b/tests/test_markdown.py
--- a/tests/test_markdown.py
+++ b/tests/test_markdown.py
@@ -121,7 +121,7 @@ def test_markdown_table():
         """\
 | Year |                      Title                       | Director          |  Box Office (USD) |
 |------|:------------------------------------------------:|:------------------|------------------:|
-| 1982 |            *E.T. the Extra-Terrestrial*            | Steven Spielberg  |    $792.9 million |
+| 1982 |            *E.T. the Extra-Terrestrial*          | Steven Spielberg  |    $792.9 million |
 | 1980 |  Star Wars: Episode V – The Empire Strikes Back  | Irvin Kershner    |    $538.4 million |
 | 1983 |    Star Wars: Episode VI – Return of the Jedi    | Richard Marquand  |    $475.1 million |
 | 1981 |             Raiders of the Lost Ark              | Steven Spielberg  |    $389.9 million |
@@ -129,7 +129,40 @@ def test_markdown_table():
 """
     )
     result = render(markdown)
-    expected = "\n                                                                                               \n \x1b[1m \x1b[0m\x1b[1mYear\x1b[0m\x1b[1m \x1b[0m \x1b[1m \x1b[0m\x1b[1m                    Title                     \x1b[0m\x1b[1m \x1b[0m \x1b[1m \x1b[0m\x1b[1mDirector        \x1b[0m\x1b[1m \x1b[0m \x1b[1m \x1b[0m\x1b[1mBox Office (USD)\x1b[0m\x1b[1m \x1b[0m \n ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \n  1982   \x1b[3m          E.T. the Extra-Terrestrial          \x1b[0m   Steven Spielberg     $792.9 million  \n  1980   Star Wars: Episode V – The Empire Strikes Back   Irvin Kershner       $538.4 million  \n  1983     Star Wars: Episode VI – Return of the Jedi     Richard Marquand     $475.1 million  \n  1981              Raiders of the Lost Ark               Steven Spielberg     $389.9 million  \n  1984        Indiana Jones and the Temple of Doom        Steven Spielberg     $333.1 million  \n                                                                                               \n"
+    expected = "\n                                                                                               \n \x1b[1m \x1b[0m\x1b[1mYear\x1b[0m\x1b[1m \x1b[0m \x1b[1m \x1b[0m\x1b[1m                    Title                     \x1b[0m\x1b[1m \x1b[0m \x1b[1m \x1b[0m\x1b[1mDirector        \x1b[0m\x1b[1m \x1b[0m \x1b[1m \x1b[0m\x1b[1mBox Office (USD)\x1b[0m\x1b[1m \x1b[0m \n ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \n  1982             \x1b[3mE.T. the Extra-Terrestrial\x1b[0m             Steven Spielberg     $792.9 million  \n  1980   Star Wars: Episode V – The Empire Strikes Back   Irvin Kershner       $538.4 million  \n  1983     Star Wars: Episode VI – Return of the Jedi     Richard Marquand     $475.1 million  \n  1981              Raiders of the Lost Ark               Steven Spielberg     $389.9 million  \n  1984        Indiana Jones and the Temple of Doom        Steven Spielberg     $333.1 million  \n                                                                                               \n"
+    assert result == expected
+
+
+def test_inline_styles_in_table():
+    """Regression test for https://github.com/Textualize/rich/issues/3115"""
+    markdown = Markdown(
+        """\
+| Year | This **column** displays _the_ movie _title_ ~~description~~ | Director          |  Box Office (USD) |
+|------|:----------------------------------------------------------:|:------------------|------------------:|
+| 1982 | *E.T. the Extra-Terrestrial* ([Wikipedia article](https://en.wikipedia.org/wiki/E.T._the_Extra-Terrestrial)) | Steven Spielberg  |    $792.9 million |
+| 1980 |  Star Wars: Episode V – The *Empire* **Strikes** ~~Back~~  | Irvin Kershner    |    $538.4 million |
+"""
+    )
+    result = render(markdown)
+    expected = "\n                                                                                                 \n \x1b[1m \x1b[0m\x1b[1mYear\x1b[0m\x1b[1m \x1b[0m \x1b[1m \x1b[0m\x1b[1mThis \x1b[0m\x1b[1mcolumn\x1b[0m\x1b[1m displays \x1b[0m\x1b[1;3mthe\x1b[0m\x1b[1m movie \x1b[0m\x1b[1;3mtitle\x1b[0m\x1b[1m \x1b[0m\x1b[1;9mdescription\x1b[0m\x1b[1m \x1b[0m \x1b[1m \x1b[0m\x1b[1mDirector        \x1b[0m\x1b[1m \x1b[0m \x1b[1m \x1b[0m\x1b[1mBox Office (USD)\x1b[0m\x1b[1m \x1b[0m \n ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \n  1982    \x1b[3mE.T. the Extra-Terrestrial\x1b[0m (\x1b]8;id=0;foo\x1b\\\x1b[4;34mWikipedia article\x1b[0m\x1b]8;;\x1b\\)    Steven Spielberg     $792.9 million  \n  1980    Star Wars: Episode V – The \x1b[3mEmpire\x1b[0m \x1b[1mStrikes\x1b[0m \x1b[9mBack\x1b[0m    Irvin Kershner       $538.4 million  \n                                                                                                 \n"
+    assert result == expected
+
+
+def test_inline_styles_with_justification():
+    """Regression test for https://github.com/Textualize/rich/issues/3115
+
+    In particular, this tests the interaction between the change that was made to fix
+    #3115 and column text justification.
+    """
+    markdown = Markdown(
+        """\
+| left | center | right |
+| :- | :-: | -: |
+| This is a long row | because it contains | a fairly long sentence. |
+| a*b* _c_ ~~d~~ e | a*b* _c_ ~~d~~ e | a*b* _c_ ~~d~~ e |"""
+    )
+    result = render(markdown)
+    expected = "\n                                                                      \n \x1b[1m \x1b[0m\x1b[1mleft              \x1b[0m\x1b[1m \x1b[0m \x1b[1m \x1b[0m\x1b[1m      center       \x1b[0m\x1b[1m \x1b[0m \x1b[1m \x1b[0m\x1b[1m                  right\x1b[0m\x1b[1m \x1b[0m \n ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ \n  This is a long row   because it contains   a fairly long sentence.  \n  a\x1b[3mb\x1b[0m \x1b[3mc\x1b[0m \x1b[9md\x1b[0m e                  a\x1b[3mb\x1b[0m \x1b[3mc\x1b[0m \x1b[9md\x1b[0m e                        a\x1b[3mb\x1b[0m \x1b[3mc\x1b[0m \x1b[9md\x1b[0m e  \n                                                                      \n"
     assert result == expected