PointsOfOrder/extension.py

182 lines
5.6 KiB
Python

# Copyright © 2021 Lee Yingtong Li
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.https://mozilla.org/MPL/2.0/.
def setup(app):
app.add_builder(POStandaloneHTMLBuilder, override=True)
app.add_builder(POLaTeXBuilder, override=True)
app.add_builder(POEpub3Builder, override=True)
app.add_role('subref', SubRefRole())
#app.add_source_parser(PORSTParser, override=True)
# HTML Builder
from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.environment.adapters.toctree import TocTree
from sphinx.writers.html5 import HTML5Translator
import docutils.nodes
import sphinx.addnodes
from sphinx.util import logging
logger = logging.getLogger(__name__)
class POHTML5Translator(HTML5Translator):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.paragraph_num = 0
self.footnote_order = []
def visit_paragraph(self, node):
super().visit_paragraph(node)
if node.parent.tagname == 'section':
# Add paragraph numbers
self.paragraph_num += 1
self.body.append('<span class="paragraph-num"><a id="para{0}" href="#para{0}">{0}</a></span>'.format(self.paragraph_num))
def visit_footnote_reference(self, node):
super().visit_footnote_reference(node)
# Verify order
if node['refid'] not in self.footnote_order:
self.footnote_order.append(node['refid'])
def visit_footnote(self, node):
super().visit_footnote(node)
# Verify order
if not self.footnote_order:
pass
elif node['ids'][0] == self.footnote_order[0]:
del self.footnote_order[0]
else:
logger.warning('footnote defined out of sequence: expecting {}'.format(self.footnote_order[0]), location=node)
self.footnote_order = None # Ignore future errors
class POStandaloneHTMLBuilder(StandaloneHTMLBuilder):
default_translator_class = POHTML5Translator
__TocTree_resolve = TocTree.resolve
def _TocTree_resolve(self, *args, **kwargs):
result = __TocTree_resolve(self, *args, **kwargs)
if self.env.app.builder.name == 'html':
# Add Index TOC entry
bullet_list = result[1]
reference = docutils.nodes.reference('', '', internal=True, refuri='genindex.html', anchorname='', *[docutils.nodes.Text('Index')])
compact_paragraph = sphinx.addnodes.compact_paragraph('', '', reference)
list_item = docutils.nodes.list_item('', classes=['toctree-l1'], *[compact_paragraph])
bullet_list.append(list_item)
return result
TocTree.resolve = _TocTree_resolve
# LaTeX Builder
from sphinx.builders.latex import LaTeXBuilder
from sphinx.writers.latex import LaTeXTranslator
class POLaTeXTranslator(LaTeXTranslator):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.paragraph_num = 0
def visit_title(self, node):
super().visit_title(node)
if self.sectionlevel == 1:
# Reset paragraph numbers
self.paragraph_num = 0
def visit_paragraph(self, node):
super().visit_paragraph(node)
if node.parent.tagname == 'section':
# Add paragraph numbers
self.paragraph_num += 1
#self.body.append(r'\makebox[0pt][r]{{\small\textcolor[HTML]{{AAAAAA}}{{¶{0}}}\hspace{{0.8em}}}}'.format(self.paragraph_num))
#self.body.append(r'{{\small\textcolor[HTML]{{AAAAAA}}{{[¶{0}]}}}} '.format(self.paragraph_num))
self.body.append(r'{{\small\bfseries\sffamily[{0}]}} '.format(self.paragraph_num))
class POLaTeXBuilder(LaTeXBuilder):
default_translator_class = POLaTeXTranslator
# Epub3 Builder
from sphinx.builders.epub3 import Epub3Builder
class POEpub3Builder(Epub3Builder):
default_translator_class = POHTML5Translator
# :subref: Role
from sphinx.roles import XRefRole
class SubRefRole(XRefRole):
def __init__(self, *args, **kwargs):
super().__init__(lowercase=True, innernodeclass=docutils.nodes.inline, warn_dangling=True, *args, **kwargs)
def run(self):
self.name = 'std:ref'
return super().run()
def result_nodes(self, document, env, node, is_ref):
result = super().result_nodes(document, env, node, is_ref)
pending_xref = result[0][0]
inline = pending_xref[0]
substitution_reference = docutils.nodes.substitution_reference('', '', refname=docutils.nodes.fully_normalize_name(self.title))
inline.clear() # Remove raw "title" text node
inline.append(substitution_reference) # Replace with substitution_reference
pending_xref['po_subref'] = True # Flag for StandardDomain._resolve_ref_xref
return result
from sphinx.domains.std import StandardDomain
__StandardDomain_resolve_ref_xref = StandardDomain._resolve_ref_xref
def _StandardDomain_resolve_ref_xref(self, env, fromdocname, builder, typ, target, node, contnode):
if 'po_subref' in node:
# Build reference node, preserving substitution
docname, labelid = self.anonlabels.get(target, ('', ''))
# Based on StandardDomain.build_reference_node
newnode = docutils.nodes.reference('', '', internal=True)
if docname == fromdocname:
newnode['refid'] = labelid
else:
contnode = sphinx.addnodes.pending_xref('')
contnode['refdocname'] = docname
contnode['refsectname'] = node.astext()
newnode['refuri'] = builder.get_relative_uri(fromdocname, docname)
if labelid:
newnode['refuri'] += '#' + labelid
# Copy across substitution
newnode.children.extend(node.children)
return newnode
return __StandardDomain_resolve_ref_xref(self, env, fromdocname, builder, typ, target, node, contnode)
StandardDomain._resolve_ref_xref = _StandardDomain_resolve_ref_xref
# RST Parser
from sphinx.parsers import RSTParser
class PORSTParser(RSTParser):
def parse(self, inputstring, document):
super().parse(inputstring, document)
# Verify footnotes
print(document)