diff --git a/sphinx/texinputs/sphinxlatexobjects.sty b/sphinx/texinputs/sphinxlatexobjects.sty index 3deda5c946..cff89ef005 100644 --- a/sphinx/texinputs/sphinxlatexobjects.sty +++ b/sphinx/texinputs/sphinxlatexobjects.sty @@ -1,7 +1,7 @@ %% MODULE RELEASE DATA AND OBJECT DESCRIPTIONS % % change this info string if making any custom modification -\ProvidesFile{sphinxlatexobjects.sty}[2021/12/05 documentation environments] +\ProvidesFile{sphinxlatexobjects.sty}[2022/01/13 documentation environments] % Provides support for this output mark-up from Sphinx latex writer: % @@ -77,56 +77,79 @@ % Signatures, possibly multi-line % +% For legacy reasons Sphinx uses LaTeX \list and \item's for signatures +% This is delicate: +% - the actual item label is not typeset immediately by \item but later as part +% of the \everypar which will be triggered by either next paragraph or a manual +% \leavevmode, or if nothing in-between by the next \item, +% - \begingroup \item[foo] \endgroup leads to errors, +% - vertical space depends on \parskip and \itemsep values in somewhat +% subtle manners. +% +% Since the 2022/01/13 version things are simpler as \parskip is simply set +% to zero during execution of \pysigline/\pysiglinewithargsret +% +% Parameter for separation via \itemsep of multiple signatures with common desc +\newlength\sphinxsignaturesep +\setlength\sphinxsignaturesep{\smallskipamount} +% latex.py outputs mark-up like this: +% \pysigstartsignatures \pysigstopsignatures +\newcommand{\pysigstartsignatures}{% + % store current \parskip and \itemsep + \edef\pysig@restore@itemsep@and@parskip{% + \itemsep\the\itemsep\relax + \parskip\the\parskip\relax + }% + % set them to control the spacing between signatures sharing common desc + \parskip\z@skip + \itemsep\sphinxsignaturesep +} +\newcommand{\pysigstopsignatures}{% + \leavevmode + % it is important \leavevmode was issued before the \parskip reset, and + % it is also needed for the case of an object desc itself a LaTeX \list + % now restore \itemsep and \parskip + \pysig@restore@itemsep@and@parskip +} +% +% Use a \parbox to accomodate long argument list in signatures +% LaTeX did not imagine that an \item label could need multi-line rendering \newlength{\py@argswidth} \newcommand{\py@sigparams}[2]{% - % The \py@argswidth has been computed in \pysiglinewithargsret to make this - % occupy full available width on line. + % The \py@argswidth has been computed in \pysiglinewithargsret to make the + % argument list use full available width \parbox[t]{\py@argswidth}{\raggedright #1\sphinxcode{)}#2\strut}% - % final strut is to help get correct vertical separation in case of multi-line - % box with the item contents. + % final strut is to help get correct vertical separation } \newcommand{\pysigline}[1]{% -% the \py@argswidth is available we use it despite its name (no "args" here) -% the \relax\relax is because \py@argswidth is a "skip" variable and the first -% \relax only ends its "dimen" part + % as \py@argswidth is available, we use it but no "args" here + % the \relax\relax is because \py@argswidth is a "skip" variable + % this will make the label occupy the full available linewidth \py@argswidth=\dimexpr\linewidth+\labelwidth\relax\relax \item[{\parbox[t]{\py@argswidth}{\raggedright #1\strut}}] - \futurelet\sphinx@token\pysigline@preparevspace@i + \pysigadjustitemsep } \newcommand{\pysiglinewithargsret}[3]{% \settowidth{\py@argswidth}{#1\sphinxcode{(}}% \py@argswidth=\dimexpr\linewidth+\labelwidth-\py@argswidth\relax\relax \item[{#1\sphinxcode{(}\py@sigparams{#2}{#3}\strut}] - \futurelet\sphinx@token\pysigline@preparevspace@i + \pysigadjustitemsep } -\def\pysigline@preparevspace@i{% - \ifx\sphinx@token\@sptoken - \expandafter\pysigline@preparevspace@again - \else\expandafter\pysigline@preparevspace@ii - \fi -} -\@firstofone{\def\pysigline@preparevspace@again} {\futurelet\sphinx@token\pysigline@preparevspace@i} -\long\def\pysigline@preparevspace@ii#1{% - \ifx\sphinx@token\bgroup\expandafter\@firstoftwo +\newcommand{\pysigadjustitemsep}{% + % adjust \itemsep to control the separation with the next signature + % sharing common description + \ifsphinxsigismultiline + % inside a multiline signature, no extra vertical spacing + % ("multiline" here does not refer to possibly long + % list of arguments, but to a cpp domain feature) + \itemsep\z@skip \else - \ifx\sphinx@token\phantomsection - \else -% this strange incantation is because at its root LaTeX in fact did not -% imagine a multi-line label, it is always wrapped in a horizontal box at core -% LaTeX level and we have to find tricks to get correct interline distances. -% It interacts badly with a follow-up \phantomsection hence the test above - \leavevmode\par\nobreak\vskip-\parskip\prevdepth\dp\strutbox - \fi - \expandafter\@secondoftwo + \itemsep\sphinxsignaturesep \fi - {{#1}}{#1}% -} -\newcommand{\pysigstartmultiline}{% - \def\pysigstartmultiline{\vskip\smallskipamount\parskip\z@skip\itemsep\z@skip}% - \edef\pysigstopmultiline - {\noexpand\leavevmode\parskip\the\parskip\relax\itemsep\the\itemsep\relax}% - \parskip\z@skip\itemsep\z@skip } +\newif\ifsphinxsigismultiline +\newcommand{\pysigstartmultiline}{\sphinxsigismultilinetrue}% +\newcommand{\pysigstopmultiline}{\sphinxsigismultilinefalse\itemsep\sphinxsignaturesep}% % Production lists % diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 0ba4a3cc41..a18de2145f 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -673,6 +673,9 @@ def visit_desc(self, node: Element) -> None: self.table.has_problematic = True def depart_desc(self, node: Element) -> None: + if self.in_desc_signature: + self.body.append(CR + r'\pysigstopsignatures') + self.in_desc_signature = False if self.config.latex_show_urls == 'footnote': self.body.append(CR + r'\end{fulllineitems}\end{savenotes}' + BLANKLINE) else: @@ -681,10 +684,10 @@ def depart_desc(self, node: Element) -> None: def _visit_signature_line(self, node: Element) -> None: for child in node: if isinstance(child, addnodes.desc_parameterlist): - self.body.append(r'\pysiglinewithargsret{') + self.body.append(CR + r'\pysiglinewithargsret{') break else: - self.body.append(r'\pysigline{') + self.body.append(CR + r'\pysigline{') def _depart_signature_line(self, node: Element) -> None: self.body.append('}') @@ -697,15 +700,17 @@ def visit_desc_signature(self, node: Element) -> None: self.body.append(hyper) if not self.in_desc_signature: self.in_desc_signature = True - self.body.append('%' + CR) - self.body.append(r'\pysigstartmultiline' + CR) - + self.body.append(CR + r'\pysigstartsignatures') if not node.get('is_multiline'): self._visit_signature_line(node) + else: + self.body.append(CR + r'\pysigstartmultiline') def depart_desc_signature(self, node: Element) -> None: if not node.get('is_multiline'): self._depart_signature_line(node) + else: + self.body.append(CR + r'\pysigstopmultiline') def visit_desc_signature_line(self, node: Element) -> None: self._visit_signature_line(node) @@ -715,8 +720,7 @@ def depart_desc_signature_line(self, node: Element) -> None: def visit_desc_content(self, node: Element) -> None: assert self.in_desc_signature - self.body.append('%' + CR) - self.body.append(r'\pysigstopmultiline') + self.body.append(CR + r'\pysigstopsignatures') self.in_desc_signature = False def depart_desc_content(self, node: Element) -> None: