diff options
author | Matthew Woehlke <matthew.woehlke@kitware.com> | 2023-02-24 20:01:14 (GMT) |
---|---|---|
committer | Matthew Woehlke <matthew.woehlke@kitware.com> | 2023-03-03 22:05:02 (GMT) |
commit | 74e3c1d313616b5faf83ff423c8e8ef3f2010a41 (patch) | |
tree | 7f24ecc7db552a3e658a742848cdc0f408c06c7a | |
parent | c09b7604841448b0c949e5c98ea791c9dc9d477e (diff) | |
download | CMake-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.rst | 95 | ||||
-rw-r--r-- | Source/cmRST.cxx | 2 | ||||
-rw-r--r-- | Tests/CMakeLib/testRST.expect | 8 | ||||
-rw-r--r-- | Tests/CMakeLib/testRST.rst | 8 | ||||
-rw-r--r-- | Utilities/Sphinx/cmake.py | 85 | ||||
-rw-r--r-- | Utilities/Sphinx/static/cmake.css | 23 |
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 { |