diff options
-rw-r--r-- | sphinx/builders/htmlhelp.py | 136 | ||||
-rw-r--r-- | sphinx/templates/htmlhelp/project.hhc | 31 | ||||
-rw-r--r-- | tests/roots/test-htmlhelp-hhc/bar.rst | 2 | ||||
-rw-r--r-- | tests/roots/test-htmlhelp-hhc/baz.rst | 2 | ||||
-rw-r--r-- | tests/roots/test-htmlhelp-hhc/conf.py | 1 | ||||
-rw-r--r-- | tests/roots/test-htmlhelp-hhc/foo.rst | 6 | ||||
-rw-r--r-- | tests/roots/test-htmlhelp-hhc/index.rst | 15 | ||||
-rw-r--r-- | tests/test_build_htmlhelp.py | 51 |
8 files changed, 184 insertions, 60 deletions
diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py index a182c7632..ce30affba 100644 --- a/sphinx/builders/htmlhelp.py +++ b/sphinx/builders/htmlhelp.py @@ -75,24 +75,6 @@ template_dir = path.join(package_dir, 'templates', 'htmlhelp') # 0x200000 TOC Next # 0x400000 TOC Prev -contents_header = '''\ -<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> -<HTML> -<HEAD> -<meta name="GENERATOR" content="Microsoft® HTML Help Workshop 4.1"> -<!-- Sitemap 1.0 --> -</HEAD><BODY> -<OBJECT type="text/site properties"> - <param name="Window Styles" value="0x801227"> - <param name="ImageType" value="Folder"> -</OBJECT> -<UL> -''' - -contents_footer = '''\ -</UL></BODY></HTML> -''' - object_sitemap = '''\ <OBJECT type="text/sitemap"> <param name="Name" value="%s"> @@ -151,6 +133,63 @@ def chm_htmlescape(s, quote=True): return s +class ToCTreeVisitor(nodes.NodeVisitor): + def __init__(self, document): + # type: (nodes.document) -> None + super().__init__(document) + self.body = [] # type: List[str] + self.depth = 0 + + def append(self, text): + # type: (str) -> None + indent = ' ' * (self.depth - 1) + self.body.append(indent + text) + + def astext(self): + # type: () -> str + return '\n'.join(self.body) + + def unknown_visit(self, node): + # type: (nodes.Node) -> None + pass + + def unknown_departure(self, node): + # type: (nodes.Node) -> None + pass + + def visit_bullet_list(self, node): + # type: (nodes.Element) -> None + if self.depth > 0: + self.append('<UL>') + + self.depth += 1 + + def depart_bullet_list(self, node): + # type: (nodes.Element) -> None + self.depth -= 1 + if self.depth > 0: + self.append('</UL>') + + def visit_list_item(self, node): + # type: (nodes.Element) -> None + self.append('<LI>') + self.depth += 1 + + def depart_list_item(self, node): + # type: (nodes.Element) -> None + self.depth -= 1 + self.append('</LI>') + + def visit_reference(self, node): + # type: (nodes.Element) -> None + title = chm_htmlescape(node.astext(), True) + self.append('<OBJECT type="text/sitemap">') + self.append(' <PARAM name="Name" value="%s" />' % title) + self.append(' <PARAM name="Local" value="%s" />' % node['refuri']) + self.append('</OBJECT>') + raise nodes.SkipNode + + class HTMLHelpBuilder(StandaloneHTMLBuilder): """ Builder that also outputs Windows HTML help project, contents and @@ -202,6 +241,7 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): # type: () -> None self.copy_stopword_list() self.build_project_file() + self.build_toc_file() self.build_hhx(self.outdir, self.config.htmlhelp_basename) def write_doc(self, docname, doctree): @@ -263,48 +303,30 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): body = self.render('project.hhp', context) f.write(body) - def build_hhx(self, outdir, outname): - # type: (str, str) -> None - logger.info(__('writing TOC file...')) - filename = path.join(outdir, outname + '.hhc') + @progress_message(__('writing TOC file')) + def build_toc_file(self): + # type: () -> None + """Create a ToC file (.hhp) on outdir.""" + filename = path.join(self.outdir, self.config.htmlhelp_basename + '.hhc') with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: - f.write(contents_header) - # special books - f.write('<LI> ' + object_sitemap % (self.config.html_short_title, - self.config.master_doc + self.out_suffix)) - for indexname, indexcls, content, collapse in self.domain_indices: - f.write('<LI> ' + object_sitemap % (indexcls.localname, - '%s.html' % indexname)) - # the TOC - tocdoc = self.env.get_and_resolve_doctree( - self.config.master_doc, self, prune_toctrees=False) - - def write_toc(node, ullevel=0): - # type: (nodes.Node, int) -> None - if isinstance(node, nodes.list_item): - f.write('<LI> ') - for subnode in node: - write_toc(subnode, ullevel) - elif isinstance(node, nodes.reference): - link = node['refuri'] - title = chm_htmlescape(node.astext(), True) - f.write(object_sitemap % (title, link)) - elif isinstance(node, nodes.bullet_list): - if ullevel != 0: - f.write('<UL>\n') - for subnode in node: - write_toc(subnode, ullevel + 1) - if ullevel != 0: - f.write('</UL>\n') - elif isinstance(node, addnodes.compact_paragraph): - for subnode in node: - write_toc(subnode, ullevel) - + toctree = self.env.get_and_resolve_doctree(self.config.master_doc, self, + prune_toctrees=False) + visitor = ToCTreeVisitor(toctree) matcher = NodeMatcher(addnodes.compact_paragraph, toctree=True) - for node in tocdoc.traverse(matcher): # type: addnodes.compact_paragraph - write_toc(node) - f.write(contents_footer) + for node in toctree.traverse(matcher): # type: addnodes.compact_paragraph + node.walkabout(visitor) + + context = { + 'body': visitor.astext(), + 'suffix': self.out_suffix, + 'short_title': self.config.html_short_title, + 'master_doc': self.config.master_doc, + 'domain_indices': self.domain_indices, + } + f.write(self.render('project.hhc', context)) + def build_hhx(self, outdir, outname): + # type: (str, str) -> None logger.info(__('writing index file...')) index = IndexEntries(self.env).create_index(self) filename = path.join(outdir, outname + '.hhk') diff --git a/sphinx/templates/htmlhelp/project.hhc b/sphinx/templates/htmlhelp/project.hhc new file mode 100644 index 000000000..c1096e711 --- /dev/null +++ b/sphinx/templates/htmlhelp/project.hhc @@ -0,0 +1,31 @@ +{%- macro sitemap(name, docname) -%} +<OBJECT type="text/sitemap"> + <PARAM name="Name" value="{{ name|e }}" /> + <PARAM name="Local" value="{{ docname|e }}{{ suffix }}" /> +</OBJECT> +{%- endmacro -%} + +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<HTML> + <HEAD> + <META name="GENERATOR" content="Microsoft® HTML Help Workshop 4.1" /> + <!-- Sitemap 1.0 --> + </HEAD> + <BODY> + <OBJECT type="text/site properties"> + <PARAM name="Window Styles" value="0x801227" /> + <PARAM name="ImageType" value="Folder" /> + </OBJECT> + <UL> + <LI> + {{ sitemap(short_title, master_doc)|indent(8) }} + </LI> + {%- for indexname, indexcls, content, collapse in domain_indices %} + <LI> + {{ sitemap(indexcls.localname, indexname)|indent(8) }} + </LI> + {%- endfor %} + {{ body|indent(6) }} + </UL> + </BODY> +</HTML> diff --git a/tests/roots/test-htmlhelp-hhc/bar.rst b/tests/roots/test-htmlhelp-hhc/bar.rst new file mode 100644 index 000000000..0d1785bcf --- /dev/null +++ b/tests/roots/test-htmlhelp-hhc/bar.rst @@ -0,0 +1,2 @@ +bar +--- diff --git a/tests/roots/test-htmlhelp-hhc/baz.rst b/tests/roots/test-htmlhelp-hhc/baz.rst new file mode 100644 index 000000000..69fe26493 --- /dev/null +++ b/tests/roots/test-htmlhelp-hhc/baz.rst @@ -0,0 +1,2 @@ +baz +--- diff --git a/tests/roots/test-htmlhelp-hhc/conf.py b/tests/roots/test-htmlhelp-hhc/conf.py new file mode 100644 index 000000000..20447e040 --- /dev/null +++ b/tests/roots/test-htmlhelp-hhc/conf.py @@ -0,0 +1 @@ +html_short_title = "Sphinx's documentation" diff --git a/tests/roots/test-htmlhelp-hhc/foo.rst b/tests/roots/test-htmlhelp-hhc/foo.rst new file mode 100644 index 000000000..6e06b7e69 --- /dev/null +++ b/tests/roots/test-htmlhelp-hhc/foo.rst @@ -0,0 +1,6 @@ +foo +--- + +.. toctree:: + + bar diff --git a/tests/roots/test-htmlhelp-hhc/index.rst b/tests/roots/test-htmlhelp-hhc/index.rst new file mode 100644 index 000000000..9b43b4129 --- /dev/null +++ b/tests/roots/test-htmlhelp-hhc/index.rst @@ -0,0 +1,15 @@ +test-htmlhelp-domain_indices +---------------------------- + +section +~~~~~~~ + +.. py:module:: sphinx + +subsection +^^^^^^^^^^ + +.. toctree:: + + foo + baz diff --git a/tests/test_build_htmlhelp.py b/tests/test_build_htmlhelp.py index 18acca921..4ad244a4e 100644 --- a/tests/test_build_htmlhelp.py +++ b/tests/test_build_htmlhelp.py @@ -11,10 +11,9 @@ import re import pytest +from html5lib import HTMLParser -from sphinx.builders.htmlhelp import chm_htmlescape - -from sphinx.builders.htmlhelp import default_htmlhelp_basename +from sphinx.builders.htmlhelp import chm_htmlescape, default_htmlhelp_basename from sphinx.config import Config @@ -72,6 +71,52 @@ def test_chm(app): assert m is None, 'Hex escaping exists in .hhk file: ' + str(m.group(0)) +@pytest.mark.sphinx('htmlhelp', testroot='htmlhelp-hhc') +def test_htmlhelp_hhc(app): + app.build() + + def assert_sitemap(node, name, filename): + assert node.tag == 'object' + assert len(node) == 2 + assert node[0].tag == 'param' + assert node[0].attrib == {'name': 'Name', 'value': name} + assert node[1].tag == 'param' + assert node[1].attrib == {'name': 'Local', 'value': filename} + + # .hhc file + hhc = (app.outdir / 'pythondoc.hhc').text() + tree = HTMLParser(namespaceHTMLElements=False).parse(hhc) + items = tree.find('.//body/ul') + assert len(items) == 4 + + # index + assert items[0].tag == 'li' + assert len(items[0]) == 1 + assert_sitemap(items[0][0], "Sphinx's documentation", 'index.html') + + # py-modindex + assert items[1].tag == 'li' + assert len(items[1]) == 1 + assert_sitemap(items[1][0], 'Python Module Index', 'py-modindex.html') + + # toctree + assert items[2].tag == 'li' + assert len(items[2]) == 2 + assert_sitemap(items[2][0], 'foo', 'foo.html') + + assert items[2][1].tag == 'ul' + assert len(items[2][1]) == 1 + assert items[2][1][0].tag == 'li' + assert_sitemap(items[2][1][0][0], 'bar', 'bar.html') + + assert items[3].tag == 'li' + assert len(items[3]) == 1 + assert_sitemap(items[3][0], 'baz', 'baz.html') + + # single quotes should be escaped as decimal (') + assert "Sphinx's documentation" in hhc + + def test_chm_htmlescape(): assert chm_htmlescape('Hello world') == 'Hello world' assert chm_htmlescape(u'Unicode 文字') == u'Unicode 文字' |