Skip to content

Commit

Permalink
Added Markdown renderer class with unit tests and documentation (#162).
Browse files Browse the repository at this point in the history
  • Loading branch information
anderskaplan committed Jun 7, 2023
1 parent 05d6c8c commit 61cec4a
Show file tree
Hide file tree
Showing 4 changed files with 1,152 additions and 0 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ main package:
* HTML
* LaTeX
* AST (Abstract Syntax Tree; handy for debugging the parsing process)
* Markdown (Can be used to reflow the text, or make other types of automated
changes to Markdown documents)

Renderers for the following output formats can be found
in the [contrib][contrib] package:
Expand Down Expand Up @@ -108,6 +110,17 @@ with open('foo.md', 'r') as fin:
rendered = mistletoe.markdown(fin, LaTeXRenderer)
```

To reflow the text in a Markdown document with a max line length of 20 characters:

```python
import mistletoe
from mistletoe.markdown_renderer import MarkdownRenderer

with open('dev-guide.md', 'r') as fin:
with MarkdownRenderer(max_line_length=20) as renderer:
print(renderer.render(mistletoe.Document(fin)))
```

Finally, here's how you would manually specify extra tokens via a renderer.
In the following example, we use `HTMLRenderer` to render
the AST. The renderer itself adds `HTMLBlock` and `HTMLSpan` tokens to the parsing
Expand Down
56 changes: 56 additions & 0 deletions dev-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,59 @@ with open('foo.md', 'r') as fin:
For more info, take a look at the `base_renderer` module in mistletoe.
The docstrings might give you a more granular idea of customizing mistletoe
to your needs.

## Markdown to Markdown

Suppose you have some Markdown that you want to process and then output
as Markdown again. Thanks to the text-like nature of Markdown, it is often
possible to do this with text search-and-replace tools... but not always. For
example, if you want to replace a text fragment in the plain text, but not
in the embedded code samples, then the search-and-replace approach won't work.

In this case you can use mistletoe's `MarkdownRenderer`:
1. Parse Markdown to an AST tree (usually held in a `Document` token).
2. Make modifications to the AST tree.
3. Render back to Markdown using `MarkdownRenderer.render()`.

Here is an example of how you can make text replacements in selected parts
of the AST:

```python
import mistletoe
from mistletoe.block_token import BlockToken, Heading, Paragraph, SetextHeading
from mistletoe.markdown_renderer import MarkdownRenderer
from mistletoe.span_token import InlineCode, RawText, SpanToken

def update_text(token: SpanToken):
"""Update the text contents of a span token and its children.
`InlineCode` tokens are left unchanged."""
if isinstance(token, RawText):
token.content = token.content.replace("mistletoe", "The Amazing mistletoe")

if not isinstance(token, InlineCode) and hasattr(token, "children"):
for child in token.children:
update_text(child)

def update_block(token: BlockToken):
"""Update the text contents of paragraphs and headings within this block,
and recursively within its children."""
if isinstance(token, (Paragraph, SetextHeading, Heading)):
for child in token.children:
update_text(child)

for child in token.children:
if isinstance(child, BlockToken):
update_block(child)

with open("README.md", "r") as fin:
with MarkdownRenderer() as renderer:
document = mistletoe.Document(fin)
update_block(document)
md = renderer.render(document)
print(md)
```

If you're making large changes, so that the formatting of the document is
affected, then it can be useful to also have the text reflowed. This can
be done by specifying a `max_line_length` parameter in the call to the
`MarkdownRenderer` constructor.
Loading

0 comments on commit 61cec4a

Please sign in to comment.