From a0b0a08e38d59c9751f837e6a3f41a980f6aa18d Mon Sep 17 00:00:00 2001 From: jfbu Date: Fri, 5 Feb 2021 14:56:23 +0100 Subject: [PATCH 01/25] LaTeX footnotes from caption titles now hyperlinked in PDF This is fifth item of #2616 --- sphinx/texinputs/footnotehyper-sphinx.sty | 13 +++++-- sphinx/writers/latex.py | 3 +- tests/test_build_latex.py | 42 +++++++++++++++-------- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/sphinx/texinputs/footnotehyper-sphinx.sty b/sphinx/texinputs/footnotehyper-sphinx.sty index 5ed0e8e6e..8fce532b1 100644 --- a/sphinx/texinputs/footnotehyper-sphinx.sty +++ b/sphinx/texinputs/footnotehyper-sphinx.sty @@ -325,8 +325,17 @@ % some extras for Sphinx : % \sphinxfootnotemark: usable in section titles and silently removed from TOCs. \def\sphinxfootnotemark [#1]% - {\ifx\thepage\relax\else\protect\spx@opt@BeforeFootnote - \protect\footnotemark[#1]\fi}% + {\ifx\thepage\relax\else\sphinxfootref{#1}\fi}% +% \sphinxfootref: almost same as \footref from footmisc package as +% re-defined by hyperref: +% - \spx@opt@BeforeFootnote is from BeforeFootnote sphinxsetup option +% - 'fn:' prefix is output by LaTeX writer in \label of footnotetext +\protected\def\sphinxfootref#1{\spx@opt@BeforeFootnote + \begingroup + \unrestored@protected@xdef\@thefnmark{\ref{fn:#1}}% #1 = number + \endgroup + \H@@footnotemark +}% \AtBeginDocument{% % let hyperref less complain \pdfstringdefDisableCommands{\def\sphinxfootnotemark [#1]{}}% diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index b3ec5de56..79619c6ad 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1738,7 +1738,8 @@ class LaTeXTranslator(SphinxTranslator): def visit_footnotetext(self, node: Element) -> None: label = cast(nodes.label, node[0]) self.body.append('%%\n\\begin{footnotetext}[%s]' - '\\sphinxAtStartFootnote\n' % label.astext()) + '\\phantomsection\\label{fn:%s}%%\n' + '\\sphinxAtStartFootnote\n' % (label.astext(), label.astext())) def depart_footnotetext(self, node: Element) -> None: # the \ignorespaces in particular for after table header use diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index f268064c6..ad245e5f6 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -732,9 +732,11 @@ def test_footnote(app, status, warning): assert '\\sphinxcite{footnote:bar}' in result assert ('\\bibitem[bar]{footnote:bar}\n\\sphinxAtStartPar\ncite\n') in result assert '\\sphinxcaption{Table caption \\sphinxfootnotemark[4]' in result - assert ('\\hline%\n\\begin{footnotetext}[4]\\sphinxAtStartFootnote\n' + assert ('\\hline%\n\\begin{footnotetext}[4]\\phantomsection\\label{fn:4}%\n' + '\\sphinxAtStartFootnote\n' 'footnote in table caption\n%\n\\end{footnotetext}\\ignorespaces %\n' - '\\begin{footnotetext}[5]\\sphinxAtStartFootnote\n' + '\\begin{footnotetext}[5]\\phantomsection\\label{fn:5}%\n' + '\\sphinxAtStartFootnote\n' 'footnote in table header\n%\n\\end{footnotetext}\\ignorespaces ' '\n\\sphinxAtStartPar\n' 'VIDIOC\\_CROPCAP\n&\n\\sphinxAtStartPar\n') in result @@ -760,19 +762,23 @@ def test_reference_in_caption_and_codeblock_in_footnote(app, status, warning): assert '\\subsubsection*{The rubric title with a reference to {[}AuthorYear{]}}' in result assert ('\\chapter{The section with a reference to \\sphinxfootnotemark[5]}\n' '\\label{\\detokenize{index:the-section-with-a-reference-to}}' - '%\n\\begin{footnotetext}[5]\\sphinxAtStartFootnote\n' + '%\n\\begin{footnotetext}[5]\\phantomsection\\label{fn:5}%\n' + '\\sphinxAtStartFootnote\n' 'Footnote in section\n%\n\\end{footnotetext}') in result assert ('\\caption{This is the figure caption with a footnote to ' '\\sphinxfootnotemark[7].}\\label{\\detokenize{index:id29}}\\end{figure}\n' - '%\n\\begin{footnotetext}[7]\\sphinxAtStartFootnote\n' + '%\n\\begin{footnotetext}[7]\\phantomsection\\label{fn:7}%\n' + '\\sphinxAtStartFootnote\n' 'Footnote in caption\n%\n\\end{footnotetext}') in result assert ('\\sphinxcaption{footnote \\sphinxfootnotemark[8] in ' 'caption of normal table}\\label{\\detokenize{index:id30}}') in result assert ('\\caption{footnote \\sphinxfootnotemark[9] ' 'in caption \\sphinxfootnotemark[10] of longtable\\strut}') in result - assert ('\\endlastfoot\n%\n\\begin{footnotetext}[9]\\sphinxAtStartFootnote\n' + assert ('\\endlastfoot\n%\n\\begin{footnotetext}[9]\\phantomsection\\label{fn:9}%\n' + '\\sphinxAtStartFootnote\n' 'Foot note in longtable\n%\n\\end{footnotetext}\\ignorespaces %\n' - '\\begin{footnotetext}[10]\\sphinxAtStartFootnote\n' + '\\begin{footnotetext}[10]\\phantomsection\\label{fn:10}%\n' + '\\sphinxAtStartFootnote\n' 'Second footnote in caption of longtable\n') in result assert ('This is a reference to the code\\sphinxhyphen{}block in the footnote:\n' '{\\hyperref[\\detokenize{index:codeblockinfootnote}]' @@ -812,7 +818,8 @@ def test_latex_show_urls_is_inline(app, status, warning): assert '\\sphinxhref{http://sphinx-doc.org/}{Sphinx} (http://sphinx\\sphinxhyphen{}doc.org/)' in result assert ('Third footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n' 'Third \\sphinxfootnotemark[4]\n%\n\\end{footnote}%\n' - '\\begin{footnotetext}[4]\\sphinxAtStartFootnote\n' + '\\begin{footnotetext}[4]\\phantomsection\\label{fn:4}%\n' + '\\sphinxAtStartFootnote\n' 'Footnote inside footnote\n%\n\\end{footnotetext}\\ignorespaces') in result assert ('\\sphinxhref{http://sphinx-doc.org/~test/}{URL including tilde} ' '(http://sphinx\\sphinxhyphen{}doc.org/\\textasciitilde{}test/)') in result @@ -820,7 +827,8 @@ def test_latex_show_urls_is_inline(app, status, warning): '(http://sphinx\\sphinxhyphen{}doc.org/)}] ' '\\leavevmode\n\\sphinxAtStartPar\nDescription' in result) assert ('\\item[{Footnote in term \\sphinxfootnotemark[6]}] ' - '\\leavevmode%\n\\begin{footnotetext}[6]\\sphinxAtStartFootnote\n' + '\\leavevmode%\n\\begin{footnotetext}[6]\\phantomsection\\label{fn:6}%\n' + '\\sphinxAtStartFootnote\n' 'Footnote in term\n%\n\\end{footnotetext}\\ignorespaces ' '\n\\sphinxAtStartPar\nDescription') in result assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{Term in deflist} ' @@ -861,7 +869,8 @@ def test_latex_show_urls_is_footnote(app, status, warning): '\\sphinxnolinkurl{http://sphinx-doc.org/}\n%\n\\end{footnote}') in result assert ('Third footnote: %\n\\begin{footnote}[6]\\sphinxAtStartFootnote\n' 'Third \\sphinxfootnotemark[7]\n%\n\\end{footnote}%\n' - '\\begin{footnotetext}[7]\\sphinxAtStartFootnote\n' + '\\begin{footnotetext}[7]\\phantomsection\\label{fn:7}%\n' + '\\sphinxAtStartFootnote\n' 'Footnote inside footnote\n%\n' '\\end{footnotetext}\\ignorespaces') in result assert ('\\sphinxhref{http://sphinx-doc.org/~test/}{URL including tilde}' @@ -869,16 +878,19 @@ def test_latex_show_urls_is_footnote(app, status, warning): '\\sphinxnolinkurl{http://sphinx-doc.org/~test/}\n%\n\\end{footnote}') in result assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}' '{URL in term}\\sphinxfootnotemark[9]}] ' - '\\leavevmode%\n\\begin{footnotetext}[9]\\sphinxAtStartFootnote\n' + '\\leavevmode%\n\\begin{footnotetext}[9]\\phantomsection\\label{fn:9}%\n' + '\\sphinxAtStartFootnote\n' '\\sphinxnolinkurl{http://sphinx-doc.org/}\n%\n' '\\end{footnotetext}\\ignorespaces \n\\sphinxAtStartPar\nDescription') in result assert ('\\item[{Footnote in term \\sphinxfootnotemark[11]}] ' - '\\leavevmode%\n\\begin{footnotetext}[11]\\sphinxAtStartFootnote\n' + '\\leavevmode%\n\\begin{footnotetext}[11]\\phantomsection\\label{fn:11}%\n' + '\\sphinxAtStartFootnote\n' 'Footnote in term\n%\n\\end{footnotetext}\\ignorespaces ' '\n\\sphinxAtStartPar\nDescription') in result assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{Term in deflist}' '\\sphinxfootnotemark[10]}] ' - '\\leavevmode%\n\\begin{footnotetext}[10]\\sphinxAtStartFootnote\n' + '\\leavevmode%\n\\begin{footnotetext}[10]\\phantomsection\\label{fn:10}%\n' + '\\sphinxAtStartFootnote\n' '\\sphinxnolinkurl{http://sphinx-doc.org/}\n%\n' '\\end{footnotetext}\\ignorespaces \n\\sphinxAtStartPar\nDescription') in result assert ('\\sphinxurl{https://github.com/sphinx-doc/sphinx}\n' in result) @@ -914,13 +926,15 @@ def test_latex_show_urls_is_no(app, status, warning): assert '\\sphinxhref{http://sphinx-doc.org/}{Sphinx}' in result assert ('Third footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n' 'Third \\sphinxfootnotemark[4]\n%\n\\end{footnote}%\n' - '\\begin{footnotetext}[4]\\sphinxAtStartFootnote\n' + '\\begin{footnotetext}[4]\\phantomsection\\label{fn:4}%\n' + '\\sphinxAtStartFootnote\n' 'Footnote inside footnote\n%\n\\end{footnotetext}\\ignorespaces') in result assert '\\sphinxhref{http://sphinx-doc.org/~test/}{URL including tilde}' in result assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{URL in term}}] ' '\\leavevmode\n\\sphinxAtStartPar\nDescription') in result assert ('\\item[{Footnote in term \\sphinxfootnotemark[6]}] ' - '\\leavevmode%\n\\begin{footnotetext}[6]\\sphinxAtStartFootnote\n' + '\\leavevmode%\n\\begin{footnotetext}[6]\\phantomsection\\label{fn:6}%\n' + '\\sphinxAtStartFootnote\n' 'Footnote in term\n%\n\\end{footnotetext}\\ignorespaces ' '\n\\sphinxAtStartPar\nDescription') in result assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{Term in deflist}}] ' From eb91fd292f4cebc139d1139f2a34bdd3abb881ba Mon Sep 17 00:00:00 2001 From: jfbu Date: Fri, 5 Feb 2021 16:49:41 +0100 Subject: [PATCH 02/25] Update CHANGES --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index 9e3e29fe0..42fe77104 100644 --- a/CHANGES +++ b/CHANGES @@ -114,6 +114,7 @@ Bugs fixed * #8683: :confval:`html_last_updated_fmt` generates wrong time zone for %Z * #1112: ``download`` role creates duplicated copies when relative path is specified +* #2616 (fifth item): LaTeX: footnotes from captions are not clickable * #7576: LaTeX with French babel and memoir crash: "Illegal parameter number in definition of ``\FNH@prefntext``" * #8072: LaTeX: Directive :rst:dir:`hlist` not implemented in LaTeX From 98f4732a98956879796cfba476392fffc8913d55 Mon Sep 17 00:00:00 2001 From: jfbu Date: Fri, 5 Feb 2021 20:26:17 +0100 Subject: [PATCH 03/25] LaTeX: make all references to explicitly numbered footnote clickable --- sphinx/texinputs/footnotehyper-sphinx.sty | 41 ++++++++++++++++++----- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/sphinx/texinputs/footnotehyper-sphinx.sty b/sphinx/texinputs/footnotehyper-sphinx.sty index 8fce532b1..156152ea4 100644 --- a/sphinx/texinputs/footnotehyper-sphinx.sty +++ b/sphinx/texinputs/footnotehyper-sphinx.sty @@ -174,6 +174,9 @@ \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@!#1\endcsname{\Hy@footnote@currentHref}% \def\FNH@endfntext@fntext{\@footnotetext}% \FNH@startfntext }% @@ -326,15 +329,37 @@ % \sphinxfootnotemark: usable in section titles and silently removed from TOCs. \def\sphinxfootnotemark [#1]% {\ifx\thepage\relax\else\sphinxfootref{#1}\fi}% -% \sphinxfootref: almost same as \footref from footmisc package as -% re-defined by hyperref: +% \sphinxfootref: inspired by \footref from footmisc package as +% re-defined by hyperref, but with an extra FNH@! branch: % - \spx@opt@BeforeFootnote is from BeforeFootnote sphinxsetup option -% - 'fn:' prefix is output by LaTeX writer in \label of footnotetext -\protected\def\sphinxfootref#1{\spx@opt@BeforeFootnote - \begingroup - \unrestored@protected@xdef\@thefnmark{\ref{fn:#1}}% #1 = number - \endgroup - \H@@footnotemark +% - \ref{fn:#1} branch: +% the latex.py writer inserts at start of footnotetext contents +% \phantomsection\label{fn:}, so we can use \ref which +% hyperref converts into an hyperlink. This is basically the +% idea of the \footref macro from package footmisc. +% - 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@! 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 as argument. +\protected\def\sphinxfootref#1{% + \spx@opt@BeforeFootnote + \ifcsname FNH@!#1\endcsname + \def\@thefnmark{#1}% + \let\spx@saved@makefnmark\@makefnmark + \def\@makefnmark{% + \hyper@linkstart{link}{\@nameuse{FNH@!#1}}% + \spx@saved@makefnmark + \hyper@linkend + }% + \H@@footnotemark + \let\@makefnmark\spx@saved@makefnmark + \else + \def\@thefnmark{\ref{fn:#1}}% #1 = number + \H@@footnotemark + \fi }% \AtBeginDocument{% % let hyperref less complain From 9c823478e01645aff448f91d83a9d5eb89dbaf6e Mon Sep 17 00:00:00 2001 From: jfbu Date: Fri, 5 Feb 2021 20:34:15 +0100 Subject: [PATCH 04/25] Update CHANGES --- CHANGES | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 42fe77104..3c0645cb9 100644 --- a/CHANGES +++ b/CHANGES @@ -114,7 +114,9 @@ Bugs fixed * #8683: :confval:`html_last_updated_fmt` generates wrong time zone for %Z * #1112: ``download`` role creates duplicated copies when relative path is specified -* #2616 (fifth item): LaTeX: footnotes from captions are not clickable +* #2616 (fifth item): LaTeX: footnotes from captions are not clickable, + and for manually numbered footnotes only first one with same number is + an hyperlink * #7576: LaTeX with French babel and memoir crash: "Illegal parameter number in definition of ``\FNH@prefntext``" * #8072: LaTeX: Directive :rst:dir:`hlist` not implemented in LaTeX From 628c58abef12b25d491f234fef4da0b347cee0dc Mon Sep 17 00:00:00 2001 From: jfbu Date: Sat, 6 Feb 2021 10:45:50 +0100 Subject: [PATCH 05/25] Prepare for scope-limited footnote numbering --- sphinx/texinputs/footnotehyper-sphinx.sty | 35 +++++++++---------- sphinx/texinputs/sphinx.sty | 2 ++ sphinx/writers/latex.py | 2 +- tests/test_build_latex.py | 42 +++++++++++++++-------- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/sphinx/texinputs/footnotehyper-sphinx.sty b/sphinx/texinputs/footnotehyper-sphinx.sty index 156152ea4..b5babc279 100644 --- a/sphinx/texinputs/footnotehyper-sphinx.sty +++ b/sphinx/texinputs/footnotehyper-sphinx.sty @@ -176,7 +176,7 @@ \@footnotemark % Sphinx addition: store internal hyperref anchor reference to allow % multiple clickable reference to same numbered footnote - \expandafter\xdef\csname FNH@!#1\endcsname{\Hy@footnote@currentHref}% + \expandafter\xdef\csname FNH!\number\value{sphinxscope}!#1\endcsname{\Hy@footnote@currentHref}% \def\FNH@endfntext@fntext{\@footnotetext}% \FNH@startfntext }% @@ -329,35 +329,34 @@ % \sphinxfootnotemark: usable in section titles and silently removed from TOCs. \def\sphinxfootnotemark [#1]% {\ifx\thepage\relax\else\sphinxfootref{#1}\fi}% -% \sphinxfootref: inspired by \footref from footmisc package as -% re-defined by hyperref, but with an extra FNH@! branch: +% \sphinxfootref: % - \spx@opt@BeforeFootnote is from BeforeFootnote sphinxsetup option -% - \ref{fn:#1} branch: +% - \ref branch: % the latex.py writer inserts at start of footnotetext contents -% \phantomsection\label{fn:}, so we can use \ref which -% hyperref converts into an hyperlink. This is basically the -% idea of the \footref macro from package footmisc. -% - FNH@! branch: +% \phantomsection\label{.}, so we can use \ref which +% hyperref converts into an hyperlink. is the number of +% the scope and 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@! macro as storage +% \FNH@footnoteenv@i to define the \FNH!! 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 as argument. -\protected\def\sphinxfootref#1{% +% this as argument and taking into account current . +\protected\def\sphinxfootref#1{% #1 always explicit number in Sphinx usage \spx@opt@BeforeFootnote - \ifcsname FNH@!#1\endcsname - \def\@thefnmark{#1}% + \ifcsname FNH!\number\value{sphinxscope}!#1\endcsname + \gdef\@thefnmark{#1}% \@thefnmark always redefined globally by latex \let\spx@saved@makefnmark\@makefnmark - \def\@makefnmark{% - \hyper@linkstart{link}{\@nameuse{FNH@!#1}}% - \spx@saved@makefnmark - \hyper@linkend + \edef\@makefnmark{% we pre-expand the sphinxscope value, to be safe + \noexpand\hyper@linkstart{link}{\@nameuse{FNH!\number\value{sphinxscope}!#1}}% + \noexpand\spx@saved@makefnmark + \noexpand\hyper@linkend }% \H@@footnotemark \let\@makefnmark\spx@saved@makefnmark \else - \def\@thefnmark{\ref{fn:#1}}% #1 = number + \xdef\@thefnmark{\noexpand\ref{\number\value{sphinxscope}.#1}}% #1 = number \H@@footnotemark \fi }% diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index eb5617dda..831f736f1 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -844,6 +844,8 @@ %% FOOTNOTES % +% Support scopes for footnote numbering +\newcounter{sphinxscope} % 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. diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 79619c6ad..77dde08d8 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1738,7 +1738,7 @@ class LaTeXTranslator(SphinxTranslator): def visit_footnotetext(self, node: Element) -> None: label = cast(nodes.label, node[0]) self.body.append('%%\n\\begin{footnotetext}[%s]' - '\\phantomsection\\label{fn:%s}%%\n' + '\\phantomsection\\label{\\number\\value{sphinxscope}.%s}%%\n' '\\sphinxAtStartFootnote\n' % (label.astext(), label.astext())) def depart_footnotetext(self, node: Element) -> None: diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index ad245e5f6..76c254f9c 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -732,10 +732,12 @@ def test_footnote(app, status, warning): assert '\\sphinxcite{footnote:bar}' in result assert ('\\bibitem[bar]{footnote:bar}\n\\sphinxAtStartPar\ncite\n') in result assert '\\sphinxcaption{Table caption \\sphinxfootnotemark[4]' in result - assert ('\\hline%\n\\begin{footnotetext}[4]\\phantomsection\\label{fn:4}%\n' + assert ('\\hline%\n\\begin{footnotetext}[4]' + '\\phantomsection\\label{\\number\\value{sphinxscope}.4}%\n' '\\sphinxAtStartFootnote\n' 'footnote in table caption\n%\n\\end{footnotetext}\\ignorespaces %\n' - '\\begin{footnotetext}[5]\\phantomsection\\label{fn:5}%\n' + '\\begin{footnotetext}[5]' + '\\phantomsection\\label{\\number\\value{sphinxscope}.5}%\n' '\\sphinxAtStartFootnote\n' 'footnote in table header\n%\n\\end{footnotetext}\\ignorespaces ' '\n\\sphinxAtStartPar\n' @@ -762,22 +764,26 @@ def test_reference_in_caption_and_codeblock_in_footnote(app, status, warning): assert '\\subsubsection*{The rubric title with a reference to {[}AuthorYear{]}}' in result assert ('\\chapter{The section with a reference to \\sphinxfootnotemark[5]}\n' '\\label{\\detokenize{index:the-section-with-a-reference-to}}' - '%\n\\begin{footnotetext}[5]\\phantomsection\\label{fn:5}%\n' + '%\n\\begin{footnotetext}[5]' + '\\phantomsection\\label{\\number\\value{sphinxscope}.5}%\n' '\\sphinxAtStartFootnote\n' 'Footnote in section\n%\n\\end{footnotetext}') in result assert ('\\caption{This is the figure caption with a footnote to ' '\\sphinxfootnotemark[7].}\\label{\\detokenize{index:id29}}\\end{figure}\n' - '%\n\\begin{footnotetext}[7]\\phantomsection\\label{fn:7}%\n' + '%\n\\begin{footnotetext}[7]' + '\\phantomsection\\label{\\number\\value{sphinxscope}.7}%\n' '\\sphinxAtStartFootnote\n' 'Footnote in caption\n%\n\\end{footnotetext}') in result assert ('\\sphinxcaption{footnote \\sphinxfootnotemark[8] in ' 'caption of normal table}\\label{\\detokenize{index:id30}}') in result assert ('\\caption{footnote \\sphinxfootnotemark[9] ' 'in caption \\sphinxfootnotemark[10] of longtable\\strut}') in result - assert ('\\endlastfoot\n%\n\\begin{footnotetext}[9]\\phantomsection\\label{fn:9}%\n' + assert ('\\endlastfoot\n%\n\\begin{footnotetext}[9]' + '\\phantomsection\\label{\\number\\value{sphinxscope}.9}%\n' '\\sphinxAtStartFootnote\n' 'Foot note in longtable\n%\n\\end{footnotetext}\\ignorespaces %\n' - '\\begin{footnotetext}[10]\\phantomsection\\label{fn:10}%\n' + '\\begin{footnotetext}[10]' + '\\phantomsection\\label{\\number\\value{sphinxscope}.10}%\n' '\\sphinxAtStartFootnote\n' 'Second footnote in caption of longtable\n') in result assert ('This is a reference to the code\\sphinxhyphen{}block in the footnote:\n' @@ -818,7 +824,8 @@ def test_latex_show_urls_is_inline(app, status, warning): assert '\\sphinxhref{http://sphinx-doc.org/}{Sphinx} (http://sphinx\\sphinxhyphen{}doc.org/)' in result assert ('Third footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n' 'Third \\sphinxfootnotemark[4]\n%\n\\end{footnote}%\n' - '\\begin{footnotetext}[4]\\phantomsection\\label{fn:4}%\n' + '\\begin{footnotetext}[4]' + '\\phantomsection\\label{\\number\\value{sphinxscope}.4}%\n' '\\sphinxAtStartFootnote\n' 'Footnote inside footnote\n%\n\\end{footnotetext}\\ignorespaces') in result assert ('\\sphinxhref{http://sphinx-doc.org/~test/}{URL including tilde} ' @@ -827,7 +834,8 @@ def test_latex_show_urls_is_inline(app, status, warning): '(http://sphinx\\sphinxhyphen{}doc.org/)}] ' '\\leavevmode\n\\sphinxAtStartPar\nDescription' in result) assert ('\\item[{Footnote in term \\sphinxfootnotemark[6]}] ' - '\\leavevmode%\n\\begin{footnotetext}[6]\\phantomsection\\label{fn:6}%\n' + '\\leavevmode%\n\\begin{footnotetext}[6]' + '\\phantomsection\\label{\\number\\value{sphinxscope}.6}%\n' '\\sphinxAtStartFootnote\n' 'Footnote in term\n%\n\\end{footnotetext}\\ignorespaces ' '\n\\sphinxAtStartPar\nDescription') in result @@ -869,7 +877,8 @@ def test_latex_show_urls_is_footnote(app, status, warning): '\\sphinxnolinkurl{http://sphinx-doc.org/}\n%\n\\end{footnote}') in result assert ('Third footnote: %\n\\begin{footnote}[6]\\sphinxAtStartFootnote\n' 'Third \\sphinxfootnotemark[7]\n%\n\\end{footnote}%\n' - '\\begin{footnotetext}[7]\\phantomsection\\label{fn:7}%\n' + '\\begin{footnotetext}[7]' + '\\phantomsection\\label{\\number\\value{sphinxscope}.7}%\n' '\\sphinxAtStartFootnote\n' 'Footnote inside footnote\n%\n' '\\end{footnotetext}\\ignorespaces') in result @@ -878,18 +887,21 @@ def test_latex_show_urls_is_footnote(app, status, warning): '\\sphinxnolinkurl{http://sphinx-doc.org/~test/}\n%\n\\end{footnote}') in result assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}' '{URL in term}\\sphinxfootnotemark[9]}] ' - '\\leavevmode%\n\\begin{footnotetext}[9]\\phantomsection\\label{fn:9}%\n' + '\\leavevmode%\n\\begin{footnotetext}[9]' + '\\phantomsection\\label{\\number\\value{sphinxscope}.9}%\n' '\\sphinxAtStartFootnote\n' '\\sphinxnolinkurl{http://sphinx-doc.org/}\n%\n' '\\end{footnotetext}\\ignorespaces \n\\sphinxAtStartPar\nDescription') in result assert ('\\item[{Footnote in term \\sphinxfootnotemark[11]}] ' - '\\leavevmode%\n\\begin{footnotetext}[11]\\phantomsection\\label{fn:11}%\n' + '\\leavevmode%\n\\begin{footnotetext}[11]' + '\\phantomsection\\label{\\number\\value{sphinxscope}.11}%\n' '\\sphinxAtStartFootnote\n' 'Footnote in term\n%\n\\end{footnotetext}\\ignorespaces ' '\n\\sphinxAtStartPar\nDescription') in result assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{Term in deflist}' '\\sphinxfootnotemark[10]}] ' - '\\leavevmode%\n\\begin{footnotetext}[10]\\phantomsection\\label{fn:10}%\n' + '\\leavevmode%\n\\begin{footnotetext}[10]' + '\\phantomsection\\label{\\number\\value{sphinxscope}.10}%\n' '\\sphinxAtStartFootnote\n' '\\sphinxnolinkurl{http://sphinx-doc.org/}\n%\n' '\\end{footnotetext}\\ignorespaces \n\\sphinxAtStartPar\nDescription') in result @@ -926,14 +938,16 @@ def test_latex_show_urls_is_no(app, status, warning): assert '\\sphinxhref{http://sphinx-doc.org/}{Sphinx}' in result assert ('Third footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n' 'Third \\sphinxfootnotemark[4]\n%\n\\end{footnote}%\n' - '\\begin{footnotetext}[4]\\phantomsection\\label{fn:4}%\n' + '\\begin{footnotetext}[4]' + '\\phantomsection\\label{\\number\\value{sphinxscope}.4}%\n' '\\sphinxAtStartFootnote\n' 'Footnote inside footnote\n%\n\\end{footnotetext}\\ignorespaces') in result assert '\\sphinxhref{http://sphinx-doc.org/~test/}{URL including tilde}' in result assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{URL in term}}] ' '\\leavevmode\n\\sphinxAtStartPar\nDescription') in result assert ('\\item[{Footnote in term \\sphinxfootnotemark[6]}] ' - '\\leavevmode%\n\\begin{footnotetext}[6]\\phantomsection\\label{fn:6}%\n' + '\\leavevmode%\n\\begin{footnotetext}[6]' + '\\phantomsection\\label{\\number\\value{sphinxscope}.6}%\n' '\\sphinxAtStartFootnote\n' 'Footnote in term\n%\n\\end{footnotetext}\\ignorespaces ' '\n\\sphinxAtStartPar\nDescription') in result From c55216858e59f805a5655d026db6d647bff3412d Mon Sep 17 00:00:00 2001 From: jfbu Date: Sat, 6 Feb 2021 11:38:24 +0100 Subject: [PATCH 06/25] Abstract \stepcounter{sphinxscope} into macro for flexibility --- sphinx/texinputs/sphinx.sty | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 831f736f1..cfe2164c1 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -846,6 +846,7 @@ % % Support scopes for footnote numbering \newcounter{sphinxscope} +\newcommand{\sphinxstepscope}{\stepcounter{sphinxscope}} % 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. From 6275a7756f5a19ecf6968d4a7f18485f733cc94d Mon Sep 17 00:00:00 2001 From: jfbu Date: Sat, 6 Feb 2021 13:43:12 +0100 Subject: [PATCH 07/25] Use \thesphinxscope mark-up (shorter, and may help trick for page scope) --- sphinx/texinputs/footnotehyper-sphinx.sty | 8 +++---- sphinx/writers/latex.py | 2 +- tests/test_build_latex.py | 28 +++++++++++------------ 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/sphinx/texinputs/footnotehyper-sphinx.sty b/sphinx/texinputs/footnotehyper-sphinx.sty index b5babc279..f8a153c3e 100644 --- a/sphinx/texinputs/footnotehyper-sphinx.sty +++ b/sphinx/texinputs/footnotehyper-sphinx.sty @@ -176,7 +176,7 @@ \@footnotemark % Sphinx addition: store internal hyperref anchor reference to allow % multiple clickable reference to same numbered footnote - \expandafter\xdef\csname FNH!\number\value{sphinxscope}!#1\endcsname{\Hy@footnote@currentHref}% + \expandafter\xdef\csname FNH!\thesphinxscope!#1\endcsname{\Hy@footnote@currentHref}% \def\FNH@endfntext@fntext{\@footnotetext}% \FNH@startfntext }% @@ -345,18 +345,18 @@ % this as argument and taking into account current . \protected\def\sphinxfootref#1{% #1 always explicit number in Sphinx usage \spx@opt@BeforeFootnote - \ifcsname FNH!\number\value{sphinxscope}!#1\endcsname + \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!\number\value{sphinxscope}!#1}}% + \noexpand\hyper@linkstart{link}{\@nameuse{FNH!\thesphinxscope!#1}}% \noexpand\spx@saved@makefnmark \noexpand\hyper@linkend }% \H@@footnotemark \let\@makefnmark\spx@saved@makefnmark \else - \xdef\@thefnmark{\noexpand\ref{\number\value{sphinxscope}.#1}}% #1 = number + \xdef\@thefnmark{\noexpand\ref{\thesphinxscope.#1}}% #1 = number \H@@footnotemark \fi }% diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 77dde08d8..ea2f54098 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1738,7 +1738,7 @@ class LaTeXTranslator(SphinxTranslator): def visit_footnotetext(self, node: Element) -> None: label = cast(nodes.label, node[0]) self.body.append('%%\n\\begin{footnotetext}[%s]' - '\\phantomsection\\label{\\number\\value{sphinxscope}.%s}%%\n' + '\\phantomsection\\label{\\thesphinxscope.%s}%%\n' '\\sphinxAtStartFootnote\n' % (label.astext(), label.astext())) def depart_footnotetext(self, node: Element) -> None: diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 76c254f9c..a64de2595 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -733,11 +733,11 @@ def test_footnote(app, status, warning): assert ('\\bibitem[bar]{footnote:bar}\n\\sphinxAtStartPar\ncite\n') in result assert '\\sphinxcaption{Table caption \\sphinxfootnotemark[4]' in result assert ('\\hline%\n\\begin{footnotetext}[4]' - '\\phantomsection\\label{\\number\\value{sphinxscope}.4}%\n' + '\\phantomsection\\label{\\thesphinxscope.4}%\n' '\\sphinxAtStartFootnote\n' 'footnote in table caption\n%\n\\end{footnotetext}\\ignorespaces %\n' '\\begin{footnotetext}[5]' - '\\phantomsection\\label{\\number\\value{sphinxscope}.5}%\n' + '\\phantomsection\\label{\\thesphinxscope.5}%\n' '\\sphinxAtStartFootnote\n' 'footnote in table header\n%\n\\end{footnotetext}\\ignorespaces ' '\n\\sphinxAtStartPar\n' @@ -765,13 +765,13 @@ def test_reference_in_caption_and_codeblock_in_footnote(app, status, warning): assert ('\\chapter{The section with a reference to \\sphinxfootnotemark[5]}\n' '\\label{\\detokenize{index:the-section-with-a-reference-to}}' '%\n\\begin{footnotetext}[5]' - '\\phantomsection\\label{\\number\\value{sphinxscope}.5}%\n' + '\\phantomsection\\label{\\thesphinxscope.5}%\n' '\\sphinxAtStartFootnote\n' 'Footnote in section\n%\n\\end{footnotetext}') in result assert ('\\caption{This is the figure caption with a footnote to ' '\\sphinxfootnotemark[7].}\\label{\\detokenize{index:id29}}\\end{figure}\n' '%\n\\begin{footnotetext}[7]' - '\\phantomsection\\label{\\number\\value{sphinxscope}.7}%\n' + '\\phantomsection\\label{\\thesphinxscope.7}%\n' '\\sphinxAtStartFootnote\n' 'Footnote in caption\n%\n\\end{footnotetext}') in result assert ('\\sphinxcaption{footnote \\sphinxfootnotemark[8] in ' @@ -779,11 +779,11 @@ def test_reference_in_caption_and_codeblock_in_footnote(app, status, warning): assert ('\\caption{footnote \\sphinxfootnotemark[9] ' 'in caption \\sphinxfootnotemark[10] of longtable\\strut}') in result assert ('\\endlastfoot\n%\n\\begin{footnotetext}[9]' - '\\phantomsection\\label{\\number\\value{sphinxscope}.9}%\n' + '\\phantomsection\\label{\\thesphinxscope.9}%\n' '\\sphinxAtStartFootnote\n' 'Foot note in longtable\n%\n\\end{footnotetext}\\ignorespaces %\n' '\\begin{footnotetext}[10]' - '\\phantomsection\\label{\\number\\value{sphinxscope}.10}%\n' + '\\phantomsection\\label{\\thesphinxscope.10}%\n' '\\sphinxAtStartFootnote\n' 'Second footnote in caption of longtable\n') in result assert ('This is a reference to the code\\sphinxhyphen{}block in the footnote:\n' @@ -825,7 +825,7 @@ def test_latex_show_urls_is_inline(app, status, warning): assert ('Third footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n' 'Third \\sphinxfootnotemark[4]\n%\n\\end{footnote}%\n' '\\begin{footnotetext}[4]' - '\\phantomsection\\label{\\number\\value{sphinxscope}.4}%\n' + '\\phantomsection\\label{\\thesphinxscope.4}%\n' '\\sphinxAtStartFootnote\n' 'Footnote inside footnote\n%\n\\end{footnotetext}\\ignorespaces') in result assert ('\\sphinxhref{http://sphinx-doc.org/~test/}{URL including tilde} ' @@ -835,7 +835,7 @@ def test_latex_show_urls_is_inline(app, status, warning): '\\leavevmode\n\\sphinxAtStartPar\nDescription' in result) assert ('\\item[{Footnote in term \\sphinxfootnotemark[6]}] ' '\\leavevmode%\n\\begin{footnotetext}[6]' - '\\phantomsection\\label{\\number\\value{sphinxscope}.6}%\n' + '\\phantomsection\\label{\\thesphinxscope.6}%\n' '\\sphinxAtStartFootnote\n' 'Footnote in term\n%\n\\end{footnotetext}\\ignorespaces ' '\n\\sphinxAtStartPar\nDescription') in result @@ -878,7 +878,7 @@ def test_latex_show_urls_is_footnote(app, status, warning): assert ('Third footnote: %\n\\begin{footnote}[6]\\sphinxAtStartFootnote\n' 'Third \\sphinxfootnotemark[7]\n%\n\\end{footnote}%\n' '\\begin{footnotetext}[7]' - '\\phantomsection\\label{\\number\\value{sphinxscope}.7}%\n' + '\\phantomsection\\label{\\thesphinxscope.7}%\n' '\\sphinxAtStartFootnote\n' 'Footnote inside footnote\n%\n' '\\end{footnotetext}\\ignorespaces') in result @@ -888,20 +888,20 @@ def test_latex_show_urls_is_footnote(app, status, warning): assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}' '{URL in term}\\sphinxfootnotemark[9]}] ' '\\leavevmode%\n\\begin{footnotetext}[9]' - '\\phantomsection\\label{\\number\\value{sphinxscope}.9}%\n' + '\\phantomsection\\label{\\thesphinxscope.9}%\n' '\\sphinxAtStartFootnote\n' '\\sphinxnolinkurl{http://sphinx-doc.org/}\n%\n' '\\end{footnotetext}\\ignorespaces \n\\sphinxAtStartPar\nDescription') in result assert ('\\item[{Footnote in term \\sphinxfootnotemark[11]}] ' '\\leavevmode%\n\\begin{footnotetext}[11]' - '\\phantomsection\\label{\\number\\value{sphinxscope}.11}%\n' + '\\phantomsection\\label{\\thesphinxscope.11}%\n' '\\sphinxAtStartFootnote\n' 'Footnote in term\n%\n\\end{footnotetext}\\ignorespaces ' '\n\\sphinxAtStartPar\nDescription') in result assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{Term in deflist}' '\\sphinxfootnotemark[10]}] ' '\\leavevmode%\n\\begin{footnotetext}[10]' - '\\phantomsection\\label{\\number\\value{sphinxscope}.10}%\n' + '\\phantomsection\\label{\\thesphinxscope.10}%\n' '\\sphinxAtStartFootnote\n' '\\sphinxnolinkurl{http://sphinx-doc.org/}\n%\n' '\\end{footnotetext}\\ignorespaces \n\\sphinxAtStartPar\nDescription') in result @@ -939,7 +939,7 @@ def test_latex_show_urls_is_no(app, status, warning): assert ('Third footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n' 'Third \\sphinxfootnotemark[4]\n%\n\\end{footnote}%\n' '\\begin{footnotetext}[4]' - '\\phantomsection\\label{\\number\\value{sphinxscope}.4}%\n' + '\\phantomsection\\label{\\thesphinxscope.4}%\n' '\\sphinxAtStartFootnote\n' 'Footnote inside footnote\n%\n\\end{footnotetext}\\ignorespaces') in result assert '\\sphinxhref{http://sphinx-doc.org/~test/}{URL including tilde}' in result @@ -947,7 +947,7 @@ def test_latex_show_urls_is_no(app, status, warning): '\\leavevmode\n\\sphinxAtStartPar\nDescription') in result assert ('\\item[{Footnote in term \\sphinxfootnotemark[6]}] ' '\\leavevmode%\n\\begin{footnotetext}[6]' - '\\phantomsection\\label{\\number\\value{sphinxscope}.6}%\n' + '\\phantomsection\\label{\\thesphinxscope.6}%\n' '\\sphinxAtStartFootnote\n' 'Footnote in term\n%\n\\end{footnotetext}\\ignorespaces ' '\n\\sphinxAtStartPar\nDescription') in result From ff305ec25857bab34fa4aac797ea32466655b15f Mon Sep 17 00:00:00 2001 From: jfbu Date: Sat, 6 Feb 2021 13:57:07 +0100 Subject: [PATCH 08/25] Be extra cautious to not use \@arabic in \thesphinxscope --- sphinx/texinputs/sphinx.sty | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index cfe2164c1..d74e6360b 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -847,6 +847,11 @@ % Support scopes for footnote numbering \newcounter{sphinxscope} \newcommand{\sphinxstepscope}{\stepcounter{sphinxscope}} +% 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}} % 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. From 4a239bb886ffdf9e3ce9c78e0dfbc559ff043ad5 Mon Sep 17 00:00:00 2001 From: jfbu Date: Sat, 6 Feb 2021 19:34:44 +0100 Subject: [PATCH 09/25] 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. --- sphinx/texinputs/footnotehyper-sphinx.sty | 71 ++++++++++++++--------- sphinx/texinputs/sphinx.sty | 16 ++++- sphinx/writers/latex.py | 5 ++ tests/test_build_latex.py | 27 ++++++--- 4 files changed, 84 insertions(+), 35 deletions(-) diff --git a/sphinx/texinputs/footnotehyper-sphinx.sty b/sphinx/texinputs/footnotehyper-sphinx.sty index f8a153c3e..3bba385a8 100644 --- a/sphinx/texinputs/footnotehyper-sphinx.sty +++ b/sphinx/texinputs/footnotehyper-sphinx.sty @@ -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{.}, so we can use \ref which -% hyperref converts into an hyperlink. is the number of -% the scope and 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!! 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 as argument and taking into account current . +% - \ref: +% the latex.py writer inserts a \phantomsection\label{.} +% 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 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 diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index d74e6360b..57ecdd441 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -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. diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index ea2f54098..ac1f1123a 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -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: diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index a64de2595..cc79580f6 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -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' From a984f5f7cc429785882bf8bcf663b0ee2f4dc58d Mon Sep 17 00:00:00 2001 From: jfbu Date: Mon, 8 Feb 2021 14:55:30 +0100 Subject: [PATCH 10/25] LaTeX: improve docs of 'printindex' and 'makeindex' Closes #8055 --- CHANGES | 2 ++ doc/latex.rst | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index b394c54e3..55f1b8876 100644 --- a/CHANGES +++ b/CHANGES @@ -119,6 +119,8 @@ Bugs fixed specified * #7576: LaTeX with French babel and memoir crash: "Illegal parameter number in definition of ``\FNH@prefntext``" +* #8055: LaTeX (docs): A potential display bug with the LaTeX generation step + in Sphinx (how to generate one-column index) * #8072: LaTeX: Directive :rst:dir:`hlist` not implemented in LaTeX * #8214: LaTeX: The :rst:role:`index` role and the glossary generate duplicate entries in the LaTeX index (if both used for same term) diff --git a/doc/latex.rst b/doc/latex.rst index 39d4b8147..82016ed67 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -497,11 +497,22 @@ Keys that don't need to be overridden unless in special cases are: .. versionchanged:: 1.6 Remove unneeded ``{}`` after ``\\hrule``. +``'makeindex'`` + "makeindex" call, the last thing before ``\begin{document}``. With + ``'\\usepackage[columns=1]{idxlayout}\\makeindex'`` the index will use + only one column. You may have to install ``idxlayout`` LaTeX package. + + Default: ``'\\makeindex'`` + ``'printindex'`` "printindex" call, the last thing in the file. Override if you want to - generate the index differently or append some content after the index. For - example ``'\\footnotesize\\raggedright\\printindex'`` is advisable when the - index is full of long entries. + generate the index differently, append some content after the index, or + change the font. As LaTeX uses two-column mode for the index it is + often advisable to set this key to + ``'\\footnotesize\\raggedright\\printindex'``. Or, to obtain a one-column + index, use ``'\\def\\twocolumn[#1]{#1}\\printindex'`` (this trick may fail + if using a custom document class; then try the ``idxlayout`` approach + described in the documentation of the ``'makeindex'`` key). Default: ``'\\printindex'`` @@ -527,7 +538,6 @@ Keys that are set by other options and therefore should not be overridden are: ``'title'`` ``'release'`` ``'author'`` -``'makeindex'`` .. _latexsphinxsetup: From 702545da1c075d3b19e6a5f1749e9812bceb7e49 Mon Sep 17 00:00:00 2001 From: jfbu Date: Mon, 8 Feb 2021 23:13:46 +0100 Subject: [PATCH 11/25] LaTeX: optionally apply a second forceful wrapping of long code lines Closes #8849 --- CHANGES | 1 + doc/latex.rst | 12 +++++ sphinx/texinputs/sphinx.sty | 96 +++++++++++++++++++++++++++++++++++-- 3 files changed, 106 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index b394c54e3..e39180cd3 100644 --- a/CHANGES +++ b/CHANGES @@ -131,6 +131,7 @@ Bugs fixed * #8780: LaTeX: long words in narrow columns may not be hyphenated * #8788: LaTeX: ``\titleformat`` last argument in sphinx.sty should be bracketed, not braced (and is anyhow not needed) +* #8849: LaTex: code-block printed out of margin Testing -------- diff --git a/doc/latex.rst b/doc/latex.rst index 39d4b8147..636781753 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -638,6 +638,18 @@ macros may be significant. Default: ``true`` +``verbatimforcewraps`` + Boolean to specify if long lines in :rst:dir:`code-block`\ 's contents + which the wrapping algorithm could not reduce to at most an excess of 3 + characters on a line will be cut forcefully to achieve this maximal excess + of 3 characters on each line. (*this is possibly fragile, so by default is + not done; please try it out and report issues to the maintainers to help + improve and decide whether to make this default*) + + Default: ``false`` + + .. versionadded:: 3.5.0 + ``literalblockcappos`` Decides the caption position: either ``b`` ("bottom") or ``t`` ("top"). diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index da03ff989..85f67270d 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -334,6 +334,7 @@ % verbatim \DeclareBoolOption[true]{verbatimwithframe} \DeclareBoolOption[true]{verbatimwrapslines} +\DeclareBoolOption[false]{verbatimforcewraps} \DeclareBoolOption[true]{verbatimhintsturnover} \DeclareBoolOption[true]{inlineliteralwraps} \DeclareStringOption[t]{literalblockcappos} @@ -1171,13 +1172,102 @@ % no need to restore \fboxsep here, as this ends up in a \hbox from fancyvrb }% % \sphinxVerbatimFormatLine will be set locally to one of those two: -\newcommand\sphinxVerbatimFormatLineWrap[1]{% - \hsize\linewidth +\newcommand\sphinxVerbatimFormatLineWrap{% + \hsize\linewidth + \ifspx@opt@verbatimforcewraps + \expandafter\spx@verb@FormatLineForceWrap + \else\expandafter\spx@verb@FormatLineWrap + \fi +}% +\newcommand\sphinxVerbatimFormatLineNoWrap[1]{\hb@xt@\linewidth{\strut #1\hss}}% +\long\def\spx@verb@FormatLineWrap#1{% \vtop{\raggedright\hyphenpenalty\z@\exhyphenpenalty\z@ \doublehyphendemerits\z@\finalhyphendemerits\z@ \strut #1\strut}% }% -\newcommand\sphinxVerbatimFormatLineNoWrap[1]{\hb@xt@\linewidth{\strut #1\hss}}% +% We implement an alternative to wrapping long code lines. This +% alternative works only if the contents are in the expected +% Pygments mark-up: i.e. some character escapes such as \PYGZdl{} +% and the highlighting \PYG macro with always 2 arguments, and no +% other macros. This means: +% - the command prefix *must* be PYG +% - the texcomments Pygments option *must* be set to False (it could +% work by luck if True) +% For non-highlighted tokens a break point is installed at each of them. +% For highlighted tokens (i.e. in 2nd argument of \PYG) every four such +% characters. \PYGZdl{} etc will count for 2, although corresponding to +% only one. The result is that no line should be more than 3 characters +% overfull. +% First a measurement step is done of what would our standard wrapping +% approach give. This is a bit tricky, cf TeX by Topic for the basic +% dissecting technique, because TeX unfortunately when building a +% vertical box does not store in an accessible way what was the maximal +% line-width: the width of the box will be the set \hsize. +% Anyway, if the max width exceed the linewidth by at least 4 character +% widths, then we apply the "force wrapping" of previous paragraph, +% else we apply our "standard wrapping". +\long\def\spx@verb@FormatLineForceWrap#1{% + % \spx@image@box is a scratch box register that we can use here + \global\let\spx@verb@maxwidth\z@ + \setbox\spx@image@box + \vtop{\raggedright\hyphenpenalty\z@\exhyphenpenalty\z@ + \doublehyphendemerits\z@\finalhyphendemerits\z@ + \strut #1\strut\@@par + \spx@verb@getmaxwidth}% + \ifdim\spx@verb@maxwidth>\dimexpr\linewidth+3\fontcharwd\font`X\relax + \spx@verb@FormatLineWrap{\spx@forcewrapPYG #1\spx@forcewrapPYG}% + \else + \spx@verb@FormatLineWrap{#1}% + \fi +}% +% auxiliary paragraph dissector to get max width +\newbox\spx@line@box +\def\spx@verb@getmaxwidth {% + \unskip\unpenalty + \setbox\spx@line@box\lastbox + \ifvoid\spx@line@box + \else + \setbox\spx@line@box\hbox{\unhbox\spx@line@box}% + \ifdim\spx@verb@maxwidth<\wd\spx@line@box + \xdef\spx@verb@maxwidth{\number\wd\spx@line@box sp}% + \fi + \expandafter\spx@verb@getmaxwidth + \fi +}% +% auxiliary macros to implement "cut long line even in middle of word" +\def\spx@forcewrapPYG{% + \futurelet\spx@nexttoken\spx@forcewrapPYG@i +}% +\def\spx@forcewrapPYG@i{% + \ifx\spx@nexttoken\spx@forcewrapPYG\let\next=\@gobble\else + \ifx\spx@nexttoken\PYG\let\next=\spx@forcewrapPYG@PYG\else + \discretionary{}{\sphinxafterbreak}{}% + \let\next=\spx@forcewrapPYG@ii + \fi\fi + \next +}% +\def\spx@forcewrapPYG@ii#1{#1\futurelet\spx@nexttoken\spx@forcewrapPYG@i}% +% Replace \PYG by itself applied to short strings of 4 characters at a time +% and insert breakpoints in-between +\def\spx@forcewrapPYG@PYG\PYG#1#2{% + \def\spx@PYGspec{{#1}}% + \spx@PYG#2\@empty\@empty\@empty\@empty\relax +}% +\def\spx@PYG#1#2#3#4{% + \discretionary{}{\sphinxafterbreak}{}% + \expandafter\PYG\spx@PYGspec{#1#2#3#4}% +% I assume here contents never contain \@empty. If #4={} originally then +% it is empty here and the \ifx will compare \@empty to \relax and choose +% the else branch, i.e. to continue applying \PYG repeatedly + \ifx#4\@empty\relax + \expandafter\spx@PYG@done + \else + \expandafter\spx@PYG + \fi +}% +% Once \PYG is handled we get back to our forward scan token by token +\def\spx@PYG@done#1\relax{\futurelet\spx@nexttoken\spx@forcewrapPYG@i}% +% \g@addto@macro\FV@SetupFont{% \sbox\sphinxcontinuationbox {\spx@opt@verbatimcontinued}% \sbox\sphinxvisiblespacebox {\spx@opt@verbatimvisiblespace}% From 0076ad3fd3163a93cd8e300894eebe561d02a0a9 Mon Sep 17 00:00:00 2001 From: jfbu Date: Mon, 8 Feb 2021 23:57:10 +0100 Subject: [PATCH 12/25] Indicated force wrapping code lines is fragile with Unicode input --- doc/latex.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/doc/latex.rst b/doc/latex.rst index 636781753..8b31f9206 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -642,9 +642,13 @@ macros may be significant. Boolean to specify if long lines in :rst:dir:`code-block`\ 's contents which the wrapping algorithm could not reduce to at most an excess of 3 characters on a line will be cut forcefully to achieve this maximal excess - of 3 characters on each line. (*this is possibly fragile, so by default is - not done; please try it out and report issues to the maintainers to help - improve and decide whether to make this default*) + of 3 characters on each line. (*this is possibly fragile, and in + particular will break with* ``'pdflatex'``, *if the code-block contains + Unicode input, so by default is not activated; however it will not have + this problem with Unicode engines. Please try it out and report further + issues to the maintainers; injecting* ``\sphinxsetup{verbatimforcewraps}`` + *and* ``\sphinxsetup{verbatimforcewraps=false}`` *via* ``.. raw:: latex`` + *directives will localize usage to only certain code-blocks*) Default: ``false`` From dffb819ec49f6daf386a46477ccfc9eb6f035eee Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 9 Feb 2021 11:24:24 +0100 Subject: [PATCH 13/25] Support Unicode input when forcefully wrapping code lines This needs special coding only for pdflatex, Unicode TeX engines already handle Unicode characters as one token. The utf8x LaTeX input encoding is not supported, only utf8. --- sphinx/texinputs/sphinx.sty | 180 +++++++++++++++++++++++++----------- 1 file changed, 128 insertions(+), 52 deletions(-) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 85f67270d..953dd0298 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1185,27 +1185,46 @@ \doublehyphendemerits\z@\finalhyphendemerits\z@ \strut #1\strut}% }% -% We implement an alternative to wrapping long code lines. This -% alternative works only if the contents are in the expected -% Pygments mark-up: i.e. some character escapes such as \PYGZdl{} -% and the highlighting \PYG macro with always 2 arguments, and no -% other macros. This means: -% - the command prefix *must* be PYG -% - the texcomments Pygments option *must* be set to False (it could -% work by luck if True) -% For non-highlighted tokens a break point is installed at each of them. -% For highlighted tokens (i.e. in 2nd argument of \PYG) every four such -% characters. \PYGZdl{} etc will count for 2, although corresponding to -% only one. The result is that no line should be more than 3 characters -% overfull. -% First a measurement step is done of what would our standard wrapping -% approach give. This is a bit tricky, cf TeX by Topic for the basic -% dissecting technique, because TeX unfortunately when building a -% vertical box does not store in an accessible way what was the maximal -% line-width: the width of the box will be the set \hsize. -% Anyway, if the max width exceed the linewidth by at least 4 character -% widths, then we apply the "force wrapping" of previous paragraph, -% else we apply our "standard wrapping". +% +% The normal line wrapping allows breaks at spaces and ascii non +% letters, non digits. The \raggedright above means there will be +% an overfilled line only if some non-breakable "word" was +% encountered, which is longer than a line (it is moved always to +% be on its own on a new line). +% +% The "forced" line wrapping will parse the tokens to add potential +% breakpoints at each character. As some strings are highlighted, +% we have to apply the highlighting character per character, which +% requires to manipulate the output of the Pygments LaTeXFormatter. +% +% Doing this at latex level is complicated. The contents should +% be as expected: i.e. some active characters from +% \sphinxbreaksviaactive, some Pygments character escapes such as +% \PYGZdl{}, and the highlighting \PYG macro with always 2 +% arguments. No other macros should be there, except perhaps +% zero-parameter macros. In particular: +% - the texcomments Pygments option must be set to False +% +% With pdflatex, Unicode input gives multi-bytes characters +% where the first byte is active. We support the "utf8" macros +% only. "utf8x" is not supported. +% +% The highlighting macro \PYG will be applied character per +% character. Highlighting via a colored background gives thus a +% chain of small colored boxes which may cause some artefact in +% some pdf viewers. Can't do anything here if we do want the line +% break to be possible. +% +% First a measurement step is done of what would the standard line +% wrapping give (i.e line breaks only at spaces and non-letter, +% non-digit ascii characters), cf TeX by Topic for the basic +% dissecting technique: TeX unfortunately when building a vertical +% box does not store in an accessible way what was the maximal +% line-width during paragraph building. +% +% If the max width exceed the linewidth by at least 4 character +% widths, then we apply the "force wrapping" with potential line +% break at each character, else we don't. \long\def\spx@verb@FormatLineForceWrap#1{% % \spx@image@box is a scratch box register that we can use here \global\let\spx@verb@maxwidth\z@ @@ -1215,58 +1234,115 @@ \strut #1\strut\@@par \spx@verb@getmaxwidth}% \ifdim\spx@verb@maxwidth>\dimexpr\linewidth+3\fontcharwd\font`X\relax - \spx@verb@FormatLineWrap{\spx@forcewrapPYG #1\spx@forcewrapPYG}% + \spx@verb@FormatLineWrap{\spx@verb@wrapPYG #1\spx@verb@wrapPYG}% \else \spx@verb@FormatLineWrap{#1}% \fi }% % auxiliary paragraph dissector to get max width -\newbox\spx@line@box +\newbox\spx@scratchbox \def\spx@verb@getmaxwidth {% \unskip\unpenalty - \setbox\spx@line@box\lastbox - \ifvoid\spx@line@box + \setbox\spx@scratchbox\lastbox + \ifvoid\spx@scratchbox \else - \setbox\spx@line@box\hbox{\unhbox\spx@line@box}% - \ifdim\spx@verb@maxwidth<\wd\spx@line@box - \xdef\spx@verb@maxwidth{\number\wd\spx@line@box sp}% + \setbox\spx@scratchbox\hbox{\unhbox\spx@scratchbox}% + \ifdim\spx@verb@maxwidth<\wd\spx@scratchbox + \xdef\spx@verb@maxwidth{\number\wd\spx@scratchbox sp}% \fi \expandafter\spx@verb@getmaxwidth \fi }% % auxiliary macros to implement "cut long line even in middle of word" -\def\spx@forcewrapPYG{% - \futurelet\spx@nexttoken\spx@forcewrapPYG@i +\catcode`Z=3 % safe delimiter +\def\spx@verb@wrapPYG{% + \futurelet\spx@nexttoken\spx@verb@wrapPYG@i }% -\def\spx@forcewrapPYG@i{% - \ifx\spx@nexttoken\spx@forcewrapPYG\let\next=\@gobble\else - \ifx\spx@nexttoken\PYG\let\next=\spx@forcewrapPYG@PYG\else +\def\spx@verb@wrapPYG@i{% + \ifx\spx@nexttoken\spx@verb@wrapPYG\let\next=\@gobble\else + \ifx\spx@nexttoken\PYG\let\next=\spx@verb@wrapPYG@PYG@onebyone\else \discretionary{}{\sphinxafterbreak}{}% - \let\next=\spx@forcewrapPYG@ii + \let\next\spx@verb@wrapPYG@ii \fi\fi \next }% -\def\spx@forcewrapPYG@ii#1{#1\futurelet\spx@nexttoken\spx@forcewrapPYG@i}% -% Replace \PYG by itself applied to short strings of 4 characters at a time -% and insert breakpoints in-between -\def\spx@forcewrapPYG@PYG\PYG#1#2{% - \def\spx@PYGspec{{#1}}% - \spx@PYG#2\@empty\@empty\@empty\@empty\relax +% Let's recognize active characters. We don't support utf8x only utf8. +% And here #1 should not have picked up (non empty) braced contents +\long\def\spx@verb@wrapPYG@ii#1{% + \ifcat\noexpand~\noexpand#1\relax% active character + \expandafter\spx@verb@wrapPYG@active + \else % non-active character, control sequence such as \PYGZdl, or empty + \expandafter\spx@verb@wrapPYG@one + \fi {#1}% }% -\def\spx@PYG#1#2#3#4{% - \discretionary{}{\sphinxafterbreak}{}% - \expandafter\PYG\spx@PYGspec{#1#2#3#4}% -% I assume here contents never contain \@empty. If #4={} originally then -% it is empty here and the \ifx will compare \@empty to \relax and choose -% the else branch, i.e. to continue applying \PYG repeatedly - \ifx#4\@empty\relax - \expandafter\spx@PYG@done - \else - \expandafter\spx@PYG +\long\def\spx@verb@wrapPYG@active#1{% +% Let's hope expansion of active character does not really require arguments, +% as we certainly don't want to go into expanding upfront token stream anyway. + \expandafter\spx@verb@wrapPYG@iii#1{}{}{}{}{}{}{}{}{}Z#1% +}% +\long\def\spx@verb@wrapPYG@iii#1#2Z{% + \ifx\UTFviii@four@octets#1\let\next=\spx@verb@wrapPYG@four\else + \ifx\UTFviii@three@octets#1\let\next=\spx@verb@wrapPYG@three\else + \ifx\UTFviii@two@octets#1\let\next=\spx@verb@wrapPYG@two\else + \let\next=\spx@verb@wrapPYG@one + \fi\fi\fi + \next +}% +\long\def\spx@verb@wrapPYG@one #1{#1\futurelet\spx@nexttoken\spx@verb@wrapPYG@i}% +\long\def\spx@verb@wrapPYG@two #1#2{#1#2\futurelet\spx@nexttoken\spx@verb@wrapPYG@i}% +\long\def\spx@verb@wrapPYG@three #1#2#3{#1#2#3\futurelet\spx@nexttoken\spx@verb@wrapPYG@i}% +\long\def\spx@verb@wrapPYG@four #1#2#3#4{#1#2#3#4\futurelet\spx@nexttoken\spx@verb@wrapPYG@i}% +% Replace \PYG by itself applied one character at a time! This way breakpoints +% can be inserted. +\def\spx@verb@wrapPYG@PYG@onebyone#1#2#3{% #1 = \PYG, #2 = highlight spec, #3 = tokens + \def\spx@verb@wrapPYG@PYG@spec{{#2}}% + \futurelet\spx@nexttoken\spx@verb@wrapPYG@PYG@i#3Z% +}% +\def\spx@verb@wrapPYG@PYG@i{% + \ifx\spx@nexttokenZ\let\next=\spx@verb@wrapPYG@PYG@done\else + \discretionary{}{\sphinxafterbreak}{}% + \let\next\spx@verb@wrapPYG@PYG@ii \fi + \next }% -% Once \PYG is handled we get back to our forward scan token by token -\def\spx@PYG@done#1\relax{\futurelet\spx@nexttoken\spx@forcewrapPYG@i}% +\def\spx@verb@wrapPYG@PYG@doneZ{\futurelet\spx@nexttoken\spx@verb@wrapPYG@i}% +\long\def\spx@verb@wrapPYG@PYG@ii#1{% + \ifcat\noexpand~\noexpand#1\relax% active character + \expandafter\spx@verb@wrapPYG@PYG@active + \else % non-active character, control sequence such as \PYGZdl, or empty + \expandafter\spx@verb@wrapPYG@PYG@one + \fi {#1}% +}% +\long\def\spx@verb@wrapPYG@PYG@active#1{% +% Let's hope expansion of active character does not really require arguments, +% as we certainly don't want to go into expanding upfront token stream anyway. + \expandafter\spx@verb@wrapPYG@PYG@iii#1{}{}{}{}{}{}{}{}{}Z#1% +}% +\long\def\spx@verb@wrapPYG@PYG@iii#1#2Z{% + \ifx\UTFviii@four@octets#1\let\next=\spx@verb@wrapPYG@PYG@four\else + \ifx\UTFviii@three@octets#1\let\next=\spx@verb@wrapPYG@PYG@three\else + \ifx\UTFviii@two@octets#1\let\next=\spx@verb@wrapPYG@PYG@two\else + \let\next=\spx@verb@wrapPYG@PYG@one + \fi\fi\fi + \next +}% +\long\def\spx@verb@wrapPYG@PYG@one#1{% + \expandafter\PYG\spx@verb@wrapPYG@PYG@spec{#1}% + \futurelet\spx@nexttoken\spx@verb@wrapPYG@PYG@i +}% +\long\def\spx@verb@wrapPYG@PYG@two#1#2{% + \expandafter\PYG\spx@verb@wrapPYG@PYG@spec{#1#2}% + \futurelet\spx@nexttoken\spx@verb@wrapPYG@PYG@i +}% +\long\def\spx@verb@wrapPYG@PYG@three#1#2#3{% + \expandafter\PYG\spx@verb@wrapPYG@PYG@spec{#1#2#3}% + \futurelet\spx@nexttoken\spx@verb@wrapPYG@PYG@i +}% +\long\def\spx@verb@wrapPYG@PYG@four#1#2#3#4{% + \expandafter\PYG\spx@verb@wrapPYG@PYG@spec{#1#2#3#4}% + \futurelet\spx@nexttoken\spx@verb@wrapPYG@PYG@i +}% +\catcode`Z 11% % \g@addto@macro\FV@SetupFont{% \sbox\sphinxcontinuationbox {\spx@opt@verbatimcontinued}% From c6a8fb1625a16c44edebd0aa706792510c64239d Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 9 Feb 2021 12:03:55 +0100 Subject: [PATCH 14/25] Add verbatimmaxoverfull and verbatimmaxunderfull --- doc/latex.rst | 63 ++++++++++++++++++++++++++++--------- sphinx/texinputs/sphinx.sty | 13 ++++++-- 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/doc/latex.rst b/doc/latex.rst index 8b31f9206..a95e130f4 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -585,7 +585,9 @@ The below is included at the end of the chapter:: \endgroup -LaTeX boolean keys require *lowercase* ``true`` or ``false`` values. +LaTeX syntax for boolean keys require *lowercase* ``true`` or ``false`` +e.g ``'sphinxsetup': "verbatimwrapslines=false"``. If setting the +boolean key to ``true``, ``=true`` is optional. Spaces around the commas and equal signs are ignored, spaces inside LaTeX macros may be significant. @@ -636,30 +638,63 @@ macros may be significant. Boolean to specify if long lines in :rst:dir:`code-block`\ 's contents are wrapped. + If ``true``, line breaks may happen at spaces (the last space before the + line break will be rendered using a special symbol), and at ascii + punctuation characters (i.e. not at letters or digits). Whenever a long + string has no break points, it is moved to next line. If its length is + longer than the line width it will overflow. + Default: ``true`` ``verbatimforcewraps`` Boolean to specify if long lines in :rst:dir:`code-block`\ 's contents - which the wrapping algorithm could not reduce to at most an excess of 3 - characters on a line will be cut forcefully to achieve this maximal excess - of 3 characters on each line. (*this is possibly fragile, and in - particular will break with* ``'pdflatex'``, *if the code-block contains - Unicode input, so by default is not activated; however it will not have - this problem with Unicode engines. Please try it out and report further - issues to the maintainers; injecting* ``\sphinxsetup{verbatimforcewraps}`` - *and* ``\sphinxsetup{verbatimforcewraps=false}`` *via* ``.. raw:: latex`` - *directives will localize usage to only certain code-blocks*) + should be forcefully wrapped to never overflow due to long strings. + + .. notice:: + + It is assumed that the Pygments_ LaTeXFormatter has not been used with + its ``texcomments`` or similar options which allow additional + (arbitrary) LaTeX mark-up. + + Also, in case of :confval:`latex_engine` set to ``'pdflatex'``, only + the default LaTeX handling of Unicode code points, i.e. ``utf8`` not + ``utf8x`` is allowed. Default: ``false`` .. versionadded:: 3.5.0 -``literalblockcappos`` - Decides the caption position: either ``b`` ("bottom") or ``t`` ("top"). +``verbatimmaxoverfull`` + A number. If an unbreakable long string has length larger than the total + linewidth plus this number of characters, and if ``verbatimforcewraps`` + mode is on, the input line will be reset using the forceful algorithm + which applies breakpoints at each character. - Default: ``t`` + Default: ``3`` - .. versionadded:: 1.7 + .. versionadded:: 3.5.0 + +``verbatimmaxunderfull`` + A number. If ``verbatimforcewraps`` mode applies, and if after applying + the line wrapping at spaces and punctuation, the first part of the split + line is lacking at least that number of characters to fill the available + width, then the input line will be reset using the forceful algorithm. + + As the default is set to a high value, the forceful algorithm is triggered + only in overfull case, i.e. in presence of a string longer than full + linewidth. Set this to ``0`` to force all input lines to be hard wrapped + at the current avaiable linewidth:: + + latex_elements = { + 'sphinxsetup': "verbatimforcewraps, verbatimmaxunderfull=0", + } + + This can be done locally for a given code-block via the use of raw latex + directives to insert suitable ``\sphinxsetup`` into the latex file. + + Default: ``100`` + + .. versionadded:: 3.5.0 ``verbatimhintsturnover`` Boolean to specify if code-blocks display "continued on next page" and diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 953dd0298..b2c855acb 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -335,6 +335,8 @@ \DeclareBoolOption[true]{verbatimwithframe} \DeclareBoolOption[true]{verbatimwrapslines} \DeclareBoolOption[false]{verbatimforcewraps} +\DeclareStringOption[3]{verbatimmaxoverfull} +\DeclareStringOption[100]{verbatimmaxunderfull} \DeclareBoolOption[true]{verbatimhintsturnover} \DeclareBoolOption[true]{inlineliteralwraps} \DeclareStringOption[t]{literalblockcappos} @@ -1233,11 +1235,16 @@ \doublehyphendemerits\z@\finalhyphendemerits\z@ \strut #1\strut\@@par \spx@verb@getmaxwidth}% - \ifdim\spx@verb@maxwidth>\dimexpr\linewidth+3\fontcharwd\font`X\relax + \ifdim\spx@verb@maxwidth> + \dimexpr\linewidth+\spx@opt@verbatimmaxoverfull\fontcharwd\font`X \relax \spx@verb@FormatLineWrap{\spx@verb@wrapPYG #1\spx@verb@wrapPYG}% \else - \spx@verb@FormatLineWrap{#1}% - \fi + \ifdim\spx@verb@maxwidth< + \dimexpr\linewidth-\spx@opt@verbatimmaxunderfull\fontcharwd\font`X \relax + \spx@verb@FormatLineWrap{\spx@verb@wrapPYG #1\spx@verb@wrapPYG}% + \else + \spx@verb@FormatLineWrap{#1}% + \fi\fi }% % auxiliary paragraph dissector to get max width \newbox\spx@scratchbox From abddafa98a1221a669c35a3955a8e3d3c6aa2f66 Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 9 Feb 2021 12:28:03 +0100 Subject: [PATCH 15/25] Fix inexistent directive "notice", it is "note"... --- doc/latex.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/latex.rst b/doc/latex.rst index a95e130f4..59fcc3850 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -650,7 +650,7 @@ macros may be significant. Boolean to specify if long lines in :rst:dir:`code-block`\ 's contents should be forcefully wrapped to never overflow due to long strings. - .. notice:: + .. note:: It is assumed that the Pygments_ LaTeXFormatter has not been used with its ``texcomments`` or similar options which allow additional From eee17ff61a79994886caa4943cecd4c86ee9f46e Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 9 Feb 2021 12:33:16 +0100 Subject: [PATCH 16/25] Fix forgotten link to Pygments home site, mmmmpf --- doc/latex.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/latex.rst b/doc/latex.rst index 59fcc3850..69a0a470d 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -660,6 +660,8 @@ macros may be significant. the default LaTeX handling of Unicode code points, i.e. ``utf8`` not ``utf8x`` is allowed. + .. _Pygments: https://pygments.org/ + Default: ``false`` .. versionadded:: 3.5.0 From 7175d11f05a25fc6c7be0cf7e2037510e10f3b93 Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 9 Feb 2021 13:16:12 +0100 Subject: [PATCH 17/25] Add forgotten minwidth computation --- sphinx/texinputs/sphinx.sty | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index b2c855acb..99f7664bd 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1230,25 +1230,26 @@ \long\def\spx@verb@FormatLineForceWrap#1{% % \spx@image@box is a scratch box register that we can use here \global\let\spx@verb@maxwidth\z@ + \global\let\spx@verb@minwidth\linewidth \setbox\spx@image@box \vtop{\raggedright\hyphenpenalty\z@\exhyphenpenalty\z@ \doublehyphendemerits\z@\finalhyphendemerits\z@ \strut #1\strut\@@par - \spx@verb@getmaxwidth}% + \spx@verb@getwidths}% \ifdim\spx@verb@maxwidth> \dimexpr\linewidth+\spx@opt@verbatimmaxoverfull\fontcharwd\font`X \relax \spx@verb@FormatLineWrap{\spx@verb@wrapPYG #1\spx@verb@wrapPYG}% \else - \ifdim\spx@verb@maxwidth< + \ifdim\spx@verb@minwidth< \dimexpr\linewidth-\spx@opt@verbatimmaxunderfull\fontcharwd\font`X \relax \spx@verb@FormatLineWrap{\spx@verb@wrapPYG #1\spx@verb@wrapPYG}% \else \spx@verb@FormatLineWrap{#1}% \fi\fi }% -% auxiliary paragraph dissector to get max width +% auxiliary paragraph dissector to get max and min widths \newbox\spx@scratchbox -\def\spx@verb@getmaxwidth {% +\def\spx@verb@getwidths {% \unskip\unpenalty \setbox\spx@scratchbox\lastbox \ifvoid\spx@scratchbox @@ -1257,7 +1258,10 @@ \ifdim\spx@verb@maxwidth<\wd\spx@scratchbox \xdef\spx@verb@maxwidth{\number\wd\spx@scratchbox sp}% \fi - \expandafter\spx@verb@getmaxwidth + \ifdim\spx@verb@minwidth>\wd\spx@scratchbox + \xdef\spx@verb@minwidth{\number\wd\spx@scratchbox sp}% + \fi + \expandafter\spx@verb@getwidths \fi }% % auxiliary macros to implement "cut long line even in middle of word" From 035d13fd248f386073390578672802fe91e8395a Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 9 Feb 2021 15:47:15 +0100 Subject: [PATCH 18/25] Update CHANGES and docs relative to new verbatimforcewraps --- CHANGES | 4 +++- doc/latex.rst | 5 ++++- sphinx/texinputs/sphinx.sty | 7 ++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index e39180cd3..fb8013888 100644 --- a/CHANGES +++ b/CHANGES @@ -131,7 +131,9 @@ Bugs fixed * #8780: LaTeX: long words in narrow columns may not be hyphenated * #8788: LaTeX: ``\titleformat`` last argument in sphinx.sty should be bracketed, not braced (and is anyhow not needed) -* #8849: LaTex: code-block printed out of margin +* #8849: LaTex: code-block printed out of margin (see the opt-in LaTeX syntax + boolean :ref:`verbatimforcewraps ` for use via + the :ref:`'sphinxsetup' ` key of ``latex_elements``) Testing -------- diff --git a/doc/latex.rst b/doc/latex.rst index 69a0a470d..ed99c8994 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -646,6 +646,8 @@ macros may be significant. Default: ``true`` +.. _latexsphinxsetupforcewraps: + ``verbatimforcewraps`` Boolean to specify if long lines in :rst:dir:`code-block`\ 's contents should be forcefully wrapped to never overflow due to long strings. @@ -692,7 +694,8 @@ macros may be significant. } This can be done locally for a given code-block via the use of raw latex - directives to insert suitable ``\sphinxsetup`` into the latex file. + directives to insert suitable ``\sphinxsetup`` (before and after) into the + latex file. Default: ``100`` diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 99f7664bd..adc9a23a7 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1224,9 +1224,10 @@ % box does not store in an accessible way what was the maximal % line-width during paragraph building. % -% If the max width exceed the linewidth by at least 4 character -% widths, then we apply the "force wrapping" with potential line -% break at each character, else we don't. +% If the max width exceeds the linewidth by more than verbatimmaxoverfull +% character widths, or if the min width plus verbatimmaxunderfull character +% widths is inferior to linewidth, then we apply the "force wrapping" with +% potential line break at each character, else we don't. \long\def\spx@verb@FormatLineForceWrap#1{% % \spx@image@box is a scratch box register that we can use here \global\let\spx@verb@maxwidth\z@ From aaac7211049d79ef0a71a4cf516eedd63cf0a72b Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 9 Feb 2021 16:04:01 +0100 Subject: [PATCH 19/25] Typo --- doc/latex.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/latex.rst b/doc/latex.rst index ed99c8994..ffd5c274e 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -585,7 +585,7 @@ The below is included at the end of the chapter:: \endgroup -LaTeX syntax for boolean keys require *lowercase* ``true`` or ``false`` +LaTeX syntax for boolean keys requires *lowercase* ``true`` or ``false`` e.g ``'sphinxsetup': "verbatimwrapslines=false"``. If setting the boolean key to ``true``, ``=true`` is optional. Spaces around the commas and equal signs are ignored, spaces inside LaTeX From e759bd14c87097cb6ea5633e2cd5c62039c9953a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= Date: Tue, 9 Feb 2021 16:45:49 +0100 Subject: [PATCH 20/25] Update sphinx/writers/latex.py Co-authored-by: Takeshi KOMIYA --- sphinx/writers/latex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index fbe77ba4b..cf166fb21 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -853,7 +853,7 @@ class LaTeXTranslator(SphinxTranslator): else: self.body.append('%\n') self.body.append('\\begin{footnote}[%s]' % label.astext()) - if 'auto' not in node.attributes: + if 'auto' not in node: self.body.append('\\phantomsection' '\\label{\\thesphinxscope.%s}%%\n' % label.astext()) self.body.append('\\sphinxAtStartFootnote\n') From 52fc9bdd860ce6d2cbde1b743aff83d48b98fef0 Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 9 Feb 2021 17:51:55 +0100 Subject: [PATCH 21/25] s/node.attributes/node/ at one other location --- sphinx/writers/latex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index cf166fb21..ca5b17e2d 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -846,7 +846,7 @@ 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: + if 'auto' not in node: self.body.append('\\sphinxstepexplicit ') if self.in_parsed_literal: self.body.append('\\begin{footnote}[%s]' % label.astext()) From 32d8b4e4454e8035b4f60569cc5fcda2de89300b Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 9 Feb 2021 18:11:24 +0100 Subject: [PATCH 22/25] Minor refactoring in doc/latex.rst --- doc/latex.rst | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/doc/latex.rst b/doc/latex.rst index 6ca0b525c..9dac8d32f 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -802,6 +802,11 @@ macros may be significant. Default: ``{rgb}{0,0,0}`` (black) +.. note:: + + Starting with the next colour, and for all others following, the + names declared to "color" or "xcolor" are prefixed with "sphinx". + ``VerbatimHighlightColor`` The color for highlighted lines. @@ -809,11 +814,6 @@ macros may be significant. .. versionadded:: 1.6.6 -.. note:: - - Starting with this colour key, and for all others coming next, the actual - names declared to "color" or "xcolor" are prefixed with "sphinx". - ``verbatimsep`` The separation between code lines and the frame. @@ -900,9 +900,8 @@ macros may be significant. ``attentionBorderColor``, ``dangerBorderColor``, ``errorBorderColor`` -.. |wgbdcolorslatex| replace:: ``warningBorderColor``, ``cautionBorderColor``, - ``attentionB..C..``, ``dangerB..C..``, - ``errorB..C..`` +.. |wgbdcolorslatex| replace:: ``warningBorderColor``, and + ``(caution|attention|danger|error)BorderColor`` .. else latex goes into right margin, as it does not hyphenate the names From a310c4c5b68cf2c1f83ebc6586cfa20634a05cf4 Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 9 Feb 2021 18:25:37 +0100 Subject: [PATCH 23/25] Indent note directive inside list item --- doc/latex.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/latex.rst b/doc/latex.rst index 9dac8d32f..bbaa42cc8 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -802,11 +802,6 @@ macros may be significant. Default: ``{rgb}{0,0,0}`` (black) -.. note:: - - Starting with the next colour, and for all others following, the - names declared to "color" or "xcolor" are prefixed with "sphinx". - ``VerbatimHighlightColor`` The color for highlighted lines. @@ -814,6 +809,11 @@ macros may be significant. .. versionadded:: 1.6.6 + .. note:: + + Starting with this colour, and for all others following, the + names declared to "color" or "xcolor" are prefixed with "sphinx". + ``verbatimsep`` The separation between code lines and the frame. From 87d0b15eabb40b46acdc0631f88ff76444e904cf Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 9 Feb 2021 18:29:56 +0100 Subject: [PATCH 24/25] Also indent warning to not interrupt description list --- doc/latex.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/latex.rst b/doc/latex.rst index bbaa42cc8..446731964 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -775,10 +775,10 @@ macros may be significant. Default: ``{rgb}{0.126,0.263,0.361}`` -.. warning:: + .. warning:: - Colours set via ``'sphinxsetup'`` must obey the syntax of the - argument of the ``color/xcolor`` packages ``\definecolor`` command. + Colours set via ``'sphinxsetup'`` must obey the syntax of the + argument of the ``color/xcolor`` packages ``\definecolor`` command. ``InnerLinkColor`` A colour passed to ``hyperref`` as value of ``linkcolor`` and From e4ba80f92df79e1660c00f2b802ef5042319193f Mon Sep 17 00:00:00 2001 From: jfbu Date: Tue, 9 Feb 2021 19:34:09 +0100 Subject: [PATCH 25/25] Minor style issue in texinputs/sphinx.sty --- sphinx/texinputs/sphinx.sty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 556302930..b765957c5 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1376,7 +1376,7 @@ \expandafter\PYG\spx@verb@wrapPYG@PYG@spec{#1#2#3#4}% \futurelet\spx@nexttoken\spx@verb@wrapPYG@PYG@i }% -\catcode`Z 11% +\catcode`Z 11 % % \g@addto@macro\FV@SetupFont{% \sbox\sphinxcontinuationbox {\spx@opt@verbatimcontinued}%