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
		
	
			
		
		
	
	
			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)
 |