@@ -9,10 +9,12 @@
AbstractSet,
Any,
Dict,
+ NamedTuple,
Tuple,
)
from sphinx.domains import Domain, ObjType
+from sphinx.locale import __
from sphinx.util import logging
@@ -22,22 +24,93 @@
logger = logging.getLogger(__name__)
+class ObjectEntry(NamedTuple):
+ docname: str
+ node_id: str
+ objtype: str
+ aliased: bool
+
+
class QAPIDomain(Domain):
"""QAPI language domain."""
name = "qapi"
label = "QAPI"
+ # This table associates cross-reference object types (key) with an
+ # ObjType instance, which defines the valid cross-reference roles
+ # for each object type.
+
+ # Actual table entries for module, command, event, etc will come in
+ # forthcoming commits.
object_types: Dict[str, ObjType] = {}
+
directives = {}
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]:
+ ret = self.data.setdefault("objects", {})
+ return ret # 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: AbstractSet[str], otherdata: Dict[str, Any]
) -> None:
- pass
+ for fullname, obj in otherdata["objects"].items():
+ if obj.docname in docnames:
+ # Sphinx's own python domain doesn't appear to bother to
+ # check for collisions. Assert they don't happen and
+ # we'll fix it if/when the case arises.
+ assert fullname not in self.objects, (
+ "bug - collision on merge?"
+ f" {fullname=} {obj=} {self.objects[fullname]=}"
+ )
+ self.objects[fullname] = obj
def resolve_any_xref(self, *args: Any, **kwargs: Any) -> Any:
# pylint: disable=unused-argument