summaryrefslogtreecommitdiff
path: root/Utilities
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2023-03-10 16:09:52 +0000
committerKitware Robot <kwrobot@kitware.com>2023-03-10 11:09:59 -0500
commit9db40bec4e1004baa8b397c8b52c248d8de71f67 (patch)
tree044bc41b03ae184fbdc571eccc772a2c7d2d4c17 /Utilities
parent46d5349e8fcfc3966f45b8b2ed586a59d3313eba (diff)
parentcc21d0e47802fb1d054eb56b8bb4bd8ee66f9caa (diff)
downloadcmake-9db40bec4e1004baa8b397c8b52c248d8de71f67.tar.gz
Merge topic 'signature-refs'
cc21d0e478 Utilities/Sphinx: Make signatures linkable 37e015d4a6 Utilities/Sphinx: Refactor Sphinx reference recording Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !8305
Diffstat (limited to 'Utilities')
-rw-r--r--Utilities/Sphinx/cmake.py96
1 files changed, 68 insertions, 28 deletions
diff --git a/Utilities/Sphinx/cmake.py b/Utilities/Sphinx/cmake.py
index 9043709f9f..a07b6e9091 100644
--- a/Utilities/Sphinx/cmake.py
+++ b/Utilities/Sphinx/cmake.py
@@ -4,6 +4,9 @@
import os
import re
+from dataclasses import dataclass
+from typing import Any, cast
+
# Override much of pygments' CMakeLexer.
# We need to parse CMake syntax definitions, not CMake code.
@@ -69,9 +72,11 @@ from sphinx.directives import ObjectDescription, nl_escape_re
from sphinx.domains import Domain, ObjType
from sphinx.roles import XRefRole
from sphinx.util.nodes import make_refnode
-from sphinx.util import ws_re
+from sphinx.util import logging, ws_re
from sphinx import addnodes
+logger = logging.getLogger(__name__)
+
sphinx_before_1_4 = False
sphinx_before_1_7_2 = False
try:
@@ -104,6 +109,14 @@ if sphinx_before_1_7_2:
return new_items
QtHelpBuilder.build_keywords = new_build_keywords
+@dataclass
+class ObjectEntry:
+ docname: str
+ objtype: str
+ node_id: str
+ name: str
+
+
class CMakeModule(Directive):
required_arguments = 1
optional_arguments = 0
@@ -205,14 +218,6 @@ _cmake_index_objs = {
'variable': _cmake_index_entry('variable'),
}
-def _cmake_object_inventory(env, document, line, objtype, targetid):
- inv = env.domaindata['cmake']['objects']
- if targetid in inv:
- document.reporter.warning(
- 'CMake object "%s" also described in "%s".' %
- (targetid, env.doc2path(inv[targetid][0])), line=line)
- inv[targetid] = (env.docname, objtype)
-
class CMakeTransform(Transform):
# Run this transform early since we insert nodes we want
@@ -275,8 +280,10 @@ class CMakeTransform(Transform):
indexnode = addnodes.index()
indexnode['entries'] = [make_index_entry(title, targetid)]
self.document.insert(0, indexnode)
+
# Add to cmake domain object inventory
- _cmake_object_inventory(env, self.document, 1, objtype, targetid)
+ domain = cast(CMakeDomain, env.get_domain('cmake'))
+ domain.note_object(objtype, targetname, targetid, targetid)
class CMakeObject(ObjectDescription):
@@ -300,8 +307,10 @@ class CMakeObject(ObjectDescription):
signode['ids'].append(targetid)
signode['first'] = (not self.names)
self.state.document.note_explicit_target(signode)
- _cmake_object_inventory(self.env, self.state.document,
- self.lineno, self.objtype, targetid)
+
+ domain = cast(CMakeDomain, self.env.get_domain('cmake'))
+ domain.note_object(self.objtype, targetname, targetid, targetid,
+ location=signode)
make_index_entry = _cmake_index_objs.get(self.objtype)
if make_index_entry:
@@ -351,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:
@@ -361,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:
@@ -370,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:
@@ -384,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:
@@ -452,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)
@@ -518,25 +537,46 @@ class CMakeDomain(Domain):
def clear_doc(self, docname):
to_clear = set()
- for fullname, (fn, _) in self.data['objects'].items():
- if fn == docname:
+ for fullname, obj in self.data['objects'].items():
+ if obj.docname == docname:
to_clear.add(fullname)
for fullname in to_clear:
del self.data['objects'][fullname]
def resolve_xref(self, env, fromdocname, builder,
typ, target, node, contnode):
- targetid = '%s:%s' % (typ, target)
+ 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
- return make_refnode(builder, fromdocname, obj[0], targetid,
+
+ return make_refnode(builder, fromdocname, obj.docname, obj.node_id,
contnode, target)
+ def note_object(self, objtype: str, name: str, target_id: str,
+ node_id: str, location: Any = None):
+ if target_id in self.data['objects']:
+ other = self.data['objects'][target_id].docname
+ logger.warning(
+ f'CMake object {target_id!r} also described in {other!r}',
+ location=location)
+
+ self.data['objects'][target_id] = ObjectEntry(
+ self.env.docname, objtype, node_id, name)
+
def get_objects(self):
- for refname, (docname, type) in self.data['objects'].items():
- yield (refname, refname, type, docname, refname, 1)
+ for refname, obj in self.data['objects'].items():
+ yield (refname, obj.name, obj.objtype, obj.docname, obj.node_id, 1)
def setup(app):
app.add_directive('cmake-module', CMakeModule)