mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Fix #9529: LaTeX: named footnotes are converted to "?"
Named auto numbered footnote (ex. ``[#named]``) that is referred multiple times was rendered to a question mark. This calls `\sphinxstepexplicit` for every footnote node that is referred multiple times.
This commit is contained in:
parent
9b142f15e6
commit
1546b21f89
2
CHANGES
2
CHANGES
@ -24,6 +24,8 @@ Bugs fixed
|
||||
----------
|
||||
|
||||
* #10133: autodoc: Crashed when mocked module is used for type annotation
|
||||
* #9529: LaTeX: named auto numbered footnote (ex. ``[#named]``) that is referred
|
||||
multiple times was rendered to a question mark
|
||||
* #10122: sphinx-build: make.bat does not check the installation of sphinx-build
|
||||
command before showing help
|
||||
|
||||
|
@ -237,7 +237,8 @@ class LaTeXFootnoteTransform(SphinxPostTransform):
|
||||
blah blah blah ...
|
||||
|
||||
* Replace second and subsequent footnote references which refers same footnote definition
|
||||
by footnotemark node.
|
||||
by footnotemark node. Additionally, the footnote definition node is marked as
|
||||
"referred".
|
||||
|
||||
Before::
|
||||
|
||||
@ -258,7 +259,7 @@ class LaTeXFootnoteTransform(SphinxPostTransform):
|
||||
After::
|
||||
|
||||
blah blah blah
|
||||
<footnote ids="id1">
|
||||
<footnote ids="id1" referred=True>
|
||||
<label>
|
||||
1
|
||||
<paragraph>
|
||||
@ -358,7 +359,7 @@ class LaTeXFootnoteTransform(SphinxPostTransform):
|
||||
|
||||
class LaTeXFootnoteVisitor(nodes.NodeVisitor):
|
||||
def __init__(self, document: nodes.document, footnotes: List[nodes.footnote]) -> None:
|
||||
self.appeared: Set[Tuple[str, str]] = set()
|
||||
self.appeared: Dict[Tuple[str, str], nodes.footnote] = {}
|
||||
self.footnotes: List[nodes.footnote] = footnotes
|
||||
self.pendings: List[nodes.footnote] = []
|
||||
self.table_footnotes: List[nodes.footnote] = []
|
||||
@ -439,22 +440,24 @@ class LaTeXFootnoteVisitor(nodes.NodeVisitor):
|
||||
def visit_footnote_reference(self, node: nodes.footnote_reference) -> None:
|
||||
number = node.astext().strip()
|
||||
docname = node['docname']
|
||||
if self.restricted:
|
||||
mark = footnotemark('', number, refid=node['refid'])
|
||||
node.replace_self(mark)
|
||||
if (docname, number) not in self.appeared:
|
||||
footnote = self.get_footnote_by_reference(node)
|
||||
self.pendings.append(footnote)
|
||||
elif (docname, number) in self.appeared:
|
||||
if (docname, number) in self.appeared:
|
||||
footnote = self.appeared.get((docname, number))
|
||||
footnote["referred"] = True
|
||||
|
||||
mark = footnotemark('', number, refid=node['refid'])
|
||||
node.replace_self(mark)
|
||||
else:
|
||||
footnote = self.get_footnote_by_reference(node)
|
||||
self.footnotes.remove(footnote)
|
||||
node.replace_self(footnote)
|
||||
footnote.walkabout(self)
|
||||
if self.restricted:
|
||||
mark = footnotemark('', number, refid=node['refid'])
|
||||
node.replace_self(mark)
|
||||
self.pendings.append(footnote)
|
||||
else:
|
||||
self.footnotes.remove(footnote)
|
||||
node.replace_self(footnote)
|
||||
footnote.walkabout(self)
|
||||
|
||||
self.appeared.add((docname, number))
|
||||
self.appeared[(docname, number)] = footnote
|
||||
raise nodes.SkipNode
|
||||
|
||||
def get_footnote_by_reference(self, node: nodes.footnote_reference) -> nodes.footnote:
|
||||
|
@ -856,14 +856,14 @@ class LaTeXTranslator(SphinxTranslator):
|
||||
def visit_footnote(self, node: Element) -> None:
|
||||
self.in_footnote += 1
|
||||
label = cast(nodes.label, node[0])
|
||||
if 'auto' not in node:
|
||||
if 'referred' in node:
|
||||
self.body.append(r'\sphinxstepexplicit ')
|
||||
if self.in_parsed_literal:
|
||||
self.body.append(r'\begin{footnote}[%s]' % label.astext())
|
||||
else:
|
||||
self.body.append('%' + CR)
|
||||
self.body.append(r'\begin{footnote}[%s]' % label.astext())
|
||||
if 'auto' not in node:
|
||||
if 'referred' in node:
|
||||
self.body.append(r'\phantomsection'
|
||||
r'\label{\thesphinxscope.%s}%%' % label.astext() + CR)
|
||||
self.body.append(r'\sphinxAtStartFootnote' + CR)
|
||||
|
@ -177,3 +177,12 @@ The section with an object description
|
||||
|
||||
.. py:function:: dummy(N)
|
||||
:noindex:
|
||||
|
||||
Footnotes referred twice
|
||||
========================
|
||||
|
||||
* Explicitly numbered footnote: [100]_ [100]_
|
||||
* Named footnote: [#twice]_ [#twice]_
|
||||
|
||||
.. [100] Numbered footnote
|
||||
.. [#twice] Named footnote
|
||||
|
@ -723,9 +723,8 @@ def test_footnote(app, status, warning):
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert ('\\sphinxstepexplicit %\n\\begin{footnote}[1]\\phantomsection'
|
||||
'\\label{\\thesphinxscope.1}%\n\\sphinxAtStartFootnote\nnumbered\n%\n'
|
||||
'\\end{footnote}') in result
|
||||
assert ('\\sphinxAtStartPar\n%\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
'numbered\n%\n\\end{footnote}') in result
|
||||
assert ('\\begin{footnote}[2]\\sphinxAtStartFootnote\nauto numbered\n%\n'
|
||||
'\\end{footnote}') in result
|
||||
assert '\\begin{footnote}[3]\\sphinxAtStartFootnote\nnamed\n%\n\\end{footnote}' in result
|
||||
@ -769,13 +768,13 @@ def test_reference_in_caption_and_codeblock_in_footnote(app, status, warning):
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Footnote in section\n%\n\\end{footnotetext}') in result
|
||||
assert ('\\caption{This is the figure caption with a footnote to '
|
||||
'\\sphinxfootnotemark[8].}\\label{\\detokenize{index:id30}}\\end{figure}\n'
|
||||
'\\sphinxfootnotemark[8].}\\label{\\detokenize{index:id35}}\\end{figure}\n'
|
||||
'%\n\\begin{footnotetext}[8]'
|
||||
'\\phantomsection\\label{\\thesphinxscope.8}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Footnote in caption\n%\n\\end{footnotetext}') in result
|
||||
assert ('\\sphinxcaption{footnote \\sphinxfootnotemark[9] in '
|
||||
'caption of normal table}\\label{\\detokenize{index:id31}}') in result
|
||||
'caption of normal table}\\label{\\detokenize{index:id36}}') in result
|
||||
assert ('\\caption{footnote \\sphinxfootnotemark[10] '
|
||||
'in caption \\sphinxfootnotemark[11] of longtable\\strut}') in result
|
||||
assert ('\\endlastfoot\n%\n\\begin{footnotetext}[10]'
|
||||
@ -796,6 +795,26 @@ def test_reference_in_caption_and_codeblock_in_footnote(app, status, warning):
|
||||
assert '\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]' in result
|
||||
|
||||
|
||||
@pytest.mark.sphinx('latex', testroot='footnotes')
|
||||
def test_footnote_referred_multiple_times(app, status, warning):
|
||||
app.builder.build_all()
|
||||
result = (app.outdir / 'python.tex').read_text()
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
|
||||
assert ('Explicitly numbered footnote: \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[100]\\phantomsection\\label{\\thesphinxscope.100}%\n'
|
||||
'\\sphinxAtStartFootnote\nNumbered footnote\n%\n'
|
||||
'\\end{footnote} \\sphinxfootnotemark[100]\n'
|
||||
in result)
|
||||
assert ('Named footnote: \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[13]\\phantomsection\\label{\\thesphinxscope.13}%\n'
|
||||
'\\sphinxAtStartFootnote\nNamed footnote\n%\n'
|
||||
'\\end{footnote} \\sphinxfootnotemark[13]\n'
|
||||
in result)
|
||||
|
||||
|
||||
@pytest.mark.sphinx(
|
||||
'latex', testroot='footnotes',
|
||||
confoverrides={'latex_show_urls': 'inline'})
|
||||
@ -805,25 +824,23 @@ def test_latex_show_urls_is_inline(app, status, warning):
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert ('Same footnote number \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
assert ('Same footnote number %\n'
|
||||
'\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
'footnote in bar\n%\n\\end{footnote} in bar.rst') in result
|
||||
assert ('Auto footnote number %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
'footnote in baz\n%\n\\end{footnote} in baz.rst') in result
|
||||
assert ('\\phantomsection\\label{\\detokenize{index:id33}}'
|
||||
assert ('\\phantomsection\\label{\\detokenize{index:id38}}'
|
||||
'{\\hyperref[\\detokenize{index:the-section'
|
||||
'-with-a-reference-to-authoryear}]'
|
||||
'{\\sphinxcrossref{The section with a reference to '
|
||||
'\\sphinxcite{index:authoryear}}}}') in result
|
||||
assert ('\\phantomsection\\label{\\detokenize{index:id34}}'
|
||||
assert ('\\phantomsection\\label{\\detokenize{index:id39}}'
|
||||
'{\\hyperref[\\detokenize{index:the-section-with-a-reference-to}]'
|
||||
'{\\sphinxcrossref{The section with a reference to }}}' in result)
|
||||
assert ('First footnote: %\n\\begin{footnote}[2]\\sphinxAtStartFootnote\n'
|
||||
'First\n%\n\\end{footnote}') in result
|
||||
assert ('Second footnote: \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
assert ('Second footnote: %\n'
|
||||
'\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
'Second\n%\n\\end{footnote}\n') in result
|
||||
assert '\\sphinxhref{http://sphinx-doc.org/}{Sphinx} (http://sphinx\\sphinxhyphen{}doc.org/)' in result
|
||||
assert ('Third footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n'
|
||||
@ -863,24 +880,22 @@ def test_latex_show_urls_is_footnote(app, status, warning):
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert ('Same footnote number \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
assert ('Same footnote number %\n'
|
||||
'\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
'footnote in bar\n%\n\\end{footnote} in bar.rst') in result
|
||||
assert ('Auto footnote number %\n\\begin{footnote}[2]\\sphinxAtStartFootnote\n'
|
||||
'footnote in baz\n%\n\\end{footnote} in baz.rst') in result
|
||||
assert ('\\phantomsection\\label{\\detokenize{index:id33}}'
|
||||
assert ('\\phantomsection\\label{\\detokenize{index:id38}}'
|
||||
'{\\hyperref[\\detokenize{index:the-section-with-a-reference-to-authoryear}]'
|
||||
'{\\sphinxcrossref{The section with a reference '
|
||||
'to \\sphinxcite{index:authoryear}}}}') in result
|
||||
assert ('\\phantomsection\\label{\\detokenize{index:id34}}'
|
||||
assert ('\\phantomsection\\label{\\detokenize{index:id39}}'
|
||||
'{\\hyperref[\\detokenize{index:the-section-with-a-reference-to}]'
|
||||
'{\\sphinxcrossref{The section with a reference to }}}') in result
|
||||
assert ('First footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n'
|
||||
'First\n%\n\\end{footnote}') in result
|
||||
assert ('Second footnote: \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
assert ('Second footnote: %\n'
|
||||
'\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
'Second\n%\n\\end{footnote}') in result
|
||||
assert ('\\sphinxhref{http://sphinx-doc.org/}{Sphinx}'
|
||||
'%\n\\begin{footnote}[4]\\sphinxAtStartFootnote\n'
|
||||
@ -932,24 +947,22 @@ def test_latex_show_urls_is_no(app, status, warning):
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert ('Same footnote number \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
assert ('Same footnote number %\n'
|
||||
'\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
'footnote in bar\n%\n\\end{footnote} in bar.rst') in result
|
||||
assert ('Auto footnote number %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
'footnote in baz\n%\n\\end{footnote} in baz.rst') in result
|
||||
assert ('\\phantomsection\\label{\\detokenize{index:id33}}'
|
||||
assert ('\\phantomsection\\label{\\detokenize{index:id38}}'
|
||||
'{\\hyperref[\\detokenize{index:the-section-with-a-reference-to-authoryear}]'
|
||||
'{\\sphinxcrossref{The section with a reference '
|
||||
'to \\sphinxcite{index:authoryear}}}}') in result
|
||||
assert ('\\phantomsection\\label{\\detokenize{index:id34}}'
|
||||
assert ('\\phantomsection\\label{\\detokenize{index:id39}}'
|
||||
'{\\hyperref[\\detokenize{index:the-section-with-a-reference-to}]'
|
||||
'{\\sphinxcrossref{The section with a reference to }}}' in result)
|
||||
assert ('First footnote: %\n\\begin{footnote}[2]\\sphinxAtStartFootnote\n'
|
||||
'First\n%\n\\end{footnote}') in result
|
||||
assert ('Second footnote: \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
assert ('Second footnote: %\n'
|
||||
'\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
'Second\n%\n\\end{footnote}') in result
|
||||
assert '\\sphinxhref{http://sphinx-doc.org/}{Sphinx}' in result
|
||||
assert ('Third footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n'
|
||||
|
Loading…
Reference in New Issue
Block a user