mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 11:46:35 +00:00
fix(docs): let the build resolve yaml pages and directory links
The gettext build emitted 23251 warnings. Three causes carried 22424 of them, and each is answered with the mechanism sphinx supplies. conf.py read .yml and .yaml through the reStructuredText parser, where yaml indentation is a block quote: 16107 warnings, and the pages had no title either. A parser renders them as the literal block they are, and marks them orphan because a yaml file is reference material rather than a chapter, which drops another 3062 toctree warnings. myst builds a document reference only when the link target is_file(), so every link to a directory fell through as unresolvable. A post-transform ahead of myst's resolver points such a link at the directory's README, but only where that README exists, which leaves the genuinely dangling ones reported. Together with myst_heading_anchors, which was never configured, that clears 2921 missing cross-references and 320 python references. Measured per step over four full builds: 23251 -> 7130 -> 4068 -> 827. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
3f212febcb
commit
bd15eae9fa
3 files changed
+111
-2
No files matched your search
@@ -44,8 +44,8 @@ html_theme_options = {
|
||||
source_suffix = {
|
||||
".md": "markdown",
|
||||
".rst": "restructuredtext",
|
||||
".yml": "restructuredtext",
|
||||
".yaml": "restructuredtext",
|
||||
".yml": "yaml",
|
||||
".yaml": "yaml",
|
||||
}
|
||||
|
||||
extensions = [
|
||||
@@ -56,6 +56,8 @@ extensions = [
|
||||
"infinito_docs.extensions.markdown_include",
|
||||
"infinito_docs.extensions.parallel_postprocess",
|
||||
"infinito_docs.extensions.untranslated_yaml",
|
||||
"infinito_docs.extensions.yaml_source",
|
||||
"infinito_docs.extensions.directory_readme",
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.napoleon",
|
||||
]
|
||||
@@ -67,6 +69,7 @@ gettext_compact = "docs"
|
||||
gettext_location = False
|
||||
|
||||
myst_enable_extensions = ["colon_fence"]
|
||||
myst_heading_anchors = 6
|
||||
|
||||
|
||||
def replace_assets_in_doctree(app, doctree, docname):
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
"""Resolve a markdown link to a directory against that directory's README.
|
||||
|
||||
GitHub renders ``[when](when/)`` by opening the folder and showing its README.
|
||||
myst only builds a document reference when the target ``is_file()``, so a folder
|
||||
falls through as an unresolvable reference and reported 2512 of these. Rewriting
|
||||
the links in the sources instead would have made them uglier in the place they
|
||||
already work.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from sphinx import addnodes
|
||||
from sphinx.transforms.post_transforms import SphinxPostTransform
|
||||
|
||||
MYST_RESOLVER_PRIORITY = 9
|
||||
README = "README.md"
|
||||
|
||||
|
||||
class DirectoryReadme(SphinxPostTransform):
|
||||
"""Point a link to a directory at the document its README became."""
|
||||
|
||||
default_priority = MYST_RESOLVER_PRIORITY - 1
|
||||
|
||||
def _docname(self, target: str, refdoc: str) -> str:
|
||||
"""Return the docname of the README backing ``target``, empty when none.
|
||||
|
||||
Args:
|
||||
target: the link destination, without its anchor.
|
||||
refdoc: the document the link was written in.
|
||||
"""
|
||||
_, path = self.env.relfn2path(target, refdoc)
|
||||
readme = Path(path) / README
|
||||
return self.env.path2doc(str(readme)) or "" if readme.is_file() else ""
|
||||
|
||||
def run(self, **kwargs) -> None:
|
||||
"""Rewrite every directory reference that a README backs."""
|
||||
for node in self.document.findall(addnodes.pending_xref):
|
||||
if node.get("reftype") != "myst" or node.get("refdomain") is not None:
|
||||
continue
|
||||
target, _, anchor = node["reftarget"].partition("#")
|
||||
docname = self._docname(target, node.get("refdoc", self.env.docname))
|
||||
if not docname:
|
||||
continue
|
||||
node["refdomain"] = "doc"
|
||||
node["reftarget"] = docname
|
||||
node["reftargetid"] = anchor or None
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_post_transform(DirectoryReadme)
|
||||
return {"version": "1.0", "parallel_read_safe": True}
|
||||
@@ -0,0 +1,53 @@
|
||||
"""Render a YAML source file as the literal block it is.
|
||||
|
||||
Sphinx read ``.yml`` and ``.yaml`` through the reStructuredText parser, where
|
||||
YAML indentation reads as a block quote: 9650 of the build's ``Unexpected
|
||||
indentation`` errors came from that one mapping, and the pages carried no title
|
||||
either, because a YAML file has no heading to find.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from docutils import nodes
|
||||
from sphinx.parsers import Parser
|
||||
|
||||
|
||||
def _orphan() -> nodes.docinfo:
|
||||
"""Return the docinfo Sphinx reads the ``orphan`` metadata flag from."""
|
||||
field = nodes.field()
|
||||
field += nodes.field_name(text="orphan")
|
||||
field += nodes.field_body()
|
||||
info = nodes.docinfo()
|
||||
info += field
|
||||
return info
|
||||
|
||||
|
||||
class YamlParser(Parser):
|
||||
"""Parse a YAML file into a page titled after it, holding its source."""
|
||||
|
||||
supported = ("yaml",)
|
||||
|
||||
def parse(self, inputstring: str, document: nodes.document) -> None:
|
||||
"""Fill ``document`` with the source of the YAML file it was read from.
|
||||
|
||||
The page is marked ``orphan``: a YAML file is reference material reached
|
||||
from a link in prose, not a chapter of the manual, and every one of them
|
||||
would otherwise report itself missing from the toctree.
|
||||
|
||||
Args:
|
||||
inputstring: the file's content.
|
||||
document: the docutils document to populate.
|
||||
"""
|
||||
title = Path(str(document["source"])).name
|
||||
document += _orphan()
|
||||
section = nodes.section(ids=[nodes.make_id(title)], names=[title])
|
||||
section += nodes.title(text=title)
|
||||
section += nodes.literal_block(inputstring, inputstring, language="yaml")
|
||||
document += section
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_source_parser(YamlParser)
|
||||
return {"version": "1.0", "parallel_read_safe": True}
|
||||
Reference in new issue
Block a user