Allow customizing the markdown parser (#2075)

* Allow customizing the markdown parser

For instance, code using Markdown might wish to create a markdown
parser that does not parse embedded HTML:
```py
def parser_factory():
    parser = MarkdownIt("gfm-like")
    parser.options["html"] = False
    return parser
```

* blacken

* Implement requested changes

* fix AttributeError
This commit is contained in:
Jeff Epler
2023-03-18 06:19:05 -05:00
committed by GitHub
parent f5e779c4c4
commit d999c69261
2 changed files with 16 additions and 3 deletions

View File

@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Unreleased ## Unreleased
### Added
- Added `parser_factory` argument to `Markdown` and `MarkdownViewer` constructors https://github.com/Textualize/textual/pull/2075
### Changed ### Changed
- Dropped "loading-indicator--dot" component style from LoadingIndicator https://github.com/Textualize/textual/pull/2050 - Dropped "loading-indicator--dot" component style from LoadingIndicator https://github.com/Textualize/textual/pull/2050

View File

@@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
from pathlib import Path, PurePath from pathlib import Path, PurePath
from typing import Iterable from typing import Iterable, Callable
from markdown_it import MarkdownIt from markdown_it import MarkdownIt
from rich import box from rich import box
@@ -541,6 +541,7 @@ class Markdown(Widget):
name: str | None = None, name: str | None = None,
id: str | None = None, id: str | None = None,
classes: str | None = None, classes: str | None = None,
parser_factory: Callable[[], MarkdownIt] | None = None,
): ):
"""A Markdown widget. """A Markdown widget.
@@ -549,9 +550,11 @@ class Markdown(Widget):
name: The name of the widget. name: The name of the widget.
id: The ID of the widget in the DOM. id: The ID of the widget in the DOM.
classes: The CSS classes of the widget. classes: The CSS classes of the widget.
parser_factory: A factory function to return a configured MarkdownIt instance. If `None`, a "gfm-like" parser is used.
""" """
super().__init__(name=name, id=id, classes=classes) super().__init__(name=name, id=id, classes=classes)
self._markdown = markdown self._markdown = markdown
self._parser_factory = parser_factory
class TableOfContentsUpdated(Message, bubble=True): class TableOfContentsUpdated(Message, bubble=True):
"""The table of contents was updated.""" """The table of contents was updated."""
@@ -606,7 +609,11 @@ class Markdown(Widget):
""" """
output: list[MarkdownBlock] = [] output: list[MarkdownBlock] = []
stack: list[MarkdownBlock] = [] stack: list[MarkdownBlock] = []
parser = MarkdownIt("gfm-like") parser = (
MarkdownIt("gfm-like")
if self._parser_factory is None
else self._parser_factory()
)
content = Text() content = Text()
block_id: int = 0 block_id: int = 0
@@ -831,6 +838,7 @@ class MarkdownViewer(VerticalScroll, can_focus=True, can_focus_children=True):
name: str | None = None, name: str | None = None,
id: str | None = None, id: str | None = None,
classes: str | None = None, classes: str | None = None,
parser_factory: Callable[[], MarkdownIt] | None = None,
): ):
"""Create a Markdown Viewer object. """Create a Markdown Viewer object.
@@ -840,10 +848,12 @@ class MarkdownViewer(VerticalScroll, can_focus=True, can_focus_children=True):
name: The name of the widget. name: The name of the widget.
id: The ID of the widget in the DOM. id: The ID of the widget in the DOM.
classes: The CSS classes of the widget. classes: The CSS classes of the widget.
parser_factory: A factory function to return a configured MarkdownIt instance. If `None`, a "gfm-like" parser is used.
""" """
super().__init__(name=name, id=id, classes=classes) super().__init__(name=name, id=id, classes=classes)
self.show_table_of_contents = show_table_of_contents self.show_table_of_contents = show_table_of_contents
self._markdown = markdown self._markdown = markdown
self._parser_factory = parser_factory
@property @property
def document(self) -> Markdown: def document(self) -> Markdown:
@@ -882,7 +892,7 @@ class MarkdownViewer(VerticalScroll, can_focus=True, can_focus_children=True):
def compose(self) -> ComposeResult: def compose(self) -> ComposeResult:
yield MarkdownTableOfContents() yield MarkdownTableOfContents()
yield Markdown() yield Markdown(parser_factory=self._parser_factory)
def on_markdown_table_of_contents_updated( def on_markdown_table_of_contents_updated(
self, message: Markdown.TableOfContentsUpdated self, message: Markdown.TableOfContentsUpdated