Skip to content

Commit

Permalink
Allow multi-line object description signatures (#11011)
Browse files Browse the repository at this point in the history
Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
Co-authored-by: Jean-François B <2589111+jfbu@users.noreply.github.com>
Co-authored-by: TLouf <loufthomas@gmail.com>
  • Loading branch information
4 people committed May 11, 2023
1 parent c73628d commit 86b07d4
Show file tree
Hide file tree
Showing 30 changed files with 1,635 additions and 43 deletions.
7 changes: 7 additions & 0 deletions CHANGES
Expand Up @@ -26,6 +26,13 @@ Features added
generated HTML, using the CRC32 algorithm.
* :meth:`~sphinx.application.Sphinx.require_sphinx` now allows the version
requirement to be specified as ``(major, minor)``.
* #11011: Allow configuring a line-length limit for object signatures, via
:confval:`maximum_signature_line_length` and the domain-specific variants.
If the length of the signature (in characters) is greater than the configured
limit, each parameter in the signature will be split to its own logical line.
This behaviour may also be controlled by options on object description
directives, for example :rst:dir:`py:function:single-line-parameter-list`.
Patch by Thomas Louf, Adam Turner, and Jean-François Burnol.

Bugs fixed
----------
Expand Down
6 changes: 6 additions & 0 deletions doc/latex.rst
Expand Up @@ -1479,6 +1479,12 @@ Macros
.. versionadded:: 6.2.0
``\sphinxparam``, ``\sphinxsamedocref``

.. versionadded:: 7.1.0
``\sphinxparamcomma`` which defaults to a comma followed by a space and
``\sphinxparamcommaoneperline`` which is used for one-parameter-per-line
signatures (see :confval:`maximum_signature_line_length`). It defaults
to ``\texttt{,}`` to make these end-of-line separators more distinctive.

- More text styling:

.. csv-table::
Expand Down
54 changes: 54 additions & 0 deletions doc/usage/configuration.rst
Expand Up @@ -675,6 +675,25 @@ General configuration
If the value is a fully-qualified name of a custom Pygments style class,
this is then used as custom style.

.. confval:: maximum_signature_line_length

If a signature's length in characters exceeds the number set, each
parameter within the signature will be displayed on an individual logical
line.

When ``None`` (the default), there is no maximum length and the entire
signature will be displayed on a single logical line.

A 'logical line' is similar to a hard line break---builders or themes may
choose to 'soft wrap' a single logical line, and this setting does not affect
that behaviour.

Domains may provide options to suppress any hard wrapping on an individual
object directive, such as seen in the C, C++, and Python domains (e.g.
:rst:dir:`py:function:single-line-parameter-list`).

.. versionadded:: 7.1

.. confval:: add_function_parentheses

A boolean that decides whether parentheses are appended to function and
Expand Down Expand Up @@ -2912,6 +2931,14 @@ Options for the C domain

.. versionadded:: 4.0.3

.. confval:: c_maximum_signature_line_length

If a signature's length in characters exceeds the number set, each
parameter will be displayed on an individual logical line. This is a
domain-specific setting, overriding :confval:`maximum_signature_line_length`.

.. versionadded:: 7.1

.. _cpp-config:

Options for the C++ domain
Expand Down Expand Up @@ -2942,6 +2969,14 @@ Options for the C++ domain

.. versionadded:: 1.5

.. confval:: cpp_maximum_signature_line_length

If a signature's length in characters exceeds the number set, each
parameter will be displayed on an individual logical line. This is a
domain-specific setting, overriding :confval:`maximum_signature_line_length`.

.. versionadded:: 7.1

Options for the Python domain
-----------------------------

Expand Down Expand Up @@ -2984,6 +3019,25 @@ Options for the Python domain

.. note:: This configuration is still in experimental

.. confval:: python_maximum_signature_line_length

If a signature's length in characters exceeds the number set, each
argument will be displayed on an individual logical line. This is a
domain-specific setting, overriding :confval:`maximum_signature_line_length`.

.. versionadded:: 7.1

Options for the Javascript domain
---------------------------------

.. confval:: javascript_maximum_signature_line_length

If a signature's length in characters exceeds the number set, each
parameter will be displayed on an individual logical line. This is a
domain-specific setting, overriding :confval:`maximum_signature_line_length`.

.. versionadded:: 7.1

Example of configuration file
-----------------------------

Expand Down
91 changes: 91 additions & 0 deletions doc/usage/restructuredtext/domains.rst
Expand Up @@ -231,6 +231,16 @@ The following directives are provided for module and class contents:
Describe the location where the object is defined. The default value is
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the function's arguments will be emitted on a single logical
line, overriding :confval:`python_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. py:data:: name
Describes global data in a module, including both variables and values used
Expand Down Expand Up @@ -329,6 +339,15 @@ The following directives are provided for module and class contents:
Describe the location where the object is defined. The default value is
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the class constructor's arguments will be emitted on a single
logical line, overriding :confval:`python_maximum_signature_line_length`
and :confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. py:attribute:: name
Describes an object data attribute. The description should include
Expand Down Expand Up @@ -441,6 +460,15 @@ The following directives are provided for module and class contents:
Describe the location where the object is defined. The default value is
the module specified by :rst:dir:`py:currentmodule`.
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the method's arguments will be emitted on a single logical
line, overriding :confval:`python_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:option:: staticmethod
:type: no value
Expand Down Expand Up @@ -494,6 +522,15 @@ The following directives are provided for module and class contents:
There is no ``py:deco`` role to link to a decorator that is marked up with
this directive; rather, use the :rst:role:`py:func` role.

.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the decorator's arguments will be emitted on a single logical
line, overriding :confval:`python_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. py:decoratormethod:: name
.. py:decoratormethod:: name(signature)
Expand Down Expand Up @@ -763,6 +800,15 @@ The C domain (name **c**) is suited for documentation of C API.
:retval NULL: under some conditions.
:retval NULL: under some other conditions as well.
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the function's parameters will be emitted on a single logical
line, overriding :confval:`c_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. c:macro:: name
.. c:macro:: name(arg list)
Expand All @@ -776,6 +822,15 @@ The C domain (name **c**) is suited for documentation of C API.
.. versionadded:: 3.0
The function style variant.
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the macro's parameters will be emitted on a single logical
line, overriding :confval:`c_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. c:struct:: name
Describes a C struct.
Expand Down Expand Up @@ -1126,6 +1181,15 @@ visibility statement (``public``, ``private`` or ``protected``).
.. cpp:function:: template<> \
void print(int i)
.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the function's parameters will be emitted on a single logical
line, overriding :confval:`cpp_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. cpp:member:: (member) variable declaration
.. cpp:var:: (member) variable declaration
Expand Down Expand Up @@ -1908,13 +1972,31 @@ The JavaScript domain (name **js**) provides the following directives:
:throws SomeError: For whatever reason in that case.
:returns: Something.

.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the function's parameters will be emitted on a single logical
line, overriding :confval:`javascript_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. js:method:: name(signature)
This directive is an alias for :rst:dir:`js:function`, however it describes
a function that is implemented as a method on a class object.

.. versionadded:: 1.6

.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the function's parameters will be emitted on a single logical
line, overriding :confval:`javascript_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. js:class:: name
Describes a constructor that creates an object. This is basically like a
Expand All @@ -1933,6 +2015,15 @@ The JavaScript domain (name **js**) provides the following directives:
:param string name: The name of the animal
:param number age: an optional age for the animal

.. rst:directive:option:: single-line-parameter-list
:type: no value
Ensures that the function's parameters will be emitted on a single logical
line, overriding :confval:`javascript_maximum_signature_line_length` and
:confval:`maximum_signature_line_length`.
.. versionadded:: 7.1
.. rst:directive:: .. js:data:: name
Describes a global variable or constant.
Expand Down
7 changes: 6 additions & 1 deletion sphinx/addnodes.py
Expand Up @@ -246,7 +246,12 @@ def astext(self) -> str:


class desc_parameterlist(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for a general parameter list."""
"""Node for a general parameter list.
As default the parameter list is written in line with the rest of the signature.
Set ``multi_line_parameter_list = True`` to describe a multi-line parameter list.
In that case each parameter will then be written on its own, indented line.
"""
child_text_separator = ', '

def astext(self):
Expand Down
2 changes: 1 addition & 1 deletion sphinx/config.py
Expand Up @@ -137,7 +137,7 @@ class Config:
'numfig': (False, 'env', []),
'numfig_secnum_depth': (1, 'env', []),
'numfig_format': ({}, 'env', []), # will be initialized in init_numfig_format()

'maximum_signature_line_length': (None, 'env', {int, None}),
'math_number_all': (False, 'env', []),
'math_eqref_format': (None, 'env', [str]),
'math_numfig': (True, 'env', []),
Expand Down
22 changes: 21 additions & 1 deletion sphinx/domains/c.py
Expand Up @@ -727,9 +727,19 @@ def _stringify(self, transform: StringifyTransform) -> str:
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
verify_description_mode(mode)
multi_line_parameter_list = False
test_node: Element = signode
while test_node.parent:
if not isinstance(test_node, addnodes.desc_signature):
test_node = test_node.parent
continue
multi_line_parameter_list = test_node.get('multi_line_parameter_list', False)
break

# only use the desc_parameterlist for the outer list, not for inner lists
if mode == 'lastIsName':
paramlist = addnodes.desc_parameterlist()
paramlist['multi_line_parameter_list'] = multi_line_parameter_list
for arg in self.args:
param = addnodes.desc_parameter('', '', noemph=True)
arg.describe_signature(param, 'param', env, symbol=symbol)
Expand Down Expand Up @@ -3153,6 +3163,7 @@ class CObject(ObjectDescription[ASTDeclaration]):
option_spec: OptionSpec = {
'noindexentry': directives.flag,
'nocontentsentry': directives.flag,
'single-line-parameter-list': directives.flag,
}

def _add_enumerator_to_parent(self, ast: ASTDeclaration) -> None:
Expand Down Expand Up @@ -3258,6 +3269,14 @@ def run(self) -> list[Node]:
def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration:
parentSymbol: Symbol = self.env.temp_data['c:parent_symbol']

max_len = (self.env.config.c_maximum_signature_line_length
or self.env.config.maximum_signature_line_length
or 0)
signode['multi_line_parameter_list'] = (
'single-line-parameter-list' not in self.options
and (len(sig) > max_len > 0)
)

parser = DefinitionParser(sig, location=signode, config=self.env.config)
try:
ast = self.parse_definition(parser)
Expand Down Expand Up @@ -3866,11 +3885,12 @@ def setup(app: Sphinx) -> dict[str, Any]:
app.add_config_value("c_id_attributes", [], 'env')
app.add_config_value("c_paren_attributes", [], 'env')
app.add_config_value("c_extra_keywords", _macroKeywords, 'env')
app.add_config_value("c_maximum_signature_line_length", None, 'env', types={int, None})
app.add_post_transform(AliasTransform)

return {
'version': 'builtin',
'env_version': 2,
'env_version': 3,
'parallel_read_safe': True,
'parallel_write_safe': True,
}

0 comments on commit 86b07d4

Please sign in to comment.