Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2@file
3@brief Copy of `singlehtml.py <https://github.com/sphinx-doc/sphinx/blob/3.x/sphinx/builders/singlehtml.py>`_
5Single HTML builders.
7:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
8:license: BSD, see LICENSE for details.
9"""
11from os import path
12from typing import Any, Dict, Tuple
13from docutils import nodes
14from docutils.nodes import Node
15from sphinx.builders.html import StandaloneHTMLBuilder
16from sphinx.environment.adapters.toctree import TocTree
17from sphinx.locale import __
18from sphinx.util import logging
19from sphinx.util import progress_message
20from sphinx.util.console import darkgreen # type: ignore
21from sphinx.util.nodes import inline_all_toctrees
24class CustomSingleFileHTMLBuilder(StandaloneHTMLBuilder):
25 """
26 A StandaloneHTMLBuilder subclass that puts the whole document tree on one
27 HTML page.
28 """
29 name = 'singlehtml'
30 epilog = __('The HTML page is in %(outdir)s.')
32 copysource = False
34 def get_outdated_docs(self):
35 return 'all documents'
37 def get_target_uri(self, docname: str, typ: str = None) -> str:
38 if docname in self.env.all_docs:
39 # all references are on the same page...
40 return self.config.master_doc + self.out_suffix + \
41 '#document-' + docname
42 else:
43 # chances are this is a html_additional_page
44 return docname + self.out_suffix
46 def get_relative_uri(self, from_: str, to: str, typ: str = None) -> str:
47 # ignore source
48 return self.get_target_uri(to, typ)
50 def fix_refuris(self, tree: Node) -> None:
51 # fix refuris with double anchor
52 fname = self.config.master_doc + self.out_suffix
53 for refnode in tree.traverse(nodes.reference):
54 if 'refuri' not in refnode:
55 continue
56 refuri = refnode['refuri']
57 hashindex = refuri.find('#')
58 if hashindex < 0:
59 continue
60 hashindex = refuri.find('#', hashindex + 1)
61 if hashindex >= 0:
62 refnode['refuri'] = fname + refuri[hashindex:]
64 def _get_local_toctree(self, docname: str, collapse: bool = True, **kwargs: Any) -> str:
65 if 'includehidden' not in kwargs:
66 kwargs['includehidden'] = False
67 toctree = TocTree(self.env).get_toctree_for(
68 docname, self, collapse, **kwargs)
69 if toctree is not None:
70 self.fix_refuris(toctree)
71 return self.render_partial(toctree)['fragment']
73 def assemble_doctree(self) -> nodes.document:
74 master = self.config.master_doc
75 tree = self.env.get_doctree(master)
76 tree = inline_all_toctrees(
77 self, set(), master, tree, darkgreen, [master])
78 tree['docname'] = master
79 self.env.resolve_references(tree, master, self)
80 self.fix_refuris(tree)
81 return tree
83 def assemble_toc_secnumbers(self) -> Dict[str, Dict[str, Tuple[int, ...]]]:
84 # Assemble toc_secnumbers to resolve section numbers on SingleHTML.
85 # Merge all secnumbers to single secnumber.
86 #
87 # Note: current Sphinx has refid confliction in singlehtml mode.
88 # To avoid the problem, it replaces key of secnumbers to
89 # tuple of docname and refid.
90 #
91 # There are related codes in inline_all_toctres() and
92 # HTMLTranslter#add_secnumber().
93 new_secnumbers = {} # type: Dict[str, Tuple[int, ...]]
94 for docname, secnums in self.env.toc_secnumbers.items():
95 for id, secnum in secnums.items():
96 alias = "%s/%s" % (docname, id)
97 new_secnumbers[alias] = secnum
99 return {self.config.master_doc: new_secnumbers}
101 def assemble_toc_fignumbers(self) -> Dict[str, Dict[str, Dict[str, Tuple[int, ...]]]]:
102 # Assemble toc_fignumbers to resolve figure numbers on SingleHTML.
103 # Merge all fignumbers to single fignumber.
104 #
105 # Note: current Sphinx has refid confliction in singlehtml mode.
106 # To avoid the problem, it replaces key of secnumbers to
107 # tuple of docname and refid.
108 #
109 # There are related codes in inline_all_toctres() and
110 # HTMLTranslter#add_fignumber().
111 new_fignumbers = {} # type: Dict[str, Dict[str, Tuple[int, ...]]]
112 # {'foo': {'figure': {'id2': (2,), 'id1': (1,)}}, 'bar': {'figure': {'id1': (3,)}}}
113 for docname, fignumlist in self.env.toc_fignumbers.items():
114 for figtype, fignums in fignumlist.items():
115 alias = "%s/%s" % (docname, figtype)
116 new_fignumbers.setdefault(alias, {})
117 for id, fignum in fignums.items():
118 new_fignumbers[alias][id] = fignum
120 return {self.config.master_doc: new_fignumbers}
122 def get_doc_context(self, docname: str, body: str, metatags: str) -> Dict:
123 # no relation links...
124 toctree = TocTree(self.env).get_toctree_for(
125 self.config.master_doc, self, False)
126 # if there is no toctree, toc is None
127 if toctree:
128 self.fix_refuris(toctree)
129 toc = self.render_partial(toctree)['fragment']
130 display_toc = True
131 else:
132 toc = ''
133 display_toc = False
134 return {
135 'parents': [],
136 'prev': None,
137 'next': None,
138 'docstitle': None,
139 'title': self.config.html_title,
140 'meta': None,
141 'body': body,
142 'metatags': metatags,
143 'rellinks': [],
144 'sourcename': '',
145 'toc': toc,
146 'display_toc': display_toc,
147 }
149 def write(self, *ignored: Any) -> None: # pylint: disable=W0221,W0222
150 docnames = self.env.all_docs
152 with progress_message(__('preparing documents')):
153 self.prepare_writing(docnames) # type: ignore
155 with progress_message(__('assembling single document')):
156 doctree = self.assemble_doctree()
157 self.env.toc_secnumbers = self.assemble_toc_secnumbers()
158 self.env.toc_fignumbers = self.assemble_toc_fignumbers()
160 with progress_message(__('writing')):
161 self.write_doc_serialized(self.config.master_doc, doctree)
162 self.write_doc(self.config.master_doc, doctree)
164 def finish(self) -> None:
165 self.write_additional_files()
166 self.copy_image_files()
167 self.copy_download_files()
168 self.copy_static_files()
169 self.copy_extra_files()
170 self.write_buildinfo()
171 self.dump_inventory()
173 @progress_message(__('writing additional files'))
174 def write_additional_files(self) -> None:
175 # no indices or search pages are supported
176 logger = logging.getLogger(__name__)
178 # additional pages from conf.py
179 for pagename, template in self.config.html_additional_pages.items():
180 logger.info(' ' + pagename, nonl=True)
181 self.handle_page(pagename, {}, template)
183 if self.config.html_use_opensearch:
184 logger.info(' opensearch', nonl=True)
185 fn = path.join(self.outdir, '_static', 'opensearch.xml')
186 self.handle_page('opensearch', {},
187 'opensearch.xml', outfilename=fn)