mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-07-27 04:13:53 -06:00

Use the QAPI object registry to generate a special index just for QAPI definitions. The index can show entries both by definition type and all together, alphabetically. The index can be linked from anywhere in the QEMU manual by using the reference `qapi-index`. Signed-off-by: John Snow <jsnow@redhat.com> Message-ID: <20250311034303.75779-7-jsnow@redhat.com> Acked-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
196 lines
5.4 KiB
Python
196 lines
5.4 KiB
Python
"""
|
|
QAPI domain extension.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import (
|
|
TYPE_CHECKING,
|
|
AbstractSet,
|
|
Any,
|
|
Dict,
|
|
Iterable,
|
|
List,
|
|
NamedTuple,
|
|
Optional,
|
|
Tuple,
|
|
)
|
|
|
|
from sphinx.domains import (
|
|
Domain,
|
|
Index,
|
|
IndexEntry,
|
|
ObjType,
|
|
)
|
|
from sphinx.locale import _, __
|
|
from sphinx.util import logging
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
from sphinx.application import Sphinx
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ObjectEntry(NamedTuple):
|
|
docname: str
|
|
node_id: str
|
|
objtype: str
|
|
aliased: bool
|
|
|
|
|
|
class QAPIIndex(Index):
|
|
"""
|
|
Index subclass to provide the QAPI definition index.
|
|
"""
|
|
|
|
# pylint: disable=too-few-public-methods
|
|
|
|
name = "index"
|
|
localname = _("QAPI Index")
|
|
shortname = _("QAPI Index")
|
|
|
|
def generate(
|
|
self,
|
|
docnames: Optional[Iterable[str]] = None,
|
|
) -> Tuple[List[Tuple[str, List[IndexEntry]]], bool]:
|
|
assert isinstance(self.domain, QAPIDomain)
|
|
content: Dict[str, List[IndexEntry]] = {}
|
|
collapse = False
|
|
|
|
# list of all object (name, ObjectEntry) pairs, sorted by name
|
|
# (ignoring the module)
|
|
objects = sorted(
|
|
self.domain.objects.items(),
|
|
key=lambda x: x[0].split(".")[-1].lower(),
|
|
)
|
|
|
|
for objname, obj in objects:
|
|
if docnames and obj.docname not in docnames:
|
|
continue
|
|
|
|
# Strip the module name out:
|
|
objname = objname.split(".")[-1]
|
|
|
|
# Add an alphabetical entry:
|
|
entries = content.setdefault(objname[0].upper(), [])
|
|
entries.append(
|
|
IndexEntry(
|
|
objname, 0, obj.docname, obj.node_id, obj.objtype, "", ""
|
|
)
|
|
)
|
|
|
|
# Add a categorical entry:
|
|
category = obj.objtype.title() + "s"
|
|
entries = content.setdefault(category, [])
|
|
entries.append(
|
|
IndexEntry(objname, 0, obj.docname, obj.node_id, "", "", "")
|
|
)
|
|
|
|
# alphabetically sort categories; type names first, ABC entries last.
|
|
sorted_content = sorted(
|
|
content.items(),
|
|
key=lambda x: (len(x[0]) == 1, x[0]),
|
|
)
|
|
return sorted_content, collapse
|
|
|
|
|
|
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 = {}
|
|
|
|
# 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
|
|
}
|
|
|
|
# Index pages to generate; each entry is an Index class.
|
|
indices = [
|
|
QAPIIndex,
|
|
]
|
|
|
|
@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:
|
|
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
|
|
return []
|
|
|
|
|
|
def setup(app: Sphinx) -> Dict[str, Any]:
|
|
app.setup_extension("sphinx.directives")
|
|
app.add_domain(QAPIDomain)
|
|
|
|
return {
|
|
"version": "1.0",
|
|
"env_version": 1,
|
|
"parallel_read_safe": True,
|
|
"parallel_write_safe": True,
|
|
}
|