File size: 4,682 Bytes
d1ceb73 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
import re
from typing import Dict, Any
from textwrap import indent
from ._list import render_list
from ..core import BaseRenderer, BlockState
from ..util import strip_end
fenced_re = re.compile(r'^[`~]+', re.M)
class MarkdownRenderer(BaseRenderer):
"""A renderer to re-format Markdown text."""
NAME = 'markdown'
def __call__(self, tokens, state: BlockState):
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):
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, state: BlockState):
children = token['children']
return self.render_tokens(children, state)
def text(self, token: Dict[str, Any], state: BlockState) -> str:
return 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 = 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 '`' + 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 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 = 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 = attrs.get('info', '')
code = token['raw']
if code and code[-1] != '\n':
code += '\n'
marker = token.get('marker')
if not marker:
marker = _get_fenced_marker(code)
return marker + info + '\n' + code + marker + '\n\n'
def block_quote(self, token: Dict[str, Any], state: BlockState) -> str:
text = indent(self.render_children(token, state), '> ')
return text + '\n\n'
def block_html(self, token: Dict[str, Any], state: BlockState) -> str:
return 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):
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)
|