diff options
Diffstat (limited to 'src/fmt/support/rst2md.py')
-rwxr-xr-x | src/fmt/support/rst2md.py | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/src/fmt/support/rst2md.py b/src/fmt/support/rst2md.py new file mode 100755 index 000000000..cf170fe2b --- /dev/null +++ b/src/fmt/support/rst2md.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python +# reStructuredText (RST) to GitHub-flavored Markdown converter + +import re, sys +from docutils import core, nodes, writers + + +def is_github_ref(node): + return re.match('https://github.com/.*/(issues|pull)/.*', node['refuri']) + + +class Translator(nodes.NodeVisitor): + def __init__(self, document): + nodes.NodeVisitor.__init__(self, document) + self.output = '' + self.indent = 0 + self.preserve_newlines = False + + def write(self, text): + self.output += text.replace('\n', '\n' + ' ' * self.indent) + + def visit_document(self, node): + pass + + def depart_document(self, node): + pass + + def visit_section(self, node): + pass + + def depart_section(self, node): + # Skip all sections except the first one. + raise nodes.StopTraversal + + def visit_title(self, node): + self.version = re.match(r'(\d+\.\d+\.\d+).*', node.children[0]).group(1) + raise nodes.SkipChildren + + def visit_title_reference(self, node): + raise Exception(node) + + def depart_title(self, node): + pass + + def visit_Text(self, node): + if not self.preserve_newlines: + node = node.replace('\n', ' ') + self.write(node) + + def depart_Text(self, node): + pass + + def visit_bullet_list(self, node): + pass + + def depart_bullet_list(self, node): + pass + + def visit_list_item(self, node): + self.write('* ') + self.indent += 2 + + def depart_list_item(self, node): + self.indent -= 2 + self.write('\n\n') + + def visit_paragraph(self, node): + pass + + def depart_paragraph(self, node): + pass + + def visit_reference(self, node): + if not is_github_ref(node): + self.write('[') + + def depart_reference(self, node): + if not is_github_ref(node): + self.write('](' + node['refuri'] + ')') + + def visit_target(self, node): + pass + + def depart_target(self, node): + pass + + def visit_literal(self, node): + self.write('`') + + def depart_literal(self, node): + self.write('`') + + def visit_literal_block(self, node): + self.write('\n\n```') + if 'c++' in node['classes']: + self.write('c++') + self.write('\n') + self.preserve_newlines = True + + def depart_literal_block(self, node): + self.write('\n```\n') + self.preserve_newlines = False + + def visit_inline(self, node): + pass + + def depart_inline(self, node): + pass + + def visit_image(self, node): + self.write('![](' + node['uri'] + ')') + + def depart_image(self, node): + pass + + def write_row(self, row, widths): + for i, entry in enumerate(row): + text = entry[0][0] if len(entry) > 0 else '' + if i != 0: + self.write('|') + self.write('{:{}}'.format(text, widths[i])) + self.write('\n') + + def visit_table(self, node): + table = node.children[0] + colspecs = table[:-2] + thead = table[-2] + tbody = table[-1] + widths = [int(cs['colwidth']) for cs in colspecs] + sep = '|'.join(['-' * w for w in widths]) + '\n' + self.write('\n\n') + self.write_row(thead[0], widths) + self.write(sep) + for row in tbody: + self.write_row(row, widths) + raise nodes.SkipChildren + + def depart_table(self, node): + pass + +class MDWriter(writers.Writer): + """GitHub-flavored markdown writer""" + + supported = ('md',) + """Formats this writer supports.""" + + def translate(self): + translator = Translator(self.document) + self.document.walkabout(translator) + self.output = (translator.output, translator.version) + + +def convert(rst_path): + """Converts RST file to Markdown.""" + return core.publish_file(source_path=rst_path, writer=MDWriter()) + + +if __name__ == '__main__': + convert(sys.argv[1]) |