Sphinx 3.1 introduced namespaces for C cross-references. With this, each C domain type/function declaration is put inside the namespace that was active at the time of its declaration.
To support automatic cross-reference inside C namespaces: - Save the C namespace used in each doc file (if any) at the source-read phase, in the beginning of the Sphinx process. - When making the automarkup, if any namespace was used in the current file, try to cross-reference to the symbol inside of it before trying in the global namespace. To make the first step possible, disable the parallel_read_safe option in Sphinx, since the dictionary that maps the files to the C namespaces can't be concurrently updated. This unfortunately increases the build time of the documentation. Signed-off-by: Nícolas F. R. A. Prado <nfrapr...@protonmail.com> --- Documentation/sphinx/automarkup.py | 130 ++++++++++++++++++----------- 1 file changed, 82 insertions(+), 48 deletions(-) diff --git a/Documentation/sphinx/automarkup.py b/Documentation/sphinx/automarkup.py index 409dbc4100de..bca8cf5f519d 100644 --- a/Documentation/sphinx/automarkup.py +++ b/Documentation/sphinx/automarkup.py @@ -45,6 +45,8 @@ RE_typedef = re.compile(r'\b(typedef)\s+([a-zA-Z_]\w+)', flags=re.ASCII) # RE_doc = re.compile(r'\bDocumentation(/[\w\-_/]+)(\.\w+)*') +RE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$') + # # Reserved C words that we should skip when cross-referencing # @@ -62,6 +64,8 @@ Skipfuncs = [ 'open', 'close', 'read', 'write', 'fcntl', 'mmap', 'select', 'poll', 'fork', 'execve', 'clone', 'ioctl', 'socket' ] +c_namespace = {} + def markup_refs(docname, app, node): t = node.astext() done = 0 @@ -120,30 +124,42 @@ def markup_func_ref_sphinx3(docname, app, match): # # Go through the dance of getting an xref out of the C domain # - target = match.group(2) + base_target = match.group(2) target_text = nodes.Text(match.group(0)) xref = None - if not (target in Skipfuncs or target in Skipnames): - for class_s, reftype_s in zip(class_str, reftype_str): - lit_text = nodes.literal(classes=['xref', 'c', class_s]) - lit_text += target_text - pxref = addnodes.pending_xref('', refdomain = 'c', - reftype = reftype_s, - reftarget = target, modname = None, - classname = None) - # - # XXX The Latex builder will throw NoUri exceptions here, - # work around that by ignoring them. - # - try: - xref = cdom.resolve_xref(app.env, docname, app.builder, - reftype_s, target, pxref, - lit_text) - except NoUri: - xref = None - - if xref: - return xref + possible_targets = [base_target] + # Check if this document has a namespace, and if so, try + # cross-referencing inside it first. + try: + namespace = c_namespace[docname] + except KeyError: + pass + else: + possible_targets.insert(0, namespace + "." + base_target) + + if base_target not in Skipnames: + for target in possible_targets: + if target not in Skipfuncs: + for class_s, reftype_s in zip(class_str, reftype_str): + lit_text = nodes.literal(classes=['xref', 'c', class_s]) + lit_text += target_text + pxref = addnodes.pending_xref('', refdomain = 'c', + reftype = reftype_s, + reftarget = target, modname = None, + classname = None) + # + # XXX The Latex builder will throw NoUri exceptions here, + # work around that by ignoring them. + # + try: + xref = cdom.resolve_xref(app.env, docname, app.builder, + reftype_s, target, pxref, + lit_text) + except NoUri: + xref = None + + if xref: + return xref return target_text @@ -171,34 +187,43 @@ def markup_c_ref(docname, app, match): # # Go through the dance of getting an xref out of the C domain # - target = match.group(2) + base_target = match.group(2) target_text = nodes.Text(match.group(0)) xref = None - if not ((match.re == RE_function and target in Skipfuncs) - or (target in Skipnames)): - lit_text = nodes.literal(classes=['xref', 'c', class_str[match.re]]) - lit_text += target_text - pxref = addnodes.pending_xref('', refdomain = 'c', - reftype = reftype_str[match.re], - reftarget = target, modname = None, - classname = None) - # - # XXX The Latex builder will throw NoUri exceptions here, - # work around that by ignoring them. - # - try: - xref = cdom.resolve_xref(app.env, docname, app.builder, - reftype_str[match.re], target, pxref, - lit_text) - except NoUri: - xref = None - # - # Return the xref if we got it; otherwise just return the plain text. - # - if xref: - return xref + possible_targets = [base_target] + # Check if this document has a namespace, and if so, try + # cross-referencing inside it first. + try: + namespace = c_namespace[docname] + except KeyError: + pass else: - return target_text + possible_targets.insert(0, namespace + "." + base_target) + + if base_target not in Skipnames: + for target in possible_targets: + if not (match.re == RE_function and target in Skipfuncs): + lit_text = nodes.literal(classes=['xref', 'c', class_str[match.re]]) + lit_text += target_text + pxref = addnodes.pending_xref('', refdomain = 'c', + reftype = reftype_str[match.re], + reftarget = target, modname = None, + classname = None) + # + # XXX The Latex builder will throw NoUri exceptions here, + # work around that by ignoring them. + # + try: + xref = cdom.resolve_xref(app.env, docname, app.builder, + reftype_str[match.re], target, pxref, + lit_text) + except NoUri: + xref = None + + if xref: + return xref + + return target_text # # Try to replace a documentation reference of the form Documentation/... with a @@ -246,9 +271,18 @@ def auto_markup(app, doctree, name): if not isinstance(node.parent, nodes.literal): node.parent.replace(node, markup_refs(name, app, node)) +def save_c_namespace(app, docname, source): + lines = iter(source[0].splitlines(True)) + for l in lines: + match = RE_namespace.search(l) + if match: + c_namespace[docname] = match.group(1) + return + def setup(app): + app.connect('source-read', save_c_namespace) app.connect('doctree-resolved', auto_markup) return { - 'parallel_read_safe': True, + 'parallel_read_safe': False, 'parallel_write_safe': True, } -- 2.28.0