1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-21 06:10:16 +08:00

Utilities/Sphinx: Make signatures linkable

Add signatures to the collection of observed objects (which can be
referenced elsewhere). Don't automatically strip parameters from a
:command: reference, as these may now link signatures. (Do, however,
munge them into 'text <ref>' form if they aren't already, as not doing
so adds an extra '()' for some reason.) Correspondingly, change xref
resolution to try to match 'command' when a ref like 'command(args)' is
not matched, so that existing links to commands that have not been
converted to use the new signature directive don't immediately break.
This commit is contained in:
Matthew Woehlke
2023-03-09 11:32:07 -05:00
parent 37e015d4a6
commit cc21d0e478
2 changed files with 32 additions and 13 deletions

View File

@@ -270,8 +270,7 @@ The ``signature`` directive requires one argument, the signature summary:
abbreviate it in the ``signature`` directive argument and specify the full
signature in a ``code-block`` in the description.
The ``signature`` directive generates a document-local hyperlink target
for each signature:
The ``signature`` directive generates a hyperlink target for each signature:
* Default target names are automatically extracted from leading "keyword"
arguments in the signatures, where a keyword is any sequence of
@@ -299,7 +298,8 @@ for each signature:
* The targets may be referenced from within the same document using
```REF`_`` or ```TEXT <REF_>`_`` syntax. Like reStructuredText section
headers, the targets do not work with Sphinx ``:ref:`` syntax.
headers, the targets do not work with Sphinx ``:ref:`` syntax, however
they can be globally referenced using e.g. ``:command:`string(APPEND)```.
The directive treats its content as the documentation of the signature(s).
Indent the signature documentation accordingly.

View File

@@ -360,7 +360,7 @@ class CMakeSignatureObject(CMakeObject):
def add_target_and_index(self, name, sig, signode):
if name in self.targetnames:
targetname = self.targetnames[name].lower()
sigargs = self.targetnames[name]
else:
def extract_keywords(params):
for p in params:
@@ -370,7 +370,8 @@ class CMakeSignatureObject(CMakeObject):
return
keywords = extract_keywords(name.split('(')[1].split())
targetname = ' '.join(keywords).lower()
sigargs = ' '.join(keywords)
targetname = sigargs.lower()
targetid = nodes.make_id(targetname)
if targetid not in self.state.document.ids:
@@ -379,6 +380,15 @@ class CMakeSignatureObject(CMakeObject):
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
# Register the signature as a command object.
command = name.split('(')[0].lower()
refname = f'{command}({sigargs})'
refid = f'command:{command}({targetname})'
domain = cast(CMakeDomain, self.env.get_domain('cmake'))
domain.note_object('command', name=refname, target_id=refid,
node_id=targetid, location=signode)
def run(self):
targets = self.options.get('target')
if targets is not None:
@@ -393,19 +403,15 @@ class CMakeXRefRole(XRefRole):
# See sphinx.util.nodes.explicit_title_re; \x00 escapes '<'.
_re = re.compile(r'^(.+?)(\s*)(?<!\x00)<(.*?)>$', re.DOTALL)
_re_sub = re.compile(r'^([^()\s]+)\s*\(([^()]*)\)$', re.DOTALL)
_re_ref = re.compile(r'^.*\s<\w+([(][\w\s]+[)])?>$', re.DOTALL)
_re_genex = re.compile(r'^\$<([^<>:]+)(:[^<>]+)?>$', re.DOTALL)
_re_guide = re.compile(r'^([^<>/]+)/([^<>]*)$', re.DOTALL)
def __call__(self, typ, rawtext, text, *args, **keys):
# Translate CMake command cross-references of the form:
# `command_name(SUB_COMMAND)`
# to have an explicit target:
# `command_name(SUB_COMMAND) <command_name>`
if typ == 'cmake:command':
m = CMakeXRefRole._re_sub.match(text)
if m:
text = '%s <%s>' % (text, m.group(1))
m = CMakeXRefRole._re_ref.match(text)
if m is None:
text = f'{text} <{text}>'
elif typ == 'cmake:genex':
m = CMakeXRefRole._re_genex.match(text)
if m:
@@ -461,6 +467,10 @@ class CMakeXRefTransform(Transform):
# Do not index cross-references to guide sections.
continue
if objtype == 'command':
# Index signature references to their parent command.
objname = objname.split('(')[0].lower()
targetnum = env.new_serialno('index-%s:%s' % (objtype, objname))
targetid = 'index-%s-%s:%s' % (targetnum, objtype, objname)
@@ -537,6 +547,15 @@ class CMakeDomain(Domain):
typ, target, node, contnode):
targetid = f'{typ}:{target}'
obj = self.data['objects'].get(targetid)
if obj is None and typ == 'command':
# If 'command(args)' wasn't found, try just 'command'.
# TODO: remove this fallback? warn?
# logger.warning(f'no match for {targetid}')
command = target.split('(')[0]
targetid = f'{typ}:{command}'
obj = self.data['objects'].get(targetid)
if obj is None:
# TODO: warn somehow?
return None