diff options
Diffstat (limited to 'sphinx/builders/htmlhelp.py')
-rw-r--r-- | sphinx/builders/htmlhelp.py | 372 |
1 files changed, 17 insertions, 355 deletions
diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py index ce30affba..2e7e8f083 100644 --- a/sphinx/builders/htmlhelp.py +++ b/sphinx/builders/htmlhelp.py @@ -9,374 +9,36 @@ :license: BSD, see LICENSE for details. """ -import html -import os import warnings -from os import path -from docutils import nodes +from sphinxcontrib.htmlhelp import ( + chm_locales, chm_htmlescape, HTMLHelpBuilder, default_htmlhelp_basename +) + +from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias -from sphinx import addnodes -from sphinx import package_dir -from sphinx.builders.html import StandaloneHTMLBuilder -from sphinx.deprecation import RemovedInSphinx40Warning -from sphinx.environment.adapters.indexentries import IndexEntries -from sphinx.locale import __ -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, relpath -from sphinx.util.template import SphinxRenderer if False: # For type annotation - from typing import Any, Dict, IO, List, Match, Tuple # NOQA + from typing import Any, Dict # NOQA from sphinx.application import Sphinx # NOQA - from sphinx.config import Config # NOQA - - -logger = logging.getLogger(__name__) - -template_dir = path.join(package_dir, 'templates', 'htmlhelp') - - -# Project file (*.hhp) template. 'outname' is the file basename (like -# the pythlp in pythlp.hhp); 'version' is the doc version number (like -# the 2.2 in Python 2.2). -# The magical numbers in the long line under [WINDOWS] set most of the -# user-visible features (visible buttons, tabs, etc). -# About 0x10384e: This defines the buttons in the help viewer. The -# following defns are taken from htmlhelp.h. Not all possibilities -# actually work, and not all those that work are available from the Help -# Workshop GUI. In particular, the Zoom/Font button works and is not -# available from the GUI. The ones we're using are marked with 'x': -# -# 0x000002 Hide/Show x -# 0x000004 Back x -# 0x000008 Forward x -# 0x000010 Stop -# 0x000020 Refresh -# 0x000040 Home x -# 0x000080 Forward -# 0x000100 Back -# 0x000200 Notes -# 0x000400 Contents -# 0x000800 Locate x -# 0x001000 Options x -# 0x002000 Print x -# 0x004000 Index -# 0x008000 Search -# 0x010000 History -# 0x020000 Favorites -# 0x040000 Jump 1 -# 0x080000 Jump 2 -# 0x100000 Zoom/Font x -# 0x200000 TOC Next -# 0x400000 TOC Prev - -object_sitemap = '''\ -<OBJECT type="text/sitemap"> - <param name="Name" value="%s"> - <param name="Local" value="%s"> -</OBJECT> -''' - -# The following list includes only languages supported by Sphinx. See -# https://docs.microsoft.com/en-us/previous-versions/windows/embedded/ms930130(v=msdn.10) -# for more. -chm_locales = { - # lang: LCID, encoding - 'ca': (0x403, 'cp1252'), - 'cs': (0x405, 'cp1250'), - 'da': (0x406, 'cp1252'), - 'de': (0x407, 'cp1252'), - 'en': (0x409, 'cp1252'), - 'es': (0x40a, 'cp1252'), - 'et': (0x425, 'cp1257'), - 'fa': (0x429, 'cp1256'), - 'fi': (0x40b, 'cp1252'), - 'fr': (0x40c, 'cp1252'), - 'hr': (0x41a, 'cp1250'), - 'hu': (0x40e, 'cp1250'), - 'it': (0x410, 'cp1252'), - 'ja': (0x411, 'cp932'), - 'ko': (0x412, 'cp949'), - 'lt': (0x427, 'cp1257'), - 'lv': (0x426, 'cp1257'), - 'nl': (0x413, 'cp1252'), - 'no_NB': (0x414, 'cp1252'), - 'pl': (0x415, 'cp1250'), - 'pt_BR': (0x416, 'cp1252'), - 'ru': (0x419, 'cp1251'), - 'sk': (0x41b, 'cp1250'), - 'sl': (0x424, 'cp1250'), - 'sv': (0x41d, 'cp1252'), - 'tr': (0x41f, 'cp1254'), - 'uk_UA': (0x422, 'cp1251'), - 'zh_CN': (0x804, 'cp936'), - 'zh_TW': (0x404, 'cp950'), -} - - -def chm_htmlescape(s, quote=True): - # type: (str, bool) -> str - """ - chm_htmlescape() is a wrapper of html.escape(). - .hhc/.hhk files don't recognize hex escaping, we need convert - hex escaping to decimal escaping. for example: ``'`` -> ``'`` - html.escape() may generates a hex escaping ``'`` for single - quote ``'``, this wrapper fixes this. - """ - s = html.escape(s, quote) - s = s.replace(''', ''') # re-escape as decimal - 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 - index files. Adapted from the original Doc/tools/prechm.py. - """ - name = 'htmlhelp' - epilog = __('You can now run HTML Help Workshop with the .htp file in ' - '%(outdir)s.') - - # don't copy the reST source - copysource = False - supported_image_types = ['image/png', 'image/gif', 'image/jpeg'] - - # don't add links - add_permalinks = False - # don't add sidebar etc. - embedded = True - - # don't generate search index or include search page - search = False - - lcid = 0x409 - encoding = 'cp1252' - - def init(self): - # type: () -> None - # the output files for HTML help is .html by default - self.out_suffix = '.html' - self.link_suffix = '.html' - super().init() - # determine the correct locale setting - locale = chm_locales.get(self.config.language) - if locale is not None: - self.lcid, self.encoding = locale - - def open_file(self, outdir, basename, mode='w'): - # type: (str, str, str) -> IO - # open a file with the correct encoding for the selected language - warnings.warn('HTMLHelpBuilder.open_file() is deprecated.', - RemovedInSphinx40Warning) - return open(path.join(outdir, basename), mode, encoding=self.encoding, - errors='xmlcharrefreplace') - - def update_page_context(self, pagename, templatename, ctx, event_arg): - # type: (str, str, Dict, str) -> None - ctx['encoding'] = self.encoding - - def handle_finish(self): - # 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): - # type: (str, nodes.document) -> None - for node in doctree.traverse(nodes.reference): - # add ``target=_blank`` attributes to external links - if node.get('internal') is None and 'refuri' in node: - node['target'] = '_blank' - - 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 - """Copy a stopword list (.stp) to outdir. - - The stopword list contains a list of words the full text search facility - shouldn't index. Note that this list must be pretty small. Different - versions of the MS docs claim the file has a maximum size of 256 or 512 - bytes (including \r\n at the end of each line). Note that "and", "or", - "not" and "near" are operators in the search language, so no point - indexing them even if we wanted to. - """ - template = path.join(template_dir, 'project.stp') - filename = path.join(self.outdir, self.config.htmlhelp_basename + '.stp') - copy_asset_file(template, filename) - - @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: - 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, - 'files': project_files, - } - body = self.render('project.hhp', context) - f.write(body) - - @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: - 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 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') - with open(filename, 'w', encoding=self.encoding, errors='xmlcharrefreplace') as f: - f.write('<UL>\n') - - def write_index(title, refs, subitems): - # type: (str, List[Tuple[str, str]], List[Tuple[str, List[Tuple[str, str]]]]) -> None # NOQA - def write_param(name, value): - # type: (str, str) -> None - item = ' <param name="%s" value="%s">\n' % (name, value) - f.write(item) - title = chm_htmlescape(title, True) - f.write('<LI> <OBJECT type="text/sitemap">\n') - write_param('Keyword', title) - if len(refs) == 0: - write_param('See Also', title) - elif len(refs) == 1: - write_param('Local', refs[0][1]) - else: - for i, ref in enumerate(refs): - # XXX: better title? - write_param('Name', '[%d] %s' % (i, ref[1])) - write_param('Local', ref[1]) - f.write('</OBJECT>\n') - if subitems: - f.write('<UL> ') - for subitem in subitems: - write_index(subitem[0], subitem[1], []) - f.write('</UL>') - for (key, group) in index: - for title, (refs, subitems, key_) in group: - write_index(title, refs, subitems) - f.write('</UL>\n') - - -def default_htmlhelp_basename(config): - # type: (Config) -> str - """Better default htmlhelp_basename setting.""" - return make_filename_from_project(config.project) + 'doc' +deprecated_alias('sphinx.builders.devhelp', + { + 'chm_locales': chm_locales, + 'chm_htmlescape': chm_htmlescape, + 'HTMLHelpBuilder': HTMLHelpBuilder, + 'default_htmlhelp_basename': default_htmlhelp_basename, + }, + RemovedInSphinx40Warning) def setup(app): # type: (Sphinx) -> Dict[str, Any] - app.setup_extension('sphinx.builders.html') - app.add_builder(HTMLHelpBuilder) - - app.add_config_value('htmlhelp_basename', default_htmlhelp_basename, None) - app.add_config_value('htmlhelp_file_suffix', None, 'html', [str]) - app.add_config_value('htmlhelp_link_suffix', None, 'html', [str]) + warnings.warn('sphinx.builders.htmlhelp has been moved to sphinxcontrib-htmlhelp.', + RemovedInSphinx40Warning) + app.setup_extension('sphinxcontrib.htmlhelp') return { 'version': 'builtin', |