@@ -11,6 +11,7 @@
Dict,
Iterable,
List,
+ NamedTuple,
Tuple,
cast,
)
@@ -20,6 +21,7 @@
from sphinx import addnodes
from sphinx.domains import Domain, ObjType
+from sphinx.locale import _, __
from sphinx.util import logging
from sphinx.util.docutils import SphinxDirective, switch_source_input
from sphinx.util.nodes import make_id, nested_parse_with_titles
@@ -34,6 +36,13 @@
logger = logging.getLogger(__name__)
+class ObjectEntry(NamedTuple):
+ docname: str
+ node_id: str
+ objtype: str
+ aliased: bool
+
+
def _nested_parse(directive: SphinxDirective, content_node: Element) -> None:
"""
This helper preserves error parsing context across sphinx versions.
@@ -101,6 +110,7 @@ class QAPIModule(SphinxDirective):
}
def run(self) -> List[Node]:
+ domain = cast(QAPIDomain, self.env.get_domain("qapi"))
modname = self.arguments[0].strip()
no_index = "no-index" in self.options or "noindex" in self.options
@@ -113,11 +123,14 @@ def run(self) -> List[Node]:
inode = addnodes.index(entries=[])
if not no_index:
+ # note module to the domain
node_id = make_id(self.env, self.state.document, "module", modname)
target = nodes.target("", "", ids=[node_id], ismod=True)
self.set_source_info(target)
self.state.document.note_explicit_target(target)
+ domain.note_object(modname, "module", node_id, location=target)
+
indextext = f"QAPI module; {modname}"
inode = addnodes.index(
entries=[
@@ -148,7 +161,12 @@ class QAPIDomain(Domain):
name = "qapi"
label = "QAPI"
- object_types: Dict[str, ObjType] = {}
+ # This table associates cross-reference object types (key) with an
+ # ObjType instance, which defines the valid cross-reference roles
+ # for each object type.
+ object_types: Dict[str, ObjType] = {
+ "module": ObjType(_("module"), "mod", "obj"),
+ }
# Each of these provides a ReST directive,
# e.g. .. qapi:module:: block-core
@@ -157,11 +175,63 @@ class QAPIDomain(Domain):
}
roles = {}
- initial_data: Dict[str, Dict[str, Tuple[Any]]] = {}
+
+ # Moved into the data property at runtime;
+ # this is the internal index of reference-able objects.
+ initial_data: Dict[str, Dict[str, Tuple[Any]]] = {
+ "objects": {}, # fullname -> ObjectEntry
+ }
+
indices = []
+ @property
+ def objects(self) -> Dict[str, ObjectEntry]:
+ return self.data.setdefault("objects", {}) # type: ignore[no-any-return]
+
+ def note_object(
+ self,
+ name: str,
+ objtype: str,
+ node_id: str,
+ aliased: bool = False,
+ location: Any = None,
+ ) -> None:
+ """Note a QAPI object for cross reference."""
+ if name in self.objects:
+ other = self.objects[name]
+ if other.aliased and aliased is False:
+ # The original definition found. Override it!
+ pass
+ elif other.aliased is False and aliased:
+ # The original definition is already registered.
+ return
+ else:
+ # duplicated
+ logger.warning(
+ __(
+ "duplicate object description of %s, "
+ "other instance in %s, use :no-index: for one of them"
+ ),
+ name,
+ other.docname,
+ location=location,
+ )
+ self.objects[name] = ObjectEntry(self.env.docname, node_id, objtype, aliased)
+
+ def clear_doc(self, docname: str) -> None:
+ for fullname, obj in list(self.objects.items()):
+ if obj.docname == docname:
+ del self.objects[fullname]
+
def merge_domaindata(self, docnames: List[str], otherdata: Dict[str, Any]) -> None:
- pass
+ for fullname, obj in otherdata["objects"].items():
+ if obj.docname in docnames:
+ # FIXME: Unsure of the implications of merge conflicts.
+ # Sphinx's own python domain doesn't appear to bother.
+ assert (
+ fullname not in self.objects
+ ), f"!?!? collision on merge? {fullname=} {obj=} {self.objects[fullname]=}"
+ self.objects[fullname] = obj
def setup(app: Sphinx) -> Dict[str, Any]:
This is the first step towards QAPI domain cross-references and a QAPI reference index. For now, just create the object registry and amend the qapi:module directive to use that registry. Update the merge_domaindata method now that we have actual data we may need to merge. RFC: Much of this code is adapted directly from sphinx.domains.python; it is unclear to me if there ever would be a collision on merge, hence the assertion. I haven't been able to trigger it or find better code to use as a template, so probably I'll just leave the assertion in there. Signed-off-by: John Snow <jsnow@redhat.com> --- docs/sphinx/qapi-domain.py | 76 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 3 deletions(-)