Skip to content

Commit

Permalink
LaTeX: \pysig{start,stop}signatures and related \pysigline refactoring
Browse files Browse the repository at this point in the history
This fixes sphinx-doc#9924

Removes sphinx-doc#9941 complex LaTeX macros, which incidentally had broken
effect of sphinx-doc#9946 on issue sphinx-doc#9926, as they become unneeded after the
refactoring of \pysigline/\pysiglinewithargs expansion context.
  • Loading branch information
jfbu authored and marxin committed Jan 14, 2022
1 parent 7a5ed92 commit bc486b1
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 42 deletions.
93 changes: 58 additions & 35 deletions 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:
%
Expand Down Expand Up @@ -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 <set-up>\item[foo] <setup>\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 <signatures> \pysigstopsignatures <actual desc>
\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
%
Expand Down
18 changes: 11 additions & 7 deletions sphinx/writers/latex.py
Expand Up @@ -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:
Expand All @@ -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('}')
Expand All @@ -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)
Expand All @@ -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:
Expand Down

0 comments on commit bc486b1

Please sign in to comment.