From de38f0ad5d348f99b71dbdbfe318380118143aa0 Mon Sep 17 00:00:00 2001 From: Yves Chevallier Date: Thu, 1 Aug 2019 17:28:28 +0200 Subject: [PATCH 1/8] Add get_secnumber to simplify add_secnumber --- sphinx/writers/html5.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index e412ea167..9f23a4bc6 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -278,11 +278,10 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): # type: (nodes.Element) -> None self.depart_admonition(node) - def add_secnumber(self, node): + def get_secnumber(self, node): # type: (nodes.Element) -> None if node.get('secnumber'): - self.body.append('.'.join(map(str, node['secnumber'])) + - self.secnumber_suffix) + return node['secnumber'] elif isinstance(node.parent, nodes.section): if self.builder.name == 'singlehtml': docname = self.docnames[-1] @@ -294,9 +293,13 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): if anchorname not in self.builder.secnumbers: anchorname = '' # try first heading which has no anchor if self.builder.secnumbers.get(anchorname): - numbers = self.builder.secnumbers[anchorname] - self.body.append('.'.join(map(str, numbers)) + - self.secnumber_suffix) + return self.builder.secnumbers[anchorname] + return None + + def add_secnumber(self, node): + secnumber = self.get_secnumber(node) + if secnumber: + self.body.append('.'.join(map(str, secnumber)) + self.secnumber_suffix) def add_fignumber(self, node): # type: (nodes.Element) -> None From 9827851507dfdea3accf3bc9ed6f5b58bfa0bd9c Mon Sep 17 00:00:00 2001 From: Yves Chevallier Date: Thu, 1 Aug 2019 18:14:34 +0200 Subject: [PATCH 2/8] Wrap around sectnum in HTML titles --- sphinx/writers/html5.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 9f23a4bc6..f37647189 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -299,7 +299,8 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): def add_secnumber(self, node): secnumber = self.get_secnumber(node) if secnumber: - self.body.append('.'.join(map(str, secnumber)) + self.secnumber_suffix) + self.body.append('%s' % + '.'.join(map(str, secnumber)) + self.secnumber_suffix) def add_fignumber(self, node): # type: (nodes.Element) -> None From 06648a3157d19cb2a5fa6992b5ff8f87732b22b9 Mon Sep 17 00:00:00 2001 From: Yves Chevallier Date: Sat, 3 Aug 2019 13:03:16 +0200 Subject: [PATCH 3/8] Fix annotations --- sphinx/writers/html5.py | 12 ++++-- tests/test_build_html.py | 85 +++++++++++++++++++++++++--------------- 2 files changed, 62 insertions(+), 35 deletions(-) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index f37647189..aae588e6b 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -279,10 +279,11 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): self.depart_admonition(node) def get_secnumber(self, node): - # type: (nodes.Element) -> None + # type: (nodes.Element) -> Tuple[int, ...] if node.get('secnumber'): return node['secnumber'] - elif isinstance(node.parent, nodes.section): + + if isinstance(node.parent, nodes.section): if self.builder.name == 'singlehtml': docname = self.docnames[-1] anchorname = "%s/#%s" % (docname, node.parent['ids'][0]) @@ -292,15 +293,18 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): anchorname = '#' + node.parent['ids'][0] if anchorname not in self.builder.secnumbers: anchorname = '' # try first heading which has no anchor + if self.builder.secnumbers.get(anchorname): return self.builder.secnumbers[anchorname] + return None def add_secnumber(self, node): + # type: (nodes.Element) -> None secnumber = self.get_secnumber(node) if secnumber: - self.body.append('%s' % - '.'.join(map(str, secnumber)) + self.secnumber_suffix) + self.body.append('%s' % + ('.'.join(map(str, secnumber)) + self.secnumber_suffix)) def add_fignumber(self, node): # type: (nodes.Element) -> None diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 3255bb71e..2521ea68d 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -17,7 +17,7 @@ import pytest from html5lib import HTMLParser from sphinx.builders.html import validate_html_extra_path, validate_html_static_path -from sphinx.errors import ConfigError +from sphinx .errors import ConfigError from sphinx.testing.util import strip_escseq from sphinx.util import docutils from sphinx.util.inventory import InventoryFile @@ -82,6 +82,7 @@ def tail_check(check): def check_xpath(etree, fname, path, check, be_found=True): nodes = list(etree.findall(path)) + if check is None: assert nodes == [], ('found any nodes matching xpath ' '%r in file %s' % (path, fname)) @@ -96,15 +97,15 @@ def check_xpath(etree, fname, path, check, be_found=True): pass else: def get_text(node): - if node.text is not None: - return node.text - else: - # Since pygments-2.1.1, empty tag is inserted at top of - # highlighting block - if len(node) == 1 and node[0].tag == 'span' and node[0].text is None: - if node[0].tail is not None: - return node[0].tail - return '' + """ Get all text inside an HTML tag: + My 1dog 1likes bones --> My dog likes bones + """ + return ''.join( + filter( + lambda x: x and not re.match(r'^\s+$', x), + [node.text] + [n.tail for n in node.getchildren()] + ) + ) rex = re.compile(check) if be_found: @@ -116,7 +117,7 @@ def check_xpath(etree, fname, path, check, be_found=True): assert False, ('%r not found in any node matching ' 'path %s in %s: %r' % (check, path, fname, - [node.text for node in nodes])) + [get_all_text(node) for node in nodes])) @pytest.mark.sphinx('html', testroot='warnings') @@ -491,28 +492,40 @@ def test_html_translator(app): (".//li[@class='toctree-l3']/a", '2.2.1. Bar B1', False), ], 'foo.html': [ - (".//h1", '1. Foo', True), - (".//h2", '1.1. Foo A', True), - (".//h3", '1.1.1. Foo A1', True), - (".//h2", '1.2. Foo B', True), - (".//h3", '1.2.1. Foo B1', True), + (".//h1", 'Foo', True), + (".//h2", 'Foo A', True), + (".//h3", 'Foo A1', True), + (".//h2", 'Foo B', True), + (".//h3", 'Foo B1', True), + + (".//h1//span[@class='section-number']", '1. ', True), + (".//h2//span[@class='section-number']", '1.1. ', True), + (".//h3//span[@class='section-number']", '1.1.1. ', True), + (".//h2//span[@class='section-number']", '1.2. ', True), + (".//h3//span[@class='section-number']", '1.2.1. ', True), + (".//div[@class='sphinxsidebarwrapper']//li/a", '1.1. Foo A', True), (".//div[@class='sphinxsidebarwrapper']//li/a", '1.1.1. Foo A1', True), (".//div[@class='sphinxsidebarwrapper']//li/a", '1.2. Foo B', True), (".//div[@class='sphinxsidebarwrapper']//li/a", '1.2.1. Foo B1', True), ], 'bar.html': [ - (".//h1", '2. Bar', True), - (".//h2", '2.1. Bar A', True), - (".//h2", '2.2. Bar B', True), - (".//h3", '2.2.1. Bar B1', True), + (".//h1", 'Bar', True), + (".//h2", 'Bar A', True), + (".//h2", 'Bar B', True), + (".//h3", 'Bar B1', True), + (".//h1//span[@class='section-number']", '2. ', True), + (".//h2//span[@class='section-number']", '2.1. ', True), + (".//h2//span[@class='section-number']", '2.2. ', True), + (".//h3//span[@class='section-number']", '2.2.1. ', True), (".//div[@class='sphinxsidebarwrapper']//li/a", '2. Bar', True), (".//div[@class='sphinxsidebarwrapper']//li/a", '2.1. Bar A', True), (".//div[@class='sphinxsidebarwrapper']//li/a", '2.2. Bar B', True), (".//div[@class='sphinxsidebarwrapper']//li/a", '2.2.1. Bar B1', False), ], 'baz.html': [ - (".//h1", '2.1.1. Baz A', True), + (".//h1", 'Baz A', True), + (".//h1//span[@class='section-number']", '2.1.1. ', True), ], })) @pytest.mark.skipif(docutils.__version_info__ < (0, 13), @@ -536,20 +549,30 @@ def test_tocdepth(app, cached_etree_parse, fname, expect): (".//h1", 'test-tocdepth', True), # foo.rst - (".//h2", '1. Foo', True), - (".//h3", '1.1. Foo A', True), - (".//h4", '1.1.1. Foo A1', True), - (".//h3", '1.2. Foo B', True), - (".//h4", '1.2.1. Foo B1', True), + (".//h2", 'Foo', True), + (".//h3", 'Foo A', True), + (".//h4", 'Foo A1', True), + (".//h3", 'Foo B', True), + (".//h4", 'Foo B1', True), + (".//h2//span[@class='section-number']", '1. ', True), + (".//h3//span[@class='section-number']", '1.1. ', True), + (".//h4//span[@class='section-number']", '1.1.1. ', True), + (".//h3//span[@class='section-number']", '1.2. ', True), + (".//h4//span[@class='section-number']", '1.2.1. ', True), # bar.rst - (".//h2", '2. Bar', True), - (".//h3", '2.1. Bar A', True), - (".//h3", '2.2. Bar B', True), - (".//h4", '2.2.1. Bar B1', True), + (".//h2", 'Bar', True), + (".//h3", 'Bar A', True), + (".//h3", 'Bar B', True), + (".//h4", 'Bar B1', True), + (".//h2//span[@class='section-number']", '2. ', True), + (".//h3//span[@class='section-number']", '2.1. ', True), + (".//h3//span[@class='section-number']", '2.2. ', True), + (".//h4//span[@class='section-number']", '2.2.1. ', True), # baz.rst - (".//h4", '2.1.1. Baz A', True), + (".//h4", 'Baz A', True), + (".//h4//span[@class='section-number']", '2.1.1. ', True), ], })) @pytest.mark.skipif(docutils.__version_info__ < (0, 13), From 871e0b58a815d38a66bfcf6f5119fad4812c422a Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 14 Dec 2019 22:26:17 +0900 Subject: [PATCH 4/8] Remove meaningless spaces --- tests/test_build_html.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_build_html.py b/tests/test_build_html.py index 2521ea68d..e2932229f 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -17,7 +17,7 @@ import pytest from html5lib import HTMLParser from sphinx.builders.html import validate_html_extra_path, validate_html_static_path -from sphinx .errors import ConfigError +from sphinx.errors import ConfigError from sphinx.testing.util import strip_escseq from sphinx.util import docutils from sphinx.util.inventory import InventoryFile @@ -82,7 +82,6 @@ def tail_check(check): def check_xpath(etree, fname, path, check, be_found=True): nodes = list(etree.findall(path)) - if check is None: assert nodes == [], ('found any nodes matching xpath ' '%r in file %s' % (path, fname)) From 0dc456fee17bbb30a696bd06221bca2567c1b5d6 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 14 Dec 2019 22:50:55 +0900 Subject: [PATCH 5/8] refactor test: check_xpath() --- tests/test_build_html.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/tests/test_build_html.py b/tests/test_build_html.py index e2932229f..66164dd1c 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -96,15 +96,12 @@ def check_xpath(etree, fname, path, check, be_found=True): pass else: def get_text(node): - """ Get all text inside an HTML tag: - My 1dog 1likes bones --> My dog likes bones - """ - return ''.join( - filter( - lambda x: x and not re.match(r'^\s+$', x), - [node.text] + [n.tail for n in node.getchildren()] - ) - ) + if node.text is not None: + # the node has only one text + return node.text + else: + # the node has tags and text; gather texts just under the node + return ''.join(n.tail or '' for n in node) rex = re.compile(check) if be_found: @@ -116,7 +113,7 @@ def check_xpath(etree, fname, path, check, be_found=True): assert False, ('%r not found in any node matching ' 'path %s in %s: %r' % (check, path, fname, - [get_all_text(node) for node in nodes])) + [node.text for node in nodes])) @pytest.mark.sphinx('html', testroot='warnings') From e14b86ef7d4fee539c378f635f7caf8fa1823275 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 14 Dec 2019 22:55:42 +0900 Subject: [PATCH 6/8] html4: Wrap around sectnum in HTML titles --- sphinx/writers/html.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 381457926..e27183e90 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -28,7 +28,7 @@ from sphinx.util.images import get_image_size if False: # For type annotation - from typing import Any # NOQA + from typing import Any, Tuple # NOQA from sphinx.builders.html import StandaloneHTMLBuilder # NOQA @@ -309,11 +309,10 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): # type: (nodes.Element) -> None self.depart_admonition(node) - def add_secnumber(self, node): - # type: (nodes.Element) -> None + def get_secnumber(self, node): + # type: (nodes.Element) -> Tuple[int, ...] if node.get('secnumber'): - self.body.append('.'.join(map(str, node['secnumber'])) + - self.secnumber_suffix) + return node['secnumber'] elif isinstance(node.parent, nodes.section): if self.builder.name == 'singlehtml': docname = self.docnames[-1] @@ -324,10 +323,16 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): anchorname = '#' + node.parent['ids'][0] if anchorname not in self.builder.secnumbers: anchorname = '' # try first heading which has no anchor + if self.builder.secnumbers.get(anchorname): - numbers = self.builder.secnumbers[anchorname] - self.body.append('.'.join(map(str, numbers)) + - self.secnumber_suffix) + return self.builder.secnumbers[anchorname] + + def add_secnumber(self, node): + # type: (nodes.Element) -> None + secnumber = self.get_secnumber(node) + if secnumber: + self.body.append('%s' % + ('.'.join(map(str, secnumber)) + self.secnumber_suffix)) def add_fignumber(self, node): # type: (nodes.Element) -> None From 5b53b74bd9f278942436a75e45a692180c8d990d Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 14 Dec 2019 22:56:24 +0900 Subject: [PATCH 7/8] Fix mypy and flake8 violations --- sphinx/writers/html5.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index aae588e6b..89065f9a9 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -27,7 +27,7 @@ from sphinx.util.images import get_image_size if False: # For type annotation - from typing import Any # NOQA + from typing import Any, Tuple # NOQA from sphinx.builders.html import StandaloneHTMLBuilder # NOQA @@ -304,7 +304,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): secnumber = self.get_secnumber(node) if secnumber: self.body.append('%s' % - ('.'.join(map(str, secnumber)) + self.secnumber_suffix)) + ('.'.join(map(str, secnumber)) + self.secnumber_suffix)) def add_fignumber(self, node): # type: (nodes.Element) -> None From 1642978cc9cacaa8089bfe29673daf18f1fe2477 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 14 Dec 2019 23:18:27 +0900 Subject: [PATCH 8/8] Fix mypy violation --- sphinx/writers/html.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 4eade8453..11a9bbbc4 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -289,6 +289,8 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): if self.builder.secnumbers.get(anchorname): return self.builder.secnumbers[anchorname] + return None + def add_secnumber(self, node: Element) -> None: secnumber = self.get_secnumber(node) if secnumber: