You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
4.8 KiB
Python

import re
from textwrap import indent
from typing import Any, Dict, Iterable, cast
from ..core import BaseRenderer, BlockState
from ..util import strip_end
from ._list import render_list
fenced_re = re.compile(r"^[`~]+", re.M)
class MarkdownRenderer(BaseRenderer):
"""A renderer to re-format Markdown text."""
NAME = "markdown"
def __call__(self, tokens: Iterable[Dict[str, Any]], state: BlockState) -> str:
out = self.render_tokens(tokens, state)
# special handle for line breaks
out += "\n\n".join(self.render_referrences(state)) + "\n"
return strip_end(out)
def render_referrences(self, state: BlockState) -> Iterable[str]:
ref_links = state.env["ref_links"]
for key in ref_links:
attrs = ref_links[key]
text = "[" + attrs["label"] + "]: " + attrs["url"]
title = attrs.get("title")
if title:
text += ' "' + title + '"'
yield text
def render_children(self, token: Dict[str, Any], state: BlockState) -> str:
children = token["children"]
return self.render_tokens(children, state)
def text(self, token: Dict[str, Any], state: BlockState) -> str:
return cast(str, token["raw"])
def emphasis(self, token: Dict[str, Any], state: BlockState) -> str:
return "*" + self.render_children(token, state) + "*"
def strong(self, token: Dict[str, Any], state: BlockState) -> str:
return "**" + self.render_children(token, state) + "**"
def link(self, token: Dict[str, Any], state: BlockState) -> str:
label = cast(str, token.get("label"))
text = self.render_children(token, state)
out = "[" + text + "]"
if label:
return out + "[" + label + "]"
attrs = token["attrs"]
url = attrs["url"]
title = attrs.get("title")
if text == url and not title:
return "<" + text + ">"
elif "mailto:" + text == url and not title:
return "<" + text + ">"
out += "("
if "(" in url or ")" in url:
out += "<" + url + ">"
else:
out += url
if title:
out += ' "' + title + '"'
return out + ")"
def image(self, token: Dict[str, Any], state: BlockState) -> str:
return "!" + self.link(token, state)
def codespan(self, token: Dict[str, Any], state: BlockState) -> str:
return "`" + cast(str, token["raw"]) + "`"
def linebreak(self, token: Dict[str, Any], state: BlockState) -> str:
return " \n"
def softbreak(self, token: Dict[str, Any], state: BlockState) -> str:
return "\n"
def blank_line(self, token: Dict[str, Any], state: BlockState) -> str:
return ""
def inline_html(self, token: Dict[str, Any], state: BlockState) -> str:
return cast(str, token["raw"])
def paragraph(self, token: Dict[str, Any], state: BlockState) -> str:
text = self.render_children(token, state)
return text + "\n\n"
def heading(self, token: Dict[str, Any], state: BlockState) -> str:
level = cast(int, token["attrs"]["level"])
marker = "#" * level
text = self.render_children(token, state)
return marker + " " + text + "\n\n"
def thematic_break(self, token: Dict[str, Any], state: BlockState) -> str:
return "***\n\n"
def block_text(self, token: Dict[str, Any], state: BlockState) -> str:
return self.render_children(token, state) + "\n"
def block_code(self, token: Dict[str, Any], state: BlockState) -> str:
attrs = token.get("attrs", {})
info = cast(str, attrs.get("info", ""))
code = cast(str, token["raw"])
if code and code[-1] != "\n":
code += "\n"
marker = token.get("marker")
if not marker:
marker = _get_fenced_marker(code)
marker2 = cast(str, marker)
return marker2 + info + "\n" + code + marker2 + "\n\n"
def block_quote(self, token: Dict[str, Any], state: BlockState) -> str:
text = indent(self.render_children(token, state), "> ", lambda _: True)
text = text.rstrip("> \n")
return text + "\n\n"
def block_html(self, token: Dict[str, Any], state: BlockState) -> str:
return cast(str, token["raw"]) + "\n\n"
def block_error(self, token: Dict[str, Any], state: BlockState) -> str:
return ""
def list(self, token: Dict[str, Any], state: BlockState) -> str:
return render_list(self, token, state)
def _get_fenced_marker(code: str) -> str:
found = fenced_re.findall(code)
if not found:
return "```"
ticks = [] # `
waves = [] # ~
for s in found:
if s[0] == "`":
ticks.append(len(s))
else:
waves.append(len(s))
if not ticks:
return "```"
if not waves:
return "~~~"
return "`" * (max(ticks) + 1)