Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/scripts/gen_ref_pages.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""
Generate docs/reference/ files to disk.
This is roughly equivalent to what the `mkdocs-gen-files` plugin would have done.
"""

from __future__ import annotations

from pathlib import Path

out_dir = Path("docs/reference")
out_dir.mkdir(parents=True, exist_ok=True)

nav_lines = []
seen_dirs: set[tuple[str, ...]] = set()

for path in sorted(Path("src").rglob("*.py")):
module_path = path.relative_to("src").with_suffix("")
doc_path = path.relative_to("src").with_suffix(".md")
full_doc_path = out_dir / doc_path

parts = tuple(module_path.parts)
ignore = ["_cli", "_version", "__init__", "__main__"]
if any(p in ignore for p in parts):
continue

full_doc_path.parent.mkdir(parents=True, exist_ok=True)
full_doc_path.write_text(f"::: {'.'.join(parts)}\n")

# Emit section headers for intermediate directories not yet seen
for i in range(1, len(parts)):
dir_key = parts[:i]
if dir_key not in seen_dirs:
seen_dirs.add(dir_key)
indent = " " * (len(dir_key) - 1)
nav_lines.append(f"{indent}* {dir_key[-1]}\n")

# Emit leaf link
name = parts[-1]
indent = " " * (len(parts) - 1)
nav_lines.append(f"{indent}* [{name}]({doc_path.as_posix()})\n")

(out_dir / "SUMMARY.md").write_text("".join(nav_lines))
107 changes: 107 additions & 0 deletions .github/scripts/gen_zensical_toml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
""" "
Using _zensical.toml as a starting file, generate a zensical.toml with navigation links
generated from `docs/reference/SUMMARY.md`.

This is roughly equivalent to what the `mkdocs-literate-nav` plugin would have done.
"""

from __future__ import annotations

import re
import sys
from pathlib import Path


def parse_summary(path: Path) -> list[tuple[int, str, str | None]]:
"""Parse SUMMARY.md into a flat list of (indent_level, label, path_or_None)."""
items = []
for line in path.read_text().splitlines():
if not line.strip():
continue
n_spaces = len(line) - len(line.lstrip(" "))
indent = n_spaces // 4
rest = line.strip()
if not rest.startswith("* "):
raise ValueError(f"Unexpected line format: {line!r}")
rest = rest[2:]
m = re.match(r"\[([^\]]+)\]\(([^)]+)\)", rest)
if m:
items.append((indent, m.group(1), m.group(2)))
else:
items.append((indent, rest, None))
return items


def build_tree(items: list[tuple[int, str, str | None]]) -> list[dict]:
"""Build a nested tree from flat (indent, label, path) items."""
root: list[dict] = []
# stack entries: (indent_level, children_list)
stack: list[tuple[int, list]] = [(-1, root)]

for indent, label, path in items:
node: dict = {"label": label, "path": path, "children": []}
while stack[-1][0] >= indent:
stack.pop()
stack[-1][1].append(node)
stack.append((indent, node["children"]))

return root


def render_children(children: list[dict], indent: int, path_prefix: str) -> list[str]:
"""Render a list of nav nodes as indented TOML lines."""
lines = []
pad = " " * indent
for i, child in enumerate(children):
comma = "," if i < len(children) - 1 else ""
if not child["children"]:
path = path_prefix + child["path"]
lines.append(f'{pad}{{ "{child["label"]}" = "{path}" }}{comma}')
else:
lines.append(f'{pad}{{ "{child["label"]}" = [')
lines.extend(render_children(child["children"], indent + 1, path_prefix))
lines.append(f"{pad}] }}{comma}")
return lines


def generate_code_docs_entry(
tree: list[dict], indent: int = 1, path_prefix: str = "reference/"
) -> str:
"""Generate the full 'Code Documentation' nav entry as a TOML string."""
pad = " " * indent
lines = [f'{pad}{{ "Code Documentation" = [']
lines.extend(render_children(tree, indent + 1, path_prefix))
lines.append(f"{pad}] }},")
return "\n".join(lines)


def main() -> None:
args = sys.argv[1:]
summary_path = Path(args[0] if args else "docs/reference/SUMMARY.md")
input_toml = Path(args[1] if len(args) > 1 else "_zensical.toml")
output_toml = Path(args[2] if len(args) > 2 else "zensical.toml")

items = parse_summary(summary_path)
tree = build_tree(items)
code_docs_entry = generate_code_docs_entry(tree)

toml_content = input_toml.read_text()

placeholder_re = re.compile(
r'[ \t]*\{ "Code Documentation" = \[[\s\S]*?\] \},?[ \t]*\n'
)
match = placeholder_re.search(toml_content)
if not match:
sys.exit(1)

result = (
toml_content[: match.start()]
+ code_docs_entry
+ "\n"
+ toml_content[match.end() :]
)
output_toml.write_text(result)


if __name__ == "__main__":
main()
15 changes: 12 additions & 3 deletions .github/workflows/docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,19 @@ jobs:
pip install uv
uv pip install --system .[docs]

- name: Generate reference pages
run: python .github/scripts/gen_ref_pages.py

- name: Generate zensical.toml
run: python .github/scripts/gen_zensical_toml.py

- name: Build docs
run: mkdocs build
run: zensical build --clean
id: build_docs

- name: Rebuild and deploy docs
run: mkdocs gh-deploy --force
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
if: github.ref == 'refs/heads/main' && steps.build_docs.outcome == 'success'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./site
163 changes: 163 additions & 0 deletions _zensical.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
[project]
site_name = "template"
site_description = "This is the template package!"
site_author = "YourName"
repo_url = "https://github.com/Quantum-Accelerators/template/"
edit_uri = "blob/main/docs/"
use_directory_urls = false

nav = [
{ "Home" = "index.md" },
{ "Overview" = [
"example_docs/intro/why.md",
"example_docs/intro/resources.md"
] },
{ "Setup" = [
"example_docs/setup/prep.md",
"example_docs/setup/name.md",
"example_docs/setup/basics.md"
] },
{ "Installation" = [
"example_docs/installation/pyproject.md",
"example_docs/installation/install.md"
] },
{ "Code" = [
"example_docs/code/source.md",
"example_docs/code/hints.md",
"example_docs/code/tests.md"
] },
{ "Documentation" = [
"example_docs/zensical/docs.md",
"example_docs/zensical/build.md"
] },
{ "GitHub and CI" = [
"example_docs/github/commits.md",
"example_docs/github/workflows.md"
] },
{ "Code Documentation" = [
"reference/"
] },
{ "About" = [
"example_docs/about/changelog.md",
"example_docs/about/conduct.md",
"example_docs/about/license.md"
] }
]

[project.theme]
features = [
"content.action.edit",
"content.code.copy",
"content.code.select",
"content.code.annotate",
"content.tabs.link",
"content.tooltips",
"navigation.footer",
"navigation.path",
"navigation.tracking",
"navigation.sections",
"navigation.top",
"search.highlight",
"search.suggest",
"search.share",
"header.autohide",
"toc.follow"
]

[project.theme.palette]
primary = "orange"
scheme = "slate"

[project.markdown_extensions.abbr]

[project.markdown_extensions.admonition]

[project.markdown_extensions.attr_list]

[project.markdown_extensions.def_list]

[project.markdown_extensions.footnotes]

[project.markdown_extensions.md_in_html]

[project.markdown_extensions.toc]
permalink = true

[project.markdown_extensions.pymdownx.arithmatex]
generic = true

[project.markdown_extensions.pymdownx.betterem]
smart_enable = "all"

[project.markdown_extensions.pymdownx.caret]

[project.markdown_extensions.pymdownx.details]

[project.markdown_extensions.pymdownx.highlight]
anchor_linenums = true
line_spans = "__span"
pygments_lang_class = true

[project.markdown_extensions.pymdownx.inlinehilite]

[project.markdown_extensions.pymdownx.keys]

[project.markdown_extensions.pymdownx.magiclink]

[project.markdown_extensions.pymdownx.mark]

[project.markdown_extensions.pymdownx.snippets]

[project.markdown_extensions.pymdownx.smartsymbols]

[project.markdown_extensions.pymdownx.superfences]
custom_fences = [
{ name = "mermaid", class = "mermaid", format = "pymdownx.superfences.fence_code_format" }
]

[project.markdown_extensions.pymdownx.tabbed]
alternate_style = true

[project.markdown_extensions.pymdownx.tasklist]
custom_checkbox = true

[project.markdown_extensions.pymdownx.tilde]

[project.plugins.search]
separator = '[\\s\\-,:!=\\[\\]()\"`/]+|\\.(?!\\d)|&[lg]t;|(?!\\b)(?=[A-Z][a-z])'

[project.plugins.autorefs]

# https://github.com/zensical/backlog/issues/37
[project.plugins.social]

[project.plugins.offline]

[project.plugins.mkdocstrings]
default_handler = "python"

[project.plugins.mkdocstrings.handlers.python]
inventories = [
"https://docs.python.org/3/objects.inv",
"https://numpy.org/doc/stable/objects.inv"
]

[project.plugins.mkdocstrings.handlers.python.options]
docstring_style = "numpy"
docstring_section_style = "list"
separate_signature = true
merge_init_into_class = true
show_signature_annotations = true
signature_crossrefs = true
show_if_no_docstring = true

[project.plugins.mkdocstrings.handlers.python.options.docstring_options]
ignore_init_summary = true

# https://github.com/zensical/backlog/issues/8
[project.plugins.gen-files]
scripts = ["docs/gen_ref_pages.py"]

# https://github.com/zensical/backlog/issues/13
[project.plugins.literate-nav]
nav_file = "SUMMARY.md"
23 changes: 0 additions & 23 deletions docs/example_docs/mkdocs/build.md

This file was deleted.

29 changes: 29 additions & 0 deletions docs/example_docs/zensical/build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Building the Docs

!!! Note

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.

## The `zensical.toml` File

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.

!!! Note

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.

## The Build Process

To see how your documentation will look in advance, you can build it locally by running the following command in the base directory:

```bash
python .github/scripts/gen_ref_pages.py
python .github/scripts/gen_zensical_toml.py
zensical serve
```

A URL will be printed out that you can open in your browser.

## Deploying the Docs

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".
Loading
Loading