Skip to content

Commit f3a53d4

Browse files
authored
Changes for zensical migration (#68)
Changes similar to what we did for migrating `quacc` from `mkdocs` to `zensical`.
1 parent 562e6f3 commit f3a53d4

9 files changed

Lines changed: 360 additions & 35 deletions

File tree

.github/scripts/gen_ref_pages.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"""
2+
Generate docs/reference/ files to disk.
3+
This is roughly equivalent to what the `mkdocs-gen-files` plugin would have done.
4+
"""
5+
6+
from __future__ import annotations
7+
8+
from pathlib import Path
9+
10+
out_dir = Path("docs/reference")
11+
out_dir.mkdir(parents=True, exist_ok=True)
12+
13+
nav_lines = []
14+
seen_dirs: set[tuple[str, ...]] = set()
15+
16+
for path in sorted(Path("src").rglob("*.py")):
17+
module_path = path.relative_to("src").with_suffix("")
18+
doc_path = path.relative_to("src").with_suffix(".md")
19+
full_doc_path = out_dir / doc_path
20+
21+
parts = tuple(module_path.parts)
22+
ignore = ["_cli", "_version", "__init__", "__main__"]
23+
if any(p in ignore for p in parts):
24+
continue
25+
26+
full_doc_path.parent.mkdir(parents=True, exist_ok=True)
27+
full_doc_path.write_text(f"::: {'.'.join(parts)}\n")
28+
29+
# Emit section headers for intermediate directories not yet seen
30+
for i in range(1, len(parts)):
31+
dir_key = parts[:i]
32+
if dir_key not in seen_dirs:
33+
seen_dirs.add(dir_key)
34+
indent = " " * (len(dir_key) - 1)
35+
nav_lines.append(f"{indent}* {dir_key[-1]}\n")
36+
37+
# Emit leaf link
38+
name = parts[-1]
39+
indent = " " * (len(parts) - 1)
40+
nav_lines.append(f"{indent}* [{name}]({doc_path.as_posix()})\n")
41+
42+
(out_dir / "SUMMARY.md").write_text("".join(nav_lines))
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
""" "
2+
Using _zensical.toml as a starting file, generate a zensical.toml with navigation links
3+
generated from `docs/reference/SUMMARY.md`.
4+
5+
This is roughly equivalent to what the `mkdocs-literate-nav` plugin would have done.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
import re
11+
import sys
12+
from pathlib import Path
13+
14+
15+
def parse_summary(path: Path) -> list[tuple[int, str, str | None]]:
16+
"""Parse SUMMARY.md into a flat list of (indent_level, label, path_or_None)."""
17+
items = []
18+
for line in path.read_text().splitlines():
19+
if not line.strip():
20+
continue
21+
n_spaces = len(line) - len(line.lstrip(" "))
22+
indent = n_spaces // 4
23+
rest = line.strip()
24+
if not rest.startswith("* "):
25+
raise ValueError(f"Unexpected line format: {line!r}")
26+
rest = rest[2:]
27+
m = re.match(r"\[([^\]]+)\]\(([^)]+)\)", rest)
28+
if m:
29+
items.append((indent, m.group(1), m.group(2)))
30+
else:
31+
items.append((indent, rest, None))
32+
return items
33+
34+
35+
def build_tree(items: list[tuple[int, str, str | None]]) -> list[dict]:
36+
"""Build a nested tree from flat (indent, label, path) items."""
37+
root: list[dict] = []
38+
# stack entries: (indent_level, children_list)
39+
stack: list[tuple[int, list]] = [(-1, root)]
40+
41+
for indent, label, path in items:
42+
node: dict = {"label": label, "path": path, "children": []}
43+
while stack[-1][0] >= indent:
44+
stack.pop()
45+
stack[-1][1].append(node)
46+
stack.append((indent, node["children"]))
47+
48+
return root
49+
50+
51+
def render_children(children: list[dict], indent: int, path_prefix: str) -> list[str]:
52+
"""Render a list of nav nodes as indented TOML lines."""
53+
lines = []
54+
pad = " " * indent
55+
for i, child in enumerate(children):
56+
comma = "," if i < len(children) - 1 else ""
57+
if not child["children"]:
58+
path = path_prefix + child["path"]
59+
lines.append(f'{pad}{{ "{child["label"]}" = "{path}" }}{comma}')
60+
else:
61+
lines.append(f'{pad}{{ "{child["label"]}" = [')
62+
lines.extend(render_children(child["children"], indent + 1, path_prefix))
63+
lines.append(f"{pad}] }}{comma}")
64+
return lines
65+
66+
67+
def generate_code_docs_entry(
68+
tree: list[dict], indent: int = 1, path_prefix: str = "reference/"
69+
) -> str:
70+
"""Generate the full 'Code Documentation' nav entry as a TOML string."""
71+
pad = " " * indent
72+
lines = [f'{pad}{{ "Code Documentation" = [']
73+
lines.extend(render_children(tree, indent + 1, path_prefix))
74+
lines.append(f"{pad}] }},")
75+
return "\n".join(lines)
76+
77+
78+
def main() -> None:
79+
args = sys.argv[1:]
80+
summary_path = Path(args[0] if args else "docs/reference/SUMMARY.md")
81+
input_toml = Path(args[1] if len(args) > 1 else "_zensical.toml")
82+
output_toml = Path(args[2] if len(args) > 2 else "zensical.toml")
83+
84+
items = parse_summary(summary_path)
85+
tree = build_tree(items)
86+
code_docs_entry = generate_code_docs_entry(tree)
87+
88+
toml_content = input_toml.read_text()
89+
90+
placeholder_re = re.compile(
91+
r'[ \t]*\{ "Code Documentation" = \[[\s\S]*?\] \},?[ \t]*\n'
92+
)
93+
match = placeholder_re.search(toml_content)
94+
if not match:
95+
sys.exit(1)
96+
97+
result = (
98+
toml_content[: match.start()]
99+
+ code_docs_entry
100+
+ "\n"
101+
+ toml_content[match.end() :]
102+
)
103+
output_toml.write_text(result)
104+
105+
106+
if __name__ == "__main__":
107+
main()

.github/workflows/docs.yaml

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,19 @@ jobs:
3131
pip install uv
3232
uv pip install --system .[docs]
3333
34+
- name: Generate reference pages
35+
run: python .github/scripts/gen_ref_pages.py
36+
37+
- name: Generate zensical.toml
38+
run: python .github/scripts/gen_zensical_toml.py
39+
3440
- name: Build docs
35-
run: mkdocs build
41+
run: zensical build --clean
3642
id: build_docs
3743

38-
- name: Rebuild and deploy docs
39-
run: mkdocs gh-deploy --force
44+
- name: Deploy to GitHub Pages
45+
uses: peaceiris/actions-gh-pages@v4
4046
if: github.ref == 'refs/heads/main' && steps.build_docs.outcome == 'success'
47+
with:
48+
github_token: ${{ secrets.GITHUB_TOKEN }}
49+
publish_dir: ./site

_zensical.toml

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
[project]
2+
site_name = "template"
3+
site_description = "This is the template package!"
4+
site_author = "YourName"
5+
repo_url = "https://github.com/Quantum-Accelerators/template/"
6+
edit_uri = "blob/main/docs/"
7+
use_directory_urls = false
8+
9+
nav = [
10+
{ "Home" = "index.md" },
11+
{ "Overview" = [
12+
"example_docs/intro/why.md",
13+
"example_docs/intro/resources.md"
14+
] },
15+
{ "Setup" = [
16+
"example_docs/setup/prep.md",
17+
"example_docs/setup/name.md",
18+
"example_docs/setup/basics.md"
19+
] },
20+
{ "Installation" = [
21+
"example_docs/installation/pyproject.md",
22+
"example_docs/installation/install.md"
23+
] },
24+
{ "Code" = [
25+
"example_docs/code/source.md",
26+
"example_docs/code/hints.md",
27+
"example_docs/code/tests.md"
28+
] },
29+
{ "Documentation" = [
30+
"example_docs/zensical/docs.md",
31+
"example_docs/zensical/build.md"
32+
] },
33+
{ "GitHub and CI" = [
34+
"example_docs/github/commits.md",
35+
"example_docs/github/workflows.md"
36+
] },
37+
{ "Code Documentation" = [
38+
"reference/"
39+
] },
40+
{ "About" = [
41+
"example_docs/about/changelog.md",
42+
"example_docs/about/conduct.md",
43+
"example_docs/about/license.md"
44+
] }
45+
]
46+
47+
[project.theme]
48+
features = [
49+
"content.action.edit",
50+
"content.code.copy",
51+
"content.code.select",
52+
"content.code.annotate",
53+
"content.tabs.link",
54+
"content.tooltips",
55+
"navigation.footer",
56+
"navigation.path",
57+
"navigation.tracking",
58+
"navigation.sections",
59+
"navigation.top",
60+
"search.highlight",
61+
"search.suggest",
62+
"search.share",
63+
"header.autohide",
64+
"toc.follow"
65+
]
66+
67+
[project.theme.palette]
68+
primary = "orange"
69+
scheme = "slate"
70+
71+
[project.markdown_extensions.abbr]
72+
73+
[project.markdown_extensions.admonition]
74+
75+
[project.markdown_extensions.attr_list]
76+
77+
[project.markdown_extensions.def_list]
78+
79+
[project.markdown_extensions.footnotes]
80+
81+
[project.markdown_extensions.md_in_html]
82+
83+
[project.markdown_extensions.toc]
84+
permalink = true
85+
86+
[project.markdown_extensions.pymdownx.arithmatex]
87+
generic = true
88+
89+
[project.markdown_extensions.pymdownx.betterem]
90+
smart_enable = "all"
91+
92+
[project.markdown_extensions.pymdownx.caret]
93+
94+
[project.markdown_extensions.pymdownx.details]
95+
96+
[project.markdown_extensions.pymdownx.highlight]
97+
anchor_linenums = true
98+
line_spans = "__span"
99+
pygments_lang_class = true
100+
101+
[project.markdown_extensions.pymdownx.inlinehilite]
102+
103+
[project.markdown_extensions.pymdownx.keys]
104+
105+
[project.markdown_extensions.pymdownx.magiclink]
106+
107+
[project.markdown_extensions.pymdownx.mark]
108+
109+
[project.markdown_extensions.pymdownx.snippets]
110+
111+
[project.markdown_extensions.pymdownx.smartsymbols]
112+
113+
[project.markdown_extensions.pymdownx.superfences]
114+
custom_fences = [
115+
{ name = "mermaid", class = "mermaid", format = "pymdownx.superfences.fence_code_format" }
116+
]
117+
118+
[project.markdown_extensions.pymdownx.tabbed]
119+
alternate_style = true
120+
121+
[project.markdown_extensions.pymdownx.tasklist]
122+
custom_checkbox = true
123+
124+
[project.markdown_extensions.pymdownx.tilde]
125+
126+
[project.plugins.search]
127+
separator = '[\\s\\-,:!=\\[\\]()\"`/]+|\\.(?!\\d)|&[lg]t;|(?!\\b)(?=[A-Z][a-z])'
128+
129+
[project.plugins.autorefs]
130+
131+
# https://github.com/zensical/backlog/issues/37
132+
[project.plugins.social]
133+
134+
[project.plugins.offline]
135+
136+
[project.plugins.mkdocstrings]
137+
default_handler = "python"
138+
139+
[project.plugins.mkdocstrings.handlers.python]
140+
inventories = [
141+
"https://docs.python.org/3/objects.inv",
142+
"https://numpy.org/doc/stable/objects.inv"
143+
]
144+
145+
[project.plugins.mkdocstrings.handlers.python.options]
146+
docstring_style = "numpy"
147+
docstring_section_style = "list"
148+
separate_signature = true
149+
merge_init_into_class = true
150+
show_signature_annotations = true
151+
signature_crossrefs = true
152+
show_if_no_docstring = true
153+
154+
[project.plugins.mkdocstrings.handlers.python.options.docstring_options]
155+
ignore_init_summary = true
156+
157+
# https://github.com/zensical/backlog/issues/8
158+
[project.plugins.gen-files]
159+
scripts = ["docs/gen_ref_pages.py"]
160+
161+
# https://github.com/zensical/backlog/issues/13
162+
[project.plugins.literate-nav]
163+
nav_file = "SUMMARY.md"

docs/example_docs/mkdocs/build.md

Lines changed: 0 additions & 23 deletions
This file was deleted.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Building the Docs
2+
3+
!!! Note
4+
5+
This documentation refers to `zensical.toml` at several places. You may find that this repository has a `_zensical.toml` file instead. This is because Zensical is a very new project and still evolving. Before it matures into a fully-functional product, we'll be running a couple of scripts in the `.github/scripts` folder to convert this `_zensical.toml` to a final `zensical.toml` file. This extra manual step will go away soon (as will this note!). For the time being, you can assume that anything you read here in the context of `zensical.toml` applies to `_zensical.toml` interchangeably.
6+
7+
## The `zensical.toml` File
8+
9+
Once you have added your documentation, you will need to update the `/zensical.toml` file with information about how you want to arrange the files. Specifically, you will need to update the `nav` secction of the `zensical.toml` file to point to all your individual `.md` files, organizing them by category.
10+
11+
!!! Note
12+
13+
Keep the `- Code Documentation: reference/` line in the `nav` section of `zensical.toml`. It will automatically transform your docstrings into beautiful documentation! The rest of the `nav` items you can replace.
14+
15+
## The Build Process
16+
17+
To see how your documentation will look in advance, you can build it locally by running the following command in the base directory:
18+
19+
```bash
20+
python .github/scripts/gen_ref_pages.py
21+
python .github/scripts/gen_zensical_toml.py
22+
zensical serve
23+
```
24+
25+
A URL will be printed out that you can open in your browser.
26+
27+
## Deploying the Docs
28+
29+
To allow your documentation to be visible via GitHub Pages, go to "Settings > Pages" in your repository's settings and make sure "Branch" is set to "gh-pages" instead of "main".

0 commit comments

Comments
 (0)