aboutsummaryrefslogtreecommitdiff
path: root/_ext
diff options
context:
space:
mode:
Diffstat (limited to '_ext')
-rw-r--r--_ext/gemtext_builder.py188
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,
+ }