Browse Source

Merge topic 'sphinx-cref'

302f5171d8 Utilities/Sphinx: Add 'cref' role
bc77ddb90c Utilities/Sphinx: Factor out part of CMakeXRefRole

Acked-by: Kitware Robot <[email protected]>
Acked-by: buildbot <[email protected]>
Merge-request: !8313
Brad King 2 years ago
parent
commit
1ec4c65791

+ 4 - 0
Help/dev/documentation.rst

@@ -395,6 +395,10 @@ object names like ``OUTPUT_NAME_<CONFIG>``.  The form ``a <b>``,
 with a space preceding ``<``, is still interpreted as a link text
 with an explicit target.
 
+Additionally, the ``cref`` role may be used to create references
+to local targets that have literal styling.  This is especially
+useful for referencing a subcommand in the command's documentation.
+
 .. _`list()`: https://cmake.org/cmake/help/latest/command/list.html
 .. _`list(APPEND)`: https://cmake.org/cmake/help/latest/command/list.html
 .. _`list(APPEND) sub-command`: https://cmake.org/cmake/help/latest/command/list.html

+ 1 - 0
Source/cmRST.cxx

@@ -33,6 +33,7 @@ cmRST::cmRST(std::ostream& os, std::string docroot)
   , VersionDirective("^.. version(added|changed)::[ \t]*(.*)$")
   , ModuleRST(R"(^#\[(=*)\[\.rst:$)")
   , CMakeRole("(:cmake)?:("
+              "cref|"
               "command|cpack_gen|generator|genex|"
               "variable|envvar|module|policy|"
               "prop_cache|prop_dir|prop_gbl|prop_inst|prop_sf|"

+ 6 - 1
Tests/CMakeLib/testRST.expect

@@ -26,10 +26,15 @@ Generator expression ``$<SOME_GENEX:...>`` with brackets and parameter.
 Generator expression ``some genex`` with space and target.
 Generator expression ``$<SOME_GENEX>`` with brackets, space, and target.
 Generator expression ``$<SOME_GENEX:...>`` with brackets, parameter, space, and target.
-Inline literal ``~!@#$%^&*( )_+-=\\[]{}'":;,<>.?/``.
+Inline cref ``Link Dest``.
+Inline cref ``Link_Dest_<Placeholder>``.
+Inline cref ``Link Text``.
+Inline cref ``Link_Text_<Placeholder>``.
+Inline link Link Dest.
 Inline link Link Text.
 Inline link Link Text <With \-escaped Brackets>.
 Inline literal ``__`` followed by inline link Link Text.
+Inline literal ``~!@#$%^&*( )_+-=\\[]{}'":;,<>.?/``.
 
 First TOC entry.
 

+ 6 - 1
Tests/CMakeLib/testRST.rst

@@ -33,10 +33,15 @@ Generator expression :genex:`$<SOME_GENEX:...>` with brackets and parameter.
 Generator expression :genex:`some genex <SOME_GENEX>` with space and target.
 Generator expression :genex:`$<SOME_GENEX> <SOME_GENEX>` with brackets, space, and target.
 Generator expression :genex:`$<SOME_GENEX:...> <SOME_GENEX>` with brackets, parameter, space, and target.
-Inline literal ``~!@#$%^&*( )_+-=\\[]{}'":;,<>.?/``.
+Inline cref :cref:`Link Dest`.
+Inline cref :cref:`Link_Dest_<Placeholder>`.
+Inline cref :cref:`Link Text <ExternalDest>`.
+Inline cref :cref:`Link_Text_<Placeholder> <ExternalDest>`.
+Inline link `Link Dest`_.
 Inline link `Link Text <ExternalDest>`_.
 Inline link `Link Text \<With \\-escaped Brackets\> <ExternalDest>`_.
 Inline literal ``__`` followed by inline link `Link Text <InternalDest_>`_.
+Inline literal ``~!@#$%^&*( )_+-=\\[]{}'":;,<>.?/``.
 
 .. |not replaced| replace:: not replaced through toctree
 .. |not replaced in literal| replace:: replaced in parsed literal

+ 46 - 13
Utilities/Sphinx/cmake.py

@@ -5,7 +5,7 @@ import os
 import re
 
 from dataclasses import dataclass
-from typing import Any, List, cast
+from typing import Any, List, Tuple, Type, cast
 
 # Override much of pygments' CMakeLexer.
 # We need to parse CMake syntax definitions, not CMake code.
@@ -66,11 +66,13 @@ CMakeLexer.tokens["root"] = [
 from docutils.utils.code_analyzer import Lexer, LexerError
 from docutils.parsers.rst import Directive, directives
 from docutils.transforms import Transform
+from docutils.nodes import Element, Node, TextElement, system_message
 from docutils import io, nodes
 
 from sphinx.directives import ObjectDescription, nl_escape_re
 from sphinx.domains import Domain, ObjType
 from sphinx.roles import XRefRole
+from sphinx.util.docutils import ReferenceRole
 from sphinx.util.nodes import make_refnode
 from sphinx.util import logging, ws_re
 from sphinx import addnodes
@@ -482,15 +484,53 @@ class CMakeSignatureObject(CMakeObject):
 
         return super().run()
 
-class CMakeXRefRole(XRefRole):
-
+class CMakeReferenceRole:
     # See sphinx.util.nodes.explicit_title_re; \x00 escapes '<'.
     _re = re.compile(r'^(.+?)(\s*)(?<!\x00)<(.*?)>$', re.DOTALL)
+
+    @staticmethod
+    def _escape_angle_brackets(text: str) -> str:
+        # CMake cross-reference targets frequently contain '<' so escape
+        # any explicit `<target>` with '<' not preceded by whitespace.
+        while True:
+            m = CMakeReferenceRole._re.match(text)
+            if m and len(m.group(2)) == 0:
+                text = f'{m.group(1)}\x00<{m.group(3)}>'
+            else:
+                break
+        return text
+
+    def __class_getitem__(cls, parent: Any):
+        class Class(parent):
+            def __call__(self, name: str, rawtext: str, text: str,
+                         *args, **kwargs
+                        ) -> Tuple[List[Node], List[system_message]]:
+                text = CMakeReferenceRole._escape_angle_brackets(text)
+                return super().__call__(name, rawtext, text, *args, **kwargs)
+        return Class
+
+class CMakeCRefRole(CMakeReferenceRole[ReferenceRole]):
+    nodeclass: Type[Element] = nodes.reference
+    innernodeclass: Type[TextElement] = nodes.literal
+    classes: List[str] = ['cmake', 'literal']
+
+    def run(self) -> Tuple[List[Node], List[system_message]]:
+        refnode = self.nodeclass(self.rawtext)
+        self.set_source_info(refnode)
+
+        refnode['refid'] = nodes.make_id(self.target)
+        refnode += self.innernodeclass(self.rawtext, self.title,
+                                       classes=self.classes)
+
+        return [refnode], []
+
+class CMakeXRefRole(CMakeReferenceRole[XRefRole]):
+
     _re_sub = re.compile(r'^([^()\s]+)\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):
+    def __call__(self, typ, rawtext, text, *args, **kwargs):
         if typ == 'cmake:command':
             # Translate a CMake command cross-reference of the form:
             #  `command_name(SUB_COMMAND)`
@@ -508,15 +548,7 @@ class CMakeXRefRole(XRefRole):
             m = CMakeXRefRole._re_guide.match(text)
             if m:
                 text = '%s <%s>' % (m.group(2), text)
-        # CMake cross-reference targets frequently contain '<' so escape
-        # any explicit `<target>` with '<' not preceded by whitespace.
-        while True:
-            m = CMakeXRefRole._re.match(text)
-            if m and len(m.group(2)) == 0:
-                text = '%s\x00<%s>' % (m.group(1), m.group(3))
-            else:
-                break
-        return XRefRole.__call__(self, typ, rawtext, text, *args, **keys)
+        return super().__call__(typ, rawtext, text, *args, **kwargs)
 
     # We cannot insert index nodes using the result_nodes method
     # because CMakeXRefRole is processed before substitution_reference
@@ -601,6 +633,7 @@ class CMakeDomain(Domain):
         # Other `object_types` cannot be created except by the `CMakeTransform`
     }
     roles = {
+        'cref':       CMakeCRefRole(),
         'command':    CMakeXRefRole(fix_parens = True, lowercase = True),
         'cpack_gen':  CMakeXRefRole(),
         'envvar':     CMakeXRefRole(),