From 0bf99879d107376cb6499818f2f61ea1839ed3bc Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 26 Jul 2026 14:34:43 -0700 Subject: [PATCH] docs fixups --- docs/serve.py | 265 ++++++++++++++++++++++++++++++++------------- docs/template.html | 15 +++ uv.lock | 19 +--- 3 files changed, 205 insertions(+), 94 deletions(-) diff --git a/docs/serve.py b/docs/serve.py index dcc78f7b0e..5cfff33dec 100644 --- a/docs/serve.py +++ b/docs/serve.py @@ -14,7 +14,7 @@ from pathlib import Path DOCS_DIR = Path(__file__).resolve().parent SITE_DIR = DOCS_DIR / "_site" TEMPLATE_FILE = DOCS_DIR / "template.html" -EXCLUDE_DIRS = {"_site"} +EXCLUDE_DIRS = {"_site", "__pycache__"} REPO_URL = "https://github.com/commaai/openpilot/" @@ -61,15 +61,20 @@ _HEADING = re.compile(r"^(#{1,6})\s+(.*)$") _HR = re.compile(r"^(-{3,}|\*{3,}|_{3,})$") _ATTR_URL = re.compile(r"""(?P
\b(?:href|src)=(?P["']))(?P.*?)(?P=q)""")
 _VOID = frozenset("br img hr meta link input".split())
+_URL = re.compile(r"https?://[^\s<>\[\]\"']+")
+_ADMONITION = re.compile(r"^\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]$", re.I)
+
 
 def page_route(path: str) -> str:
   path = path.removesuffix(".md")
   return posixpath.dirname(path) or "." if posixpath.basename(path) == "index" else path
 
+
 def page_href(current: str, target: str) -> str:
   route = posixpath.relpath(page_route(target), page_route(current))
   return ("." if route == "." else route) + "/"
 
+
 def rewrite_relative_url(value: str, page: str) -> str | None:
   url = urllib.parse.urlparse(value)
   if value.startswith(("#", "/")) or url.scheme or url.netloc or not url.path:
@@ -80,33 +85,37 @@ def rewrite_relative_url(value: str, page: str) -> str | None:
   path = page_href(page, target) if target.endswith(".md") else posixpath.relpath(target, page_route(page))
   return url._replace(path=path).geturl()
 
+
 def rewrite_html_urls(fragment: str, page: str) -> str:
   def repl(m: re.Match[str]) -> str:
     r = rewrite_relative_url(m.group("url"), page)
     return m.group(0) if r is None else f'{m.group("pre")}{r}{m.group("q")}'
+
   return _ATTR_URL.sub(repl, fragment)
 
+
 def esc(text: str, attr: bool = False) -> str:
   held: list[str] = []
+
   def hold(m: re.Match[str]) -> str:
     held.append(m.group(0))
     return f"\0{len(held) - 1}\0"
+
   return re.sub(r"\0(\d+)\0", lambda m: held[int(m.group(1))], html.escape(_ENTITY.sub(hold, text), quote=attr))
 
+
 def clean_tooltip(description: str) -> str:
   text = re.sub(r"\[([^\]]+)]\([^)]+\)", r"\1", description)
   return re.sub(r"\s+", " ", re.sub(r"[*_~]", "", re.sub(r"`([^`]+)`", r"\1", text))).strip()
 
+
 def glossary_slug(label: str) -> str:
   return label.replace(" ", "-").replace("_", "-").lower()
 
-GLOSSARY_TERMS = [
-  (glossary_slug(l), re.compile(rf"(?**{l}**: {d}' for l, d in GLOSSARY_DESCRIPTIONS.items()
-)
+
+GLOSSARY_TERMS = [(glossary_slug(l), re.compile(rf"(?**{l}**: {d}' for l, d in GLOSSARY_DESCRIPTIONS.items())
+
 
 def inject_glossary(body: str, page: str) -> str:
   if page == GLOSSARY_PAGE:
@@ -131,7 +140,8 @@ def inject_glossary(body: str, page: str) -> str:
         skip, depth = (tag, 1) if skip is None else (skip, depth + (skip == tag))
       continue
     if depth:
-      out.append(part); continue
+      out.append(part)
+      continue
     cur, text = 0, part
     while True:
       best = None
@@ -142,21 +152,25 @@ def inject_glossary(body: str, page: str) -> str:
         if best is None or cand[:3] < best[:3]:
           best = cand
       if best is None:
-        out.append(text[cur:]); break
+        out.append(text[cur:])
+        break
       start, _, _, slug, tip, end, matched = best
       out.append(text[cur:start])
       out.append(
         f''
-        f'{matched}'
-        f'{esc(tip)}'
+        + f'{matched}'
+        + f'{esc(tip)}'
       )
-      seen.add(slug); cur = end
+      seen.add(slug)
+      cur = end
   return "".join(out)
 
+
 def slugify(text: str) -> str:
   text = html.unescape(re.sub(r"<[^>]+>", "", text)).lower()
   return re.sub(r"[-\s]+", "-", re.sub(r"[^\w\s-]", "", text, flags=re.UNICODE)).strip("-")
 
+
 def _parse_link(text: str, start: int) -> tuple[str, str, int] | None:
   if start >= len(text) or text[start] != "[":
     return None
@@ -164,153 +178,227 @@ def _parse_link(text: str, start: int) -> tuple[str, str, int] | None:
   while i < len(text):
     depth += (text[i] == "[") - (text[i] == "]")
     if text[i] == "]" and depth == 0:
-      label = text[start + 1:i]
+      label = text[start + 1 : i]
       if i + 1 >= len(text) or text[i + 1] != "(":
         return None
       j, dp = i + 2, 1
       while j < len(text) and dp:
-        dp += (text[j] == "(") - (text[j] == ")"); j += 1
-      return None if dp else (label, text[i + 2:j - 1], j)
+        dp += (text[j] == "(") - (text[j] == ")")
+        j += 1
+      return None if dp else (label, text[i + 2 : j - 1], j)
     i += 1
   return None
 
+
+def autolink_plain(text: str) -> str:
+  parts: list[str] = []
+  last = 0
+  for m in _URL.finditer(text):
+    start = m.start()
+    if start > 0 and text[start - 1].isalnum():
+      continue
+    parts.append(esc(text[last:start]))
+    url = m.group(0).rstrip(".,;:!?)]")
+    parts.append(f'{esc(url)}')
+    last = start + len(url)
+  parts.append(esc(text[last:]))
+  return "".join(parts)
+
+
 def render_inline(text: str, page: str) -> str:
   out, i, n = [], 0, len(text)
   while i < n:
-    if text[i] == "\n" and i >= 2 and text[i-2:i] == "  " and out and out[-1].endswith("  "):
-      out[-1] = out[-1][:-2]; out.append("
\n"); i += 1; continue + if text[i] == "\n" and i >= 2 and text[i - 2 : i] == " " and out and out[-1].endswith(" "): + out[-1] = out[-1][:-2] + out.append("
\n") + i += 1 + continue if text[i] == "`" and (end := text.find("`", i + 1)) != -1: - out.append(f"{esc(text[i+1:end])}"); i = end + 1; continue - if text[i] == "!" and i + 1 < n and text[i+1] == "[" and (p := _parse_link(text, i + 1)): + out.append(f"{esc(text[i + 1 : end])}") + i = end + 1 + continue + if text[i] == "!" and i + 1 < n and text[i + 1] == "[" and (p := _parse_link(text, i + 1)): label, url, end = p src = rewrite_relative_url(url, page) or url - out.append(f'{esc(label, True)}'); i = end; continue + out.append(f'{esc(label, True)}') + i = end + continue if text[i] == "[" and (p := _parse_link(text, i)): label, url, end = p href = rewrite_relative_url(url, page) or url - out.append(f'{render_inline(label, page)}'); i = end; continue + out.append(f'{render_inline(label, page)}') + i = end + continue if text[i] == "<": if text.startswith("", i + 4); end = n if end < 0 else end + 3 - out.append(rewrite_html_urls(text[i:end], page)); i = end; continue + end = text.find("-->", i + 4) + end = n if end < 0 else end + 3 + out.append(rewrite_html_urls(text[i:end], page)) + i = end + continue if m := re.match(r"<[^>]+>", text[i:]): - out.append(rewrite_html_urls(m.group(0), page)); i += len(m.group(0)); continue - if (text.startswith("**", i) or text.startswith("__", i)) and (end := text.find(text[i:i+2], i+2)) != -1: - out.append(f"{render_inline(text[i+2:end], page)}"); i = end + 2; continue - if text[i] in "*_" and i + 1 < n and text[i+1] not in " \t\n" and (end := text.find(text[i], i+1)) > i + 1: - out.append(f"{render_inline(text[i+1:end], page)}"); i = end + 1; continue + out.append(rewrite_html_urls(m.group(0), page)) + i += len(m.group(0)) + continue + if (text.startswith("**", i) or text.startswith("__", i)) and (end := text.find(text[i : i + 2], i + 2)) != -1: + out.append(f"{render_inline(text[i + 2 : end], page)}") + i = end + 2 + continue + if text[i] in "*_" and i + 1 < n and text[i + 1] not in " \t\n" and (end := text.find(text[i], i + 1)) > i + 1: + out.append(f"{render_inline(text[i + 1 : end], page)}") + i = end + 1 + continue j = i + 1 while j < n and text[j] not in "`[ list[str]: return [c.strip() for c in line.strip().removeprefix("|").removesuffix("|").split("|")] + def _is_sep(line: str) -> bool: return "|" in line and all(re.fullmatch(r":?-{3,}:?", c.strip()) for c in _trow(line)) + def _align(sep: str) -> str: s = sep.strip() - if s.startswith(":") and s.endswith(":"): return ' style="text-align: center;"' - if s.endswith(":"): return ' style="text-align: right;"' - if s.startswith(":"): return ' style="text-align: left;"' + if s.startswith(":") and s.endswith(":"): + return ' style="text-align: center;"' + if s.endswith(":"): + return ' style="text-align: right;"' + if s.startswith(":"): + return ' style="text-align: left;"' return "" + def _list_info(line: str) -> tuple[int, str, str] | None: m = _LIST.match(line) return None if not m else (len(m.group(1)) // 4, "ol" if m.group(2)[-1] == "." else "ul", m.group(3)) -def render_markdown(text: str, page: str) -> str: + +def _render_blocks(text: str, page: str) -> str: lines, out, i, n = text.splitlines(), [], 0, 0 n = len(lines) while i < n: line, s = lines[i], lines[i].strip() if not s: - i += 1; continue + i += 1 + continue if s.startswith("```"): lang, body = s[3:].strip(), [] i += 1 while i < n and not lines[i].strip().startswith("```"): - body.append(lines[i]); i += 1 - if i < n: i += 1 - code, cls = html.escape("\n".join(body) + ("\n" if body else "")), (f' class="language-{html.escape(lang)}"' if lang else "") - out.append(f"
{code}
"); continue + body.append(lines[i]) + i += 1 + if i < n: + i += 1 + code = html.escape("\n".join(body) + ("\n" if body else "")) + cls = f' class="language-{html.escape(lang)}"' if lang else "" + out.append(f"
{code}
") + continue if m := _HEADING.match(s): content, level = m.group(2).rstrip("#").strip(), len(m.group(1)) sid = slugify(content) out.append(f'{render_inline(content, page)}#') - i += 1; continue + i += 1 + continue if _HR.fullmatch(s): - out.append("
"); i += 1; continue + out.append("
") + i += 1 + continue if "|" in line and i + 1 < n and _is_sep(lines[i + 1]): headers, aligns = _trow(line), [_align(c) for c in _trow(lines[i + 1])] - i += 2; rows = [] + i += 2 + rows = [] while i < n and "|" in lines[i] and lines[i].strip(): - rows.append(_trow(lines[i])); i += 1 - parts = ["", "", ""] + [ - f"{render_inline(h, page)}" for j, h in enumerate(headers) - ] + ["", "", ""] + rows.append(_trow(lines[i])) + i += 1 + parts = ( + ["
", "", ""] + + [f"{render_inline(h, page)}" for j, h in enumerate(headers)] + + ["", "", ""] + ) for row in rows: parts.append("") for j in range(len(headers)): parts.append(f"{render_inline(row[j] if j < len(row) else '', page)}") parts.append("") - out.append("\n".join(parts + ["", "
"])); continue + out.append("\n".join(parts + ["", ""])) + continue if _list_info(line): items: list[tuple[int, str, list[str]]] = [] while i < n: if not lines[i].strip(): if i + 1 < n and _list_info(lines[i + 1]): - i += 1; continue + i += 1 + continue break info = _list_info(lines[i]) - if not info: break + if not info: + break level, kind, body = info - chunk = [body]; i += 1 + chunk = [body] + i += 1 while i < n and lines[i].strip() and _list_info(lines[i]) is None: t = lines[i].strip() - if t.startswith("```") or _HEADING.match(t) or _HR.fullmatch(t): break - chunk.append(lines[i]); i += 1 + if t.startswith(("```", ">")) or _HEADING.match(t) or _HR.fullmatch(t): + break + chunk.append(lines[i]) + i += 1 items.append((level, kind, chunk)) - def render_list(start: int, min_level: int) -> tuple[str, int]: + def render_list(items: list[tuple[int, str, list[str]]], start: int, min_level: int) -> tuple[str, int]: if start >= len(items) or items[start][0] < min_level: return "", start kind, chunks, idx = items[start][1], [f"<{items[start][1]}>"], start while idx < len(items) and items[idx][0] >= min_level: level, ikind, body_lines = items[idx] if level > min_level: - nested, idx = render_list(idx, level) + nested, idx = render_list(items, idx, level) chunks[-1] = (chunks[-1][:-5] + nested + "") if chunks[-1].endswith("") else chunks[-1] + nested continue if ikind != kind: - chunks += [f"", f"<{ikind}>"]; kind = ikind + chunks += [f"", f"<{ikind}>"] + kind = ikind idx += 1 body = render_inline("\n".join(body_lines), page) nested = "" if idx < len(items) and items[idx][0] > min_level: - nested, idx = render_list(idx, min_level + 1) + nested, idx = render_list(items, idx, min_level + 1) chunks.append(f"
  • {body}{nested}\n
  • " if nested else f"
  • {body}
  • ") chunks.append(f"") return "\n".join(chunks), idx - out.append(render_list(0, items[0][0])[0]); continue + out.append(render_list(items, 0, items[0][0])[0]) + continue if s.startswith(">"): q = [] while i < n and lines[i].strip().startswith(">"): - q.append(re.sub(r"^>\s?", "", lines[i].strip())); i += 1 - out.append(f"
    \n

    {render_inline(chr(10).join(q), page)}

    \n
    "); continue + q.append(re.sub(r"^>\s?", "", lines[i].strip())) + i += 1 + m = _ADMONITION.match(q[0].strip()) if q else None + if m: + kind = m.group(1).lower() + title = m.group(1).capitalize() + body = _render_blocks("\n".join(q[1:]), page) + out.append(f'
    \n

    {title}

    \n{body}\n
    ') + else: + out.append(f"
    \n

    {render_inline(chr(10).join(q), page)}

    \n
    ") + continue - if s.startswith("