Skip to content

Markdown Widget crashes on close tag in heading #3689

@GuutBoy

Description

@GuutBoy

The Markdown Widget crashes if there is a closing rich tag in a heading. I.e., if there is a string like [/some_text] in a markdown heading. This can become problematic when using Markdown extensions to WikiLinks and having something linke [[/test.md]] in a heading.

Note in regular text this issue does not seem to be present. I have only seen this for headings.

Example

Running this

from textual.app import App, ComposeResult
from textual.widgets import MarkdownViewer

EXAMPLE_MARKDOWN = """\
# Markdown [/test]
"""


class MarkdownExampleApp(App):
    def compose(self) -> ComposeResult:
        yield MarkdownViewer(EXAMPLE_MARKDOWN, show_table_of_contents=True)


if __name__ == "__main__":
    app = MarkdownExampleApp()
    app.run()

will produce this error:

python3 markdown.py
╭──────────────────────────────────────────────────────────────────────────────────────────── Traceback (most recent call last) ─────────────────────────────────────────────────────────────────────────────────────────────╮
│ /usr/local/lib/python3.11/site-packages/textual/widgets/_markdown.py:1056 in _on_markdown_table_of_contents_updated                                                                                                        │
│                                                                                                                                                                                                                            │
│   1053 │   ) -> None:                                                                           ╭────────────── locals ──────────────╮                                                                                     │
│   1054 │   │   self.query_one(                                                                  │ message = TableOfContentsUpdated() │                                                                                     │
│   1055 │   │   │   MarkdownTableOfContents                                                      │    self = MarkdownViewer()         │                                                                                     │
│ ❱ 1056 │   │   ).table_of_contents = message.table_of_contents                                  ╰────────────────────────────────────╯                                                                                     │
│   1057 │   │   message.stop()                                                                                                                                                                                              │
│   1058 │                                                                                                                                                                                                                   │
│   1059 │   def _on_markdown_table_of_contents_selected(                                                                                                                                                                    │
│                                                                                                                                                                                                                            │
│ /usr/local/lib/python3.11/site-packages/textual/widgets/_markdown.py:920 in watch_table_of_contents                                                                                                                        │
│                                                                                                                                                                                                                            │
│    917 │                                                                                        ╭──────────────────────── locals ─────────────────────────╮                                                                │
│    918 │   def watch_table_of_contents(self, table_of_contents: TableOfContentsType) -> None:   │              self = MarkdownTableOfContents()           │                                                                │
│    919 │   │   """Triggered when the table of contents changes."""                              │ table_of_contents = [(1, 'Markdown [/test]', 'block1')] │                                                                │
│ ❱  920 │   │   self.set_table_of_contents(table_of_contents)                                    ╰─────────────────────────────────────────────────────────╯                                                                │
│    921 │                                                                                                                                                                                                                   │
│    922 │   def set_table_of_contents(self, table_of_contents: TableOfContentsType) -> None:                                                                                                                                │
│    923 │   │   """Set the table of contents.                                                                                                                                                                               │
│                                                                                                                                                                                                                            │
│ /usr/local/lib/python3.11/site-packages/textual/widgets/_markdown.py:940 in set_table_of_contents                                                                                                                          │
│                                                                                                                                                                                                                            │
│    937 │   │   │   │   │   node.allow_expand = True                                             ╭──────────────────────── locals ─────────────────────────╮                                                                │
│    938 │   │   │   │   else:                                                                    │          block_id = 'block1'                            │                                                                │
│    939 │   │   │   │   │   node = node.add(NUMERALS[level], expand=True)                        │             level = 1                                   │                                                                │
│ ❱  940 │   │   │   node.add_leaf(f"[dim]{NUMERALS[level]}[/] {name}", {"block_id": block_id})   │              name = 'Markdown [/test]'                  │                                                                │
│    941 │                                                                                        │              node = TreeNode('TOC', None)               │                                                                │
│    942 │   async def _on_tree_node_selected(self, message: Tree.NodeSelected) -> None:          │              root = TreeNode('TOC', None)               │                                                                │
│    943 │   │   node_data = message.node.data                                                    │              self = MarkdownTableOfContents()           │                                                                │
│                                                                                                 │ table_of_contents = [(1, 'Markdown [/test]', 'block1')] │                                                                │
│                                                                                                 │              tree = Tree()                              │                                                                │
│                                                                                                 ╰─────────────────────────────────────────────────────────╯                                                                │
│                                                                                                                                                                                                                            │
│ /usr/local/lib/python3.11/site-packages/textual/widgets/_tree.py:352 in add_leaf                                                                                                                                           │
│                                                                                                                                                                                                                            │
│    349 │   │   Returns:                                                                         ╭─────────────── locals ───────────────╮                                                                                   │
│    350 │   │   │   New node.                                                                    │  data = {'block_id': 'block1'}       │                                                                                   │
│    351 │   │   """                                                                              │ label = '[dim]Ⅰ[/] Markdown [/test]' │                                                                                   │
│ ❱  352 │   │   node = self.add(label, data, expand=False, allow_expand=False)                   │  self = TreeNode('TOC', None)        │                                                                                   │
│    353 │   │   return node                                                                      ╰──────────────────────────────────────╯                                                                                   │
│    354 │                                                                                                                                                                                                                   │
│    355 │   class RemoveRootError(Exception):                                                                                                                                                                               │
│                                                                                                                                                                                                                            │
│ /usr/local/lib/python3.11/site-packages/textual/widgets/_tree.py:331 in add                                                                                                                                                │
│                                                                                                                                                                                                                            │
│    328 │   │   Returns:                                                                         ╭────────────────── locals ───────────────────╮                                                                            │
│    329 │   │   │   A new Tree node                                                              │ allow_expand = False                        │                                                                            │
│    330 │   │   """                                                                              │         data = {'block_id': 'block1'}       │                                                                            │
│ ❱  331 │   │   text_label = self._tree.process_label(label)                                     │       expand = False                        │                                                                            │
│    332 │   │   node = self._tree._add_node(self, text_label, data)                              │        label = '[dim]Ⅰ[/] Markdown [/test]' │                                                                            │
│    333 │   │   node._expanded = expand                                                          │         self = TreeNode('TOC', None)        │                                                                            │
│    334 │   │   node._allow_expand = allow_expand                                                ╰─────────────────────────────────────────────╯                                                                            │
│                                                                                                                                                                                                                            │
│ /usr/local/lib/python3.11/site-packages/textual/widgets/_tree.py:635 in process_label                                                                                                                                      │
│                                                                                                                                                                                                                            │
│    632 │   │   │   A Rich Text object.                                                          ╭─────────────── locals ───────────────╮                                                                                   │
│    633 │   │   """                                                                              │ label = '[dim]Ⅰ[/] Markdown [/test]' │                                                                                   │
│    634 │   │   if isinstance(label, str):                                                       │  self = Tree()                       │                                                                                   │
│ ❱  635 │   │   │   text_label = Text.from_markup(label)                                         ╰──────────────────────────────────────╯                                                                                   │
│    636 │   │   else:                                                                                                                                                                                                       │
│    637 │   │   │   text_label = label                                                                                                                                                                                      │
│    638 │   │   first_line = text_label.split()[0]                                                                                                                                                                          │
│                                                                                                                                                                                                                            │
│ /usr/local/lib/python3.11/site-packages/rich/text.py:286 in from_markup                                                                                                                                                    │
│                                                                                                                                                                                                                            │
│ /usr/local/lib/python3.11/site-packages/rich/markup.py:167 in render                                                                                                                                                       │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
MarkupError: closing tag '[/test]' at position 19 doesn't match any open tag```

Metadata

Metadata

Labels

TaskbugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions