diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py index 4a3ca98c4..a182c7632 100644 --- a/sphinx/builders/htmlhelp.py +++ b/sphinx/builders/htmlhelp.py @@ -26,7 +26,8 @@ from sphinx.util import logging from sphinx.util import progress_message from sphinx.util.fileutil import copy_asset_file from sphinx.util.nodes import NodeMatcher -from sphinx.util.osutil import make_filename_from_project +from sphinx.util.osutil import make_filename_from_project, relpath +from sphinx.util.template import SphinxRenderer if False: # For type annotation @@ -74,28 +75,6 @@ template_dir = path.join(package_dir, 'templates', 'htmlhelp') # 0x200000 TOC Next # 0x400000 TOC Prev -project_template = '''\ -[OPTIONS] -Binary TOC=No -Binary Index=No -Compiled file=%(outname)s.chm -Contents file=%(outname)s.hhc -Default Window=%(outname)s -Default topic=%(master_doc)s -Display compile progress=No -Full text search stop list file=%(outname)s.stp -Full-text search=Yes -Index file=%(outname)s.hhk -Language=%(lcid)#x -Title=%(title)s - -[WINDOWS] -%(outname)s="%(title)s","%(outname)s.hhc","%(outname)s.hhk",\ -"%(master_doc)s","%(master_doc)s",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0 - -[FILES] -''' - contents_header = '''\ @@ -222,6 +201,7 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): def handle_finish(self): # type: () -> None self.copy_stopword_list() + self.build_project_file() self.build_hhx(self.outdir, self.config.htmlhelp_basename) def write_doc(self, docname, doctree): @@ -233,6 +213,11 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): super().write_doc(docname, doctree) + def render(self, name, context): + # type: (str, Dict) -> str + template = SphinxRenderer(template_dir) + return template.render(name, context) + @progress_message(__('copying stopword list')) def copy_stopword_list(self): # type: () -> None @@ -249,32 +234,37 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): filename = path.join(self.outdir, self.config.htmlhelp_basename + '.stp') copy_asset_file(template, filename) - def build_hhx(self, outdir, outname): - # type: (str, str) -> None - logger.info(__('writing project file...')) - filename = path.join(outdir, outname + '.hhp') + @progress_message(__('writing project file')) + def build_project_file(self): + # type: () -> None + """Create a project file (.hhp) on outdir.""" + # scan project files + project_files = [] # type: List[str] + for root, dirs, files in os.walk(self.outdir): + dirs.sort() + files.sort() + in_staticdir = root.startswith(path.join(self.outdir, '_static')) + for fn in sorted(files): + if (in_staticdir and not fn.endswith('.js')) or fn.endswith('.html'): + fn = relpath(path.join(root, fn), self.outdir) + project_files.append(fn.replace(os.sep, '\\')) + + filename = path.join(self.outdir, self.config.htmlhelp_basename + '.hhp') with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: - f.write(project_template % { - 'outname': outname, + context = { + 'outname': self.config.htmlhelp_basename, 'title': self.config.html_title, 'version': self.config.version, 'project': self.config.project, 'lcid': self.lcid, - 'master_doc': self.config.master_doc + self.out_suffix - }) - if not outdir.endswith(os.sep): - outdir += os.sep - olen = len(outdir) - for root, dirs, files in os.walk(outdir): - dirs.sort() - files.sort() - staticdir = root.startswith(path.join(outdir, '_static')) - for fn in sorted(files): - if (staticdir and not fn.endswith('.js')) or \ - fn.endswith('.html'): - print(path.join(root, fn)[olen:].replace(os.sep, '\\'), - file=f) + 'master_doc': self.config.master_doc + self.out_suffix, + 'files': project_files, + } + 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') with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: diff --git a/sphinx/templates/htmlhelp/project.hhp b/sphinx/templates/htmlhelp/project.hhp new file mode 100644 index 000000000..b647b3713 --- /dev/null +++ b/sphinx/templates/htmlhelp/project.hhp @@ -0,0 +1,21 @@ +[OPTIONS] +Binary TOC=No +Binary Index=No +Compiled file={{ outname }}.chm +Contents file={{ outname }}.hhc +Default Window={{ outname }} +Default topic={{ master_doc }} +Display compile progress=No +Full text search stop list file={{ outname }}.stp +Full-text search=Yes +Index file={{ outname }}.hhk +Language={{ "%#x"|format(lcid) }} +Title={{ title }} + +[WINDOWS] +{{ outname }}="{{ title }}","{{ outname }}.hhc","{{ outname }}.hhk","{{ master_doc }}","{{ master_doc }}",,,,,0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0 + +[FILES] +{%- for filename in files %} +{{ filename }} +{%- endfor %} diff --git a/tests/test_build_htmlhelp.py b/tests/test_build_htmlhelp.py index 980a565e5..18acca921 100644 --- a/tests/test_build_htmlhelp.py +++ b/tests/test_build_htmlhelp.py @@ -18,6 +18,29 @@ from sphinx.builders.htmlhelp import default_htmlhelp_basename from sphinx.config import Config +@pytest.mark.sphinx('htmlhelp', testroot='basic') +def test_build_htmlhelp(app, status, warning): + app.build() + + hhp = (app.outdir / 'pythondoc.hhp').text() + assert 'Compiled file=pythondoc.chm' in hhp + assert 'Contents file=pythondoc.hhc' in hhp + assert 'Default Window=pythondoc' in hhp + assert 'Default topic=index.html' in hhp + assert 'Full text search stop list file=pythondoc.stp' in hhp + assert 'Index file=pythondoc.hhk' in hhp + assert 'Language=0x409' in hhp + assert 'Title=Python documentation' in hhp + assert ('pythondoc="Python documentation","pythondoc.hhc",' + '"pythondoc.hhk","index.html","index.html",,,,,' + '0x63520,220,0x10384e,[0,0,1024,768],,,,,,,0' in hhp) + + files = ['genindex.html', 'index.html', '_static\\alabaster.css', '_static\\basic.css', + '_static\\custom.css', '_static\\file.png', '_static\\minus.png', + '_static\\plus.png', '_static\\pygments.css'] + assert '[FILES]\n%s' % '\n'.join(files) in hhp + + @pytest.mark.sphinx('htmlhelp', testroot='basic') def test_default_htmlhelp_file_suffix(app, warning): assert app.builder.out_suffix == '.html'