HTML.py
Summary
- Lines:
194 of 198 covered (98.0%)
- Branches:
28 of 28 taken (100.0%), 0 lines partially
- Coverage:
98.2%
- Directory:
- Missed lines:
627, 638, 653, 664
Source
pyTooling/Sphinx/HTML.pycoveredpartialuncoveredexcludednot executable
1# ==================================================================================================================== #
2# _____ _ _ ____ _ _ #
3# _ __ _ |_ _|__ ___ | (_)_ __ __ _ / ___| _ __ | |__ (_)_ __ __ __ #
4# | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` | \___ \| '_ \| '_ \| | '_ \\ \/ / #
5# | |_) | |_| || | (_) | (_) | | | | | | (_| |_ ___) | |_) | | | | | | | |> < #
6# | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)____/| .__/|_| |_|_|_| |_/_/\_\ #
7# |_| |___/ |___/ |_| #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2026-2026 Patrick Lehmann - Bötzingen, Germany #
15# #
16# Licensed under the Apache License, Version 2.0 (the "License"); #
17# you may not use this file except in compliance with the License. #
18# You may obtain a copy of the License at #
19# #
20# http://www.apache.org/licenses/LICENSE-2.0 #
21# #
22# Unless required by applicable law or agreed to in writing, software #
23# distributed under the License is distributed on an "AS IS" BASIS, #
24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
25# See the License for the specific language governing permissions and #
26# limitations under the License. #
27# #
28# SPDX-License-Identifier: Apache-2.0 #
29# ==================================================================================================================== #
30#
31"""
32Visitors writing pyTooling.Sphinx's nodes in HTML.
33
34Each node has a ``visit_*`` and a ``depart_*`` function and a ``translate*`` pair of both, which
35:data:`~pyTooling.Sphinx.NODES` registers.
36"""
37from docutils import nodes
38from sphinx.writers.html5 import HTML5Translator
39
40from pyTooling.Decorators import export
41from pyTooling.Sphinx.Node import Abbreviation, CopyButton, Landscape, TreeDescription, TreeItem, TreeLabel, \
42 TreeLegend, TreeLegendItem, TreeSeparator, COVERAGE_STATES, DOCUMENTATION_STATES, \
43 CoverageListing, DocCoverageListing, Emoji, LineBreak, ReportIndentation, \
44 TaskListCheckbox, FoldedDependencies, FoldedSummary, visitFunc, \
45 departFunc
46
47
48__all__ = [
49 "translateLandscape", "translateTreeItem", "translateTreeLabel", "translateTreeSeparator", "translateTreeDescription",
50 "translateTreeLegend", "translateTreeLegendItem", "translateAbbreviation", "translateCoverageListing",
51 "translateDocCoverageListing", "translateCopyButton", "translateEmoji", "translateTaskListCheckbox",
52 "translateLineBreak", "translateFoldedDependencies", "translateFoldedSummary", "COPY_ICONS"
53]
54
55_SVG = (
56 'aria-hidden="true" focusable="false" width="1em" height="1em" viewBox="0 0 24 24" fill="none" '
57 'stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"'
58)
59"""The attributes of a copy button's icons: hidden from a screen reader, as large as the text, and drawn as lines."""
60
61COPY_ICONS = (
62 f'<svg class="copy-icon" {_SVG}><rect x="8" y="8" width="12" height="12" rx="2"/>'
63 '<path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2"/></svg>'
64 f'<svg class="copied-icon" {_SVG}><path d="M5 12l5 5L20 7"/></svg>'
65)
66"""
67The icons of a copy button, as inline SVG hidden from a screen reader: two sheets of paper, and a check mark, which
68the stylesheet shows instead while the button reports a copied text.
69"""
70
71
72@export
73def visit_TreeItem(translator: HTML5Translator, node: TreeItem) -> None:
74 """
75 Open an entry in HTML: a list item, and a ``<details>`` element if the entry has children.
76
77 The root of a tree with descriptions states the width of the column of the entries' texts, as CSS custom property
78 ``--pyTooling-tree-column``, which its descendants inherit.
79
80 :param translator: The HTML translator writing the page.
81 :param node: The entry.
82 """
83 if node["columnWidth"] is None:
84 translator.body.append(translator.starttag(node, "li", ""))
85 else:
86 translator.body.append(
87 translator.starttag(node, "li", "", style=f"--pyTooling-tree-column: {node['columnWidth']}")
88 )
89
90 if node["expanded"] is not None:
91 translator.body.append("<details open>" if node["expanded"] else "<details>")
92
93
94@export
95def depart_TreeItem(translator: HTML5Translator, node: TreeItem) -> None:
96 """
97 Close an entry in HTML.
98
99 :param translator: The HTML translator writing the page.
100 :param node: The entry.
101 """
102 if node["expanded"] is not None:
103 translator.body.append("</details>")
104 translator.body.append("</li>\n")
105
106
107@export
108def visit_TreeLabel(translator: HTML5Translator, node: TreeLabel) -> None:
109 """
110 Open an entry's text in HTML, behind its icons.
111
112 An entry with children opens a ``<summary>`` and writes both expander icons, the stylesheet showing one of them.
113 Any other entry writes an empty expander instead, so its text is aligned with its siblings'. Icons are hidden from
114 a screen reader.
115
116 In a tree with descriptions, the text ends with a :class:`~pyTooling.Sphinx.Node.TreeDescription`: the entry is a
117 row, whose first column holds the icons and the text, and whose second column the description. The row states the
118 entry's level as CSS custom property ``--pyTooling-tree-depth``, from which the stylesheet computes how wide the
119 first column is at this level.
120
121 :param translator: The HTML translator writing the page.
122 :param node: The entry's text.
123 """
124 if node["expandedIcon"] is not None:
125 translator.body.append("<summary>")
126
127 if isinstance(node.children[-1], TreeDescription):
128 translator.body.append(
129 f'<span class="tree-row" style="--pyTooling-tree-depth: {node["level"]}"><span class="tree-label">'
130 )
131
132 if node["expandedIcon"] is None:
133 translator.body.append('<span class="tree-expander" aria-hidden="true"></span>')
134 else:
135 for state, stateIcon in (("expanded", node["expandedIcon"]), ("collapsed", node["collapsedIcon"])):
136 translator.body.append(
137 f'<span class="tree-expander tree-{state}" aria-hidden="true">{translator.encode(stateIcon)}</span>'
138 )
139
140 if node["icon"] != "":
141 translator.body.append(f'<span class="tree-icon" aria-hidden="true">{translator.encode(node["icon"])}</span>')
142
143 translator.body.append('<span class="tree-text">')
144
145
146@export
147def depart_TreeLabel(translator: HTML5Translator, node: TreeLabel) -> None:
148 """
149 Close an entry's text in HTML - or its row, in a tree with descriptions -, and its ``<summary>`` if the entry has
150 children.
151
152 :param translator: The HTML translator writing the page.
153 :param node: The entry's text.
154 """
155 translator.body.append("</span>")
156 if node["expandedIcon"] is not None:
157 translator.body.append("</summary>")
158
159
160@export
161def visit_TreeSeparator(translator: HTML5Translator, node: TreeSeparator) -> None:
162 """
163 Skip the separator between an entry's text and its description in HTML, which draws the description in a column of
164 its own.
165
166 :param translator: The HTML translator writing the page.
167 :param node: The separator.
168 :raises SkipNode: Always, so neither the separator's text nor its depart function is written.
169 """
170 raise nodes.SkipNode()
171
172
173@export
174def depart_TreeSeparator(translator: HTML5Translator, node: TreeSeparator) -> None:
175 """
176 Close the separator between an entry's text and its description in HTML, which is never called:
177 :func:`visit_TreeSeparator` skips the node.
178
179 :param translator: The HTML translator writing the page.
180 :param node: The separator.
181 """
182
183
184@export
185def visit_TreeDescription(translator: HTML5Translator, node: TreeDescription) -> None:
186 """
187 Open an entry's description in HTML: close the entry's text and the first column, and open the second column.
188
189 An entry without a description has an empty one, so every row of a tree with descriptions has both columns.
190
191 :param translator: The HTML translator writing the page.
192 :param node: The description.
193 """
194 translator.body.append('</span></span><span class="tree-description">')
195
196
197@export
198def depart_TreeDescription(translator: HTML5Translator, node: TreeDescription) -> None:
199 """
200 Close an entry's description in HTML.
201
202 :param translator: The HTML translator writing the page.
203 :param node: The description.
204 """
205 translator.body.append("</span>")
206
207
208@export
209def visit_TreeLegend(translator: HTML5Translator, node: TreeLegend) -> None:
210 """
211 Open a tree's legend in HTML: a paragraph, hidden from a screen reader like the icons it explains.
212
213 :param translator: The HTML translator writing the page.
214 :param node: The legend.
215 """
216 translator.body.append(translator.starttag(node, "p", "", **{"aria-hidden": "true"}))
217
218
219@export
220def depart_TreeLegend(translator: HTML5Translator, node: TreeLegend) -> None:
221 """
222 Close a tree's legend in HTML.
223
224 :param translator: The HTML translator writing the page.
225 :param node: The legend.
226 """
227 translator.body.append("</p>\n")
228
229
230@export
231def visit_TreeLegendItem(translator: HTML5Translator, node: TreeLegendItem) -> None:
232 """
233 Open an item of a tree's legend in HTML: its icon, and the text explaining it.
234
235 :param translator: The HTML translator writing the page.
236 :param node: The legend's item.
237 """
238 translator.body.append(
239 f'<span class="tree-legend-item"><span class="tree-icon">{translator.encode(node["icon"])}</span>'
240 '<span class="tree-text">'
241 )
242
243
244@export
245def depart_TreeLegendItem(translator: HTML5Translator, node: TreeLegendItem) -> None:
246 """
247 Close an item of a tree's legend in HTML.
248
249 :param translator: The HTML translator writing the page.
250 :param node: The legend's item.
251 """
252 translator.body.append("</span></span>")
253
254
255@export
256def visit_Landscape(translator: HTML5Translator, node: Landscape) -> None:
257 """
258 Open a landscape container in HTML, which writes nothing: a web page has no page orientation.
259
260 :param translator: The HTML translator writing the page.
261 :param node: The landscape container.
262 """
263
264
265@export
266def depart_Landscape(translator: HTML5Translator, node: Landscape) -> None:
267 """
268 Close a landscape container in HTML, which writes nothing.
269
270 :param translator: The HTML translator writing the page.
271 :param node: The landscape container.
272 """
273
274
275translateLandscape: tuple[visitFunc, departFunc] = (visit_Landscape, depart_Landscape)
276"""Visit and depart function writing a :class:`~pyTooling.Sphinx.Node.Landscape` in HTML."""
277
278translateTreeItem: tuple[visitFunc, departFunc] = (visit_TreeItem, depart_TreeItem)
279"""Visit and depart function writing a :class:`~pyTooling.Sphinx.Node.TreeItem` in HTML."""
280
281translateTreeLabel: tuple[visitFunc, departFunc] = (visit_TreeLabel, depart_TreeLabel)
282"""Visit and depart function writing a :class:`~pyTooling.Sphinx.Node.TreeLabel` in HTML."""
283
284translateTreeSeparator: tuple[visitFunc, departFunc] = (visit_TreeSeparator, depart_TreeSeparator)
285"""Visit and depart function skipping a :class:`~pyTooling.Sphinx.Node.TreeSeparator` in HTML."""
286
287translateTreeDescription: tuple[visitFunc, departFunc] = (visit_TreeDescription, depart_TreeDescription)
288"""Visit and depart function writing a :class:`~pyTooling.Sphinx.Node.TreeDescription` in HTML."""
289
290translateTreeLegend: tuple[visitFunc, departFunc] = (visit_TreeLegend, depart_TreeLegend)
291"""Visit and depart function writing a :class:`~pyTooling.Sphinx.Node.TreeLegend` in HTML."""
292
293translateTreeLegendItem: tuple[visitFunc, departFunc] = (visit_TreeLegendItem, depart_TreeLegendItem)
294"""Visit and depart function writing a :class:`~pyTooling.Sphinx.Node.TreeLegendItem` in HTML."""
295
296
297@export
298def visit_Abbreviation(translator: HTML5Translator, node: Abbreviation) -> None:
299 """
300 Open an abbreviation in HTML: an ``<abbr>`` element.
301
302 :param translator: The HTML translator writing the page.
303 :param node: The abbreviation.
304 """
305 translator.body.append(translator.starttag(node, "abbr", "", CLASS="pytooling-abbreviation"))
306
307
308@export
309def depart_Abbreviation(translator: HTML5Translator, node: Abbreviation) -> None:
310 """
311 Close an abbreviation in HTML, behind the box the stylesheet shows on hover.
312
313 The box shows the long form and, if the node carries one, the summary of the abbreviation's description. The short
314 form is shown in front of the long form only if a title replaced it in the text.
315
316 :param translator: The HTML translator writing the page.
317 :param node: The abbreviation.
318 """
319 long = translator.encode(node["long"])
320 short = "" if node.astext() == node["short"] else f'<strong>{translator.encode(node["short"])}</strong> '
321 summary = node.get("summary", "")
322 summary = f'<span class="pytooling-abbreviation-summary">{translator.encode(summary)}</span>' if summary != "" else ""
323 translator.body.append(
324 f'<span class="pytooling-abbreviation-box" role="tooltip">{short}{long}{summary}</span></abbr>'
325 )
326
327
328translateAbbreviation: tuple[visitFunc, departFunc] = (visit_Abbreviation, depart_Abbreviation)
329"""Visit and depart function writing an :class:`~pyTooling.Sphinx.Node.Abbreviation` in HTML."""
330
331
332@export
333def highlightTokens(translator: HTML5Translator, tokens: list[tuple[str, str]]) -> str:
334 """
335 Write a listing line's tokens in HTML, each in a ``<span>`` of its Pygments class, so Sphinx' Pygments stylesheet
336 colors it.
337
338 :param translator: The HTML translator writing the page.
339 :param tokens: The line's tokens as ``(CSS class, text)`` pairs; a token without a class is written as text.
340 :returns: The line's HTML.
341 """
342 return "".join(
343 translator.encode(text) if cssClass == "" else f'<span class="{cssClass}">{translator.encode(text)}</span>'
344 for cssClass, text in tokens
345 )
346
347
348@export
349def writeListing(translator: HTML5Translator, node: nodes.Element, lines: list[str], states: dict[str, str]) -> None:
350 """
351 Write a listing in HTML: a header with the file's path and a legend of the states, then the lines.
352
353 The lines are a ``<pre>`` inside the ``<div>`` elements of Sphinx' highlighting, so Sphinx' Pygments stylesheet colors
354 the tokens.
355
356 :param translator: The HTML translator writing the page.
357 :param node: The listing, whose attribute ``path`` holds the file's path.
358 :param lines: The lines, each written as HTML.
359 :param states: The states the legend explains, by CSS class suffix: the label of each.
360 """
361 classes = "highlight-default notranslate report-coverage-listing"
362 legend = "".join(
363 f'<span class="report-coverage-legend-{state or "none"}">{translator.encode(label)}</span>'
364 for state, label in states.items()
365 )
366 translator.body.append(translator.starttag(node, "div", "", CLASS=classes))
367 translator.body.append(
368 f'<div class="report-coverage-header"><span class="report-coverage-path">{translator.encode(node["path"])}</span>'
369 f'<span class="report-coverage-legend">{legend}</span></div>'
370 )
371 translator.body.append('<div class="highlight"><pre>')
372 translator.body.extend(lines)
373 translator.body.append("</pre></div></div>\n")
374
375
376@export
377def visit_CoverageListing(translator: HTML5Translator, node: CoverageListing) -> None:
378 """
379 Write a source file's code coverage in HTML, completely.
380
381 A header shows the file's path and a legend of the coverage states (see :func:`writeListing`). Each line is a
382 ``<span>`` with class ``report-line`` and ``report-line-<state>``, holding its number, its hits - if the report has
383 any - and its tokens. A line, which ran without taking all its branches, says on hover how many were taken. The
384 node's text - the plain source - isn't written again.
385
386 :param translator: The HTML translator writing the page.
387 :param node: The listing.
388 :raises SkipNode: Always, so the node's text isn't written again.
389 """
390 withHits = any(hits is not None for _, _, hits, _, _, _ in node["lines"])
391
392 lines: list[str] = []
393 for number, status, hits, branches, coveredBranches, tokens in node["lines"]:
394 classes = "report-line" if status == "" else f"report-line report-line-{status}"
395 attributes = f' id="L{number}"' if node["anchors"] else ""
396 if status == "partial":
397 attributes += f' title="{coveredBranches} of {branches} branches taken"'
398
399 hitsText = "" if hits is None else f"{hits}"
400 gutter = f'<span class="linenos">{number}</span>'
401 if withHits:
402 gutter += f'<span class="linenos report-hits">{hitsText}</span>'
403
404 lines.append(f'<span class="{classes}"{attributes}>{gutter}{highlightTokens(translator, tokens)}\n</span>')
405
406 writeListing(translator, node, lines, COVERAGE_STATES)
407
408 raise nodes.SkipNode
409
410
411@export
412def depart_CoverageListing(translator: HTML5Translator, node: CoverageListing) -> None:
413 """
414 Close a source file's code coverage in HTML, which writes nothing: :func:`visit_CoverageListing` wrote it all.
415
416 :param translator: The HTML translator writing the page.
417 :param node: The listing.
418 """
419
420
421translateCoverageListing: tuple[visitFunc, departFunc] = (visit_CoverageListing, depart_CoverageListing)
422"""Visit and depart function writing a :class:`~pyTooling.Sphinx.Node.CoverageListing` in HTML."""
423
424
425@export
426def visit_DocCoverageListing(translator: HTML5Translator, node: DocCoverageListing) -> None:
427 """
428 Write a Python module's documentation coverage in HTML, completely.
429
430 A header shows the module's path and a legend of the documentation states (see :func:`writeListing`). Each line is
431 a ``<span>`` with class ``report-line`` and ``report-line-<state>``, holding its number and its tokens; an elided
432 body's ``...`` has no number and the class ``report-line-elided``. The node's text - the condensed source - isn't
433 written again.
434
435 :param translator: The HTML translator writing the page.
436 :param node: The listing.
437 :raises SkipNode: Always, so the node's text isn't written again.
438 """
439 lines: list[str] = []
440 for number, status, tokens in node["lines"]:
441 classes = "report-line" if status == "" else f"report-line report-line-{status}"
442 if number is None:
443 classes += " report-line-elided"
444
445 gutter = f'<span class="linenos">{"" if number is None else number}</span>'
446 lines.append(f'<span class="{classes}">{gutter}{highlightTokens(translator, tokens)}\n</span>')
447
448 writeListing(translator, node, lines, DOCUMENTATION_STATES)
449
450 raise nodes.SkipNode
451
452
453@export
454def visit_CopyButton(translator: HTML5Translator, node: CopyButton) -> None:
455 """
456 Write a copy button in HTML, completely: a ``<button>`` with the text to copy as attribute ``data-copy``.
457
458 The button's label is its accessible name and its tooltip; its icons are :data:`COPY_ICONS`. The script
459 :file:`pyTooling.js` copies the text on a click.
460
461 :param translator: The HTML translator writing the page.
462 :param node: The copy button.
463 :raises SkipNode: Always, as the button is written completely.
464 """
465 translator.body.append(translator.starttag(
466 node, "button", "", type="button", CLASS="pytooling-copy", title=node["label"],
467 **{"aria-label": node["label"], "data-copy": node["copy"]}
468 ))
469 translator.body.append(f"{COPY_ICONS}</button>")
470
471 raise nodes.SkipNode
472
473
474@export
475def depart_DocCoverageListing(translator: HTML5Translator, node: DocCoverageListing) -> None:
476 """
477 Close a Python module's documentation coverage in HTML, which writes nothing: :func:`visit_DocCoverageListing` wrote
478 it all.
479
480 :param translator: The HTML translator writing the page.
481 :param node: The listing.
482 """
483
484
485translateDocCoverageListing: tuple[visitFunc, departFunc] = (visit_DocCoverageListing, depart_DocCoverageListing)
486"""Visit and depart function writing a :class:`~pyTooling.Sphinx.Node.DocCoverageListing` in HTML."""
487
488
489@export
490def depart_CopyButton(translator: HTML5Translator, node: CopyButton) -> None:
491 """
492 Close a copy button in HTML, which writes nothing: :func:`visit_CopyButton` wrote it all.
493
494 :param translator: The HTML translator writing the page.
495 :param node: The copy button.
496 """
497
498
499translateCopyButton: tuple[visitFunc, departFunc] = (visit_CopyButton, depart_CopyButton)
500"""Visit and depart function writing a :class:`~pyTooling.Sphinx.Node.CopyButton` in HTML."""
501
502
503@export
504def visit_Emoji(translator: HTML5Translator, node: Emoji) -> None:
505 """
506 Open an emoji in HTML: a ``<span>`` with the emoji's shortcode as tooltip.
507
508 :param translator: The HTML translator writing the page.
509 :param node: The emoji.
510 """
511 translator.body.append(translator.starttag(node, "span", "", CLASS="github-emoji", title=f":{node['shortcode']}:"))
512
513
514@export
515def depart_Emoji(translator: HTML5Translator, node: Emoji) -> None:
516 """
517 Close an emoji in HTML.
518
519 :param translator: The HTML translator writing the page.
520 :param node: The emoji.
521 """
522 translator.body.append("</span>")
523
524
525translateEmoji: tuple[visitFunc, departFunc] = (visit_Emoji, depart_Emoji)
526"""Visit and depart function writing an :class:`~pyTooling.Sphinx.Node.Emoji` in HTML."""
527
528
529@export
530def visit_TaskListCheckbox(translator: HTML5Translator, node: TaskListCheckbox) -> None:
531 """
532 Write a task list item's checkbox in HTML, completely: a disabled ``<input>`` of type ``checkbox``, checked if the
533 task is done.
534
535 :param translator: The HTML translator writing the page.
536 :param node: The checkbox.
537 :raises SkipNode: Always, so the node's text isn't written.
538 """
539 checked = ' checked=""' if node["checked"] else ""
540 label = "Completed task" if node["checked"] else "Incomplete task"
541 translator.body.append(
542 f'<input class="github-task-list-checkbox" type="checkbox" disabled=""{checked} aria-label="{label}" />'
543 )
544
545 raise nodes.SkipNode
546
547
548@export
549def depart_TaskListCheckbox(translator: HTML5Translator, node: TaskListCheckbox) -> None:
550 """
551 Close a task list item's checkbox in HTML, which writes nothing: :func:`visit_TaskListCheckbox` wrote it all.
552
553 :param translator: The HTML translator writing the page.
554 :param node: The checkbox.
555 """
556
557
558translateTaskListCheckbox: tuple[visitFunc, departFunc] = (visit_TaskListCheckbox, depart_TaskListCheckbox)
559"""Visit and depart function writing a :class:`~pyTooling.Sphinx.Node.TaskListCheckbox` in HTML."""
560
561
562@export
563def visit_LineBreak(translator: HTML5Translator, node: LineBreak) -> None:
564 """
565 Write a line break in HTML, completely: ``<br />``.
566
567 :param translator: The HTML translator writing the page.
568 :param node: The line break.
569 :raises SkipNode: Always, so the node's text isn't written.
570 """
571 translator.body.append("<br />\n")
572
573 raise nodes.SkipNode
574
575
576@export
577def depart_LineBreak(translator: HTML5Translator, node: LineBreak) -> None:
578 """
579 Close a line break in HTML, which writes nothing: :func:`visit_LineBreak` wrote it all.
580
581 :param translator: The HTML translator writing the page.
582 :param node: The line break.
583 """
584
585
586translateLineBreak: tuple[visitFunc, departFunc] = (visit_LineBreak, depart_LineBreak)
587"""Visit and depart function writing a :class:`~pyTooling.Sphinx.Node.LineBreak` in HTML."""
588
589
590@export
591def visit_ReportIndentation(translator: HTML5Translator, node: ReportIndentation) -> None:
592 """
593 Open an indented name of a report table in HTML: a ``<span>`` setting the CSS custom property
594 ``--pyTooling-report-level`` to the number of indentation steps.
595
596 :param translator: The HTML translator writing the page.
597 :param node: The indented name.
598 """
599 translator.body.append(translator.starttag(
600 node, "span", "", CLASS="report-indentation", style=f"--pyTooling-report-level: {node['level']}"
601 ))
602
603
604@export
605def depart_ReportIndentation(translator: HTML5Translator, node: ReportIndentation) -> None:
606 """
607 Close an indented name of a report table in HTML.
608
609 :param translator: The HTML translator writing the page.
610 :param node: The indented name.
611 """
612 translator.body.append("</span>")
613
614
615translateReportIndentation: tuple[visitFunc, departFunc] = (visit_ReportIndentation, depart_ReportIndentation)
616"""Visit and depart function writing a :class:`~pyTooling.Sphinx.Node.ReportIndentation` in HTML."""
617
618
619@export
620def visit_FoldedDependencies(translator: HTML5Translator, node: FoldedDependencies) -> None:
621 """
622 Open a duplicate's folded dependencies in HTML: a ``<details>`` element, closed until its summary is clicked.
623
624 :param translator: The HTML translator writing the page.
625 :param node: The folded dependencies.
626 """
627 translator.body.append(translator.starttag(node, "details", ""))
628
629
630@export
631def depart_FoldedDependencies(translator: HTML5Translator, node: FoldedDependencies) -> None:
632 """
633 Close a duplicate's folded dependencies in HTML.
634
635 :param translator: The HTML translator writing the page.
636 :param node: The folded dependencies.
637 """
638 translator.body.append("</details>\n")
639
640
641translateFoldedDependencies: tuple[visitFunc, departFunc] = (visit_FoldedDependencies, depart_FoldedDependencies)
642"""Visit and depart function writing a :class:`~pyTooling.Sphinx.Node.FoldedDependencies` in HTML."""
643
644
645@export
646def visit_FoldedSummary(translator: HTML5Translator, node: FoldedSummary) -> None:
647 """
648 Open the summary of a duplicate's folded dependencies in HTML: the ``<summary>`` of its ``<details>`` element.
649
650 :param translator: The HTML translator writing the page.
651 :param node: The summary.
652 """
653 translator.body.append(translator.starttag(node, "summary", ""))
654
655
656@export
657def depart_FoldedSummary(translator: HTML5Translator, node: FoldedSummary) -> None:
658 """
659 Close the summary of a duplicate's folded dependencies in HTML.
660
661 :param translator: The HTML translator writing the page.
662 :param node: The summary.
663 """
664 translator.body.append("</summary>\n")
665
666
667translateFoldedSummary: tuple[visitFunc, departFunc] = (
668 visit_FoldedSummary, depart_FoldedSummary
669)
670"""Visit and depart function writing a :class:`~pyTooling.Sphinx.Node.FoldedSummary` in HTML."""