Skip to content

Commit

Permalink
Add object description options mechanism
Browse files Browse the repository at this point in the history
This replaces manny config options with a more general "object
description options" mechanism that allows various options to be
configured on a per domain/object type basis.

This commit also adds a number of features built on top of that mechanism:

- Generating object description synopses for the std domain, including
  custom object types added using `app.add_object_type`.

- Excluding certain object types from the TOC

- Excluding the object type from the cross-reference tooltip.

Fixes #70.

Co-authored-by: Brendan <2bndy5@gmail.com>
  • Loading branch information
jbms and 2bndy5 committed Apr 28, 2022
1 parent 0e0dcc8 commit 7cb4682
Show file tree
Hide file tree
Showing 15 changed files with 895 additions and 238 deletions.
185 changes: 174 additions & 11 deletions docs/api.rst
Original file line number Diff line number Diff line change
@@ -1,23 +1,186 @@
General API customization
=========================

.. confval:: include_object_description_fields_in_toc
This theme supports a number of options that can be customized for each
domain/object type pair.

:python:`bool` indicating whether to include domain object description
fields, like "Parameters", "Returns", "Raises", etc. in the table of
contents. Defaults to :python:`True`.
.. confval:: object_description_options

:python:`list` of :python:`(pattern, options)` pairs, where :python:`pattern`
is a regular expression matching strings of the form
:python:`"domain:objtype"` and :python:`options` is a dictionary of supported
`object-description-options`.

We need something `object_description_options`.

The actual options for a given object type are determined by first
initializing each option to its default value, and then applying as overrides
the options associated with each matching pattern in order.

.. _object-description-options:

Object description options
--------------------------

The following options can be customized for each object type using
:confval:`object_description_options`.

.. objconf:: include_in_toc

Indicates whether to include the object description in the table of contents.

.. admonition:: Example
:class: example

To prevent C++ parameter descriptions from appearing in the TOC, add the
following to :file:`conf.py`:

.. code-block:: python
object_description_options = [
("cpp:.*Param": dict(include_in_toc=False)),
]
.. objconf:: generate_synopses

Indicates whether to generate a *synopsis* from the object description. The
synopsis is shown as a tooltip when hovering over a cross-reference link, and
is also shown in the search results list. Supported values are:

:python:`None`
Disables synopsis generation.

:python:`"first_paragraph"`
Uses the first paragraph of the description as the synopsis.

:python:`"first_sentence"`
Uses the first sentence of the first paragraph of the description as the synopsis.

The default is :python:`"first_paragraph"` except for :regexp:`c(pp)?:.*Param`
where the default is :python:`"first_sentence"`.

.. note::

Synopsis generation is currently supported only for the following domains:

- ``std`` (including object types added using :py:obj:`sphinx.application.Sphinx.add_object_type`)
- ``c`` and ``cpp``

.. admonition:: Example
:class: example

To use the first sentence rather than the first paragraph as the synopsis
for C++ class descriptions, add the following to :file:`conf.py`:

.. code-block:: python
object_description_options = [
("cpp:class", dict(generate_synopses="first_sentence")),
]
.. objconf:: include_object_type_in_xref_tooltip

Indicates whether to include the object type in cross-reference and TOC
tooltips.

.. note::

For TOC entries, this is supported for all domains. For regular cross
references, this is supported only for the following domains:

- ``std`` (including object types added using :py:obj:`sphinx.application.Sphinx.add_object_type`)
- ``c`` and ``cpp``

.. admonition:: Example
:class: example

To exclude the object type from all ``py`` domain xrefs, add the following
to :file:`conf.py`:

.. code-block:: python
object_description_options = [
("py:.*", dict(include_object_type_in_xref_tooltip=False)),
]
.. objconf:: include_fields_in_toc

Indicates whether to include fields, like "Parameters", "Returns", "Raises",
etc. in the table of contents.

For an example, see: :cpp:expr:`synopses_ex::Foo` and note the ``Template
Parameters``, ``Parameters``, and ``Returns`` headings shown in the
right-side table of contents.

.. note::

This option does not control whether there are separate TOC entries for
individual parameters, such as for :cpp:expr:`synopses_ex::Foo::T`,
To control whether there are separate TOC entries for individual
parameters, such as for :cpp:expr:`synopses_ex::Foo::T`,
:cpp:expr:`synopses_ex::Foo::N`, :cpp:expr:`synopses_ex::Foo::param`, and
:cpp:expr:`synopses_ex::Foo::arr`. Currently, for the C and C++ domains,
any parameter documented by a :rst:``:param x:`` field will always result
in a TOC entry, regardless of the value of
:confval:`include_object_description_fields_in_toc`. Other domains are
not yet supported.
:cpp:expr:`synopses_ex::Foo::arr`, use the :objconf:`include_in_toc`
option.


.. admonition:: Example
:class: example

To exclude object description fields from the table of contents for all
``py`` domain objects, add the following to :file:`conf.py`:

.. code-block:: python
object_description_options = [
("py:.*", dict(include_fields_in_toc=False)),
]
Other options described elsewhere include:

- :objconf:`wrap_signatures_with_css`
- :objconf:`wrap_signatures_column_limit`
- :objconf:`clang_format_style`

Table of contents icons
^^^^^^^^^^^^^^^^^^^^^^^

For object descriptions included in the table of contents (when
:objconf:`include_in_toc` is :python:`True`), a text-based "icon" can optionally
be included to indicate the object type.

Default icons are specified for a number of object types, but they can be
overridden using the following options:

.. objconf:: toc_icon_class

Indicates the icon class, or :python:`None` to disable the icon. The class
must be one of:

- :python:`"alias"`
- :python:`"procedure"`
- :python:`"data"`
- :python:`"sub-data"`

.. objconf:: toc_icon_text

Indicates the text content of the icon, or :python:`None` to disable the
icon. This should normally be a single character, such as :python:`"C"` to
indicate a class or :python:`"F"` to indicate a function.

.. admonition:: Example
:class: example

To define a custom object type and specify an icon for it, add the following to
:file:`conf.py`:

.. code-block:: python
object_description_options = [
("std:confval", dict(toc_icon_class="data", toc_icon_text="C")),
]
def setup(app):
app.add_object_type(
"confval",
"confval",
objname="configuration value",
indextemplate="pair: %s; configuration value",
)
77 changes: 70 additions & 7 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@

sys.path.insert(0, os.path.abspath("."))

import docutils
import sphinx
import sphinx.domains.python
import sphinx.environment
import sphinx.util.logging
import sphinx.util.typing

from sphinx_immaterial import apidoc_formatting

logger = sphinx.util.logging.getLogger(__name__)

# -- Project information -----------------------------------------------------

project = "Sphinx-Immaterial"
Expand Down Expand Up @@ -161,16 +172,15 @@
"dudir": ("http://docutils.sourceforge.net/docs/ref/rst/directives.html#%s", ""),
}

object_description_options = []

# BEGIN: sphinx_immaterial.format_signatures extension options
clang_format_signatures_domain_styles = {
"cpp": """{
BasedOnStyle: LLVM,
ColumnLimit: 68,
}""",
}
object_description_options.append(
("cpp:.*", dict(clang_format_style={"BasedOnStyle": "LLVM"}))
)
# END: sphinx_immaterial.format_signatures extension options

html_wrap_signatures_with_css = ["py"]
object_description_options.append(("py:.*", dict(wrap_signatures_with_css=True)))

# BEGIN: sphinx_immaterial.external_cpp_references extension options
external_cpp_references = {
Expand Down Expand Up @@ -210,10 +220,63 @@
"""


object_description_options.append(
(
"std:confval",
dict(
toc_icon_class="data", toc_icon_text="C", generate_synopses="first_sentence"
),
)
)

object_description_options.append(
(
"std:objconf",
dict(
toc_icon_class="data", toc_icon_text="O", generate_synopses=None,
),
)
)


def _parse_object_description_signature(
env: sphinx.environment.BuildEnvironment, signature: str, node: docutils.nodes.Node
) -> str:
registry = apidoc_formatting.get_object_description_option_registry(env.app)
registry_option = registry.get(signature)
node += sphinx.addnodes.desc_name(signature, signature)
if registry_option is None:
logger.error("Invalid object description option: %r", signature)
else:
node += sphinx.addnodes.desc_sig_punctuation(" : ", " : ")
annotations = sphinx.domains.python._parse_annotation(
sphinx.util.typing.stringify(registry_option.type_constraint), env
)
node += sphinx.addnodes.desc_type("", "", *annotations)
node += sphinx.addnodes.desc_sig_punctuation(" = ", " = ")
default_repr = repr(registry_option.default)
node += docutils.nodes.literal(
default_repr,
default_repr,
language="python",
classes=["python", "code", "highlight"],
)
return signature


def setup(app):
app.add_object_type(
"confval",
"confval",
objname="configuration value",
indextemplate="pair: %s; configuration value",
)

app.add_object_type(
"objconf",
"objconf",
objname="object description option",
indextemplate="pair: %s; object description option",
parse_node=_parse_object_description_signature,
)
return {"parallel_read_safe": True, "parallel_write_safe": True}
53 changes: 0 additions & 53 deletions docs/cpp.rst
Original file line number Diff line number Diff line change
@@ -1,59 +1,6 @@
C++ domain customization
========================

.. confval:: cpp_generate_synopses

:python:`bool` specifying whether to generate a *synopsis* for C++ domain
objects based on the first paragraph of their content (first sentence for
parameters). The synopsis is shown as a tooltip when hovering over a
cross-reference link, and is also shown in the search results list.

Defaults to :python:`True`.

.. rst-example:: C++ synopses

.. cpp:type:: synopses_ex::SomeType

Description will be shown as a tooltip when hovering over
cross-references to :cpp:expr:`SomeType` in other signatures as well as
in the TOC.

Additional description not shown in tooltip. This is the return type
for :cpp:expr:`Foo`.

.. cpp:function:: template <typename T, int N> \
synopses_ex::SomeType synopses_ex::Foo(\
T param, \
const int (&arr)[N]\
);

Synopsis for this function, shown when hovering over cross references
as well as in the TOC.

:tparam T: Tooltip shown when hovering over cross-references to this
template parameter. Additional description not included in
tooltip.
:tparam N: Tooltip shown for N.
:param param: Tooltip shown for cross-references to this function
parameter param.
:param arr: Tooltip shown for cross-references to this function
parameter arr. To cross reference another parameter, use the
:rst:role:`cpp:expr` role, e.g.: :cpp:expr:`N`. Parameters can
also be referenced via their fake qualified name,
e.g. :cpp:expr:`synopses_ex::Foo::N`.
:returns: Something or other.


.. rst-example::

.. cpp:class:: synopses_ex::Class

.. cpp:function:: Class(uint16_t _cepin, uint16_t _cspin, uint32_t _spi_speed=RF24_SPI_SPEED)

:param _cepin: The pin attached to Chip Enable on the RF module
:param _cspin: The pin attached to Chip Select (often labeled CSN) on the radio module.
:param _spi_speed: The SPI speed in Hz ie: 1000000 == 1Mhz

.. confval:: cpp_strip_namespaces_from_signatures

:python:`list[str]` specifying namespaces to strip from signatures. This
Expand Down
Loading

0 comments on commit 7cb4682

Please sign in to comment.