mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Fix #4803: latex: too slow in proportion to number of auto numbered footnotes
This commit is contained in:
parent
185987a517
commit
36a8781688
1
CHANGES
1
CHANGES
@ -26,6 +26,7 @@ Bugs fixed
|
||||
* #4817: wrong URLs on warning messages
|
||||
* #4784: latex: :confval:`latex_show_urls` assigns incorrect footnote numbers if
|
||||
hyperlinks exists inside substitutions
|
||||
* #4803: latex: too slow in proportion to number of auto numbered footnotes
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
@ -10,20 +10,25 @@
|
||||
"""
|
||||
|
||||
from docutils import nodes
|
||||
from six import iteritems
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.transforms import SphinxTransform
|
||||
from sphinx.util.nodes import traverse_parent
|
||||
|
||||
if False:
|
||||
# For type annotation
|
||||
from typing import Dict, List, Set, Union # NOQA
|
||||
|
||||
URI_SCHEMES = ('mailto:', 'http:', 'https:', 'ftp:')
|
||||
|
||||
|
||||
class ShowUrlsTransform(SphinxTransform, object):
|
||||
def __init__(self, document, startnode=None):
|
||||
# type: (nodes.document, nodes.Node) -> None
|
||||
super(ShowUrlsTransform, self).__init__(document, startnode)
|
||||
self.expanded = False
|
||||
class ShowUrlsTransform(SphinxTransform):
|
||||
"""Expand references to inline text or footnotes.
|
||||
|
||||
For more information, see :confval:`latex_show_urls`.
|
||||
"""
|
||||
default_priority = 400
|
||||
|
||||
# references are expanded to footnotes (or not)
|
||||
expanded = False
|
||||
|
||||
def apply(self):
|
||||
# type: () -> None
|
||||
@ -80,46 +85,77 @@ class ShowUrlsTransform(SphinxTransform, object):
|
||||
|
||||
def renumber_footnotes(self):
|
||||
# type: () -> None
|
||||
def is_used_number(number):
|
||||
# type: (unicode) -> bool
|
||||
for node in self.document.traverse(nodes.footnote):
|
||||
if not node.get('auto') and number in node['names']:
|
||||
return True
|
||||
collector = FootnoteCollector(self.document)
|
||||
self.document.walkabout(collector)
|
||||
|
||||
return False
|
||||
|
||||
def is_auto_footnote(node):
|
||||
# type: (nodes.Node) -> bool
|
||||
return isinstance(node, nodes.footnote) and node.get('auto')
|
||||
|
||||
def footnote_ref_by(node):
|
||||
# type: (nodes.Node) -> Callable[[nodes.Node], bool]
|
||||
ids = node['ids']
|
||||
parent = list(traverse_parent(node, (nodes.document, addnodes.start_of_file)))[0]
|
||||
|
||||
def is_footnote_ref(node):
|
||||
# type: (nodes.Node) -> bool
|
||||
return (isinstance(node, nodes.footnote_reference) and
|
||||
ids[0] == node['refid'] and
|
||||
parent in list(traverse_parent(node)))
|
||||
|
||||
return is_footnote_ref
|
||||
|
||||
startnum = 1
|
||||
for footnote in self.document.traverse(is_auto_footnote):
|
||||
num = 0
|
||||
for document, footnote in collector.auto_footnotes:
|
||||
# search unused footnote number
|
||||
while True:
|
||||
label = str(startnum)
|
||||
startnum += 1
|
||||
if not is_used_number(label):
|
||||
num += 1
|
||||
if str(num) not in collector.used_footnote_numbers:
|
||||
break
|
||||
|
||||
# assign new footnote number
|
||||
old_label = footnote[0].astext()
|
||||
footnote.remove(footnote[0])
|
||||
footnote.insert(0, nodes.label('', label))
|
||||
footnote[0].replace_self(nodes.label('', str(num)))
|
||||
if old_label in footnote['names']:
|
||||
footnote['names'].remove(old_label)
|
||||
footnote['names'].append(label)
|
||||
footnote['names'].append(str(num))
|
||||
|
||||
for footnote_ref in self.document.traverse(footnote_ref_by(footnote)):
|
||||
footnote_ref.remove(footnote_ref[0])
|
||||
footnote_ref += nodes.Text(label)
|
||||
# update footnote_references by new footnote number
|
||||
for ref in collector.footnote_refs.get(document, []):
|
||||
if footnote['ids'][0] == ref['refid']:
|
||||
ref.remove(ref[0])
|
||||
ref += nodes.Text(str(num))
|
||||
|
||||
|
||||
class FootnoteCollector(nodes.NodeVisitor):
|
||||
"""Collect footnotes and footnote references on the document"""
|
||||
|
||||
def __init__(self, document):
|
||||
# type: (nodes.document) -> None
|
||||
self.auto_footnotes = [] # type: List[nodes.footnote]
|
||||
self.used_footnote_numbers = set() # type: Set[unicode]
|
||||
self.footnote_refs = {} # type: Dict[nodes.Node, List[nodes.footnote_reference]] # NOQA
|
||||
self.current_document = [] # type: List[nodes.Node]
|
||||
nodes.NodeVisitor.__init__(self, document)
|
||||
|
||||
def visit_document(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.current_document.append(node)
|
||||
|
||||
def depart_document(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.current_document.pop()
|
||||
|
||||
def visit_start_of_file(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.current_document.append(node)
|
||||
|
||||
def depart_start_of_file(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
self.current_document.pop()
|
||||
|
||||
def unknown_visit(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
pass
|
||||
|
||||
def visit_footnote(self, node):
|
||||
# type: (nodes.footnote) -> None
|
||||
document = self.current_document[-1]
|
||||
if node.get('auto'):
|
||||
self.auto_footnotes.append((document, node))
|
||||
else:
|
||||
for name in node['names']:
|
||||
self.used_footnote_numbers.add(name)
|
||||
|
||||
def visit_footnote_reference(self, node):
|
||||
# type: (nodes.footnote_reference) -> None
|
||||
document = self.current_document[-1]
|
||||
footnote_refs = self.footnote_refs.setdefault(document, [])
|
||||
footnote_refs.append(node)
|
||||
|
||||
def unknown_departure(self, node):
|
||||
# type: (nodes.Node) -> None
|
||||
pass
|
||||
|
Loading…
Reference in New Issue
Block a user