From 8c2a36072f2b1c89539c615d47d5639f9268ce3f Mon Sep 17 00:00:00 2001 From: Melody Horn Date: Thu, 29 Oct 2020 01:29:15 -0600 Subject: re-add gemtext output --- .build.yml | 10 +-- Makefile | 2 +- _ext/gemtext_builder.py | 188 ++++++++++++++++++++++++++++++++++++++++++++++++ conf.py | 1 + index.rst | 1 + 5 files changed, 196 insertions(+), 6 deletions(-) create mode 100644 _ext/gemtext_builder.py diff --git a/.build.yml b/.build.yml index 49d5db7..1f2891f 100644 --- a/.build.yml +++ b/.build.yml @@ -16,8 +16,8 @@ tasks: - compress: | cd crowbar-spec/_build/html tar czf ../crowbar-spec-html.tar.gz * - #cd ../gmi - #tar czf ../crowbar-spec-gmi.tar.gz * + cd ../gmi + tar czf ../crowbar-spec-gmi.tar.gz * - upload: | upload-to() { dest=$1 @@ -26,8 +26,8 @@ tasks: } cd crowbar-spec/_build/html upload-to /var/www/html/crowbar-lang.org/ * ../rinoh/crowbar-spec.pdf ../epub/crowbar-spec.epub - #cd ../gmi - #upload-to /var/gemini/gmi/crowbar-lang.org/ * ../rinoh/crowbar-spec.pdf ../epub/crowbar-spec.epub + cd ../gmi + upload-to /var/gemini/gmi/crowbar-lang.org/ * ../rinoh/crowbar-spec.pdf ../epub/crowbar-spec.epub - notify: | cd crowbar-spec message="crowbar spec updated: $(git log -1 --no-decorate --oneline)" @@ -36,4 +36,4 @@ artifacts: - crowbar-spec/_build/rinoh/crowbar-spec.pdf - crowbar-spec/_build/epub/crowbar-spec.epub - crowbar-spec/_build/crowbar-spec-html.tar.gz - #- crowbar-spec/_build/crowbar-spec-gmi.tar.gz + - crowbar-spec/_build/crowbar-spec-gmi.tar.gz diff --git a/Makefile b/Makefile index ebe0ed5..f3fc47f 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ SOURCEDIR = . BUILDDIR = _build PDFINFO = pdfinfo -all: html pdf epub # gmi +all: html pdf epub gmi html: Makefile @$(SPHINXBUILD) -M html "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXFULLOPTS) 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, + } diff --git a/conf.py b/conf.py index 58f2f66..9aa2aa3 100644 --- a/conf.py +++ b/conf.py @@ -30,6 +30,7 @@ author = 'boringcactus (Melody Horn)' extensions = [ 'recommonmark', 'crowbar_lexer', + 'gemtext_builder', ] # Add any paths that contain templates here, relative to this directory. diff --git a/index.rst b/index.rst index 08ad2d7..76dd607 100644 --- a/index.rst +++ b/index.rst @@ -52,6 +52,7 @@ Discuss .. toctree:: :maxdepth: 2 :hidden: + :numbered: vs-c tagged-unions -- cgit v1.2.3