summaryrefslogtreecommitdiff
path: root/src/mistune/renderers/markdown.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/mistune/renderers/markdown.py')
-rw-r--r--src/mistune/renderers/markdown.py146
1 files changed, 146 insertions, 0 deletions
diff --git a/src/mistune/renderers/markdown.py b/src/mistune/renderers/markdown.py
new file mode 100644
index 0000000..78334bc
--- /dev/null
+++ b/src/mistune/renderers/markdown.py
@@ -0,0 +1,146 @@
+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)