Table of contents#
Generate heading IDs, render tables of contents, and customize slug behavior for documentation pages.
Use the heading_ids plugin and register the built-in TableOfContents
directive renderer when the table of contents should be declared inside the
Markdown document.
from wenmode import Wenmode
from wenmode.directives import TableOfContents
from wenmode.plugins import heading_ids
from wenmode.rules import AtxHeading, LeafDirective
wen = Wenmode(
[AtxHeading, LeafDirective],
directives=[TableOfContents()],
plugins=[heading_ids],
)
text = '''
::toc{min=2 max=3}
# Title
## Usage
'''
html = wen.render(text)
assert '<nav aria-label="Table of contents" class="toc">' in html
assert '<a href="#usage">Usage</a>' in html
assert '<h2 id="usage">Usage</h2>' in html
You can also build a table of contents manually when you want to place or style it outside the Markdown document.
from wenmode import HTMLRenderer, Wenmode
from wenmode.headings import Slugger, add_heading_ids
from wenmode.toc import collect_toc, render_toc_html
wen = Wenmode()
text = '''
# Title
## Usage
'''
root = wen.parse(text)
add_heading_ids(root, slugger=Slugger(), min_depth=2)
toc = collect_toc(root, min_depth=2, max_depth=3)
html = render_toc_html(toc) + HTMLRenderer().render(root)
assert '<a href="#usage">Usage</a>' in html
Generate heading IDs only#
Use the heading_ids plugin when you want Wenmode to add generated heading IDs
during parsing without rendering a table of contents.
from wenmode import Wenmode
from wenmode.plugins import heading_ids
from wenmode.rules import AtxHeading
wen = Wenmode([AtxHeading], plugins=[heading_ids])
text = '# Hello World'
expected = '''
<h1 id="hello-world">Hello World</h1>
'''
html = wen.render(text)
assert html == expected.lstrip()
For already-parsed trees, use add_heading_ids().
from wenmode import HTMLRenderer, Wenmode
from wenmode.headings import Slugger, add_heading_ids
text = '''
# Title
## Usage
'''
root = Wenmode().parse(text)
add_heading_ids(root, slugger=Slugger(), min_depth=2)
html = HTMLRenderer().render(root)
assert '<h1>Title</h1>' in html
assert '<h2 id="usage">Usage</h2>' in html
Customize heading slugs#
Create a Slugger subclass when your product needs a different heading ID
format. Pass the slugger class to heading_ids.configure() for IDs generated
during parsing.
from wenmode import Wenmode
from wenmode.headings import Slugger
from wenmode.plugins import heading_ids
from wenmode.rules import AtxHeading
class PrefixedSlugger(Slugger):
name = 'prefixed'
def slug(self, value: str) -> str:
return 'section-' + super().slug(value)
wen = Wenmode([AtxHeading], plugins=[heading_ids.configure(PrefixedSlugger)])
html = wen.render('## Install\n\n## Install\n')
assert '<h2 id="section-install">Install</h2>' in html
assert '<h2 id="section-install-1">Install</h2>' in html
For already-parsed trees, pass an instance to add_heading_ids().
from wenmode import HTMLRenderer, Wenmode
from wenmode.headings import Slugger, add_heading_ids
class DocsSlugger(Slugger):
name = 'docs'
def slug(self, value: str) -> str:
return 'docs-' + super().slug(value)
root = Wenmode().parse('## Usage\n')
add_heading_ids(root, slugger=DocsSlugger(), min_depth=2)
html = HTMLRenderer().render(root)
assert '<h2 id="docs-usage">Usage</h2>' in html