Sphinx hack

The Sphinx software, which I’m using to process this website, is supposed to be used to write software manuals. This is mostly a superset of what one needs to write something more freeform, but there’s a few exceptions. Mostly, displaying stuff newest-up for the re-visitors.

I’ve repeatedly read the Sphinx documentation’s chapter on constructing a “todo” extension, and after an interval of wrestling with Python I got the following piece of… spaghetti? Maybe it’s more of a cheesey lasagne.

from docutils import nodes
from docutils.parsers.rst import Directive

from sphinx.locale import _
from sphinx.util.docutils import SphinxDirective

# This is closely based on todo extension from the manual.
# Viewers beware: this is not solid Python code.
# I'm not what one could call a dead parrot operator.

from os.path import getmtime
import re

class k11specialrecentlymodified(nodes.General, nodes.Element):
    def __init__(self, contentArg, limitArg):
        super().__init__()
        self.content = contentArg
        self.limit = limitArg

class K11SpecialRecentlyModifiedDirective(SphinxDirective):
    has_content = True
    required_arguments = 1
    optional_arguments = 0
    def run(self):
        return [k11specialrecentlymodified(self.content, self.arguments[0])]

def k11special_get_matcher(content):
    expression = "^("
    for line in content:
        for char in line:
            if char == '*':
                expression = expression + '[^/]*'
            elif char == '%':
                expression = expression + '.*'
            else:
                expression = expression + char
        expression = expression + ")|("
    expression = expression + "/)$"
    return re.compile(expression)

def process_k11specialrecentlymodified_nodes(app, doctree, fromdocname):
    env = app.builder.env
    project = env.project
    if not hasattr(project, 'docnames'):
        project.discover()
    if (project.docnames == None):
        project.discover()
    timed_docnames = [(getmtime(env.doc2path(x)), x) for x in project.docnames]
    revordered_docnames = [b for (a, b) in sorted(timed_docnames)]
    ordered_docnames = [x for x in reversed(revordered_docnames)]
    titlemap = env.titles

    for node in doctree.traverse(k11specialrecentlymodified):

        contentNode = nodes.bullet_list()
        ctr = len(ordered_docnames) if node.limit == None else int(node.limit)
        pattern = k11special_get_matcher(node.content)

        for dn in ordered_docnames:
            if not pattern.fullmatch(dn):
                continue
            bulletNode = nodes.list_item()
            entryNode = nodes.paragraph()
            refNode = nodes.reference('', '')
            ti = titlemap[dn].astext()
            innardNode = nodes.Text(ti, ti)
            refNode['refdocname'] = dn
            refNode['refuri'] = app.builder.get_relative_uri(fromdocname, dn)
            refNode.append(innardNode)
            entryNode.append(refNode)
            bulletNode.append(entryNode)
            contentNode.append(bulletNode)
            ctr -= 1
            if ctr <= 0:
                break

        node.replace_self([contentNode])
        #node.replace_self([])

def setup(app):
    app.add_node(k11specialrecentlymodified)
    app.add_directive('k11specialrecentlymodified', K11SpecialRecentlyModifiedDirective)
    app.connect('doctree-resolved', process_k11specialrecentlymodified_nodes)
    return {
        'version': '0.0.1',
        'parallel_read_safe': True,
        'parallel_write_safe': True,
    }