Files
librsvg/devel-docs/_extensions/internals.py

217 lines
6.4 KiB
Python

"""Custom reStructuredText entities/helpers for referencing entities in the
librsvg internals documentation.
For any changes made in this module, please ensure
``devel-docs/devel_docs_mod_guide.rst`` is updated accordingly, if necessary.
For help/reference on modifying these, see:
- https://www.sphinx-doc.org/en/master/development/tutorials/extending_syntax.html
- https://docutils.sourceforge.io/docs/howto/rst-roles.html
- https://protips.readthedocs.io/link-roles.html
- https://www.sphinx-doc.org/en/master/development/tutorials/adding_domain.html
- https://www.sphinx-doc.org/en/master/extdev/utils.html#sphinx.util.docutils.SphinxRole
- https://www.sphinx-doc.org/en/master/extdev/utils.html#sphinx.util.docutils.ReferenceRole
- https://github.com/sphinx-doc/sphinx/blob/master/sphinx/roles.py
"""
from __future__ import annotations
from docutils.nodes import literal, reference
from sphinx.domains import Domain
from sphinx.util.docutils import ReferenceRole
BASE_URL = "https://gnome.pages.gitlab.gnome.org/librsvg/internals"
class CrateRole(ReferenceRole):
"""A custom Sphinx role for referencing crates in the librsvg internals
documentation.
"""
def run(self):
node = reference(
self.rawtext,
refuri=f"{BASE_URL}/{self.target}/index.html",
**self.options,
)
node += literal(self.rawtext, self.title)
return [node], []
class ModuleRole(ReferenceRole):
"""A custom Sphinx role for referencing modules in the librsvg internals
documentation.
"""
def run(self):
components = self.target.split("::")
try:
*parents, module = components
if not parents:
raise ValueError
except ValueError:
msg = self.inliner.reporter.error(
f"Invalid module target: {self.target!r}", line=self.lineno
)
prb = self.inliner.problematic(self.rawtext, self.rawtext, msg)
return [prb], [msg]
node = reference(
self.rawtext,
refuri=f"{BASE_URL}/{'/'.join(parents)}/{module}/index.html",
**self.options,
)
node += literal(
self.rawtext, self.title if self.has_explicit_title else module
)
return [node], []
class TopLevelRole(ReferenceRole):
"""A custom Sphinx role for referencing top-level entities in the librsvg
internals documentation.
"""
def __init__(self, target_kind: str, *, target_is_callable: bool = False):
super().__init__()
self.target_kind = target_kind
self.target_is_callable = target_is_callable
def run(self):
*parents, item = self.target.split("::")
if not parents:
msg = self.inliner.reporter.error(
f"Invalid {self.target_kind} target: {self.target!r}",
line=self.lineno,
)
prb = self.inliner.problematic(self.rawtext, self.rawtext, msg)
return [prb], [msg]
node = reference(
self.rawtext,
refuri=(
f"{BASE_URL}/{'/'.join(parents)}/{self.target_kind}"
f".{item}.html"
),
**self.options,
)
node += literal(
self.rawtext,
(
self.title if self.has_explicit_title
else f"{item}()" if self.target_is_callable
else item
),
)
return [node], []
class MemberRole(ReferenceRole):
"""A custom Sphinx role for referencing members of structs, enums, etc
in the librsvg internals documentation.
"""
def __init__(
self,
target_kind: str,
parent_tag: str,
member_tag: str,
*,
target_is_callable: bool = False,
):
super().__init__()
self.target_kind = target_kind
self.parent_tag = parent_tag
self.member_tag = member_tag
self.target_is_callable = target_is_callable
def run(self):
show_parent = not self.target.startswith("~")
target = self.target if show_parent else self.target[1:]
components = target.split("::")
try:
*parents, parent, member = components
if not parents:
raise ValueError
except ValueError:
msg = self.inliner.reporter.error(
f"Invalid {self.target_kind} target: {target!r}",
line=self.lineno,
)
prb = self.inliner.problematic(self.rawtext, self.rawtext, msg)
return [prb], [msg]
node = reference(
self.rawtext,
refuri=(
f"{BASE_URL}/{'/'.join(parents)}/{self.parent_tag}"
f".{parent}.html#{self.member_tag}.{member}"
),
**self.options,
)
if not self.has_explicit_title:
title = f"{parent}::{member}" if show_parent else member
node += literal(
self.rawtext,
(
self.title if self.has_explicit_title
else f"{title}()" if self.target_is_callable
else title
),
)
return [node], []
class InternalsDomain(Domain):
"""A custom Sphinx domain for referencing the librsvg internals docs."""
name = "internals"
label = "Librsvg Internals Docs"
roles = {
"crate": CrateRole(),
"module": ModuleRole(),
# Top-level entities
"struct": TopLevelRole("struct"),
"enum": TopLevelRole("enum"),
"trait": TopLevelRole("trait"),
"type": TopLevelRole("type"),
"fn": TopLevelRole("fn", target_is_callable=True),
"macro": TopLevelRole("macro"),
"constant": TopLevelRole("constant"),
"static": TopLevelRole("static"),
# Member entities
"struct-field": MemberRole("struct field", "struct", "structfield"),
"struct-method": MemberRole(
"struct method", "struct", "method", target_is_callable=True
),
"enum-variant": MemberRole("enum variant", "enum", "variant"),
"trait-method": MemberRole(
"provided trait method", "trait", "method", target_is_callable=True
),
"trait-tymethod": MemberRole(
"required trait method",
"trait",
"tymethod",
target_is_callable=True,
),
}
def setup(app):
app.add_domain(InternalsDomain)