From ff3ac70c76e91f38b6be5cb7acbbe16cdb35e835 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 19 May 2018 15:20:30 +0900 Subject: [PATCH 1/8] Fix #4973: latex: glossary directive adds whitespace to each item --- CHANGES | 1 + sphinx/writers/latex.py | 7 ++--- tests/roots/test-glossary/conf.py | 7 +++++ tests/roots/test-glossary/index.rst | 22 ++++++++++++++ tests/test_build_latex.py | 46 +++++++++++++++++++++-------- 5 files changed, 67 insertions(+), 16 deletions(-) create mode 100644 tests/roots/test-glossary/conf.py create mode 100644 tests/roots/test-glossary/index.rst diff --git a/CHANGES b/CHANGES index 3fa74fea5..78f8ca05f 100644 --- a/CHANGES +++ b/CHANGES @@ -36,6 +36,7 @@ Bugs fixed * #4979: latex: Incorrect escaping of curly braces in index entries * #4956: autodoc: Failed to extract document from a subclass of the class on mocked module +* #4973: latex: glossary directive adds whitespace to each item Testing -------- diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index b4b0ce7d7..680dc4f40 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -725,7 +725,7 @@ class LaTeXTranslator(nodes.NodeVisitor): # type: (unicode, bool, bool) -> unicode if withdoc: id = self.curfilestack[-1] + ':' + id - return (anchor and '\\phantomsection' or '') + \ + return (anchor and r'\phantomsection\relax' or '') + \ '\\label{%s}' % self.idescape(id) def hyperlink(self, id): @@ -1555,9 +1555,8 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_term(self, node): # type: (nodes.Node) -> None self.in_term += 1 - ctx = '}] \\leavevmode' # type: unicode - if node.get('ids'): - ctx += self.hypertarget(node['ids'][0]) + ctx = ''.join(self.hypertarget(node_id) for node_id in node['ids']) + ctx += '}] \\leavevmode' self.body.append('\\item[{') self.restrict_footnote(node) self.context.append(ctx) diff --git a/tests/roots/test-glossary/conf.py b/tests/roots/test-glossary/conf.py new file mode 100644 index 000000000..31e7a6ed4 --- /dev/null +++ b/tests/roots/test-glossary/conf.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- + +master_doc = 'index' + +latex_documents = [ + (master_doc, 'test.tex', 'The basic Sphinx documentation for testing', 'Sphinx', 'report') +] diff --git a/tests/roots/test-glossary/index.rst b/tests/roots/test-glossary/index.rst new file mode 100644 index 000000000..88f6ef1b3 --- /dev/null +++ b/tests/roots/test-glossary/index.rst @@ -0,0 +1,22 @@ +test-glossary +============= + +.. glossary:: + :sorted: + + boson + Particle with integer spin. + + *fermion* + Particle with half-integer spin. + + tauon + myon + electron + Examples for fermions. + + über + Gewisse + + änhlich + Dinge diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 1a038c309..b628f38f2 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -592,12 +592,12 @@ def test_footnote(app, status, warning): assert '\\begin{footnote}[3]\\sphinxAtStartFootnote\nnamed\n%\n\\end{footnote}' in result assert '{\\hyperref[\\detokenize{footnote:bar}]{\\sphinxcrossref{{[}bar{]}}}}' in result assert ('\\bibitem[bar]{\\detokenize{bar}}' - '{\\phantomsection\\label{\\detokenize{footnote:bar}} ') in result + '{\\phantomsection\\relax\\label{\\detokenize{footnote:bar}} ') in result assert ('\\bibitem[bar]{\\detokenize{bar}}' - '{\\phantomsection\\label{\\detokenize{footnote:bar}} ' + '{\\phantomsection\\relax\\label{\\detokenize{footnote:bar}} ' '\ncite') in result assert ('\\bibitem[bar]{\\detokenize{bar}}' - '{\\phantomsection\\label{\\detokenize{footnote:bar}} ' + '{\\phantomsection\\relax\\label{\\detokenize{footnote:bar}} ' '\ncite\n}') in result assert '\\sphinxcaption{Table caption \\sphinxfootnotemark[4]' in result assert ('\\hline%\n\\begin{footnotetext}[4]\\sphinxAtStartFootnote\n' @@ -666,14 +666,14 @@ def test_latex_show_urls_is_inline(app, status, warning): '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 - assert ('\\phantomsection\\label{\\detokenize{index:id30}}' + assert ('\\phantomsection\\relax\\label{\\detokenize{index:id30}}' '{\\hyperref[\\detokenize{index:the-section' '-with-a-reference-to-authoryear}]' '{\\sphinxcrossref{The section with a reference to ' - '\\phantomsection\\label{\\detokenize{index:id1}}' + '\\phantomsection\\relax\\label{\\detokenize{index:id1}}' '{\\hyperref[\\detokenize{index:authoryear}]' '{\\sphinxcrossref{{[}AuthorYear{]}}}}}}}') in result - assert ('\\phantomsection\\label{\\detokenize{index:id31}}' + assert ('\\phantomsection\\relax\\label{\\detokenize{index:id31}}' '{\\hyperref[\\detokenize{index:the-section-with-a-reference-to}]' '{\\sphinxcrossref{The section with a reference to }}}' in result) assert ('First footnote: %\n\\begin{footnote}[2]\\sphinxAtStartFootnote\n' @@ -711,13 +711,13 @@ def test_latex_show_urls_is_footnote(app, status, warning): '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 - assert ('\\phantomsection\\label{\\detokenize{index:id30}}' + assert ('\\phantomsection\\relax\\label{\\detokenize{index:id30}}' '{\\hyperref[\\detokenize{index:the-section-with-a-reference-to-authoryear}]' '{\\sphinxcrossref{The section with a reference ' - 'to \\phantomsection\\label{\\detokenize{index:id1}}' + 'to \\phantomsection\\relax\\label{\\detokenize{index:id1}}' '{\\hyperref[\\detokenize{index:authoryear}]' '{\\sphinxcrossref{{[}AuthorYear{]}}}}}}}') in result - assert ('\\phantomsection\\label{\\detokenize{index:id31}}' + assert ('\\phantomsection\\relax\\label{\\detokenize{index:id31}}' '{\\hyperref[\\detokenize{index:the-section-with-a-reference-to}]' '{\\sphinxcrossref{The section with a reference to }}}') in result assert ('First footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n' @@ -764,13 +764,13 @@ def test_latex_show_urls_is_no(app, status, warning): '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 - assert ('\\phantomsection\\label{\\detokenize{index:id30}}' + assert ('\\phantomsection\\relax\\label{\\detokenize{index:id30}}' '{\\hyperref[\\detokenize{index:the-section-with-a-reference-to-authoryear}]' '{\\sphinxcrossref{The section with a reference ' - 'to \\phantomsection\\label{\\detokenize{index:id1}}' + 'to \\phantomsection\\relax\\label{\\detokenize{index:id1}}' '{\\hyperref[\\detokenize{index:authoryear}]' '{\\sphinxcrossref{{[}AuthorYear{]}}}}}}}') in result - assert ('\\phantomsection\\label{\\detokenize{index:id31}}' + assert ('\\phantomsection\\relax\\label{\\detokenize{index:id31}}' '{\\hyperref[\\detokenize{index:the-section-with-a-reference-to}]' '{\\sphinxcrossref{The section with a reference to }}}' in result) assert ('First footnote: %\n\\begin{footnote}[2]\\sphinxAtStartFootnote\n' @@ -1242,3 +1242,25 @@ def test_latex_nested_enumerated_list(app, status, warning): assert r'\setcounter{enumiii}{9}' in result assert r'\setcounter{enumiv}{23}' in result assert r'\setcounter{enumii}{2}' in result + + +@pytest.mark.sphinx('latex', testroot='glossary') +def test_latex_glossary(app, status, warning): + app.builder.build_all() + + result = (app.outdir / 'test.tex').text(encoding='utf8') + assert (u'\\item[{änhlich\\index{änhlich|textbf}\\phantomsection\\relax' + r'\label{\detokenize{index:term-anhlich}}}] \leavevmode' in result) + assert (r'\item[{boson\index{boson|textbf}\phantomsection\relax' + r'\label{\detokenize{index:term-boson}}}] \leavevmode' in result) + assert (r'\item[{\sphinxstyleemphasis{fermion}\index{fermion|textbf}' + r'\phantomsection\relax\label{\detokenize{index:term-fermion}}}] ' + r'\leavevmode' in result) + assert (r'\item[{tauon\index{tauon|textbf}\phantomsection\relax' + r'\label{\detokenize{index:term-tauon}}}] \leavevmode' + r'\item[{myon\index{myon|textbf}\phantomsection\relax' + r'\label{\detokenize{index:term-myon}}}] \leavevmode' + r'\item[{electron\index{electron|textbf}\phantomsection\relax' + r'\label{\detokenize{index:term-electron}}}] \leavevmode' in result) + assert (u'\\item[{über\\index{über|textbf}\\phantomsection\\relax' + r'\label{\detokenize{index:term-uber}}}] \leavevmode' in result) From 0f2dffb0c8a84cb797c96b212e155d91084d28fe Mon Sep 17 00:00:00 2001 From: jfbu Date: Mon, 21 May 2018 16:17:58 +0200 Subject: [PATCH 2/8] Avoid unneeded \relax after \phantomsection in glossary item labels --- sphinx/writers/latex.py | 6 +++++- tests/test_build_latex.py | 16 ++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 680dc4f40..9ecba29a9 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1555,7 +1555,11 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_term(self, node): # type: (nodes.Node) -> None self.in_term += 1 - ctx = ''.join(self.hypertarget(node_id) for node_id in node['ids']) + ctx = '' # type: unicode + if node.get('ids'): + ctx = '\\phantomsection' + for node_id in node['ids']: + ctx += self.hypertarget(node_id, anchor=False) ctx += '}] \\leavevmode' self.body.append('\\item[{') self.restrict_footnote(node) diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index b628f38f2..7a5ab6b41 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -1249,18 +1249,18 @@ def test_latex_glossary(app, status, warning): app.builder.build_all() result = (app.outdir / 'test.tex').text(encoding='utf8') - assert (u'\\item[{änhlich\\index{änhlich|textbf}\\phantomsection\\relax' + assert (u'\\item[{änhlich\\index{änhlich|textbf}\\phantomsection' r'\label{\detokenize{index:term-anhlich}}}] \leavevmode' in result) - assert (r'\item[{boson\index{boson|textbf}\phantomsection\relax' + assert (r'\item[{boson\index{boson|textbf}\phantomsection' r'\label{\detokenize{index:term-boson}}}] \leavevmode' in result) assert (r'\item[{\sphinxstyleemphasis{fermion}\index{fermion|textbf}' - r'\phantomsection\relax\label{\detokenize{index:term-fermion}}}] ' - r'\leavevmode' in result) - assert (r'\item[{tauon\index{tauon|textbf}\phantomsection\relax' + r'\phantomsection' + r'\label{\detokenize{index:term-fermion}}}] \leavevmode' in result) + assert (r'\item[{tauon\index{tauon|textbf}\phantomsection' r'\label{\detokenize{index:term-tauon}}}] \leavevmode' - r'\item[{myon\index{myon|textbf}\phantomsection\relax' + r'\item[{myon\index{myon|textbf}\phantomsection' r'\label{\detokenize{index:term-myon}}}] \leavevmode' - r'\item[{electron\index{electron|textbf}\phantomsection\relax' + r'\item[{electron\index{electron|textbf}\phantomsection' r'\label{\detokenize{index:term-electron}}}] \leavevmode' in result) - assert (u'\\item[{über\\index{über|textbf}\\phantomsection\\relax' + assert (u'\\item[{über\\index{über|textbf}\\phantomsection' r'\label{\detokenize{index:term-uber}}}] \leavevmode' in result) From e7adae9799c6dad1c1c2eb706c6429505b8ed459 Mon Sep 17 00:00:00 2001 From: jfbu Date: Mon, 21 May 2018 16:27:37 +0200 Subject: [PATCH 3/8] Revert extra \relax after \phantomsection in LaTeXTranslator.hypertarget --- sphinx/writers/latex.py | 2 +- tests/test_build_latex.py | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 9ecba29a9..ff81f9cdd 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -725,7 +725,7 @@ class LaTeXTranslator(nodes.NodeVisitor): # type: (unicode, bool, bool) -> unicode if withdoc: id = self.curfilestack[-1] + ':' + id - return (anchor and r'\phantomsection\relax' or '') + \ + return (anchor and '\\phantomsection' or '') + \ '\\label{%s}' % self.idescape(id) def hyperlink(self, id): diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 7a5ab6b41..9c6edd2f1 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -592,12 +592,12 @@ def test_footnote(app, status, warning): assert '\\begin{footnote}[3]\\sphinxAtStartFootnote\nnamed\n%\n\\end{footnote}' in result assert '{\\hyperref[\\detokenize{footnote:bar}]{\\sphinxcrossref{{[}bar{]}}}}' in result assert ('\\bibitem[bar]{\\detokenize{bar}}' - '{\\phantomsection\\relax\\label{\\detokenize{footnote:bar}} ') in result + '{\\phantomsection\\label{\\detokenize{footnote:bar}} ') in result assert ('\\bibitem[bar]{\\detokenize{bar}}' - '{\\phantomsection\\relax\\label{\\detokenize{footnote:bar}} ' + '{\\phantomsection\\label{\\detokenize{footnote:bar}} ' '\ncite') in result assert ('\\bibitem[bar]{\\detokenize{bar}}' - '{\\phantomsection\\relax\\label{\\detokenize{footnote:bar}} ' + '{\\phantomsection\\label{\\detokenize{footnote:bar}} ' '\ncite\n}') in result assert '\\sphinxcaption{Table caption \\sphinxfootnotemark[4]' in result assert ('\\hline%\n\\begin{footnotetext}[4]\\sphinxAtStartFootnote\n' @@ -666,14 +666,14 @@ def test_latex_show_urls_is_inline(app, status, warning): '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 - assert ('\\phantomsection\\relax\\label{\\detokenize{index:id30}}' + assert ('\\phantomsection\\label{\\detokenize{index:id30}}' '{\\hyperref[\\detokenize{index:the-section' '-with-a-reference-to-authoryear}]' '{\\sphinxcrossref{The section with a reference to ' - '\\phantomsection\\relax\\label{\\detokenize{index:id1}}' + '\\phantomsection\\label{\\detokenize{index:id1}}' '{\\hyperref[\\detokenize{index:authoryear}]' '{\\sphinxcrossref{{[}AuthorYear{]}}}}}}}') in result - assert ('\\phantomsection\\relax\\label{\\detokenize{index:id31}}' + assert ('\\phantomsection\\label{\\detokenize{index:id31}}' '{\\hyperref[\\detokenize{index:the-section-with-a-reference-to}]' '{\\sphinxcrossref{The section with a reference to }}}' in result) assert ('First footnote: %\n\\begin{footnote}[2]\\sphinxAtStartFootnote\n' @@ -711,13 +711,13 @@ def test_latex_show_urls_is_footnote(app, status, warning): '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 - assert ('\\phantomsection\\relax\\label{\\detokenize{index:id30}}' + assert ('\\phantomsection\\label{\\detokenize{index:id30}}' '{\\hyperref[\\detokenize{index:the-section-with-a-reference-to-authoryear}]' '{\\sphinxcrossref{The section with a reference ' - 'to \\phantomsection\\relax\\label{\\detokenize{index:id1}}' + 'to \\phantomsection\\label{\\detokenize{index:id1}}' '{\\hyperref[\\detokenize{index:authoryear}]' '{\\sphinxcrossref{{[}AuthorYear{]}}}}}}}') in result - assert ('\\phantomsection\\relax\\label{\\detokenize{index:id31}}' + assert ('\\phantomsection\\label{\\detokenize{index:id31}}' '{\\hyperref[\\detokenize{index:the-section-with-a-reference-to}]' '{\\sphinxcrossref{The section with a reference to }}}') in result assert ('First footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n' @@ -764,13 +764,13 @@ def test_latex_show_urls_is_no(app, status, warning): '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 - assert ('\\phantomsection\\relax\\label{\\detokenize{index:id30}}' + assert ('\\phantomsection\\label{\\detokenize{index:id30}}' '{\\hyperref[\\detokenize{index:the-section-with-a-reference-to-authoryear}]' '{\\sphinxcrossref{The section with a reference ' - 'to \\phantomsection\\relax\\label{\\detokenize{index:id1}}' + 'to \\phantomsection\\label{\\detokenize{index:id1}}' '{\\hyperref[\\detokenize{index:authoryear}]' '{\\sphinxcrossref{{[}AuthorYear{]}}}}}}}') in result - assert ('\\phantomsection\\relax\\label{\\detokenize{index:id31}}' + assert ('\\phantomsection\\label{\\detokenize{index:id31}}' '{\\hyperref[\\detokenize{index:the-section-with-a-reference-to}]' '{\\sphinxcrossref{The section with a reference to }}}' in result) assert ('First footnote: %\n\\begin{footnote}[2]\\sphinxAtStartFootnote\n' From f621fe8533c896610758baa8440057b3bb324305 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 18 May 2018 21:21:46 +0900 Subject: [PATCH 4/8] Close #4980: latex: Improve label handling of LaTeX builder (figure) --- sphinx/writers/latex.py | 29 +++++++++++++++---------- tests/roots/test-latex-labels/conf.py | 7 ++++++ tests/roots/test-latex-labels/index.rst | 17 +++++++++++++++ tests/test_build_latex.py | 15 +++++++++++++ 4 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 tests/roots/test-latex-labels/conf.py create mode 100644 tests/roots/test-latex-labels/index.rst diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index ff81f9cdd..04b10b0da 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -728,6 +728,14 @@ class LaTeXTranslator(nodes.NodeVisitor): return (anchor and '\\phantomsection' or '') + \ '\\label{%s}' % self.idescape(id) + def hypertarget_to(self, node, anchor=False): + # type: (nodes.Node, bool) -> unicode + labels = ''.join(self.hypertarget(node_id, anchor=False) for node_id in node['ids']) + if anchor: + return r'\phantomsection' + labels + else: + return labels + def hyperlink(self, id): # type: (unicode) -> unicode return '{\\hyperref[%s]{' % self.idescape(id) @@ -1758,16 +1766,8 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_figure(self, node): # type: (nodes.Node) -> None - ids = '' # type: unicode - for id in self.pop_hyperlink_ids('figure'): - ids += self.hypertarget(id, anchor=False) - if node['ids']: - ids += self.hypertarget(node['ids'][0], anchor=False) + labels = self.hypertarget_to(node) self.restrict_footnote(node) - if (len(node.children) and - isinstance(node.children[0], nodes.image) and - node.children[0]['ids']): - ids += self.hypertarget(node.children[0]['ids'][0], anchor=False) if self.table: # TODO: support align option if 'width' in node: @@ -1779,7 +1779,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\\begin{sphinxfigure-in-table}\n\\centering\n') if any(isinstance(child, nodes.caption) for child in node): self.body.append('\\capstart') - self.context.append(ids + '\\end{sphinxfigure-in-table}\\relax\n') + self.context.append(labels + '\\end{sphinxfigure-in-table}\\relax\n') elif node.get('align', '') in ('left', 'right'): length = None if 'width' in node: @@ -1788,7 +1788,7 @@ class LaTeXTranslator(nodes.NodeVisitor): length = self.latex_image_length(node[0]['width']) self.body.append('\\begin{wrapfigure}{%s}{%s}\n\\centering' % (node['align'] == 'right' and 'r' or 'l', length or '0pt')) - self.context.append(ids + '\\end{wrapfigure}\n') + self.context.append(labels + '\\end{wrapfigure}\n') elif self.in_minipage: self.body.append('\n\\begin{center}') self.context.append('\\end{center}\n') @@ -1797,7 +1797,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.elements['figure_align']) if any(isinstance(child, nodes.caption) for child in node): self.body.append('\\capstart\n') - self.context.append(ids + '\\end{figure}\n') + self.context.append(labels + '\\end{figure}\n') def depart_figure(self, node): # type: (nodes.Node) -> None @@ -1899,6 +1899,11 @@ class LaTeXTranslator(nodes.NodeVisitor): anchor = not self.in_title self.body.append(self.hypertarget(id, anchor=anchor)) + # skip if visitor for next node supports hyperlink + next_node = node.next_node(ascend=True) + if isinstance(next_node, nodes.figure): + return + # postpone the labels until after the sectioning command parindex = node.parent.index(node) try: diff --git a/tests/roots/test-latex-labels/conf.py b/tests/roots/test-latex-labels/conf.py new file mode 100644 index 000000000..31e7a6ed4 --- /dev/null +++ b/tests/roots/test-latex-labels/conf.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- + +master_doc = 'index' + +latex_documents = [ + (master_doc, 'test.tex', 'The basic Sphinx documentation for testing', 'Sphinx', 'report') +] diff --git a/tests/roots/test-latex-labels/index.rst b/tests/roots/test-latex-labels/index.rst new file mode 100644 index 000000000..8015ea761 --- /dev/null +++ b/tests/roots/test-latex-labels/index.rst @@ -0,0 +1,17 @@ +latex-labels +============ + +figures +------- + +.. _figure1: +.. _figure2: + +.. figure:: logo.jpg + + labeled figure + +.. figure:: logo.jpg + :name: figure3 + + labeled figure diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 9c6edd2f1..530b3771e 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -1264,3 +1264,18 @@ def test_latex_glossary(app, status, warning): r'\label{\detokenize{index:term-electron}}}] \leavevmode' in result) assert (u'\\item[{über\\index{über|textbf}\\phantomsection' r'\label{\detokenize{index:term-uber}}}] \leavevmode' in result) + + +@pytest.mark.sphinx('latex', testroot='latex-labels') +def test_latex_labels(app, status, warning): + app.builder.build_all() + + result = (app.outdir / 'test.tex').text(encoding='utf8') + assert (r'\caption{labeled figure}' + r'\label{\detokenize{index:id1}}' + r'\label{\detokenize{index:figure2}}' + r'\label{\detokenize{index:figure1}}' + r'\end{figure}' in result) + assert (r'\caption{labeled figure}' + r'\label{\detokenize{index:figure3}}' + r'\end{figure}' in result) From fdc06976902b8598d81b6a3d14f8f0f125ea3afa Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 18 May 2018 23:18:11 +0900 Subject: [PATCH 5/8] Close #4980: latex: Improve label handling of LaTeX builder (code-block) --- sphinx/writers/latex.py | 17 ++++++++--------- tests/roots/test-latex-labels/index.rst | 15 +++++++++++++++ tests/test_build_latex.py | 9 +++++++++ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 04b10b0da..9429f1f9a 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -49,6 +49,10 @@ BEGIN_DOC = r''' LATEXSECTIONNAMES = ["part", "chapter", "section", "subsection", "subsubsection", "paragraph", "subparagraph"] +HYPERLINK_SUPPORT_NODES = ( + nodes.figure, + nodes.literal_block, +) DEFAULT_SETTINGS = { 'latex_engine': 'pdflatex', @@ -1901,7 +1905,7 @@ class LaTeXTranslator(nodes.NodeVisitor): # skip if visitor for next node supports hyperlink next_node = node.next_node(ascend=True) - if isinstance(next_node, nodes.figure): + if isinstance(next_node, HYPERLINK_SUPPORT_NODES): return # postpone the labels until after the sectioning command @@ -2237,15 +2241,10 @@ class LaTeXTranslator(nodes.NodeVisitor): self.in_parsed_literal += 1 self.body.append('\\begin{sphinxalltt}\n') else: - ids = '' # type: unicode - for id in self.pop_hyperlink_ids('code-block'): - ids += self.hypertarget(id, anchor=False) - if node['ids']: - # suppress with anchor=False \phantomsection insertion - ids += self.hypertarget(node['ids'][0], anchor=False) + labels = self.hypertarget_to(node) # LaTeX code will insert \phantomsection prior to \label - if ids and not self.in_footnote: - self.body.append('\n\\def\\sphinxLiteralBlockLabel{' + ids + '}') + if labels and not self.in_footnote: + self.body.append('\n\\def\\sphinxLiteralBlockLabel{' + labels + '}') code = node.astext() lang = self.hlsettingstack[-1][0] linenos = code.count('\n') >= self.hlsettingstack[-1][1] - 1 diff --git a/tests/roots/test-latex-labels/index.rst b/tests/roots/test-latex-labels/index.rst index 8015ea761..940279853 100644 --- a/tests/roots/test-latex-labels/index.rst +++ b/tests/roots/test-latex-labels/index.rst @@ -15,3 +15,18 @@ figures :name: figure3 labeled figure + +code-blocks +----------- + +.. _codeblock1: +.. _codeblock2: + +.. code-block:: none + + blah blah blah + +.. code-block:: none + :name: codeblock3 + + blah blah blah diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 530b3771e..f48fc9c79 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -1271,6 +1271,8 @@ def test_latex_labels(app, status, warning): app.builder.build_all() result = (app.outdir / 'test.tex').text(encoding='utf8') + + # figures assert (r'\caption{labeled figure}' r'\label{\detokenize{index:id1}}' r'\label{\detokenize{index:figure2}}' @@ -1279,3 +1281,10 @@ def test_latex_labels(app, status, warning): assert (r'\caption{labeled figure}' r'\label{\detokenize{index:figure3}}' r'\end{figure}' in result) + + # code-blocks + assert (r'\def\sphinxLiteralBlockLabel{' + r'\label{\detokenize{index:codeblock2}}' + r'\label{\detokenize{index:codeblock1}}}' in result) + assert (r'\def\sphinxLiteralBlockLabel{' + r'\label{\detokenize{index:codeblock3}}}' in result) From 1b5e910059eff99310ed027cd9b28ab8c4eead3a Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 19 May 2018 00:26:18 +0900 Subject: [PATCH 6/8] Close #4980: latex: Improve label handling of LaTeX builder (table) --- sphinx/writers/latex.py | 8 ++------ tests/roots/test-latex-labels/index.rst | 21 +++++++++++++++++++++ tests/test_build_latex.py | 8 ++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 9429f1f9a..2431eb117 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -52,6 +52,7 @@ LATEXSECTIONNAMES = ["part", "chapter", "section", "subsection", HYPERLINK_SUPPORT_NODES = ( nodes.figure, nodes.literal_block, + nodes.table, ) DEFAULT_SETTINGS = { @@ -1318,12 +1319,7 @@ class LaTeXTranslator(nodes.NodeVisitor): def depart_table(self, node): # type: (nodes.Node) -> None - labels = '' # type: unicode - for labelid in self.pop_hyperlink_ids('table'): - labels += self.hypertarget(labelid, anchor=False) - if node['ids']: - labels += self.hypertarget(node['ids'][0], anchor=False) - + labels = self.hypertarget_to(node) table_type = self.table.get_table_type() table = self.render(table_type + '.tex_t', dict(table=self.table, labels=labels)) diff --git a/tests/roots/test-latex-labels/index.rst b/tests/roots/test-latex-labels/index.rst index 940279853..e29177f71 100644 --- a/tests/roots/test-latex-labels/index.rst +++ b/tests/roots/test-latex-labels/index.rst @@ -30,3 +30,24 @@ code-blocks :name: codeblock3 blah blah blah + +tables +------ + +.. _table1: +.. _table2: + +.. table:: table caption + + ==== ==== + head head + cell cell + ==== ==== + +.. table:: table caption + :name: table3 + + ==== ==== + head head + cell cell + ==== ==== diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index f48fc9c79..64c896fda 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -1288,3 +1288,11 @@ def test_latex_labels(app, status, warning): r'\label{\detokenize{index:codeblock1}}}' in result) assert (r'\def\sphinxLiteralBlockLabel{' r'\label{\detokenize{index:codeblock3}}}' in result) + + # tables + assert (r'\sphinxcaption{table caption}' + r'\label{\detokenize{index:id2}}' + r'\label{\detokenize{index:table2}}' + r'\label{\detokenize{index:table1}}' in result) + assert (r'\sphinxcaption{table caption}' + r'\label{\detokenize{index:table3}}' in result) From 0ba5c24f5ee74027df36fef18aff6c48cd059b50 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 19 May 2018 01:21:57 +0900 Subject: [PATCH 7/8] Close #4980: latex: Improve label handling of LaTeX builder (section) --- sphinx/writers/latex.py | 29 ++++++++-------------- tests/roots/test-latex-labels/index.rst | 15 +++++++++++ tests/roots/test-latex-labels/otherdoc.rst | 2 ++ tests/test_build_latex.py | 12 +++++++++ 4 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 tests/roots/test-latex-labels/otherdoc.rst diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 2431eb117..bd88ca842 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -53,6 +53,7 @@ HYPERLINK_SUPPORT_NODES = ( nodes.figure, nodes.literal_block, nodes.table, + nodes.section, ) DEFAULT_SETTINGS = { @@ -950,8 +951,6 @@ class LaTeXTranslator(nodes.NodeVisitor): if not self.this_is_the_title: self.sectionlevel += 1 self.body.append('\n\n') - if node.get('ids'): - self.next_section_ids.update(node['ids']) def depart_section(self, node): # type: (nodes.Node) -> None @@ -1046,9 +1045,9 @@ class LaTeXTranslator(nodes.NodeVisitor): except IndexError: # just use "subparagraph", it's not numbered anyway self.body.append(r'\%s%s{' % (self.sectionnames[-1], short)) - self.context.append('}\n') - + self.context.append('}\n' + self.hypertarget_to(node.parent)) self.restrict_footnote(node) + if self.next_section_ids: for id in self.next_section_ids: self.context[-1] += self.hypertarget(id, anchor=False) @@ -1917,22 +1916,16 @@ class LaTeXTranslator(nodes.NodeVisitor): node.parent.parent.index(node.parent)] else: raise - if isinstance(next, nodes.section): + domain = self.builder.env.get_domain('std') + figtype = domain.get_figtype(next) + if figtype and domain.get_numfig_title(next): + ids = set() + # labels for figures go in the figure body, not before if node.get('refid'): - self.next_section_ids.add(node['refid']) - self.next_section_ids.update(node['ids']) + ids.add(node['refid']) + ids.update(node['ids']) + self.push_hyperlink_ids(figtype, ids) return - else: - domain = self.builder.env.get_domain('std') - figtype = domain.get_figtype(next) - if figtype and domain.get_numfig_title(next): - ids = set() - # labels for figures go in the figure body, not before - if node.get('refid'): - ids.add(node['refid']) - ids.update(node['ids']) - self.push_hyperlink_ids(figtype, ids) - return except IndexError: pass if 'refuri' in node: diff --git a/tests/roots/test-latex-labels/index.rst b/tests/roots/test-latex-labels/index.rst index e29177f71..5859fb6d2 100644 --- a/tests/roots/test-latex-labels/index.rst +++ b/tests/roots/test-latex-labels/index.rst @@ -51,3 +51,18 @@ tables head head cell cell ==== ==== + +.. _section1: +.. _section2: + +subsection +---------- + +.. _section3: + +subsubsection +~~~~~~~~~~~~~ + +.. toctree:: + + otherdoc diff --git a/tests/roots/test-latex-labels/otherdoc.rst b/tests/roots/test-latex-labels/otherdoc.rst new file mode 100644 index 000000000..55c5ca051 --- /dev/null +++ b/tests/roots/test-latex-labels/otherdoc.rst @@ -0,0 +1,2 @@ +otherdoc +======== diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 64c896fda..96ae14089 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -1296,3 +1296,15 @@ def test_latex_labels(app, status, warning): r'\label{\detokenize{index:table1}}' in result) assert (r'\sphinxcaption{table caption}' r'\label{\detokenize{index:table3}}' in result) + + # sections + assert ('\\chapter{subsection}\n' + r'\label{\detokenize{index:subsection}}' + r'\label{\detokenize{index:section2}}' + r'\label{\detokenize{index:section1}}' in result) + assert ('\\section{subsubsection}\n' + r'\label{\detokenize{index:subsubsection}}' + r'\label{\detokenize{index:section3}}' in result) + assert ('\\subsection{otherdoc}\n' + r'\label{\detokenize{otherdoc:otherdoc}}' + r'\label{\detokenize{otherdoc::doc}}' in result) From 8197bb7e1918efdd5796000cb7e096c7f45e9311 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 19 May 2018 01:32:39 +0900 Subject: [PATCH 8/8] Update CHANGES --- CHANGES | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES b/CHANGES index 78f8ca05f..760f30e6a 100644 --- a/CHANGES +++ b/CHANGES @@ -37,6 +37,7 @@ Bugs fixed * #4956: autodoc: Failed to extract document from a subclass of the class on mocked module * #4973: latex: glossary directive adds whitespace to each item +* #4980: latex: Explicit labels on code blocks are duplicated Testing --------