mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-07-29 13:23:54 -06:00
docs/qapi-domain: add resolve_any_xref()
Add the ability to resolve cross-references using the `any` cross-reference syntax. Adding QAPI-specific cross-reference roles will be added in a forthcoming commit, and will share the same find_obj() helper. (There's less code needed for the generic cross-reference resolver, so it comes first in this series.) Once again, this code is based very heavily on sphinx.domains.python. Signed-off-by: John Snow <jsnow@redhat.com> Message-ID: <20250311034303.75779-8-jsnow@redhat.com> Acked-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
parent
e93d29d27e
commit
dca2f3c471
1 changed files with 93 additions and 3 deletions
|
@ -16,6 +16,9 @@ from typing import (
|
||||||
Tuple,
|
Tuple,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
|
||||||
|
from sphinx.addnodes import pending_xref
|
||||||
from sphinx.domains import (
|
from sphinx.domains import (
|
||||||
Domain,
|
Domain,
|
||||||
Index,
|
Index,
|
||||||
|
@ -24,10 +27,15 @@ from sphinx.domains import (
|
||||||
)
|
)
|
||||||
from sphinx.locale import _, __
|
from sphinx.locale import _, __
|
||||||
from sphinx.util import logging
|
from sphinx.util import logging
|
||||||
|
from sphinx.util.nodes import make_refnode
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from docutils.nodes import Element
|
||||||
|
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
|
from sphinx.builders import Builder
|
||||||
|
from sphinx.environment import BuildEnvironment
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -179,10 +187,92 @@ class QAPIDomain(Domain):
|
||||||
)
|
)
|
||||||
self.objects[fullname] = obj
|
self.objects[fullname] = obj
|
||||||
|
|
||||||
def resolve_any_xref(self, *args: Any, **kwargs: Any) -> Any:
|
def find_obj(
|
||||||
# pylint: disable=unused-argument
|
self, modname: str, name: str, typ: Optional[str]
|
||||||
|
) -> list[tuple[str, ObjectEntry]]:
|
||||||
|
"""
|
||||||
|
Find a QAPI object for "name", perhaps using the given module.
|
||||||
|
|
||||||
|
Returns a list of (name, object entry) tuples.
|
||||||
|
|
||||||
|
:param modname: The current module context (if any!)
|
||||||
|
under which we are searching.
|
||||||
|
:param name: The name of the x-ref to resolve;
|
||||||
|
may or may not include a leading module.
|
||||||
|
:param type: The role name of the x-ref we're resolving, if provided.
|
||||||
|
(This is absent for "any" lookups.)
|
||||||
|
"""
|
||||||
|
if not name:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
names: list[str] = []
|
||||||
|
matches: list[tuple[str, ObjectEntry]] = []
|
||||||
|
|
||||||
|
fullname = name
|
||||||
|
if "." in fullname:
|
||||||
|
# We're searching for a fully qualified reference;
|
||||||
|
# ignore the contextual module.
|
||||||
|
pass
|
||||||
|
elif modname:
|
||||||
|
# We're searching for something from somewhere;
|
||||||
|
# try searching the current module first.
|
||||||
|
# e.g. :qapi:cmd:`query-block` or `query-block` is being searched.
|
||||||
|
fullname = f"{modname}.{name}"
|
||||||
|
|
||||||
|
if typ is None:
|
||||||
|
# type isn't specified, this is a generic xref.
|
||||||
|
# search *all* qapi-specific object types.
|
||||||
|
objtypes: List[str] = list(self.object_types)
|
||||||
|
else:
|
||||||
|
# type is specified and will be a role (e.g. obj, mod, cmd)
|
||||||
|
# convert this to eligible object types (e.g. command, module)
|
||||||
|
# using the QAPIDomain.object_types table.
|
||||||
|
objtypes = self.objtypes_for_role(typ, [])
|
||||||
|
|
||||||
|
if name in self.objects and self.objects[name].objtype in objtypes:
|
||||||
|
names = [name]
|
||||||
|
elif (
|
||||||
|
fullname in self.objects
|
||||||
|
and self.objects[fullname].objtype in objtypes
|
||||||
|
):
|
||||||
|
names = [fullname]
|
||||||
|
else:
|
||||||
|
# exact match wasn't found; e.g. we are searching for
|
||||||
|
# `query-block` from a different (or no) module.
|
||||||
|
searchname = "." + name
|
||||||
|
names = [
|
||||||
|
oname
|
||||||
|
for oname in self.objects
|
||||||
|
if oname.endswith(searchname)
|
||||||
|
and self.objects[oname].objtype in objtypes
|
||||||
|
]
|
||||||
|
|
||||||
|
matches = [(oname, self.objects[oname]) for oname in names]
|
||||||
|
if len(matches) > 1:
|
||||||
|
matches = [m for m in matches if not m[1].aliased]
|
||||||
|
return matches
|
||||||
|
|
||||||
|
def resolve_any_xref(
|
||||||
|
self,
|
||||||
|
env: BuildEnvironment,
|
||||||
|
fromdocname: str,
|
||||||
|
builder: Builder,
|
||||||
|
target: str,
|
||||||
|
node: pending_xref,
|
||||||
|
contnode: Element,
|
||||||
|
) -> List[Tuple[str, nodes.reference]]:
|
||||||
|
results: List[Tuple[str, nodes.reference]] = []
|
||||||
|
matches = self.find_obj(node.get("qapi:module"), target, None)
|
||||||
|
for name, obj in matches:
|
||||||
|
rolename = self.role_for_objtype(obj.objtype)
|
||||||
|
assert rolename is not None
|
||||||
|
role = f"qapi:{rolename}"
|
||||||
|
refnode = make_refnode(
|
||||||
|
builder, fromdocname, obj.docname, obj.node_id, contnode, name
|
||||||
|
)
|
||||||
|
results.append((role, refnode))
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
def setup(app: Sphinx) -> Dict[str, Any]:
|
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||||
app.setup_extension("sphinx.directives")
|
app.setup_extension("sphinx.directives")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue