additional_samples_with_very_long_name
Various examples of styling applied to Sphinx constructs. You can view the source of this page to see the specific reStructuredText used to create these examples.
Headings
This is a second level heading (h2
).
Sub-Heading
This is a third level heading (h3
).
Sub-Sub-Heading
This is a fourth level heading (h4
).
Code
The theme uses pygments for inline code text
and
multiline
code text
Here’s an included example with line numbers.
1"""OpenFF Sphinx theme."""
2
3import hashlib
4import inspect
5import os
6import sys
7from multiprocessing import Manager
8from pathlib import Path
9from typing import List, Optional
10from xml.etree import ElementTree
11
12import bs4
13import sass
14import slugify
15from bs4 import BeautifulSoup
16from css_html_js_minify.html_minifier import html_minify
17from sass import SassColor
18from sphinx.util import console, logging
19import sphinx
20
21from ._version import get_versions
22
23__version__ = get_versions()["version"]
24del get_versions
25
26ROOT_SUFFIX = "--page-root"
27
28
29def setup(app):
30 """Setup connects events to the sitemap builder"""
31 app.connect("builder-inited", register_template_functions)
32 app.connect("config-inited", set_default_settings)
33 app.connect("env-get-outdated", register_outdated)
34 app.connect("build-finished", postproc_html)
35 app.connect("build-finished", compile_css)
36 app.site_pages = []
37 app.add_html_theme(
38 "openff_sphinx_theme", os.path.join(html_theme_path()[0], "openff_sphinx_theme")
39 )
40 return {
41 "version": __version__,
42 "parallel_read_safe": True,
43 "parallel_write_safe": True,
44 }
45
46
47def set_default_settings(app, config):
48 if not config.html_sidebars:
49 config.html_sidebars["**"] = [
50 "globaltoc.html",
51 "localtoc.html",
52 "searchbox.html",
53 ]
54 if config.html_permalinks_icon == "¶":
55 config.html_permalinks_icon = "<i class='fas fa-link'></i>"
56
57
58def compile_css(app, exception):
59 """Compile Bulma SASS into CSS"""
60 if exception is not None:
61 return
62
63 theme_path = Path(html_theme_path()[0]) / "openff_sphinx_theme"
64 src = theme_path / "sass/site.sass"
65 dest = Path(app.outdir) / "_static/site.css"
66
67 if not dest.parent.exists():
68 return
69
70 accent_color = app.config["html_theme_options"].get(
71 "color_accent", "openff-toolkit-blue"
72 )
73 accent_color = {
74 "openff-blue": (1, 84, 128),
75 "openff-toolkit-blue": (47, 158, 210),
76 "openff-dataset-yellow": (240, 133, 33),
77 "openff-evaluator-orange": (240, 58, 33),
78 "aquamarine": (44, 218, 157),
79 "lilac": (228, 183, 229),
80 "amaranth": (164, 14, 76),
81 "grape": (171, 146, 191),
82 "violet": (141, 107, 148),
83 "pink": (238, 66, 102),
84 "pale-green": (238, 66, 102),
85 "green": (4, 231, 98),
86 "crimson": (214, 40, 57),
87 "eggplant": (117, 79, 91),
88 "turquoise": (45, 225, 194),
89 }.get(accent_color, accent_color)
90
91 if app.config["html_theme_options"].get("css_minify", False):
92 output_style = "compressed"
93 source_comments = False
94 else:
95 output_style = "expanded"
96 source_comments = True
97
98 css = sass.compile(
99 filename=str(src),
100 output_style=output_style,
101 custom_functions={
102 "accent_color": lambda: SassColor(*accent_color, 1),
103 "hyphenate": lambda: app.config["html_theme_options"].get(
104 "html_hyphenate_and_justify", False
105 ),
106 },
107 )
108
109 print(f"Writing compiled SASS to {console.colorize('blue', str(dest))}")
110
111 with open(dest, "w") as f:
112 print(css, file=f)
113
114
115def register_outdated(app, env, added, changed, removed):
116 if isinstance(app.builder, sphinx.builders.html.StandaloneHTMLBuilder):
117 env.openff_docs_to_postproc = added | changed
118 else:
119 env.openff_docs_to_postproc = None
120 return ()
121
122
123def postproc_html(app, exception):
124 """Prettify or minify the HTML, as well as wrap tables with .table-container"""
125 if exception is not None:
126 return
127
128 target_files = app.env.openff_docs_to_postproc
129
130 if target_files is None:
131 return
132
133 outdir = Path(app.outdir)
134
135 minify = app.config["html_theme_options"].get("html_minify", False)
136 prettify = app.config["html_theme_options"].get("html_prettify", False)
137 last = -1
138 npages = len(target_files)
139 print(f"Post-processing {npages} HTML files")
140
141 # TODO: Consider using parallel execution
142 for i, doc in enumerate(target_files):
143 try:
144 page = outdir / app.builder.get_target_uri(doc)
145 except sphinx.errors.NoUri as e:
146 print(doc, "has no URI; skipping")
147 continue
148
149 if int(100 * (i / npages)) - last >= 25:
150 last = int(100 * (i / npages))
151 color_page = console.colorize("blue", str(page))
152 msg = f"Post-processing files... [{last}%] {color_page}"
153 print("\033[K", msg, sep="", end="\r")
154
155 if not page.exists():
156 print(page, "does not exist; skipping")
157 continue
158
159 with open(page, "r", encoding="utf-8") as content:
160 soup = BeautifulSoup(content, "lxml")
161 for table in soup.find_all("table"):
162 container_attributes = {
163 "class": ["table-container"] + table.get("class", []),
164 }
165 if "table-container" not in table.parent.get("class", ()):
166 table.wrap(soup.new_tag("div", **container_attributes))
167
168 if minify:
169 html = html_minify(str(soup))
170 elif prettify:
171 html = soup.prettify()
172 else:
173 html = str(soup)
174
175 with open(page, "w", encoding="utf-8") as content:
176 content.write(html)
177
178 msg = f"Post-processing files... [100%]"
179 sys.stdout.write("\033[K" + msg + "\r")
180
181
182def register_template_functions(app):
183 config = app.config
184 config.html_context = {**get_html_context(), **config.html_context}
185
186
187def html_theme_path():
188 return [os.path.dirname(os.path.abspath(__file__))]
189
190
191def ul_to_list(node: bs4.element.Tag, fix_root: bool, page_name: str) -> List[dict]:
192 out = []
193 for child in node.find_all("li", recursive=False):
194 if callable(child.isspace) and child.isspace():
195 continue
196 formatted = {}
197 if child.a is not None:
198 formatted["href"] = child.a["href"]
199 formatted["contents"] = "".join(map(str, child.a.contents))
200 if fix_root and formatted["href"] == "#" and child.a.contents:
201 slug = slugify.slugify(page_name) + ROOT_SUFFIX
202 formatted["href"] = "#" + slug
203 formatted["current"] = "current" in child.a.get("class", [])
204 if child.ul is not None:
205 formatted["children"] = ul_to_list(child.ul, fix_root, page_name)
206 else:
207 formatted["children"] = []
208 out.append(formatted)
209 return out
210
211
212def derender_toc(
213 toc_text, fix_root=True, page_name: str = "md-page-root--link"
214) -> List[dict]:
215 nodes = []
216 try:
217 toc = BeautifulSoup(toc_text, features="html.parser")
218 for child in toc.children:
219 if callable(child.isspace) and child.isspace():
220 continue
221 if child.name == "p":
222 nodes.append({"caption": "".join(map(str, child.contents))})
223 elif child.name == "ul":
224 nodes.extend(ul_to_list(child, fix_root, page_name))
225 else:
226 raise NotImplementedError
227 except Exception as exc:
228 logger = logging.getLogger(__name__)
229 logger.warning(
230 "Failed to process toctree_text\n" + str(exc) + "\n" + str(toc_text)
231 )
232
233 return nodes
234
235
236# These final lines exist to give sphinx a stable str representation of
237# this function across runs, and to ensure that the str changes
238# if the source does.
239derender_toc_src = inspect.getsource(derender_toc)
240derender_toc_hash = hashlib.sha512(derender_toc_src.encode()).hexdigest()
241
242
243class DerenderTocMeta(type):
244 def __repr__(self):
245 return f"derender_toc, hash: {derender_toc_hash}"
246
247 def __str__(self):
248 return f"derender_toc, hash: {derender_toc_hash}"
249
250
251class DerenderToc(object, metaclass=DerenderTocMeta):
252 def __new__(cls, *args, **kwargs):
253 return derender_toc(*args, **kwargs)
254
255
256def get_html_context():
257 return {"derender_toc": DerenderToc}
It also works with existing Sphinx highlighting:
<html>
<body>Hello World</body>
</html>
def hello():
"""Greet."""
return "Hello World"
/**
* Greet.
*/
function hello(): {
return "Hello World";
}
Admonitions
The theme uses the admonition
classes for the standard Sphinx admonitions.
Warning
Warning
This is a warning.
Attention
Attention
Do I have your attention?
Caution
Caution
Use caution!
Danger
Danger
This is danger-ous.
Error
Error
You have made a grave error.
Hint
Hint
Can you take a hint?
Important
Important
It is important to correctly use admonitions.
Note
Note
This is a note.
Tip
Tip
Please tip your waiter.
Deprecated
Deprecated since version 999.999.999: This API point was deprecated.
Todo
Todo
This is something we are yet to do.
Custom Admonitions
An admonition of my own making
You can create your own admonitions with the accent color.
Example
But lots of custom admonition styles are also defined.
Quote
The needs of the many outweigh the needs of the few
Bug
Bugs weren’t always a metaphor
Success
Woohoo!
Experimental
Experiments are the heart of science
Footnotes
I have footnoted a first item [1] and second item [2]. This also references the second item [2].
Footnotes
Icons
Font Awesome and Academicons are both available:
<i class="fa fa-camera-retro fa-lg"></i>
<i class="fa fa-camera-retro fa-2x"></i>
<i class="fa fa-camera-retro fa-3x"></i>
<i class="fa fa-camera-retro fa-4x"></i>
<i class="fa fa-camera-retro fa-5x"></i>
<i class="ai ai-google-scholar-square ai-3x"></i>
<i class="ai ai-zenodo ai-3x" style="color: red"></i>
Tables
Here are some examples of Sphinx tables.
Grid
A grid table:
Header1 |
Header2 |
Header3 |
Header4 |
---|---|---|---|
row1, cell1 |
cell2 |
cell3 |
cell4 |
row2 … |
… |
… |
|
… |
… |
… |
Simple
A simple table:
H1 |
H2 |
H3 |
---|---|---|
cell1 |
cell2 |
cell3 |
… |
… |
… |
… |
… |
… |
List Tables
Alignment
Code Documentation
An example Python function.
- format_exception(etype, value, tb[, limit=None])
Format the exception with a traceback.
- Parameters:
etype – exception type
value – exception value
tb – traceback object
limit (integer or None) – maximum number of stack frames to show
- Return type:
list of strings
An example JavaScript function.
Glossaries
- environment
A structure where information about all documents under the root is saved, and used for cross-referencing. The environment is pickled after the parsing stage, so that successive runs only need to read and parse new and changed documents.
- source directory
The directory which, including its subdirectories, contains all source files for one Sphinx project.
Math
Production Lists
try_stmt ::= try1_stmt | try2_stmt try1_stmt ::= "try" ":"suite
("except" [expression
[","target
]] ":"suite
)+ ["else" ":"suite
] ["finally" ":"suite
] try2_stmt ::= "try" ":"suite
"finally" ":"suite