summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-06-29 10:16:25 +0700
committerhathach <[email protected]>2026-06-29 10:16:25 +0700
commit4b1c8d16f72bb5d8f2eb8a2e8dde35abdd2f2a88 (patch)
tree3db175aa3d96c92a44fab764dba59f473096c4b4 /docs
parent0a25cc27d7d3699536f6df01e80af3eb0423ce58 (diff)
docs: add build-doc tooling and a README for every example
Documentation tooling: - Add the `build-doc` skill and `tools/build_doc.py` wrapper for local Sphinx builds (clean / -W / open). - Enable Markdown (MyST) in conf.py and auto-collect examples/{device,host,dual}/*/README.md into a 3-level Examples nav (Examples > Device/Host/Dual > example), noting each page's source location and normalizing headings to a single H1. - Remove the stale `.claude/commands/build-doc.md`; point the AGENTS.md Documentation section at the skill. Example docs: - Add a README.md for every device/host/dual example: what it does, USB interface table, notable tusb_config.h settings, generic CMake + Make build steps, and how to try it. - Fold each *_freertos variant into its base README, noting the FreeRTOS source path and any RTOS-specific behavior. Generated docs/examples/ output is git-ignored. Builds clean with `sphinx-build -W`. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Diffstat (limited to 'docs')
-rwxr-xr-xdocs/conf.py81
-rw-r--r--docs/index.rst8
-rw-r--r--docs/requirements.txt1
3 files changed, 90 insertions, 0 deletions
diff --git a/docs/conf.py b/docs/conf.py
index 86ddcf672..4a1a7adc2 100755
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -6,6 +6,7 @@
# https://www.sphinx-doc.org/en/master/usage/configuration.html
import re
+import shutil
from pathlib import Path
# -- Path setup --------------------------------------------------------------
@@ -25,6 +26,7 @@ extensions = [
'sphinx.ext.intersphinx',
'sphinx.ext.todo',
'sphinx_autodoc_typehints',
+ 'myst_parser', # Markdown (.md) support alongside reStructuredText
]
templates_path = ['_templates']
@@ -79,3 +81,82 @@ def preprocess_readme():
tgt.write_text(content, encoding='utf-8')
preprocess_readme()
+
+
+# scan example READMEs into docs/examples/ and (re)generate examples.rst
+EXAMPLE_GROUPS = ('device', 'host', 'dual')
+
+_HEADING_RE = re.compile(r'^(#{1,6})(\s.*)$')
+_FENCE_RE = re.compile(r'^\s*(```|~~~)')
+
+def _normalize_headings(text):
+ """Make each page a single Sphinx section: promote so the first heading is
+ H1 and demote any later same-or-higher heading to at least H2. Without this,
+ a README that uses flat #### headings (no H1) becomes several top-level
+ sections and each leaks into the sidebar as a separate entry."""
+ lines = text.splitlines(keepends=True)
+ headings, in_fence = [], False
+ for i, line in enumerate(lines):
+ if _FENCE_RE.match(line):
+ in_fence = not in_fence
+ elif not in_fence and _HEADING_RE.match(line):
+ headings.append(i)
+ if not headings:
+ return text
+ delta = 1 - len(_HEADING_RE.match(lines[headings[0]]).group(1))
+ for n, i in enumerate(headings):
+ m = _HEADING_RE.match(lines[i])
+ level = max(1, min(6, len(m.group(1)) + delta))
+ if n > 0:
+ level = max(level, 2)
+ lines[i] = '#' * level + m.group(2) + ('\n' if lines[i].endswith('\n') else '')
+ return ''.join(lines)
+
+def _with_location(text, rel):
+ """Insert a source-location note right after the first H1 so each rendered
+ example page shows which example directory it came from."""
+ note = f"> **Example source:** `{rel}`\n"
+ lines = text.splitlines(keepends=True)
+ for i, line in enumerate(lines):
+ if line.lstrip().startswith("# "):
+ return "".join(lines[:i + 1]) + "\n" + note + "\n" + "".join(lines[i + 1:])
+ return note + "\n" + text # no H1: prepend
+
+def generate_examples_docs():
+ """Copy every examples/{device,host,dual}/*/README.md into
+ docs/examples/<group>/<name>.md (noting its source location) and write a
+ docs/examples/<group>/index.rst landing page per group. index.rst points at
+ those group pages, giving a 3-level sidebar: Examples > Device/Host/Dual >
+ example. Output is rebuilt each run (git-ignored)."""
+ docs_dir = Path(__file__).parent
+ examples_root = docs_dir.parent / "examples"
+ out_dir = docs_dir / "examples"
+
+ # start clean so deleted/renamed examples don't leave stale pages
+ if out_dir.exists():
+ shutil.rmtree(out_dir)
+ (docs_dir / "examples.rst").unlink(missing_ok=True) # remove legacy single-file output
+
+ for group in EXAMPLE_GROUPS:
+ group_out = out_dir / group
+ group_out.mkdir(parents=True, exist_ok=True)
+
+ names = []
+ for readme in sorted((examples_root / group).glob("*/README.md")):
+ name = readme.parent.name
+ rel = f"examples/{group}/{name}"
+ content = _normalize_headings(readme.read_text(encoding='utf-8'))
+ (group_out / f"{name}.md").write_text(_with_location(content, rel), encoding='utf-8')
+ names.append(name)
+
+ # group landing page (Device / Host / Dual) with a toctree of its examples
+ heading = group.capitalize()
+ page = [f"{'*' * len(heading)}\n{heading}\n{'*' * len(heading)}\n"]
+ if names:
+ page.append(".. toctree::\n :maxdepth: 1\n")
+ page.extend(f" {name}" for name in names)
+ else:
+ page.append("No documented examples yet.")
+ (group_out / "index.rst").write_text("\n".join(page) + "\n", encoding='utf-8')
+
+generate_examples_docs()
diff --git a/docs/index.rst b/docs/index.rst
index 39d30a038..6d212d8cb 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -12,6 +12,14 @@
troubleshooting
.. toctree::
+ :maxdepth: 2
+ :caption: Examples
+
+ examples/device/index
+ examples/host/index
+ examples/dual/index
+
+.. toctree::
:maxdepth: 1
:caption: Project Info
diff --git a/docs/requirements.txt b/docs/requirements.txt
index ad5c89922..c94a3dd30 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,4 +1,5 @@
sphinx>=5.0
furo>=2020.12.30.b24
sphinx-autodoc-typehints>=1.10
+myst-parser>=4.0
jinja2>=3.0.3