diff --git a/sphinx/texinputs/sphinxlatexlists.sty b/sphinx/texinputs/sphinxlatexlists.sty index ed7521cf8f..8e79355568 100644 --- a/sphinx/texinputs/sphinxlatexlists.sty +++ b/sphinx/texinputs/sphinxlatexlists.sty @@ -1,13 +1,47 @@ %% ALPHANUMERIC LIST ITEMS % % change this info string if making any custom modification -\ProvidesFile{sphinxlatexlists.sty}[2021/01/27 lists] +\ProvidesFile{sphinxlatexlists.sty}[2021/12/20 lists] % Provides support for this output mark-up from Sphinx latex writer: % - \sphinxsetlistlabels - +% - \sphinxlineitem +% and for the maxlistdepth key of sphinxsetup % Dependencies: the \spx@opt@maxlistdepth from sphinx.sty +% We need some helpers macros +\newtoks\spx@lineitemlabel +\long\def\sphinx@gobto@sphinxlineitem#1\sphinxlineitem{} +% TeX/LaTeX has no (easy to use) built-in "peek-ahead" mechanism, but +% we would like to know if next token is another \sphinxlineitem (this +% can happen in glossary entries with multiple terms for same definition) +% so we simply grab next token (assuming it is not {tokens} originally) +\newcommand\sphinxlineitem[2]{% + % safe test of whether #2 is \sphinxlineitem + \sphinx@gobto@sphinxlineitem#2\@gobbletwo\sphinxlineitem\unless + \iftrue + % case with sphinxlineitem immediately followed by another \sphinxlineitem: + % accumulate successive terms until actual definition or sub-list is found + \spx@lineitemlabel\expandafter{\the\spx@lineitemlabel\strut#1\\}% + \else + % now issue the \item command with possibly multi-line contents + % these weird incantations with \kern are related to how LaTeX + % handles \item generally + \item[\kern\labelwidth\kern-\itemindent\kern-\leftmargin + {\parbox[t]{\dimexpr\linewidth+\leftmargin\relax}{% + \raggedright + \the\spx@lineitemlabel% accumulated terms before this one, CR separated + \strut#1}}% due to LaTeX internals no \par token allowed here, + % but the \parbox will insert one tacitly at end + \kern-\labelsep]% + \spx@lineitemlabel{}% + % this causes the label to be typeset (filling up the line), clearing up + % things in case a nested list follows. + \leavevmode + \fi #2% +}% + + \newcommand\sphinxsetlistlabels[5] {% #1 = style, #2 = enum, #3 = enumnext, #4 = prefix, #5 = suffix % #2 and #3 are counters used by enumerate environment e.g. enumi, enumii. diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index e90fc1fb62..f7338b27ca 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1092,8 +1092,8 @@ def visit_term(self, node: Element) -> None: ctx = r'\phantomsection' for node_id in node['ids']: ctx += self.hypertarget(node_id, anchor=False) - ctx += r'}] \leavevmode' - self.body.append(r'\item[{') + ctx += r'}' + self.body.append(r'\sphinxlineitem{') self.context.append(ctx) def depart_term(self, node: Element) -> None: diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index c78cc6c81a..aa306bb9e0 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -834,18 +834,18 @@ def test_latex_show_urls_is_inline(app, status, warning): '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 - assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{URL in term} ' - '(http://sphinx\\sphinxhyphen{}doc.org/)}] ' - '\\leavevmode\n\\sphinxAtStartPar\nDescription' in result) - assert ('\\item[{Footnote in term \\sphinxfootnotemark[6]}] ' - '\\leavevmode%\n\\begin{footnotetext}[6]' + assert ('\\sphinxlineitem{\\sphinxhref{http://sphinx-doc.org/}{URL in term} ' + '(http://sphinx\\sphinxhyphen{}doc.org/)}' + '\n\\sphinxAtStartPar\nDescription' in result) + assert ('\\sphinxlineitem{Footnote in term \\sphinxfootnotemark[6]}' + '%\n\\begin{footnotetext}[6]' '\\phantomsection\\label{\\thesphinxscope.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} ' - '(http://sphinx\\sphinxhyphen{}doc.org/)}] ' - '\\leavevmode\n\\sphinxAtStartPar\nDescription') in result + assert ('\\sphinxlineitem{\\sphinxhref{http://sphinx-doc.org/}{Term in deflist} ' + '(http://sphinx\\sphinxhyphen{}doc.org/)}' + '\n\\sphinxAtStartPar\nDescription') in result assert '\\sphinxurl{https://github.com/sphinx-doc/sphinx}\n' in result assert ('\\sphinxhref{mailto:sphinx-dev@googlegroups.com}' '{sphinx\\sphinxhyphen{}dev@googlegroups.com}') in result @@ -893,22 +893,22 @@ def test_latex_show_urls_is_footnote(app, status, warning): assert ('\\sphinxhref{http://sphinx-doc.org/~test/}{URL including tilde}' '%\n\\begin{footnote}[5]\\sphinxAtStartFootnote\n' '\\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]' + assert ('\\sphinxlineitem{\\sphinxhref{http://sphinx-doc.org/}' + '{URL in term}\\sphinxfootnotemark[9]}' + '%\n\\begin{footnotetext}[9]' '\\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]' + assert ('\\sphinxlineitem{Footnote in term \\sphinxfootnotemark[11]}' + '%\n\\begin{footnotetext}[11]' '\\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]' + assert ('\\sphinxlineitem{\\sphinxhref{http://sphinx-doc.org/}{Term in deflist}' + '\\sphinxfootnotemark[10]}' + '%\n\\begin{footnotetext}[10]' '\\phantomsection\\label{\\thesphinxscope.10}%\n' '\\sphinxAtStartFootnote\n' '\\sphinxnolinkurl{http://sphinx-doc.org/}\n%\n' @@ -955,16 +955,16 @@ def test_latex_show_urls_is_no(app, status, warning): '\\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]' + assert ('\\sphinxlineitem{\\sphinxhref{http://sphinx-doc.org/}{URL in term}}' + '\n\\sphinxAtStartPar\nDescription') in result + assert ('\\sphinxlineitem{Footnote in term \\sphinxfootnotemark[6]}' + '%\n\\begin{footnotetext}[6]' '\\phantomsection\\label{\\thesphinxscope.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}}] ' - '\\leavevmode\n\\sphinxAtStartPar\nDescription') in result + assert ('\\sphinxlineitem{\\sphinxhref{http://sphinx-doc.org/}{Term in deflist}}' + '\n\\sphinxAtStartPar\nDescription') in result assert ('\\sphinxurl{https://github.com/sphinx-doc/sphinx}\n' in result) assert ('\\sphinxhref{mailto:sphinx-dev@googlegroups.com}' '{sphinx\\sphinxhyphen{}dev@googlegroups.com}\n') in result @@ -1454,23 +1454,23 @@ def test_latex_glossary(app, status, warning): app.builder.build_all() result = (app.outdir / 'python.tex').read_text() - assert ('\\item[{ähnlich\\index{ähnlich@\\spxentry{ähnlich}|spxpagem}' + assert (r'\sphinxlineitem{ähnlich\index{ähnlich@\spxentry{ähnlich}|spxpagem}' r'\phantomsection' - r'\label{\detokenize{index:term-ahnlich}}}] \leavevmode' in result) - assert (r'\item[{boson\index{boson@\spxentry{boson}|spxpagem}\phantomsection' - r'\label{\detokenize{index:term-boson}}}] \leavevmode' in result) - assert (r'\item[{\sphinxstyleemphasis{fermion}' + r'\label{\detokenize{index:term-ahnlich}}}' in result) + assert (r'\sphinxlineitem{boson\index{boson@\spxentry{boson}|spxpagem}\phantomsection' + r'\label{\detokenize{index:term-boson}}}' in result) + assert (r'\sphinxlineitem{\sphinxstyleemphasis{fermion}' r'\index{fermion@\spxentry{fermion}|spxpagem}' r'\phantomsection' - r'\label{\detokenize{index:term-fermion}}}] \leavevmode' in result) - assert (r'\item[{tauon\index{tauon@\spxentry{tauon}|spxpagem}\phantomsection' - r'\label{\detokenize{index:term-tauon}}}] \leavevmode' - r'\item[{myon\index{myon@\spxentry{myon}|spxpagem}\phantomsection' - r'\label{\detokenize{index:term-myon}}}] \leavevmode' - r'\item[{electron\index{electron@\spxentry{electron}|spxpagem}\phantomsection' - r'\label{\detokenize{index:term-electron}}}] \leavevmode' in result) - assert ('\\item[{über\\index{über@\\spxentry{über}|spxpagem}\\phantomsection' - r'\label{\detokenize{index:term-uber}}}] \leavevmode' in result) + r'\label{\detokenize{index:term-fermion}}}' in result) + assert (r'\sphinxlineitem{tauon\index{tauon@\spxentry{tauon}|spxpagem}\phantomsection' + r'\label{\detokenize{index:term-tauon}}}' + r'\sphinxlineitem{myon\index{myon@\spxentry{myon}|spxpagem}\phantomsection' + r'\label{\detokenize{index:term-myon}}}' + r'\sphinxlineitem{electron\index{electron@\spxentry{electron}|spxpagem}\phantomsection' + r'\label{\detokenize{index:term-electron}}}' in result) + assert (r'\sphinxlineitem{über\index{über@\spxentry{über}|spxpagem}\phantomsection' + r'\label{\detokenize{index:term-uber}}}' in result) @pytest.mark.sphinx('latex', testroot='latex-labels')