diff options
author | Melody Horn <melody@boringcactus.com> | 2020-10-29 01:29:15 -0600 |
---|---|---|
committer | Melody Horn <melody@boringcactus.com> | 2020-10-29 01:29:15 -0600 |
commit | 8c2a36072f2b1c89539c615d47d5639f9268ce3f (patch) | |
tree | 9f2dbf248d63d19cb8a3aeb31c41e82c8383dd92 /_ext | |
parent | e558d912a7e3583d700aa42261ef1fa17e5d0d55 (diff) | |
download | spec-8c2a36072f2b1c89539c615d47d5639f9268ce3f.tar.gz spec-8c2a36072f2b1c89539c615d47d5639f9268ce3f.zip |
re-add gemtext output
Diffstat (limited to '_ext')
-rw-r--r-- | _ext/gemtext_builder.py | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/_ext/gemtext_builder.py b/_ext/gemtext_builder.py new file mode 100644 index 0000000..9c31788 --- /dev/null +++ b/_ext/gemtext_builder.py @@ -0,0 +1,188 @@ +from pathlib import Path +from typing import Set + +from docutils import nodes, writers +from docutils.io import StringOutput +from sphinx.builders import Builder +from sphinx.util import logging +from sphinx.util.docutils import SphinxTranslator +from sphinx.util.osutil import ensuredir, os_path + +logger = logging.getLogger(__name__) + +class GemtextWriter(writers.Writer): + def __init__(self, builder): + super().__init__() + self.builder = builder + + def translate(self): + visitor = self.builder.create_translator(self.document, self.builder) + self.document.walkabout(visitor) + self.output = '\n'.join(visitor.body) + '\n' + +class GemtextTranslator(SphinxTranslator): + def __init__(self, document: nodes.document, builder): + super().__init__(document, builder) + self.heading_level = 0 + self.current_line = '' + self.body = [] + self.literal = False + + def _finish_line(self): + if self.current_line != '': + self.body.append(self.current_line) + self.current_line = '' + + def visit_document(self, node: nodes.document): + self.heading_level = 1 + self.current_line = '' + self.body = [] + self.literal = False + + def depart_document(self, node: nodes.document): + self._finish_line() + + def visit_section(self, node: nodes.section): + self.heading_level += 1 + if len(self.body) > 0 and self.body[-1] != '': + self.body.append('') + + def depart_section(self, node: nodes.section): + self.heading_level -= 1 + + def visit_title(self, node: nodes.title): + pass + + def depart_title(self, node: nodes.title): + self.current_line = '#' * (self.heading_level - 1) + ' ' + self.current_line + self._finish_line() + self.body.append('') + + def visit_Text(self, node: nodes.Text): + text = node.astext() + if not self.literal: + text = text.replace('\n', ' ') + self.current_line += text + + def depart_Text(self, node: nodes.Text): + pass + + def visit_paragraph(self, node: nodes.paragraph): + pass + + def depart_paragraph(self, node: nodes.paragraph): + self._finish_line() + + def visit_reference(self, node: nodes.reference): + pass + + def depart_reference(self, node: nodes.reference): + if self.current_line.startswith('=>'): + self._finish_line() + self.body.append('=> {} {}'.format(node.attributes['refuri'], node.astext())) + else: + self.current_line = '=> {} {}'.format(node.attributes['refuri'], self.current_line) + + def visit_image(self, node: nodes.image): + if self.current_line == '': + self.body.append('=> {} {}'.format(node.attributes['uri'], node.attributes['alt'])) + else: + raise NotImplementedError('inline images') + + def depart_image(self, node: nodes.image): + pass + + def visit_comment(self, node: nodes.comment): + raise nodes.SkipNode + + def visit_strong(self, node: nodes.strong): + self.current_line += '**' + + depart_strong = visit_strong + + def visit_emphasis(self, node: nodes.emphasis): + self.current_line += '_' + + depart_emphasis = visit_emphasis + + def visit_target(self, node: nodes.target): + raise nodes.SkipNode + + def visit_bullet_list(self, node: nodes.bullet_list): + pass + + def depart_bullet_list(self, node: nodes.bullet_list): + pass + + def visit_list_item(self, node: nodes.list_item): + self.current_line += '* ' + + def depart_list_item(self, node: nodes.list_item): + pass + + def visit_compound(self, node: nodes.compound): + pass + + def depart_compound(self, node: nodes.compound): + pass + + def visit_inline(self, node: nodes.inline): + pass + + def depart_inline(self, node: nodes.inline): + pass + + def visit_title_reference(self, nodes: nodes.title_reference): + pass + + def depart_title_reference(self, nodes: nodes.title_reference): + pass + + def visit_literal(self, nodes: nodes.literal): + self.current_line += '`' + + depart_literal = visit_literal + + def visit_literal_block(self, nodes: nodes.literal_block): + self.body.append('') + self.body.append('```') + self.literal = True + + def depart_literal_block(self, nodes: nodes.literal_block): + self._finish_line() + self.body.append('```') + self.body.append('') + self.literal = False + +class GemtextBuilder(Builder): + name = 'gmi' + format = 'gmi' + + out_suffix = '.gmi' + default_translator_class = GemtextTranslator + + def get_outdated_docs(self): + return self.env.found_docs # can't be fucked to implement this right + + def get_target_uri(self, docname: str, typ: str = None): + return docname + + def prepare_writing(self, docnames: Set[str]): + self.writer = GemtextWriter(self) + + def write_doc(self, docname: str, doctree: nodes.document): + destination = StringOutput(encoding='utf-8') + self.writer.write(doctree, destination) + out_name = Path(self.outdir, os_path(docname) + '.gmi') + out_name.parent.mkdir(parents=True, exist_ok=True) + with open(out_name, 'w', encoding='utf-8') as out_file: + out_file.write(self.writer.output) + +def setup(app): + app.add_builder(GemtextBuilder) + + return { + 'version': '0.1', + 'parallel_read_safe': True, + 'parallel_write_safe': True, + } |