summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Woehlke <matthew.woehlke@kitware.com>2023-02-24 20:01:14 (GMT)
committerMatthew Woehlke <matthew.woehlke@kitware.com>2023-03-03 22:05:02 (GMT)
commit74e3c1d313616b5faf83ff423c8e8ef3f2010a41 (patch)
tree7f24ecc7db552a3e658a742848cdc0f408c06c7a
parentc09b7604841448b0c949e5c98ea791c9dc9d477e (diff)
downloadCMake-74e3c1d313616b5faf83ff423c8e8ef3f2010a41.zip
CMake-74e3c1d313616b5faf83ff423c8e8ef3f2010a41.tar.gz
CMake-74e3c1d313616b5faf83ff423c8e8ef3f2010a41.tar.bz2
Utilities/Sphinx: Add a directive to document command signatures
Add a `signature` directive to offer a CMake version of Sphinx's `function` directive, similar to that found in other domains (py, cpp, etc.). Like others, this takes one or more signatures as arguments and creates dt/dd nodes from the signatures and the directive contents.
-rw-r--r--Help/dev/documentation.rst95
-rw-r--r--Source/cmRST.cxx2
-rw-r--r--Tests/CMakeLib/testRST.expect8
-rw-r--r--Tests/CMakeLib/testRST.rst8
-rw-r--r--Utilities/Sphinx/cmake.py85
-rw-r--r--Utilities/Sphinx/static/cmake.css23
6 files changed, 204 insertions, 17 deletions
diff --git a/Help/dev/documentation.rst b/Help/dev/documentation.rst
index cd9f784..a340739 100644
--- a/Help/dev/documentation.rst
+++ b/Help/dev/documentation.rst
@@ -241,6 +241,69 @@ Document a "genex" object:
The directive requires a single argument, the generator expression name.
+``signature`` directive
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Document `CMake Command Signatures <Style: CMake Command Signatures_>`_
+within a ``Help/command/<command-name>.rst`` document.
+
+.. code-block:: rst
+
+ .. signature:: <command-name>(<signature>)
+
+ This indented block documents one or more signatures of a CMake command.
+
+The ``signature`` directive requires one argument, the signature summary:
+
+* One or more signatures must immediately follow the ``::``.
+ The first signature may optionally be placed on the same line.
+ A blank line following the ``signature`` directive will result in a
+ documentation generation error: ``1 argument(s) required, 0 supplied``.
+
+* Signatures may be split across multiple lines, but the final ``)`` of each
+ signature must be the last character on its line.
+
+* Blank lines between signatures are not allowed. (Content after a blank line
+ is treated as part of the description.)
+
+* Whitespace in signatures is not preserved. To document a complex signature,
+ 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:
+
+* Default target names are automatically extracted from leading "keyword"
+ arguments in the signatures, where a keyword is any sequence of
+ non-space starting with a letter. For example, the signature
+ ``string(REGEX REPLACE <match-regex> ...)`` generates the target
+ ``REGEX REPLACE``, similar to ``.. _`REGEX REPLACE`:``.
+
+* Custom target names may be specified using a ``:target:`` option.
+ For example:
+
+ .. code-block:: rst
+
+ .. signature::
+ cmake_path(GET <path-var> ROOT_NAME <out-var>)
+ cmake_path(GET <path-var> ROOT_PATH <out-var>)
+ :target:
+ GET ROOT_NAME
+ GET ROOT_PATH
+
+ Provide a custom target name for each signature, one per line.
+ The first target may optionally be placed on the same line as ``:target:``.
+
+* If a target name is already in use earlier in the document, no hyperlink
+ target will be generated.
+
+* 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.
+
+The directive treats its content as the documentation of the signature(s).
+Indent the signature documentation accordingly.
+
``variable`` directive
^^^^^^^^^^^^^^^^^^^^^^
@@ -374,11 +437,11 @@ paragraph.
Style: CMake Command Signatures
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Command signatures should be marked up as plain literal blocks, not as
-cmake ``code-blocks``.
-
-Signatures are separated from preceding content by a section header.
-That is, use:
+A ``Help/command/<command-name>.rst`` document defines one ``command``
+object in the `CMake Domain`_, but some commands have multiple signatures.
+Use the CMake Domain's `signature directive`_ to document each signature.
+Separate signatures from preceding content by a section header.
+For example:
.. code-block:: rst
@@ -387,17 +450,23 @@ That is, use:
Normal Libraries
^^^^^^^^^^^^^^^^
- ::
-
+ .. signature::
add_library(<lib> ...)
- This signature is used for ...
+ This signature is used for ...
+
+Use the following conventions in command signature documentation:
+
+* Use an angle-bracket ``<placeholder>`` for arguments to be specified
+ by the caller. Refer to them in prose using
+ `inline literal <Style: Inline Literals_>`_ syntax.
+
+* Wrap optional parts with square brackets.
+
+* Mark repeatable parts with a trailing ellipsis (``...``).
-Signatures of commands should wrap optional parts with square brackets,
-and should mark list of optional arguments with an ellipsis (``...``).
-Elements of the signature which are specified by the user should be
-specified with angle brackets, and may be referred to in prose using
-``inline-literal`` syntax.
+The ``signature`` directive may be used multiple times for different
+signatures of the same command.
Style: Boolean Constants
^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Source/cmRST.cxx b/Source/cmRST.cxx
index fa9d4cc..71b77e0 100644
--- a/Source/cmRST.cxx
+++ b/Source/cmRST.cxx
@@ -20,7 +20,7 @@ cmRST::cmRST(std::ostream& os, std::string docroot)
: OS(os)
, DocRoot(std::move(docroot))
, CMakeDirective("^.. (cmake:)?("
- "command|envvar|genex|variable"
+ "command|envvar|genex|signature|variable"
")::[ \t]+([^ \t\n]+)$")
, CMakeModuleDirective("^.. cmake-module::[ \t]+([^ \t\n]+)$")
, ParsedLiteralDirective("^.. parsed-literal::[ \t]*(.*)$")
diff --git a/Tests/CMakeLib/testRST.expect b/Tests/CMakeLib/testRST.expect
index 5e3cdb1..076562a 100644
--- a/Tests/CMakeLib/testRST.expect
+++ b/Tests/CMakeLib/testRST.expect
@@ -70,6 +70,14 @@ Bracket Comment Content
Generator expression $<OTHER_GENEX> description.
+.. cmake:signature:: some_command(SOME_SIGNATURE)
+
+ Command some_command SOME_SIGNATURE description.
+
+.. signature:: other_command(OTHER_SIGNATURE)
+
+ Command other_command OTHER_SIGNATURE description.
+
.. cmake:variable:: some_var
Variable some_var description.
diff --git a/Tests/CMakeLib/testRST.rst b/Tests/CMakeLib/testRST.rst
index 4139801..43b08da 100644
--- a/Tests/CMakeLib/testRST.rst
+++ b/Tests/CMakeLib/testRST.rst
@@ -73,6 +73,14 @@ Inline literal ``__`` followed by inline link `Link Text <InternalDest_>`_.
Generator expression $<OTHER_GENEX> description.
+.. cmake:signature:: some_command(SOME_SIGNATURE)
+
+ Command some_command SOME_SIGNATURE description.
+
+.. signature:: other_command(OTHER_SIGNATURE)
+
+ Command other_command OTHER_SIGNATURE description.
+
.. cmake:variable:: some_var
Variable some_var description.
diff --git a/Utilities/Sphinx/cmake.py b/Utilities/Sphinx/cmake.py
index e9f0dc5..9043709 100644
--- a/Utilities/Sphinx/cmake.py
+++ b/Utilities/Sphinx/cmake.py
@@ -16,6 +16,9 @@ from pygments.lexers import CMakeLexer
from pygments.token import Name, Operator, Punctuation, String, Text, Comment, Generic, Whitespace, Number
from pygments.lexer import bygroups
+# RE to split multiple command signatures
+sig_end_re = re.compile(r'(?<=[)])\n')
+
# Notes on regular expressions below:
# - [\.\+-] are needed for string constants like gtk+-2.0
# - Unix paths are recognized by '/'; support for Windows paths may be added if needed
@@ -57,14 +60,16 @@ CMakeLexer.tokens["root"] = [
# (r'[^<>\])\}\|$"# \t\n]+', Name.Exception), # fallback, for debugging only
]
+from docutils.utils.code_analyzer import Lexer, LexerError
from docutils.parsers.rst import Directive, directives
from docutils.transforms import Transform
from docutils import io, nodes
-from sphinx.directives import ObjectDescription
+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 import addnodes
sphinx_before_1_4 = False
@@ -286,9 +291,9 @@ class CMakeObject(ObjectDescription):
def add_target_and_index(self, name, sig, signode):
if self.objtype == 'command':
- targetname = name.lower()
+ targetname = name.lower()
else:
- targetname = name
+ targetname = name
targetid = '%s:%s' % (self.objtype, targetname)
if targetid not in self.state.document.ids:
signode['names'].append(targetid)
@@ -302,6 +307,79 @@ class CMakeObject(ObjectDescription):
if make_index_entry:
self.indexnode['entries'].append(make_index_entry(name, targetid))
+class CMakeSignatureObject(CMakeObject):
+ object_type = 'signature'
+
+ option_spec = {
+ 'target': directives.unchanged,
+ }
+
+ def get_signatures(self):
+ content = nl_escape_re.sub('', self.arguments[0])
+ lines = sig_end_re.split(content)
+ return [ws_re.sub(' ', line.strip()) for line in lines]
+
+ def handle_signature(self, sig, signode):
+ language = 'cmake'
+ classes = ['code', 'cmake', 'highlight']
+
+ node = addnodes.desc_name(sig, '', classes=classes)
+
+ try:
+ tokens = Lexer(sig, language, 'short')
+ except LexerError as error:
+ if self.state.document.settings.report_level > 2:
+ # Silently insert without syntax highlighting.
+ tokens = Lexer(sig, language, 'none')
+ else:
+ raise self.warning(error)
+
+ for classes, value in tokens:
+ if classes:
+ node += nodes.inline(value, value, classes=classes)
+ else:
+ node += nodes.Text(value)
+
+ signode.clear()
+ signode += node
+
+ return sig
+
+ def __init__(self, *args, **kwargs):
+ self.targetnames = {}
+ super().__init__(*args, **kwargs)
+
+ def add_target_and_index(self, name, sig, signode):
+ if name in self.targetnames:
+ targetname = self.targetnames[name].lower()
+ else:
+ def extract_keywords(params):
+ for p in params:
+ if p[0].isalpha():
+ yield p
+ else:
+ return
+
+ keywords = extract_keywords(name.split('(')[1].split())
+ targetname = ' '.join(keywords).lower()
+ targetid = nodes.make_id(targetname)
+
+ if targetid not in self.state.document.ids:
+ signode['names'].append(targetname)
+ signode['ids'].append(targetid)
+ signode['first'] = (not self.names)
+ self.state.document.note_explicit_target(signode)
+
+ def run(self):
+ targets = self.options.get('target')
+ if targets is not None:
+ signatures = self.get_signatures()
+ targets = [t.strip() for t in targets.split('\n')]
+ for signature, target in zip(signatures, targets):
+ self.targetnames[signature] = target
+
+ return super().run()
+
class CMakeXRefRole(XRefRole):
# See sphinx.util.nodes.explicit_title_re; \x00 escapes '<'.
@@ -411,6 +489,7 @@ class CMakeDomain(Domain):
'command': CMakeObject,
'envvar': CMakeObject,
'genex': CMakeObject,
+ 'signature': CMakeSignatureObject,
'variable': CMakeObject,
# Other `object_types` cannot be created except by the `CMakeTransform`
}
diff --git a/Utilities/Sphinx/static/cmake.css b/Utilities/Sphinx/static/cmake.css
index 324cd92..dd0dd02 100644
--- a/Utilities/Sphinx/static/cmake.css
+++ b/Utilities/Sphinx/static/cmake.css
@@ -17,6 +17,29 @@ div.sphinxsidebarwrapper {
background-color: #dfdfdf;
}
+/* Apply <pre> style (from classic.css) to signature directive argument. */
+.signature .sig {
+ padding: 5px;
+ background-color: #eeeeee;
+ color: #333333;
+ line-height: 120%;
+ border: 1px solid #ac9;
+ border-left: none;
+ border-right: none;
+}
+
+/* Add additional styling to signature directive argument. */
+.signature .sig {
+ margin-bottom: 5px;
+ padding-left: calc(5px + 3em);
+ text-indent: -3em;
+ font-family: monospace;
+}
+
+.signature .sig .code.sig-name {
+ font-weight: normal;
+}
+
/* Remove unwanted margin in case list item contains a div-wrapping
directive like `.. versionadded` or `.. deprecated`. */
dd > :first-child > p {