mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2026-07-31 08:38:07 -05:00
Extra references to explicitly numbered footnote get page indication
To achieve this the simplest was to use also the \label/\ref mechanism as for footnotes typeset using footnotetext. Removing the hack into footnotehyper internal macro, we use an enriched scope, which will enable references with same number to each generate correct links.
This commit is contained in:
@@ -174,9 +174,6 @@
|
||||
\unrestored@protected@xdef\@thefnmark{\thempfn}%
|
||||
\endgroup
|
||||
\@footnotemark
|
||||
% Sphinx addition: store internal hyperref anchor reference to allow
|
||||
% multiple clickable reference to same numbered footnote
|
||||
\expandafter\xdef\csname FNH!\thesphinxscope!#1\endcsname{\Hy@footnote@currentHref}%
|
||||
\def\FNH@endfntext@fntext{\@footnotetext}%
|
||||
\FNH@startfntext
|
||||
}%
|
||||
@@ -331,34 +328,54 @@
|
||||
{\ifx\thepage\relax\else\sphinxfootref{#1}\fi}%
|
||||
% \sphinxfootref:
|
||||
% - \spx@opt@BeforeFootnote is from BeforeFootnote sphinxsetup option
|
||||
% - \ref branch:
|
||||
% the latex.py writer inserts at start of footnotetext contents
|
||||
% \phantomsection\label{<scope>.<num>}, so we can use \ref which
|
||||
% hyperref converts into an hyperlink. <scope> is the number of
|
||||
% the scope and <num> the number of the footnote, local to scope.
|
||||
% - FNH! branch:
|
||||
% explicitly numbered footnotes in latex.py output always use first
|
||||
% the footnote environment, then \sphinxfootnotemark. We patched
|
||||
% \FNH@footnoteenv@i to define the \FNH!<scope>!<number> macro as storage
|
||||
% for the internal hyperref target id for the hyperref anchor. So
|
||||
% we can then create an hyperlink from any \sphinxfootnotemark with
|
||||
% this <number> as argument and taking into account current <scope>.
|
||||
% - \ref:
|
||||
% the latex.py writer inserts a \phantomsection\label{<scope>.<num>}
|
||||
% whenever
|
||||
% - the footnote was explicitly numbered in sources,
|
||||
% - or it was in restrained context and is rendered using footnotetext
|
||||
%
|
||||
% These are the two types of footnotes that \sphinxfootnotemark must
|
||||
% handle. But for explicitly numbered footnotes the same number
|
||||
% can be found in document. So a secondary part in <scope> is updated
|
||||
% at each novel such footnote to know what is the target from then on
|
||||
% for \sphinxfootnotemark and already encountered [1], or [2],...
|
||||
%
|
||||
% LaTeX package varioref is not supported by hyperref (from its doc: "There
|
||||
% are too many problems with varioref. Nobody has time to sort them out.
|
||||
% Therefore this package is now unsupported.") So we will simply use our own
|
||||
% macros to access the page number of footnote text and decide whether to print
|
||||
% it. \pagename is internationalized by latex-babel.
|
||||
\def\spx@thefnmark#1#2{%
|
||||
% #1=label for reference, #2=page where footnote was printed
|
||||
\ifx\spx@tempa\spx@tempb
|
||||
% same page
|
||||
#1%
|
||||
\else
|
||||
\sphinxthefootnotemark{#1}{#2}%
|
||||
\fi
|
||||
}%
|
||||
\def\sphinxfootref@get #1#2#3#4#5\relax{%
|
||||
\def\sphinxfootref@label{#1}%
|
||||
\def\sphinxfootref@page {#2}%
|
||||
\def\sphinxfootref@Href {#4}%
|
||||
}%
|
||||
\protected\def\sphinxfootref#1{% #1 always explicit number in Sphinx usage
|
||||
\spx@opt@BeforeFootnote
|
||||
\ifcsname FNH!\thesphinxscope!#1\endcsname
|
||||
\gdef\@thefnmark{#1}% \@thefnmark always redefined globally by latex
|
||||
\let\spx@saved@makefnmark\@makefnmark
|
||||
\edef\@makefnmark{% we pre-expand the sphinxscope value, to be safe
|
||||
\noexpand\hyper@linkstart{link}{\@nameuse{FNH!\thesphinxscope!#1}}%
|
||||
\noexpand\spx@saved@makefnmark
|
||||
\noexpand\hyper@linkend
|
||||
\ltx@ifundefined{r@\thesphinxscope.#1}%
|
||||
{\gdef\@thefnmark{?}\H@@footnotemark}%
|
||||
{\expandafter\expandafter\expandafter\sphinxfootref@get
|
||||
\csname r@\thesphinxscope.#1\endcsname\relax
|
||||
\edef\spx@tempa{\thepage}\edef\spx@tempb{\sphinxfootref@page}%
|
||||
\protected@xdef\@thefnmark{\spx@thefnmark{\sphinxfootref@label}{\sphinxfootref@page}}%
|
||||
\let\spx@@makefnmark\@makefnmark
|
||||
\def\@makefnmark{%
|
||||
\hyper@linkstart{link}{\sphinxfootref@Href}%
|
||||
\spx@@makefnmark
|
||||
\hyper@linkend
|
||||
}%
|
||||
\H@@footnotemark
|
||||
\let\@makefnmark\spx@saved@makefnmark
|
||||
\else
|
||||
\xdef\@thefnmark{\noexpand\ref{\thesphinxscope.#1}}% #1 = number
|
||||
\H@@footnotemark
|
||||
\fi
|
||||
\let\@makefnmark\spx@@makefnmark
|
||||
}%
|
||||
}%
|
||||
\AtBeginDocument{%
|
||||
% let hyperref less complain
|
||||
|
||||
@@ -847,11 +847,25 @@
|
||||
% Support scopes for footnote numbering
|
||||
\newcounter{sphinxscope}
|
||||
\newcommand{\sphinxstepscope}{\stepcounter{sphinxscope}}
|
||||
% Explictly numbered footnotes may be referred to, and for this to be
|
||||
% clickable we need to have only one target. So we will step this at each
|
||||
% explicit footnote and let \thesphinxscope take it into account
|
||||
\newcounter{sphinxexplicit}
|
||||
\newcommand{\sphinxstepexplicit}{\stepcounter{sphinxexplicit}}
|
||||
% Some babel/polyglossia languages fiddle with \@arabic, so let's be extra
|
||||
% cautious and redefine \thesphinxscope with \number not \@arabic.
|
||||
% Memo: we expect some subtle redefinition of \thesphinxscope to be a part of page
|
||||
% scoping for footnotes, when we shall implement it.
|
||||
\renewcommand{\thesphinxscope}{\number\value{sphinxscope}}
|
||||
\renewcommand{\thesphinxscope}{\number\value{sphinxscope}.\number\value{sphinxexplicit}}
|
||||
\newcommand\sphinxthefootnotemark[2]{%
|
||||
% this is used to make reference to an explicitly numbered footnote not on same page
|
||||
% #1=label of footnote text, #2=page number where footnote text was printed
|
||||
\ifdefined\pagename
|
||||
\pagename\space#2, % <- space
|
||||
\else
|
||||
p. #2, % <- space
|
||||
\fi #1% no space
|
||||
}
|
||||
% Support large numbered footnotes in minipage
|
||||
% But now obsolete due to systematic use of \savenotes/\spewnotes
|
||||
% when minipages are in use in the various macro definitions next.
|
||||
|
||||
@@ -841,10 +841,15 @@ 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.attributes:
|
||||
self.body.append('\\sphinxstepexplicit ')
|
||||
if self.in_parsed_literal:
|
||||
self.body.append('\\begin{footnote}[%s]' % label.astext())
|
||||
else:
|
||||
self.body.append('%%\n\\begin{footnote}[%s]' % label.astext())
|
||||
if 'auto' not in node.attributes:
|
||||
self.body.append('\\phantomsection'
|
||||
'\\label{\\thesphinxscope.%s}%%\n' % label.astext())
|
||||
self.body.append('\\sphinxAtStartFootnote\n')
|
||||
|
||||
def depart_footnote(self, node: Element) -> None:
|
||||
|
||||
@@ -724,7 +724,8 @@ def test_footnote(app, status, warning):
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert ('\\begin{footnote}[1]\\sphinxAtStartFootnote\nnumbered\n%\n'
|
||||
assert ('\\sphinxstepexplicit %\n\\begin{footnote}[1]\\phantomsection'
|
||||
'\\label{\\thesphinxscope.1}%\n\\sphinxAtStartFootnote\nnumbered\n%\n'
|
||||
'\\end{footnote}') in result
|
||||
assert ('\\begin{footnote}[2]\\sphinxAtStartFootnote\nauto numbered\n%\n'
|
||||
'\\end{footnote}') in result
|
||||
@@ -805,7 +806,9 @@ def test_latex_show_urls_is_inline(app, status, warning):
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert ('Same footnote number %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
assert ('Same footnote number \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\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
|
||||
@@ -819,7 +822,9 @@ def test_latex_show_urls_is_inline(app, status, warning):
|
||||
'{\\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: %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
assert ('Second footnote: \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Second\n%\n\\end{footnote}') 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'
|
||||
@@ -857,7 +862,9 @@ def test_latex_show_urls_is_footnote(app, status, warning):
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert ('Same footnote number %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
assert ('Same footnote number \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\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
|
||||
@@ -870,7 +877,9 @@ def test_latex_show_urls_is_footnote(app, status, warning):
|
||||
'{\\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: %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
assert ('Second footnote: \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\sphinxAtStartFootnote\n'
|
||||
'Second\n%\n\\end{footnote}') in result
|
||||
assert ('\\sphinxhref{http://sphinx-doc.org/}{Sphinx}'
|
||||
'%\n\\begin{footnote}[4]\\sphinxAtStartFootnote\n'
|
||||
@@ -920,7 +929,9 @@ def test_latex_show_urls_is_no(app, status, warning):
|
||||
print(result)
|
||||
print(status.getvalue())
|
||||
print(warning.getvalue())
|
||||
assert ('Same footnote number %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
assert ('Same footnote number \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\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
|
||||
@@ -933,7 +944,9 @@ def test_latex_show_urls_is_no(app, status, warning):
|
||||
'{\\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: %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n'
|
||||
assert ('Second footnote: \\sphinxstepexplicit %\n'
|
||||
'\\begin{footnote}[1]\\phantomsection\\label{\\thesphinxscope.1}%\n'
|
||||
'\\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'
|
||||
|
||||
Reference in New Issue
Block a user