From 4df3b067b787b65affeccdada7102437891ef298 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 31 Oct 2018 15:58:45 +0100 Subject: [PATCH 1/3] LaTeX: add styling to general index, similar to domain indices Closes: #5579. Also closes #5576. --- sphinx/texinputs/python.ist | 3 +++ sphinx/texinputs/sphinx.sty | 5 ++++- sphinx/texinputs/sphinx.xdy | 8 ++++++-- sphinx/writers/latex.py | 40 +++++++++++++++++++++++++++---------- tests/test_build_latex.py | 26 ++++++++++++++---------- 5 files changed, 58 insertions(+), 24 deletions(-) diff --git a/sphinx/texinputs/python.ist b/sphinx/texinputs/python.ist index 7a1c06fbd..70536a668 100644 --- a/sphinx/texinputs/python.ist +++ b/sphinx/texinputs/python.ist @@ -4,6 +4,9 @@ heading_prefix " \\bigletter " preamble "\\begin{sphinxtheindex} \\let\\bigletter\\sphinxstyleindexlettergroup +\\let\\spxpagem \\sphinxstyleindexpagemain +\\let\\spxentry \\sphinxstyleindexentry +\\let\\spxextra \\sphinxstyleindexextra " diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 62d119e02..f16035973 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -1623,8 +1623,11 @@ % additional customizable styling \def\sphinxstyleindexentry #1{\texttt{#1}} -\def\sphinxstyleindexextra #1{ \emph{(#1)}} +\def\sphinxstyleindexextra #1{ (\emph{#1})} \def\sphinxstyleindexpageref #1{, \pageref{#1}} +\def\sphinxstyleindexpagemain#1{\textbf{#1}} +\protected\def\spxentry#1{#1}% will get \let to \sphinxstyleindexentry in index +\protected\def\spxextra#1{#1}% will get \let to \sphinxstyleindexextra in index \def\sphinxstyleindexlettergroup #1% {{\Large\sffamily#1}\nopagebreak\vspace{1mm}} \def\sphinxstyleindexlettergroupDefault #1% diff --git a/sphinx/texinputs/sphinx.xdy b/sphinx/texinputs/sphinx.xdy index 17b065600..0d02ef337 100644 --- a/sphinx/texinputs/sphinx.xdy +++ b/sphinx/texinputs/sphinx.xdy @@ -3,11 +3,12 @@ ;; Unfortunately xindy is out-of-the-box hyperref-incompatible. This ;; configuration is a workaround, which requires to pass option ;; hyperindex=false to hyperref. -;; textit and emph not currently used by Sphinx LaTeX writer. -(define-attributes (("textbf" "textit" "emph" "default"))) +;; textit and emph not currently used, spxpagem replaces former textbf +(define-attributes (("textbf" "textit" "emph" "spxpagem" "default"))) (markup-locref :open "\textbf{\hyperpage{" :close "}}" :attr "textbf") (markup-locref :open "\textit{\hyperpage{" :close "}}" :attr "textit") (markup-locref :open "\emph{\hyperpage{" :close "}}" :attr "emph") +(markup-locref :open "\spxpagem{\hyperpage{" :close "}}" :attr "spxpagem") (markup-locref :open "\hyperpage{" :close "}" :attr "default") (require "numeric-sort.xdy") @@ -193,6 +194,9 @@ (markup-index :open "\begin{sphinxtheindex} \let\lettergroup\sphinxstyleindexlettergroup \let\lettergroupDefault\sphinxstyleindexlettergroupDefault +\let\spxpagem\sphinxstyleindexpagemain +\let\spxentry\sphinxstyleindexentry +\let\spxextra\sphinxstyleindexextra " :close " diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 7febdef7d..6df72da93 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1884,7 +1884,7 @@ class LaTeXTranslator(nodes.NodeVisitor): # type: (nodes.Node) -> None self.body.append('\n\\end{flushright}\n') - def visit_index(self, node, scre=re.compile(r';\s*')): + def visit_index(self, node, extrare=re.compile(r'^(.*\S)\s+\(([^()]*)\)\s*$')): # type: (nodes.Node, Pattern) -> None def escape(value): value = self.encode(value) @@ -1895,33 +1895,51 @@ class LaTeXTranslator(nodes.NodeVisitor): value = value.replace('!', '"!') return value + def style(string): + match = extrare.match(string) + if match: + return match.expand(r'\\spxentry{\1}\\spxextra{\2}') + else: + return '\\spxentry{%s}' % string + if not node.get('inline', True): self.body.append('\n') entries = node['entries'] for type, string, tid, ismain, key_ in entries: m = '' if ismain: - m = '|textbf' + m = '|spxpagem' try: if type == 'single': - p = scre.sub('!', escape(string)) - self.body.append(r'\index{%s%s}' % (p, m)) + try: + p1, p2 = [escape(x) for x in split_into(2, 'single', string)] + P1, P2 = style(p1), style(p2) + self.body.append(r'\index{%s@%s!%s@%s%s}' % (p1, P1, p2, P2, m)) + except ValueError: + p = escape(split_into(1, 'single', string)[0]) + P = style(p) + self.body.append(r'\index{%s@%s%s}' % (p, P, m)) elif type == 'pair': p1, p2 = [escape(x) for x in split_into(2, 'pair', string)] - self.body.append(r'\index{%s!%s%s}\index{%s!%s%s}' % - (p1, p2, m, p2, p1, m)) + P1, P2 = style(p1), style(p2) + self.body.append(r'\index{%s@%s!%s@%s%s}\index{%s@%s!%s@%s%s}' % + (p1, P1, p2, P2, m, p2, P2, p1, P1, m)) elif type == 'triple': p1, p2, p3 = [escape(x) for x in split_into(3, 'triple', string)] self.body.append( - r'\index{%s!%s %s%s}\index{%s!%s, %s%s}' - r'\index{%s!%s %s%s}' % - (p1, p2, p3, m, p2, p3, p1, m, p3, p1, p2, m)) + r'\index{%s@\spxentry{%s}!%s %s@\spxentry{%s %s}%s}' + r'\index{%s@\spxentry{%s}!%s, %s@\spxentry{%s, %s}%s}' + r'\index{%s@\spxentry{%s}!%s %s@\spxentry{%s %s}%s}' % + (p1, p1, p2, p3, p2, p3, m, p2, p2, p3, p1, p3, p1, m, + p3, p3, p1, p2, p1, p2, m)) elif type == 'see': p1, p2 = [escape(x) for x in split_into(2, 'see', string)] - self.body.append(r'\index{%s|see{%s}}' % (p1, p2)) + P1 = style(p1) + self.body.append(r'\index{%s@%s|see{%s}}' % (p1, P1, p2)) elif type == 'seealso': p1, p2 = [escape(x) for x in split_into(2, 'seealso', string)] - self.body.append(r'\index{%s|see{%s}}' % (p1, p2)) + P1 = style(p1) + self.body.append(r'\index{%s@%s|see{%s}}' % (p1, P1, p2)) else: logger.warning(__('unknown index entry type %s found'), type) except ValueError as err: diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index b5eabc4ad..580b8a93e 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -1199,9 +1199,13 @@ def test_latex_index(app, status, warning): app.builder.build_all() result = (app.outdir / 'Python.tex').text(encoding='utf8') - assert 'A \\index{famous}famous \\index{equation}equation:\n' in result - assert '\n\\index{Einstein}\\index{relativity}\\ignorespaces \nand' in result - assert '\n\\index{main \\sphinxleftcurlybrace{}}\\ignorespaces ' in result + assert ('A \\index{famous@\\spxentry{famous}}famous ' + '\\index{equation@\\spxentry{equation}}equation:\n' in result) + assert ('\n\\index{Einstein@\\spxentry{Einstein}}' + '\\index{relativity@\\spxentry{relativity}}' + '\\ignorespaces \nand') in result + assert ('\n\\index{main \\sphinxleftcurlybrace{}@\\spxentry{' + 'main \\sphinxleftcurlybrace{}}}\\ignorespaces ' in result) @pytest.mark.sphinx('latex', testroot='latex-equations') @@ -1269,20 +1273,22 @@ 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' + assert (u'\\item[{änhlich\\index{änhlich@\\spxentry{änhlich}|spxpagem}' + r'\phantomsection' r'\label{\detokenize{index:term-anhlich}}}] \leavevmode' in result) - assert (r'\item[{boson\index{boson|textbf}\phantomsection' + assert (r'\item[{boson\index{boson@\spxentry{boson}|spxpagem}\phantomsection' r'\label{\detokenize{index:term-boson}}}] \leavevmode' in result) - assert (r'\item[{\sphinxstyleemphasis{fermion}\index{fermion|textbf}' + assert (r'\item[{\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|textbf}\phantomsection' + assert (r'\item[{tauon\index{tauon@\spxentry{tauon}|spxpagem}\phantomsection' r'\label{\detokenize{index:term-tauon}}}] \leavevmode' - r'\item[{myon\index{myon|textbf}\phantomsection' + r'\item[{myon\index{myon@\spxentry{myon}|spxpagem}\phantomsection' r'\label{\detokenize{index:term-myon}}}] \leavevmode' - r'\item[{electron\index{electron|textbf}\phantomsection' + r'\item[{electron\index{electron@\spxentry{electron}|spxpagem}\phantomsection' r'\label{\detokenize{index:term-electron}}}] \leavevmode' in result) - assert (u'\\item[{über\\index{über|textbf}\\phantomsection' + assert (u'\\item[{über\\index{über@\\spxentry{über}|spxpagem}\\phantomsection' r'\label{\detokenize{index:term-uber}}}] \leavevmode' in result) From 0f45cf75b3940a5437b7755dd94c8128d2e56e9c Mon Sep 17 00:00:00 2001 From: jfbu Date: Thu, 1 Nov 2018 11:40:17 +0100 Subject: [PATCH 2/3] LaTeX general index: also style "extras" in triple entries --- sphinx/writers/latex.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 6df72da93..69b22bf1c 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1926,12 +1926,14 @@ class LaTeXTranslator(nodes.NodeVisitor): (p1, P1, p2, P2, m, p2, P2, p1, P1, m)) elif type == 'triple': p1, p2, p3 = [escape(x) for x in split_into(3, 'triple', string)] + P1, P2, P3 = style(p1), style(p2), style(p3) self.body.append( - r'\index{%s@\spxentry{%s}!%s %s@\spxentry{%s %s}%s}' - r'\index{%s@\spxentry{%s}!%s, %s@\spxentry{%s, %s}%s}' - r'\index{%s@\spxentry{%s}!%s %s@\spxentry{%s %s}%s}' % - (p1, p1, p2, p3, p2, p3, m, p2, p2, p3, p1, p3, p1, m, - p3, p3, p1, p2, p1, p2, m)) + r'\index{%s@%s!%s %s@%s %s%s}' + r'\index{%s@%s!%s, %s@%s, %s%s}' + r'\index{%s@%s!%s %s@%s %s%s}' % + (p1, P1, p2, p3, P2, P3, m, + p2, P2, p3, p1, P3, P1, m, + p3, P3, p1, p2, P1, P2, m)) elif type == 'see': p1, p2 = [escape(x) for x in split_into(2, 'see', string)] P1 = style(p1) From 00a75b2742a608ebaedf4816d66e36c8f4f18205 Mon Sep 17 00:00:00 2001 From: jfbu Date: Sun, 4 Nov 2018 21:48:35 +0100 Subject: [PATCH 3/3] LaTeX: deprecate optional argument scre to visit_index() And it is already ignored. --- sphinx/writers/latex.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 69b22bf1c..e9d931f9e 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -169,6 +169,8 @@ ADDITIONAL_SETTINGS = { }, } # type: Dict[unicode, Dict[unicode, unicode]] +EXTRA_RE = re.compile(r'^(.*\S)\s+\(([^()]*)\)\s*$') + class collected_footnote(nodes.footnote): """Footnotes that are collected are assigned this class.""" @@ -1884,8 +1886,8 @@ class LaTeXTranslator(nodes.NodeVisitor): # type: (nodes.Node) -> None self.body.append('\n\\end{flushright}\n') - def visit_index(self, node, extrare=re.compile(r'^(.*\S)\s+\(([^()]*)\)\s*$')): - # type: (nodes.Node, Pattern) -> None + def visit_index(self, node, scre = None): + # type: (nodes.Node, None) -> None def escape(value): value = self.encode(value) value = value.replace(r'\{', r'\sphinxleftcurlybrace{}') @@ -1896,12 +1898,16 @@ class LaTeXTranslator(nodes.NodeVisitor): return value def style(string): - match = extrare.match(string) + match = EXTRA_RE.match(string) if match: return match.expand(r'\\spxentry{\1}\\spxextra{\2}') else: return '\\spxentry{%s}' % string + if scre: + warnings.warn(('LaTeXTranslator.visit_index() optional argument ' + '"scre" is deprecated. It is ignored.'), + RemovedInSphinx30Warning, stacklevel=2) if not node.get('inline', True): self.body.append('\n') entries = node['entries']