Source code for sphinx_toolbox.tweaks.revert_footnote_style

#!/usr/bin/env python3
#
#  revert_footnote_style.py
"""
Reverts the docutils footnote behaviour from ``>=0.18`` to that of ``<=0.17``.

.. versionadded:: 3.1.2
.. extensions:: sphinx_toolbox.tweaks.revert_footnote_style

-----

"""
#
#  Copyright © 2022 Dominic Davis-Foster <dominic@davis-foster.co.uk>
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to deal
#  in the Software without restriction, including without limitation the rights
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#  copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in all
#  copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
#  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
#  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
#  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
#  DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
#  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
#  OR OTHER DEALINGS IN THE SOFTWARE.
#
#  Based on docutils
#  |  Copyright © 2016 David Goodger, Günter Milde
#  |  BSD Licensed
#  |  All rights reserved.
#  |
#  |  Redistribution and use in source and binary forms, with or without
#  |  modification, are permitted provided that the following conditions are
#  |  met:
#  |
#  |  * Redistributions of source code must retain the above copyright
#  |   notice, this list of conditions and the following disclaimer.
#  |
#  |  * Redistributions in binary form must reproduce the above copyright
#  |   notice, this list of conditions and the following disclaimer in the
#  |   documentation and/or other materials provided with the distribution.
#  |
#  |  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#  |  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#  |  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#  |  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
#  |  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  |  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#  |  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
#  |  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
#  |  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#  |  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
#  |  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

# 3rd party
import docutils
from sphinx.application import Sphinx
from sphinx.writers.html import HTMLTranslator
from sphinx.writers.html5 import HTML5Translator

# this package
from sphinx_toolbox.utils import SphinxExtMetadata, metadata_add_version

__all__ = ["setup"]


def visit_footnote(self: HTMLTranslator, node: docutils.nodes.footnote) -> None:  # pragma: no cover
	if not self.in_footnote_list:
		listnode = node.copy()
		listnode["ids"] = []
		classes = [node.tagname, self.settings.footnote_references]
		self.body.append(self.starttag(listnode, "dl", CLASS=' '.join(classes)))  # role="note"
		self.in_footnote_list = True


def depart_footnote(self, node: docutils.nodes.footnote) -> None:  # pragma: no cover
	self.body.append('</dd>\n')
	if not isinstance(node.next_node(descend=False, siblings=True), docutils.nodes.footnote):
		self.body.append('</dl>\n')
		self.in_footnote_list = False


def visit_footnote_reference(self, node: docutils.nodes.footnote_reference) -> None:  # pragma: no cover
	href = '#' + node["refid"]
	classes = ["footnote-reference", self.settings.footnote_references]
	self.body.append(self.starttag(node, 'a', suffix='', CLASS=' '.join(classes), href=href))  # role='doc-noteref'


def depart_footnote_reference(self, node: docutils.nodes.footnote_reference) -> None:  # pragma: no cover
	self.body.append("</a>")


# footnote and citation labels:
def visit_label(self, node: docutils.nodes.label) -> None:  # pragma: no cover
	if (isinstance(node.parent, docutils.nodes.footnote)):
		classes = self.settings.footnote_references
	else:
		classes = "brackets"

	# pass parent node to get id into starttag:
	self.body.append(self.starttag(node.parent, "dt", '', CLASS="label"))
	self.body.append(self.starttag(node, "span", '', CLASS=classes))

	# footnote/citation backrefs:
	if self.settings.footnote_backlinks:
		backrefs = node.parent.get("backrefs", [])
		if len(backrefs) == 1:
			self.body.append('<a class="fn-backref" href="#%s">' % backrefs[0])


def depart_label(self, node: docutils.nodes.label) -> None:  # pragma: no cover
	if self.settings.footnote_backlinks:
		backrefs = node.parent["backrefs"]
		if len(backrefs) == 1:
			self.body.append("</a>")
	self.body.append("</span>")
	if self.settings.footnote_backlinks and len(backrefs) > 1:
		backlinks = [f'<a href="#{ref}">{i}</a>' for (i, ref) in enumerate(backrefs, 1)]
		self.body.append('<span class="fn-backref">(%s)</span>' % ','.join(backlinks))
	self.body.append('</dt>\n<dd>')


[docs]@metadata_add_version def setup(app: Sphinx) -> SphinxExtMetadata: """ Setup :mod:`sphinx_toolbox.tweaks.revert_footnote_style`. :param app: The Sphinx application. """ if docutils.__version_info__ >= (0, 18): # pragma: no cover app.add_node( docutils.nodes.footnote, html=(visit_footnote, depart_footnote), override=True, ) app.add_node( docutils.nodes.footnote_reference, html=(visit_footnote_reference, depart_footnote_reference), override=True, ) app.add_node( docutils.nodes.label, html=(visit_label, depart_label), override=True, ) HTMLTranslator.in_footnote_list = False HTML5Translator.in_footnote_list = False return {"parallel_read_safe": True}