160 lines
3.8 KiB
Python
160 lines
3.8 KiB
Python
from __future__ import unicode_literals
|
|
|
|
|
|
from commonmark.render.renderer import Renderer
|
|
|
|
|
|
class ReStructuredTextRenderer(Renderer):
|
|
"""
|
|
Render reStructuredText from Markdown
|
|
|
|
Example:
|
|
|
|
.. code:: python
|
|
|
|
import commonmark
|
|
|
|
parser = commonmark.Parser()
|
|
ast = parser.parse('Hello `inline code` example')
|
|
|
|
renderer = commonmark.ReStructuredTextRenderer()
|
|
rst = renderer.render(ast)
|
|
print(rst) # Hello ``inline code`` example
|
|
"""
|
|
def __init__(self, indent_char=' '):
|
|
self.indent_char = indent_char
|
|
self.indent_length = 0
|
|
|
|
def lit(self, s):
|
|
if s == '\n':
|
|
indent = '' # Avoid whitespace if we're just adding a newline
|
|
elif self.last_out != '\n':
|
|
indent = '' # Don't indent if we're in the middle of a line
|
|
else:
|
|
indent = self.indent_char * self.indent_length
|
|
|
|
return super(ReStructuredTextRenderer, self).lit(indent + s)
|
|
|
|
def cr(self):
|
|
self.lit('\n')
|
|
|
|
def indent_lines(self, literal, indent_length=4):
|
|
indent = self.indent_char * indent_length
|
|
new_lines = []
|
|
|
|
for line in literal.splitlines():
|
|
new_lines.append(indent + line)
|
|
|
|
return '\n'.join(new_lines)
|
|
|
|
# Nodes
|
|
|
|
def document(self, node, entering):
|
|
pass
|
|
|
|
def softbreak(self, node, entering):
|
|
self.cr()
|
|
|
|
def linebreak(self, node, entering):
|
|
self.cr()
|
|
self.cr()
|
|
|
|
def text(self, node, entering):
|
|
self.out(node.literal)
|
|
|
|
def emph(self, node, entering):
|
|
self.out('*')
|
|
|
|
def strong(self, node, entering):
|
|
self.out('**')
|
|
|
|
def paragraph(self, node, entering):
|
|
if node.parent.t == 'item':
|
|
pass
|
|
else:
|
|
self.cr()
|
|
|
|
def link(self, node, entering):
|
|
if entering:
|
|
self.out('`')
|
|
else:
|
|
self.out(' <%s>`_' % node.destination)
|
|
|
|
def image(self, node, entering):
|
|
directive = '.. image:: ' + node.destination
|
|
|
|
if entering:
|
|
self.out(directive)
|
|
self.cr()
|
|
self.indent_length += 4
|
|
self.out(':alt: ')
|
|
else:
|
|
self.indent_length -= 4
|
|
|
|
def code(self, node, entering):
|
|
self.out('``')
|
|
self.out(node.literal)
|
|
self.out('``')
|
|
|
|
def code_block(self, node, entering):
|
|
directive = '.. code::'
|
|
language_name = None
|
|
|
|
info_words = node.info.split() if node.info else []
|
|
if len(info_words) > 0 and len(info_words[0]) > 0:
|
|
language_name = info_words[0]
|
|
|
|
if language_name:
|
|
directive += ' ' + language_name
|
|
|
|
self.cr()
|
|
self.out(directive)
|
|
self.cr()
|
|
self.cr()
|
|
self.out(self.indent_lines(node.literal))
|
|
self.cr()
|
|
|
|
def list(self, node, entering):
|
|
if entering:
|
|
self.cr()
|
|
|
|
def item(self, node, entering):
|
|
tagname = '*' if node.list_data['type'] == 'bullet' else '#.'
|
|
|
|
if entering:
|
|
self.out(tagname + ' ')
|
|
else:
|
|
self.cr()
|
|
|
|
def block_quote(self, node, entering):
|
|
if entering:
|
|
self.indent_length += 4
|
|
else:
|
|
self.indent_length -= 4
|
|
|
|
def heading(self, node, entering):
|
|
heading_chars = [
|
|
'#',
|
|
'*',
|
|
'=',
|
|
'-',
|
|
'^',
|
|
'"'
|
|
]
|
|
|
|
try:
|
|
heading_char = heading_chars[node.level-1]
|
|
except IndexError:
|
|
# Default to the last level if we're in too deep
|
|
heading_char = heading_chars[-1]
|
|
|
|
heading_length = len(node.first_child.literal)
|
|
banner = heading_char * heading_length
|
|
|
|
if entering:
|
|
self.cr()
|
|
else:
|
|
self.cr()
|
|
self.out(banner)
|
|
self.cr()
|