Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
5781888
gp-sphinx(autodoc-docutils[domain]): Add docutils cross-reference domain
tony Jun 7, 2026
146ceed
gp-sphinx(autodoc-sphinx[domain]): Add sphinxext cross-reference domain
tony Jun 7, 2026
72a9da6
gp-sphinx(autodoc-docutils[autotransform]): Document docutils transforms
tony Jun 7, 2026
43666c6
gp-sphinx(autodoc-docutils[autoreader]): Document docutils readers
tony Jun 7, 2026
b0d426b
gp-sphinx(autodoc-docutils[autoparser]): Document docutils parsers
tony Jun 7, 2026
6608ceb
gp-sphinx(autodoc-docutils[autowriter]): Document docutils writers
tony Jun 7, 2026
cfb38f8
gp-sphinx(autodoc-docutils[autonode]): Document custom docutils nodes
tony Jun 7, 2026
36834ca
gp-sphinx(autodoc-docutils[autotranslator]): Document translator classes
tony Jun 7, 2026
8ae0931
gp-sphinx(autodoc-sphinx[autobuilder]): Document Sphinx builders
tony Jun 7, 2026
ae4d3a4
gp-sphinx(autodoc-sphinx[autodomain]): Document Sphinx domains
tony Jun 7, 2026
055d0bf
docs(packages[autodoc]): Reference pages and recipes for component au…
tony Jun 7, 2026
5d25e49
docs(CHANGES) Component autodoc for docutils and Sphinx extension points
tony Jun 7, 2026
ffa7af2
gp-sphinx(autodoc-docutils[badges]): Doctest build_kind_badge_group
tony Jun 7, 2026
827340a
gp-sphinx(test[badges]): Guard palette dark-block parity
tony Jun 7, 2026
58ea3e0
gp-sphinx(autodoc-sphinx[badges]): Doctest build_config_badge_group
tony Jun 7, 2026
9823624
gp-sphinx(ux-layout[inline]): Linked-literal and chip-list helpers
tony Jun 7, 2026
c27238f
gp-sphinx(autodoc-docutils[facts]): Link and un-clump component facts
tony Jun 7, 2026
8b174c1
gp-sphinx(autodoc-sphinx[facts]): Link and un-clump builder/domain facts
tony Jun 7, 2026
b82ac08
docs(CHANGES) Linked chips in component autodoc facts
tony Jun 7, 2026
7ec5f21
gp-sphinx(autodoc-docutils[facts]): Link directive and role Python paths
tony Jun 7, 2026
6231f31
gp-sphinx(autodoc-sphinx[facts]): Link confval Registered by
tony Jun 7, 2026
aee2601
gp-sphinx(autodoc-sphinx[facts]): Link confval Type annotations
tony Jun 7, 2026
43215f8
docs(packages[reference]): Document extension setup entry points
tony Jun 7, 2026
7fdd466
docs(CHANGES) Config types and setup entry points in linked facts
tony Jun 7, 2026
a6bde69
gp-sphinx(autodoc-sphinx[facts]): Pygments-highlight container defaults
tony Jun 7, 2026
29454e8
docs(packages[typehints-gp]): Reference page and linked API prose
tony Jun 7, 2026
dec5a6f
docs(packages[examples]): Demo module reference sections
tony Jun 7, 2026
5ee8fee
gp-sphinx(test[collection]): Never collect docs/_build
tony Jun 7, 2026
9f12e71
gp-sphinx(autodoc-docutils[facts]): Link Registered-via and option va…
tony Jun 7, 2026
886229e
docs(CHANGES) Pygments-highlighted container defaults
tony Jun 7, 2026
5f376fb
gp-sphinx(autodoc-docutils[facts]): Link directive-option validators
tony Jun 7, 2026
498b40c
docs(CHANGES) Tighten component autodoc release notes to product level
tony Jun 7, 2026
a568a22
docs(CHANGES) Show docutils role example as literal, not a live ref
tony Jun 7, 2026
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
35 changes: 35 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,41 @@ $ uv add gp-sphinx --prerelease allow

<!-- To maintainers and contributors: Please add notes for the forthcoming version below -->

### What's new

gp-sphinx 0.0.1a29 extends component autodoc to the whole docutils and
Sphinx extension family. Transforms, readers, parsers, writers, custom
nodes, translators, builders, and domains now get the same polished
reference cards that directives, roles, and config values already had —
and documented components cross-link to the APIs they reference.

#### Autodoc for docutils components

`sphinx-autodoc-docutils` now documents transforms, readers, parsers,
writers, custom nodes, and translators. Each type gets a single
directive and a bulk-by-module form (`autotransform` /
`autotransforms`, and siblings), rendering the shared reference card
with a type badge and the component's registry metadata. See
{ref}`sphinx-autodoc-docutils-examples` for live demos. (#53)

#### Autodoc for Sphinx builders and domains

`sphinx-autodoc-sphinx` adds `autobuilder` and `autodomain` (with bulk
forms) alongside its config-value support, documenting `Builder` and
`Domain` subclasses with their registered names, output formats, and
extension surfaces. See {ref}`sphinx-autodoc-sphinx-examples` for live
demos. (#53)

#### Cross-reference roles for documented components

Two Sphinx domains — `docutils` and `sphinxext` — make every documented
component linkable from prose, e.g. `` {docutils:transform}`SanitizeTransform` ``,
with a grouped component index per domain. Component facts (Python
paths, translator classes, config types, registering entry points)
become links to their APIs where a target exists, and unresolved
references warn at build time. See
{ref}`sphinx-autodoc-docutils-reference` for the role tables. (#53)

### Fixes

#### Code comments no longer trigger a font swap on first paint
Expand Down
179 changes: 179 additions & 0 deletions docs/_ext/docutils_demo_components.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
"""Synthetic docutils components for live component-autodoc demos.

Grows one demo class per component type so the
``docs/packages/sphinx-autodoc-docutils`` examples page can exercise
every ``auto*`` directive against realistic metadata.

Examples
--------
>>> DemoReorderTransform.default_priority
760
"""

from __future__ import annotations

import typing as t

from docutils import nodes
from docutils.parsers import Parser
from docutils.readers import standalone
from docutils.transforms import Transform
from docutils.writers import Writer

if t.TYPE_CHECKING:
from sphinx.application import Sphinx
from sphinx.util.typing import ExtensionMetadata


class DemoReorderTransform(Transform):
"""Move demo-badge paragraphs ahead of their sibling paragraphs.

Runs late in the read phase (priority 760) so it sees the fully
parsed document but still precedes reference resolution.
"""

default_priority = 760

def apply(self) -> None:
"""Hoist each ``demo-badge`` paragraph to the front of its parent."""
for paragraph in tuple(self.document.findall(nodes.paragraph)):
if "demo-badge" in paragraph.get("classes", ()):
parent = paragraph.parent
parent.remove(paragraph)
parent.insert(0, paragraph)


class DemoArticleReader(standalone.Reader): # type: ignore[type-arg]
"""Read standalone article sources with the demo transform applied.

Extends the stock standalone reader's transform set with
:class:`DemoReorderTransform` so demo badges surface first.
"""

supported = ("demo-article",)
config_section = "demo article reader"

def get_transforms(self) -> list[type[Transform]]:
"""Return the standalone transforms plus the demo reorderer."""
return [*super().get_transforms(), DemoReorderTransform]


class DemoLineParser(Parser):
"""Parse line-oriented demo sources into one paragraph per line."""

supported = ("demo-lines", "demolines")
config_section = "demo line parser"

def parse(self, inputstring: str, document: nodes.document) -> None:
"""Append one paragraph node per non-empty input line."""
self.setup_parse(inputstring, document)
for line in inputstring.splitlines():
if line.strip():
document += nodes.paragraph(text=line.strip())
self.finish_parse()


class demo_marker(nodes.General, nodes.Inline, nodes.Element):
"""Inline marker node rendered as a highlighted ``<mark>`` span."""


def visit_demo_marker(translator: nodes.NodeVisitor, node: demo_marker) -> None:
"""Open the ``<mark>`` wrapper for a demo marker node."""
translator.body.append("<mark>") # type: ignore[attr-defined]


def depart_demo_marker(translator: nodes.NodeVisitor, node: demo_marker) -> None:
"""Close the ``<mark>`` wrapper for a demo marker node."""
translator.body.append("</mark>") # type: ignore[attr-defined]


class DemoTextTranslator(nodes.NodeVisitor):
"""Translate paragraphs into plain text lines for the demo writer."""

def __init__(self, document: nodes.document) -> None:
super().__init__(document)
self.lines: list[str] = []

def visit_paragraph(self, node: nodes.paragraph) -> None:
"""Open a fresh output line."""
self.lines.append("")

def depart_paragraph(self, node: nodes.paragraph) -> None:
"""Close the current output line."""

def visit_Text(self, node: nodes.Text) -> None:
"""Append literal text to the current line."""
if self.lines:
self.lines[-1] += node.astext()

def unknown_visit(self, node: nodes.Node) -> None:
"""Ignore nodes the demo writer does not understand."""

def unknown_departure(self, node: nodes.Node) -> None:
"""Ignore nodes the demo writer does not understand."""


class DemoPlainWriter(Writer): # type: ignore[type-arg]
"""Write documents as plain text lines, one paragraph per line.

Assigns ``translator_class`` in ``__init__`` (the django-docutils
style) rather than as a class attribute, which exercises the
defensive resolution the ``autowriter`` directive performs.
"""

supported = ("demo-plain",)
config_section = "demo plain writer"

def __init__(self) -> None:
super().__init__()
self.translator_class = DemoTextTranslator

def translate(self) -> None:
"""Visit the document and join the collected lines."""
document = self.document
if document is None:
self.output = ""
return
visitor = DemoTextTranslator(document)
document.walkabout(visitor)
self.output = "\n".join(visitor.lines)


def setup(app: Sphinx) -> ExtensionMetadata:
"""Register the demo components with Sphinx.

Examples
--------
>>> class FakeApp:
... def __init__(self) -> None:
... self.calls: list[tuple[str, object]] = []
... def add_transform(self, cls: object) -> None:
... self.calls.append(("add_transform", cls))
... def add_source_parser(self, cls: object) -> None:
... self.calls.append(("add_source_parser", cls))
... def add_node(self, cls: object, **kwargs: object) -> None:
... self.calls.append(("add_node", cls))
... def set_translator(self, name: str, cls: object, **kwargs: object) -> None:
... self.calls.append(("set_translator", cls))
>>> fake = FakeApp()
>>> metadata = setup(fake) # type: ignore[arg-type]
>>> ("add_transform", DemoReorderTransform) in fake.calls
True
>>> ("add_source_parser", DemoLineParser) in fake.calls
True
>>> ("add_node", demo_marker) in fake.calls
True
>>> ("set_translator", DemoTextTranslator) in fake.calls
True
>>> metadata["parallel_read_safe"]
True
"""
app.add_transform(DemoReorderTransform)
app.add_source_parser(DemoLineParser)
app.add_node(demo_marker, html=(visit_demo_marker, depart_demo_marker))
app.set_translator("demo-plain", DemoTextTranslator, override=True)
return {
"version": "0.0.0",
"parallel_read_safe": True,
"parallel_write_safe": True,
}
101 changes: 101 additions & 0 deletions docs/_ext/sphinx_demo_builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
"""Synthetic Sphinx extension components for live autodoc demos.

Grows one demo class per component type so the
``docs/packages/sphinx-autodoc-sphinx`` examples page can exercise the
``autobuilder`` and ``autodomain`` directives against realistic
metadata.

Examples
--------
>>> DemoArchiveBuilder.name
'demo-archive'
"""

from __future__ import annotations

import typing as t

from sphinx.builders import Builder
from sphinx.domains import Domain, ObjType
from sphinx.locale import _
from sphinx.roles import XRefRole

if t.TYPE_CHECKING:
from collections.abc import Iterator, Set

from docutils import nodes
from sphinx.application import Sphinx
from sphinx.util.typing import ExtensionMetadata


class DemoArchiveBuilder(Builder):
"""Bundle every rendered page into one archive artifact.

A deliberately small builder: it reports all documents as outdated,
writes nothing per page, and exists so the autodoc output has a
realistic name/format/image-type surface to display.
"""

name = "demo-archive"
format = "archive"
epilog = "The demo archive is in %(outdir)s."
supported_image_types: list[str] = ["image/svg+xml", "image/png"] # noqa: RUF012 — matches upstream sphinx.builders.Builder shape

def get_outdated_docs(self) -> Iterator[str]:
"""Report every document as outdated."""
yield from self.env.found_docs

def get_target_uri(self, docname: str, typ: str | None = None) -> str:
"""Return the in-archive URI for a document."""
return f"{docname}.txt"

def prepare_writing(self, docnames: Set[str]) -> None:
"""No writer state is needed for the demo."""

def write_doc(self, docname: str, doctree: nodes.document) -> None:
"""Skip per-document output; the demo archives nothing."""


class DemoTopicDomain(Domain):
"""Describe demo topics with one object type and matching role."""

name = "demotopic"
label = "Demo topics"

object_types = { # noqa: RUF012 — matches upstream sphinx.domains.Domain shape
"topic": ObjType(_("topic"), "topic"),
}

roles = { # noqa: RUF012 — XRefRole instances are safe to share across domains
"topic": XRefRole(),
}


def setup(app: Sphinx) -> ExtensionMetadata:
"""Register the demo extension components with Sphinx.

Examples
--------
>>> class FakeApp:
... def __init__(self) -> None:
... self.calls: list[tuple[str, object]] = []
... def add_builder(self, cls: object) -> None:
... self.calls.append(("add_builder", cls))
... def add_domain(self, cls: object) -> None:
... self.calls.append(("add_domain", cls))
>>> fake = FakeApp()
>>> metadata = setup(fake) # type: ignore[arg-type]
>>> ("add_builder", DemoArchiveBuilder) in fake.calls
True
>>> ("add_domain", DemoTopicDomain) in fake.calls
True
>>> metadata["parallel_read_safe"]
True
"""
app.add_builder(DemoArchiveBuilder)
app.add_domain(DemoTopicDomain)
return {
"version": "0.0.0",
"parallel_read_safe": True,
"parallel_write_safe": True,
}
6 changes: 6 additions & 0 deletions docs/packages/sphinx-autodoc-api-style/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,9 @@ This extension uses:
| `deprecated` | `SAB.STATE_DEPRECATED` | `gp-sphinx-badge--state-deprecated` |

See {doc}`/packages/sphinx-ux-badges/index` for the full shared palette.

## Extension entry point

```{eval-rst}
.. autofunction:: sphinx_autodoc_api_style.setup
```
6 changes: 6 additions & 0 deletions docs/packages/sphinx-autodoc-argparse/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,9 @@ for program-scoped clarity.
```{eval-rst}
.. autoconfigvalues:: sphinx_autodoc_argparse.exemplar
```

## Extension entry point

```{eval-rst}
.. autofunction:: sphinx_autodoc_argparse.setup
```
Loading
Loading