John Snow <js...@redhat.com> writes:

> This is a directive that creates a syntactic sugar for creating
> "Example" boxes very similar to the ones already used in the bitmaps.rst
> document, please see e.g.
> https://www.qemu.org/docs/master/interop/bitmaps.html#creation-block-dirty-bitmap-add
>
> In its simplest form, when a custom title is not needed or wanted, and
> the example body is *solely* a QMP example:
>
> ```
> .. qmp-example::
>
>    {body}
> ```
>
> is syntactic sugar for:
>
> ```
> .. admonition:: Example:
>
>    .. code-block:: QMP
>
>       {body}
> ```
>
> When a custom, plaintext title that describes the example is desired,
> this form:
>
> ```
> .. qmp-example::
>    :title: Defrobnification
>
>    {body}
> ```
>
> Is syntactic sugar for:
>
> ```
> .. admonition:: Example: Defrobnification
>
>    .. code-block:: QMP
>
>       {body}
> ```
>
> Lastly, when Examples are multi-step processes that require non-QMP
> exposition, have lengthy titles, or otherwise involve prose with rST
> markup (lists, cross-references, etc), the most complex form:
>
> ```
> .. qmp-example::
>    :annotated:
>
>    This example shows how to use `foo-command`::
>
>      {body}
> ```
>
> Is desugared to:
>
> ```
> .. admonition:: Example:
>
>    This example shows how to use `foo-command`::
>
>      {body}
>
>    For more information, please see `frobnozz`.
> ```

Can we combine the latter two?  Like this:

  .. qmp-example::
     :title: Defrobnification
     :annotated:

     This example shows how to use `foo-command`::

       {body}

> The primary benefit here being documentation source consistently using
> the same directive for all forms of examples to ensure consistent visual
> styling, and ensuring all relevant prose is visually grouped alongside
> the code literal block.
>
> Note that as of this commit, the code-block rST syntax "::" does not
> apply QMP highlighting; you would need to use ".. code-block:: QMP". The
> very next commit changes this behavior to assume all "::" code blocks
> within this directive are QMP blocks.
>
> Signed-off-by: John Snow <js...@redhat.com>
> ---
>  docs/sphinx/qapidoc.py | 60 ++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 58 insertions(+), 2 deletions(-)

No tests?  Hmm, I see you convert existing tests in PATCH 19-21.  While
that works, test coverage now would make it easier to see how each patch
affects doc generator output.

> diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py
> index 43dd99e21e6..a2fa05fc491 100644
> --- a/docs/sphinx/qapidoc.py
> +++ b/docs/sphinx/qapidoc.py
> @@ -27,16 +27,19 @@
>  import os
>  import re
>  import textwrap
> +from typing import List
>  
>  from docutils import nodes
> -from docutils.parsers.rst import Directive, directives
> +from docutils.parsers.rst import directives
>  from docutils.statemachine import ViewList
>  from qapi.error import QAPIError, QAPISemError
>  from qapi.gen import QAPISchemaVisitor
>  from qapi.schema import QAPISchema
>  
>  import sphinx
> +from sphinx.directives.code import CodeBlock
>  from sphinx.errors import ExtensionError
> +from sphinx.util.docutils import SphinxDirective
>  from sphinx.util.nodes import nested_parse_with_titles
>  
>  
> @@ -494,7 +497,7 @@ def visit_module(self, name):
>          super().visit_module(name)
>  
>  
> -class NestedDirective(Directive):
> +class NestedDirective(SphinxDirective):

What is this about?

>      def run(self):
>          raise NotImplementedError
>  
> @@ -567,10 +570,63 @@ def run(self):
>              raise ExtensionError(str(err)) from err
>  
>  
> +class QMPExample(CodeBlock, NestedDirective):
> +    """
> +    Custom admonition for QMP code examples.
> +
> +    When the :annotated: option is present, the body of this directive
> +    is parsed as normal rST instead. Code blocks must be explicitly
> +    written by the user, but this allows for intermingling explanatory
> +    paragraphs with arbitrary rST syntax and code blocks for more
> +    involved examples.
> +
> +    When :annotated: is absent, the directive body is treated as a
> +    simple standalone QMP code block literal.
> +    """
> +
> +    required_argument = 0
> +    optional_arguments = 0
> +    has_content = True
> +    option_spec = {
> +        "annotated": directives.flag,
> +        "title": directives.unchanged,
> +    }
> +
> +    def admonition_wrap(self, *content) -> List[nodes.Node]:
> +        title = "Example:"
> +        if "title" in self.options:
> +            title = f"{title} {self.options['title']}"
> +
> +        admon = nodes.admonition(
> +            "",
> +            nodes.title("", title),
> +            *content,
> +            classes=["admonition", "admonition-example"],
> +        )
> +        return [admon]
> +
> +    def run_annotated(self) -> List[nodes.Node]:
> +        content_node: nodes.Element = nodes.section()
> +        self.do_parse(self.content, content_node)
> +        return content_node.children
> +
> +    def run(self) -> List[nodes.Node]:
> +        annotated = "annotated" in self.options
> +
> +        if annotated:
> +            content_nodes = self.run_annotated()
> +        else:
> +            self.arguments = ["QMP"]
> +            content_nodes = super().run()
> +
> +        return self.admonition_wrap(*content_nodes)
> +
> +
>  def setup(app):
>      """Register qapi-doc directive with Sphinx"""
>      app.add_config_value("qapidoc_srctree", None, "env")
>      app.add_directive("qapi-doc", QAPIDocDirective)
> +    app.add_directive("qmp-example", QMPExample)
>  
>      return {
>          "version": __version__,


Reply via email to