| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150 |
- 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: str = 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)
|