52 lines
1.5 KiB
Python
52 lines
1.5 KiB
Python
from typing import cast
|
|
from markdown_it import MarkdownIt
|
|
from markdown_it.token import Token as Token
|
|
from pygments import highlight as pygments_highlight
|
|
from pygments.formatters import HtmlFormatter
|
|
from pygments.lexers import get_lexer_by_name
|
|
from pygments.lexers.special import TextLexer
|
|
from pygments.util import ClassNotFound
|
|
from markupsafe import Markup
|
|
|
|
text_lexer = TextLexer()
|
|
html_formatter = HtmlFormatter()
|
|
|
|
|
|
class MarkdownError(RuntimeError):
|
|
pass
|
|
|
|
|
|
def highlight_style() -> str:
|
|
style = html_formatter.get_style_defs(".highlight") # type: ignore[no-untyped-call]
|
|
return cast(str, style)
|
|
|
|
|
|
def highlight(code: str, lang: str, attrs: str) -> str:
|
|
try:
|
|
lexer = get_lexer_by_name(lang) if lang else text_lexer
|
|
except ClassNotFound:
|
|
lexer = text_lexer
|
|
return pygments_highlight(code, lexer, html_formatter)
|
|
|
|
|
|
md = MarkdownIt(options_update={"highlight": highlight})
|
|
|
|
|
|
def render_summary(tokens: list[Token]) -> Markup:
|
|
if not tokens or tokens[0].type != "paragraph_open" or tokens[0].hidden:
|
|
raise MarkdownError(
|
|
"Body does not start with a paragraph; set 'summary' in frontmatter"
|
|
)
|
|
for j in range(1, len(tokens)):
|
|
if tokens[j].type == "paragraph_close":
|
|
return Markup(md.renderer.render(tokens[1:j], md.options, {}))
|
|
raise MarkdownError("Content has no paragraph to use as summary")
|
|
|
|
|
|
def render(tokens: list[Token]) -> Markup:
|
|
return Markup(md.renderer.render(tokens, md.options, {}))
|
|
|
|
|
|
def parse(body: str) -> list[Token]:
|
|
return md.parse(body)
|