docs: add figures, table captions and a macro chooser
- Figures with numbered captions: SAX stepper, json_view layout and edit stepper, exception map, JSON pointer and patch walkers, QA pipeline, scaling and binary size charts, roadmap lanes, release timeline, integration decision tree and namespace builder - Table captions (nj-table-caption) on 9 pages - Compile times: modules and PCH charts, filesystem section, re-measured Figure 1, links between features/modules.md and compile_times.md - Interactive macro chooser for features/arbitrary_types.md - Link check: exclude URLs that answer the plugin with 400/401/419 or are dead (customers.md) Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
96 files changed
+6737
-69
No files matched your search
@@ -0,0 +1,121 @@
|
||||
"""Draws the release timeline of home/releases/index.md as an inline SVG into docs/images/.
|
||||
|
||||
The releases and their dates come from the tables of that page (rows like `| [v3.11.2](v3.11.2.md) | 2022-08-12 | ...`),
|
||||
so after adding a release to the page, run `python3 scripts/figures/release_timeline.py` from docs/mkdocs and commit
|
||||
the regenerated docs/images/release_timeline.svg. The page includes the SVG text with
|
||||
`--8<-- "images/release_timeline.svg"`, so it follows the light/dark toggle (see the `nj-fig` rules in
|
||||
docs/css/brand.css and the `nj-int-*` rules in docs/css/fig-integration.css). Every marker has a <title> tooltip.
|
||||
Rows without a date (the "Unreleased" row) are skipped; any other release row that cannot be parsed stops the script.
|
||||
"""
|
||||
import datetime
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
DOCS = Path(__file__).resolve().parents[2] / "docs"
|
||||
OUT = Path(__file__).resolve().parents[2] / "docs" / "images"
|
||||
PAGE = DOCS / "home" / "releases" / "index.md"
|
||||
|
||||
ROW = re.compile(r"^\|\s*\[v(\d+)\.(\d+)\.(\d+)\]\([^)]*\)\s*\|\s*([^|]*?)\s*\|")
|
||||
W, LEFT, RIGHT = 880, 24, 856
|
||||
LABEL_ROW, CHAR_W = 17, 7.0
|
||||
|
||||
|
||||
def parse(markdown):
|
||||
releases = []
|
||||
for line in markdown.splitlines():
|
||||
if not line.startswith("| [v"):
|
||||
continue
|
||||
m = ROW.match(line)
|
||||
if not m:
|
||||
sys.exit("cannot parse release row: " + line)
|
||||
try:
|
||||
date = datetime.date.fromisoformat(m.group(4))
|
||||
except ValueError:
|
||||
sys.exit("cannot parse date in row: " + line)
|
||||
releases.append(((int(m.group(1)), int(m.group(2)), int(m.group(3))), date))
|
||||
if len(releases) < 2:
|
||||
sys.exit("found fewer than two releases in " + str(PAGE))
|
||||
if len({v for v, _ in releases}) != len(releases):
|
||||
sys.exit("duplicate release in " + str(PAGE))
|
||||
return sorted(releases, key=lambda r: (r[1], r[0]))
|
||||
|
||||
|
||||
def name(version):
|
||||
return "v%d.%d.%d" % version
|
||||
|
||||
|
||||
def draw(releases):
|
||||
first, last = releases[0][1], releases[-1][1]
|
||||
start, end = datetime.date(first.year, 1, 1), datetime.date(last.year + 1, 1, 1)
|
||||
span = (end - start).days
|
||||
|
||||
def xof(date):
|
||||
return LEFT + (RIGHT - LEFT) * (date - start).days / span
|
||||
|
||||
# minor labels, placed greedily into the lowest free row above the axis
|
||||
rows_end, labels = [], []
|
||||
for version, date in releases:
|
||||
if version[2] != 0:
|
||||
continue
|
||||
text = "%d.%d" % version[:2]
|
||||
x, half = xof(date), len(text) * CHAR_W / 2 + 2
|
||||
row = next((i for i, e in enumerate(rows_end) if e < x - half), None)
|
||||
if row is None:
|
||||
rows_end.append(0)
|
||||
row = len(rows_end) - 1
|
||||
rows_end[row] = x + half
|
||||
labels.append((x, row, text))
|
||||
top = 14 + LABEL_ROW * len(rows_end)
|
||||
axis = top + 12
|
||||
height = axis + 70
|
||||
|
||||
s = ['<svg class="nj-fig nj-fig--timeline" viewBox="0 0 %d %d" role="img" aria-labelledby="nj-int-tl-title" '
|
||||
'xmlns="http://www.w3.org/2000/svg">' % (W, height)]
|
||||
minors = sum(1 for v, _ in releases if v[2] == 0)
|
||||
s.append('<title id="nj-int-tl-title">Timeline of the %d releases from %s (%s) to %s (%s): %d minor or major '
|
||||
'releases, drawn as filled dots with their version above the axis, and %d patch releases, drawn as '
|
||||
'rings.</title>' % (len(releases), name(releases[0][0]), first.isoformat(), name(releases[-1][0]),
|
||||
last.isoformat(), minors, len(releases) - minors))
|
||||
# year grid
|
||||
for year in range(start.year, end.year + 1):
|
||||
x = xof(datetime.date(year, 1, 1))
|
||||
s.append('<line class="nj-int-grid" x1="%.1f" y1="%d" x2="%.1f" y2="%d"/>' % (x, top - 4, x, axis + 8))
|
||||
if year < end.year:
|
||||
mid = xof(datetime.date(year, 7, 1))
|
||||
s.append('<text class="nj-int-year" x="%.1f" y="%d">%d</text>' % (mid, axis + 24, year))
|
||||
s.append('<line class="nj-int-axis" x1="%d" y1="%d" x2="%d" y2="%d"/>' % (LEFT, axis, RIGHT, axis))
|
||||
# major version spans
|
||||
majors = {}
|
||||
for version, date in releases:
|
||||
majors.setdefault(version[0], []).append(date)
|
||||
for major, dates in sorted(majors.items()):
|
||||
x0, x1 = xof(min(dates)), xof(max(dates))
|
||||
y = axis + 42
|
||||
s.append('<path class="nj-int-axis" fill="none" d="M%.1f %d V%d H%.1f V%d"/>' % (x0, y - 5, y, x1, y - 5))
|
||||
s.append('<text class="nj-int-major" x="%.1f" y="%d" text-anchor="%s">%d.x</text>'
|
||||
% ((x0 + x1) / 2 if x1 - x0 > 30 else x0, y + 17, "middle" if x1 - x0 > 30 else "start", major))
|
||||
# leaders and labels
|
||||
for x, row, text in labels:
|
||||
y = top - row * LABEL_ROW
|
||||
s.append('<line class="nj-int-leader" x1="%.1f" y1="%d" x2="%.1f" y2="%d"/>' % (x, y + 3, x, axis - 7))
|
||||
s.append('<text class="nj-int-label" x="%.1f" y="%d">%s</text>' % (x, y, text))
|
||||
# markers: one per date, patch rings first so that minor dots stay on top
|
||||
by_date = {}
|
||||
for version, date in releases:
|
||||
by_date.setdefault(date, []).append(version)
|
||||
groups = sorted(by_date.items(), key=lambda g: any(v[2] == 0 for v in g[1]))
|
||||
for date, versions in groups:
|
||||
minor = any(v[2] == 0 for v in versions)
|
||||
x = xof(date)
|
||||
tip = ", ".join(name(v) for v in sorted(versions)) + " released " + date.isoformat()
|
||||
s.append('<g><title>%s</title><circle class="nj-int-hit" cx="%.1f" cy="%d" r="9"/>'
|
||||
'<circle class="%s" cx="%.1f" cy="%d" r="%g"/></g>'
|
||||
% (tip, x, axis, "nj-int-minor" if minor else "nj-int-patch", x, axis, 6 if minor else 3.5))
|
||||
s.append('</svg>')
|
||||
return "\n".join(s) + "\n"
|
||||
|
||||
|
||||
releases = parse(PAGE.read_text(encoding="utf-8"))
|
||||
(OUT / "release_timeline.svg").write_text(draw(releases), encoding="utf-8")
|
||||
print("wrote", OUT / "release_timeline.svg", "with", len(releases), "releases")
|
||||
Reference in new issue
Block a user