From 600c948ff97ffb798aeb1d0bd27d8162f85076e2 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Mon, 14 May 2018 20:09:45 +0900 Subject: [PATCH 1/4] Close #1431: latex: Add alphanumeric enumerated list support --- CHANGES | 1 + sphinx/writers/latex.py | 26 ++++++++++++++++++++++++-- tests/test_build_latex.py | 20 +++++++++++++++----- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index 57583accf..24ed7956e 100644 --- a/CHANGES +++ b/CHANGES @@ -116,6 +116,7 @@ Features added * #4785: napoleon: Add strings to translation file for localisation * #4927: Display a warning when invalid values are passed to linenothreshold option of highlight directive +* #1431: latex: Add alphanumeric enumerated list support Bugs fixed ---------- diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 9501d69d0..7e4f6eb8f 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -59,6 +59,14 @@ HYPERLINK_SUPPORT_NODES = ( nodes.table, nodes.section, ) +ENUMERATE_LIST_STYLE = defaultdict(lambda: r'\arabic', + { + 'arabic': r'\arabic', + 'loweralpha': r'\alph', + 'upperalpha': r'\Alph', + 'lowerroman': r'\roman', + 'upperroman': r'\Roman', + }) # type: Dict[unicode, unicode] DEFAULT_SETTINGS = { 'latex_engine': 'pdflatex', @@ -1498,6 +1506,15 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_enumerated_list(self, node): # type: (nodes.Node) -> None + def get_enumtype(node): + # type: (nodes.Node) -> unicode + enumtype = node.get('enumtype', 'arabic') + if 'alpha' in enumtype and 26 < node.get('start', 0) + len(node): + # fallback to arabic if alphabet counter overflows + enumtype = 'arabic' + + return enumtype + def get_nested_level(node): # type: (nodes.Node) -> int if node is None: @@ -1507,10 +1524,15 @@ class LaTeXTranslator(nodes.NodeVisitor): else: return get_nested_level(node.parent) + enum = "enum%s" % toRoman(get_nested_level(node)).lower() + style = ENUMERATE_LIST_STYLE.get(get_enumtype(node)) + self.body.append('\\begin{enumerate}\n') + self.body.append('\\renewcommand{\\the%s}{%s{%s}}\n' % (enum, style, enum)) + self.body.append('\\makeatletter\\renewcommand{\\p@%s}{%s\\the%s%s}\\makeatother\n' % + (enum, node['prefix'], enum, node['suffix'])) if 'start' in node: - enum_depth = "enum%s" % toRoman(get_nested_level(node)).lower() - self.body.append('\\setcounter{%s}{%d}\n' % (enum_depth, node['start'] - 1)) + self.body.append('\\setcounter{%s}{%d}\n' % (enum, node['start'] - 1)) if self.table: self.table.has_problematic = True diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 546ba139b..db15bd174 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -1229,11 +1229,21 @@ def test_latex_nested_enumerated_list(app, status, warning): app.builder.build_all() result = (app.outdir / 'test.tex').text(encoding='utf8') - assert r'\setcounter{enumi}{4}' in result - assert r'\setcounter{enumii}{3}' in result - assert r'\setcounter{enumiii}{9}' in result - assert r'\setcounter{enumiv}{23}' in result - assert r'\setcounter{enumii}{2}' in result + assert ('\\renewcommand{\\theenumi}{\\arabic{enumi}}\n' + '\\makeatletter\\renewcommand{\\p@enumi}{\\theenumi.}\\makeatother\n' + '\\setcounter{enumi}{4}\n' in result) + assert ('\\renewcommand{\\theenumii}{\\alph{enumii}}\n' + '\\makeatletter\\renewcommand{\\p@enumii}{\\theenumii.}\\makeatother\n' + '\\setcounter{enumii}{3}\n' in result) + assert ('\\renewcommand{\\theenumiii}{\\arabic{enumiii}}\n' + '\\makeatletter\\renewcommand{\\p@enumiii}{\\theenumiii)}\\makeatother\n' + '\\setcounter{enumiii}{9}\n' in result) + assert ('\\renewcommand{\\theenumiv}{\\arabic{enumiv}}\n' + '\\makeatletter\\renewcommand{\\p@enumiv}{(\\theenumiv)}\\makeatother\n' + '\\setcounter{enumiv}{23}\n' in result) + assert ('\\renewcommand{\\theenumii}{\\roman{enumii}}\n' + '\\makeatletter\\renewcommand{\\p@enumii}{\\theenumii.}\\makeatother\n' + '\\setcounter{enumii}{2}\n' in result) @pytest.mark.sphinx('latex', testroot='footnotes') From a08321f962bac129784caf9d0bda4e7cc413dff4 Mon Sep 17 00:00:00 2001 From: jfbu Date: Sat, 26 May 2018 15:14:57 +0200 Subject: [PATCH 2/4] Better replacement for LaTeX's enumerated list support macros --- sphinx/writers/latex.py | 8 +++++--- tests/test_build_latex.py | 25 +++++++++++++++---------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 7e4f6eb8f..837542c4d 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1525,12 +1525,14 @@ class LaTeXTranslator(nodes.NodeVisitor): return get_nested_level(node.parent) enum = "enum%s" % toRoman(get_nested_level(node)).lower() + enumnext = "enum%s" % toRoman(get_nested_level(node) + 1).lower() style = ENUMERATE_LIST_STYLE.get(get_enumtype(node)) self.body.append('\\begin{enumerate}\n') - self.body.append('\\renewcommand{\\the%s}{%s{%s}}\n' % (enum, style, enum)) - self.body.append('\\makeatletter\\renewcommand{\\p@%s}{%s\\the%s%s}\\makeatother\n' % - (enum, node['prefix'], enum, node['suffix'])) + self.body.append('\\def\\the%s{%s{%s}}\n' % (enum, style, enum)) + self.body.append('\\def\\label%s{\\the%s.}\n' % (enum, enum)) + self.body.append('\\makeatletter\\def\\p@%s{\\p@%s\\the%s.}\\makeatother\n' % + (enumnext, enum, enum)) if 'start' in node: self.body.append('\\setcounter{%s}{%d}\n' % (enum, node['start'] - 1)) if self.table: diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index db15bd174..6be772bbe 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -1229,20 +1229,25 @@ def test_latex_nested_enumerated_list(app, status, warning): app.builder.build_all() result = (app.outdir / 'test.tex').text(encoding='utf8') - assert ('\\renewcommand{\\theenumi}{\\arabic{enumi}}\n' - '\\makeatletter\\renewcommand{\\p@enumi}{\\theenumi.}\\makeatother\n' + assert ('\\def\\theenumi{\\arabic{enumi}}\n' + '\\def\\labelenumi{\\theenumi.}\n' + '\\makeatletter\\def\\p@enumii{\\p@enumi\\theenumi.}\\makeatother\n' '\\setcounter{enumi}{4}\n' in result) - assert ('\\renewcommand{\\theenumii}{\\alph{enumii}}\n' - '\\makeatletter\\renewcommand{\\p@enumii}{\\theenumii.}\\makeatother\n' + assert ('\\def\\theenumii{\\alph{enumii}}\n' + '\\def\\labelenumii{\\theenumii.}\n' + '\\makeatletter\\def\\p@enumiii{\\p@enumii\\theenumii.}\\makeatother\n' '\\setcounter{enumii}{3}\n' in result) - assert ('\\renewcommand{\\theenumiii}{\\arabic{enumiii}}\n' - '\\makeatletter\\renewcommand{\\p@enumiii}{\\theenumiii)}\\makeatother\n' + assert ('\\def\\theenumiii{\\arabic{enumiii}}\n' + '\\def\\labelenumiii{\\theenumiii.}\n' + '\\makeatletter\\def\\p@enumiv{\\p@enumiii\\theenumiii.}\\makeatother\n' '\\setcounter{enumiii}{9}\n' in result) - assert ('\\renewcommand{\\theenumiv}{\\arabic{enumiv}}\n' - '\\makeatletter\\renewcommand{\\p@enumiv}{(\\theenumiv)}\\makeatother\n' + assert ('\\def\\theenumiv{\\arabic{enumiv}}\n' + '\\def\\labelenumiv{\\theenumiv.}\n' + '\\makeatletter\\def\\p@enumv{\\p@enumiv\\theenumiv.}\\makeatother\n' '\\setcounter{enumiv}{23}\n' in result) - assert ('\\renewcommand{\\theenumii}{\\roman{enumii}}\n' - '\\makeatletter\\renewcommand{\\p@enumii}{\\theenumii.}\\makeatother\n' + assert ('\\def\\theenumii{\\roman{enumii}}\n' + '\\def\\labelenumii{\\theenumii.}\n' + '\\makeatletter\\def\\p@enumiii{\\p@enumii\\theenumii.}\\makeatother\n' '\\setcounter{enumii}{2}\n' in result) From dbb04d0a33b9eab6546e2fa5b5844b80187d3ef3 Mon Sep 17 00:00:00 2001 From: jfbu Date: Wed, 20 Jun 2018 18:11:11 +0200 Subject: [PATCH 3/4] Obide by Docutils ``node['prefix']`` and ``node['suffix']`` --- sphinx/writers/latex.py | 7 ++++--- tests/test_build_latex.py | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 837542c4d..51f0607b2 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1530,9 +1530,10 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\\begin{enumerate}\n') self.body.append('\\def\\the%s{%s{%s}}\n' % (enum, style, enum)) - self.body.append('\\def\\label%s{\\the%s.}\n' % (enum, enum)) - self.body.append('\\makeatletter\\def\\p@%s{\\p@%s\\the%s.}\\makeatother\n' % - (enumnext, enum, enum)) + self.body.append('\\def\\label%s{%s\\the%s %s}\n' % + (enum, node['prefix'], enum, node['suffix'])) + self.body.append('\\makeatletter\\def\\p@%s{\\p@%s %s\\the%s %s}\\makeatother\n' % + (enumnext, enum, node['prefix'], enum, node['suffix'])) if 'start' in node: self.body.append('\\setcounter{%s}{%d}\n' % (enum, node['start'] - 1)) if self.table: diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 6be772bbe..2f66c4e89 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -1230,24 +1230,24 @@ def test_latex_nested_enumerated_list(app, status, warning): result = (app.outdir / 'test.tex').text(encoding='utf8') assert ('\\def\\theenumi{\\arabic{enumi}}\n' - '\\def\\labelenumi{\\theenumi.}\n' - '\\makeatletter\\def\\p@enumii{\\p@enumi\\theenumi.}\\makeatother\n' + '\\def\\labelenumi{\\theenumi .}\n' + '\\makeatletter\\def\\p@enumii{\\p@enumi \\theenumi .}\\makeatother\n' '\\setcounter{enumi}{4}\n' in result) assert ('\\def\\theenumii{\\alph{enumii}}\n' - '\\def\\labelenumii{\\theenumii.}\n' - '\\makeatletter\\def\\p@enumiii{\\p@enumii\\theenumii.}\\makeatother\n' + '\\def\\labelenumii{\\theenumii .}\n' + '\\makeatletter\\def\\p@enumiii{\\p@enumii \\theenumii .}\\makeatother\n' '\\setcounter{enumii}{3}\n' in result) assert ('\\def\\theenumiii{\\arabic{enumiii}}\n' - '\\def\\labelenumiii{\\theenumiii.}\n' - '\\makeatletter\\def\\p@enumiv{\\p@enumiii\\theenumiii.}\\makeatother\n' + '\\def\\labelenumiii{\\theenumiii )}\n' + '\\makeatletter\\def\\p@enumiv{\\p@enumiii \\theenumiii )}\\makeatother\n' '\\setcounter{enumiii}{9}\n' in result) assert ('\\def\\theenumiv{\\arabic{enumiv}}\n' - '\\def\\labelenumiv{\\theenumiv.}\n' - '\\makeatletter\\def\\p@enumv{\\p@enumiv\\theenumiv.}\\makeatother\n' + '\\def\\labelenumiv{(\\theenumiv )}\n' + '\\makeatletter\\def\\p@enumv{\\p@enumiv (\\theenumiv )}\\makeatother\n' '\\setcounter{enumiv}{23}\n' in result) assert ('\\def\\theenumii{\\roman{enumii}}\n' - '\\def\\labelenumii{\\theenumii.}\n' - '\\makeatletter\\def\\p@enumiii{\\p@enumii\\theenumii.}\\makeatother\n' + '\\def\\labelenumii{\\theenumii .}\n' + '\\makeatletter\\def\\p@enumiii{\\p@enumii \\theenumii .}\\makeatother\n' '\\setcounter{enumii}{2}\n' in result) From 41b250c804a502a95e0e876d75970cb24e512d48 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Fri, 22 Jun 2018 00:55:07 +0900 Subject: [PATCH 4/4] Fix flake8 violation --- sphinx/writers/latex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index a9ac854a2..614feb219 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1510,7 +1510,7 @@ class LaTeXTranslator(nodes.NodeVisitor): self.body.append('\\begin{enumerate}\n') self.body.append('\\def\\the%s{%s{%s}}\n' % (enum, style, enum)) - self.body.append('\\def\\label%s{%s\\the%s %s}\n' % + self.body.append('\\def\\label%s{%s\\the%s %s}\n' % (enum, node['prefix'], enum, node['suffix'])) self.body.append('\\makeatletter\\def\\p@%s{\\p@%s %s\\the%s %s}\\makeatother\n' % (enumnext, enum, node['prefix'], enum, node['suffix']))