1from html.parser import HTMLParser
2
3import mistune
4from pygments import highlight
5from pygments.formatters import html
6from pygments.lexers import get_lexer_by_name
7
8from plain.utils.text import slugify
9
10
11class PagesRenderer(mistune.HTMLRenderer):
12 def heading(self, text, level, **attrs):
13 """Automatically add an ID to headings if one is not provided."""
14
15 if "id" not in attrs:
16 inner_text = get_inner_text(text)
17 inner_text = inner_text.replace(
18 ".", "-"
19 ) # Replace dots with hyphens (slugify won't)
20 attrs["id"] = slugify(inner_text)
21
22 return super().heading(text, level, **attrs)
23
24 def block_code(self, code, info=None):
25 """Highlight code blocks using Pygments."""
26
27 if info:
28 lexer = get_lexer_by_name(info, stripall=True)
29 formatter = html.HtmlFormatter(wrapcode=True)
30 return highlight(code, lexer, formatter)
31
32 return "<pre><code>" + mistune.escape(code) + "</code></pre>"
33
34
35def render_markdown(content):
36 renderer = PagesRenderer(escape=False)
37 markdown = mistune.create_markdown(
38 renderer=renderer, plugins=["strikethrough", "table"]
39 )
40 return markdown(content)
41
42
43class InnerTextParser(HTMLParser):
44 def __init__(self):
45 super().__init__()
46 self.text_content = []
47
48 def handle_data(self, data):
49 # Collect all text data
50 self.text_content.append(data.strip())
51
52
53def get_inner_text(html_content):
54 parser = InnerTextParser()
55 parser.feed(html_content)
56 return " ".join([text for text in parser.text_content if text])