Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: enable multiline signatures when too long. #11011

Merged
merged 110 commits into from May 11, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
110 commits
Select commit Hold shift + click to select a range
82f1b98
ENH: enable multiline signatures when too long.
TLouf Dec 3, 2022
8d6dfba
Make parameter nodes inherit from sig classes
TLouf Dec 17, 2022
581433b
Fix permalink for multiline Python signatures
TLouf Dec 17, 2022
d3ec233
Enable multiline sig for C and C++
TLouf Dec 17, 2022
1229b66
Simplify `desc_parameterlist` into single class
TLouf Dec 26, 2022
ec713e9
Fix flake8 errors
TLouf Dec 26, 2022
85ff493
Pass multiline as `desc_parameterlist` attribute
TLouf Dec 26, 2022
8ca560d
Add type annotation
TLouf Dec 26, 2022
9ec4698
Use new desc_parameterline instead of desc_content
TLouf Dec 28, 2022
b93ecf0
Support HTML writer
TLouf Dec 28, 2022
3b110ce
Support text writer
TLouf Dec 28, 2022
234acd6
Increment environment versions
TLouf Dec 29, 2022
7c016c9
Better variable names
TLouf Dec 30, 2022
5b8735d
Expand docstrings of new nodes' classes
TLouf Dec 30, 2022
1ca68b0
Fix oversight of param line name change in C++
TLouf Jan 3, 2023
8e198f3
Add newlines to make param list HTML prettier
TLouf Jan 3, 2023
25ae353
Tweak EPUB's CSS to match HTML's
TLouf Jan 3, 2023
d6b2dac
Add tests for python domain
TLouf Jan 3, 2023
af77cad
Add tests for C domain
TLouf Jan 3, 2023
ea89180
Add tests for C++ domain
TLouf Jan 3, 2023
5cc07d6
Add minimal implementation for man and texinfo
TLouf Jan 3, 2023
caff655
Python domain test cleanup
TLouf Jan 3, 2023
b71b06d
Make flake8 happy
TLouf Jan 3, 2023
88c2e09
Make isort happy
TLouf Jan 3, 2023
4258fd5
Add doc
TLouf Jan 3, 2023
6e6261c
Make docslint happy
TLouf Jan 3, 2023
9c52eae
Add types for `maximum_signature_line_length`
TLouf Jan 8, 2023
2d9ad80
Fix typo
TLouf Jan 8, 2023
0bc3b68
Add global `maximum_signature_line_length` config
TLouf Jan 8, 2023
2c0da6a
Add tentative LaTeX implementation
TLouf Jan 20, 2023
bba2b51
Merge branch 'master' into signature-linebreaks
TLouf Jan 20, 2023
d8df099
Make docslint happy
TLouf Jan 20, 2023
d18f973
Merge branch 'master' into signature-linebreaks
jfbu Jan 23, 2023
d803e68
Cleanup latex style
TLouf Jan 23, 2023
3753e7e
Rename new latex command for clarity
TLouf Jan 23, 2023
eb63da6
Force zero margins for dl tag
TLouf Jan 23, 2023
53e882f
latex: avoid page break above first function argument
TLouf Jan 24, 2023
d7508a0
latex: match coding style of LaTeX macro files in for loop
TLouf Jan 24, 2023
b665647
latex: rename command to `pysigwithonelineperarg`
TLouf Jan 24, 2023
ada9d00
latex: avoid page break before dedented closing brace
TLouf Jan 24, 2023
ed7fac8
Update versionadded
TLouf Jan 24, 2023
1475f5f
docs: fix linking to `single-line-signature`
TLouf Jan 24, 2023
9b9e694
docs: better linking to `single-line-signature`
TLouf Jan 25, 2023
691a780
Merge branch 'master' into signature-linebreaks
TLouf Apr 7, 2023
4deb58c
Add trailing commas for ruff
TLouf Apr 7, 2023
48d2ccb
Add trailing commas in tests
TLouf Apr 7, 2023
b065ab7
Add `desc_parameter_line` to doc
TLouf Apr 7, 2023
8d42ac1
Merge branch 'master' into signature-linebreaks
AA-Turner Apr 7, 2023
2b864fe
Update config docs
AA-Turner Apr 8, 2023
fa97a15
Update option docs
AA-Turner Apr 7, 2023
d787002
Move options to end
AA-Turner Apr 8, 2023
707650f
Revert changes to the frozen HTML 4 writer
AA-Turner Apr 7, 2023
b1cb1b3
Revert ``is_multiline`` -> ```is_multi_line`` rename for ``desc_signa…
AA-Turner Apr 7, 2023
8308742
``maximum_signature_line_length`` type to be int | None
AA-Turner Apr 7, 2023
e6ef3c2
Rename ``single-line-signature`` to ``single-line-{parameter,argument…
AA-Turner Apr 8, 2023
dfd0075
Refactor tests
AA-Turner Apr 8, 2023
bfab7ea
Set and use ``multi_line_parameter_list`` on node
AA-Turner Apr 7, 2023
26551bc
Remove ``desc_parameter_line``
AA-Turner Apr 8, 2023
9d03de0
options docs typo (macro -> function)
AA-Turner Apr 8, 2023
b3e5662
CHANGES entry
AA-Turner Apr 8, 2023
592c86c
fixup! Remove ``desc_parameter_line``
AA-Turner Apr 8, 2023
1af1a2e
Restore stripped whitespace
AA-Turner Apr 8, 2023
69dba4b
fixup! (round 2) Remove ``desc_parameter_line``
AA-Turner Apr 8, 2023
2918995
Remove unneeded py domain permalink logic
AA-Turner Apr 8, 2023
d58b63c
Restore cross-reference to single line option
TLouf Apr 8, 2023
b866b88
Add JS domain implentation
TLouf Apr 8, 2023
c6cf460
Mention @jfbu in CHANGES
TLouf Apr 9, 2023
b18c481
Unify directive option to `single-line-parameter-list`
TLouf Apr 9, 2023
4b47e79
Fix directive hyperlink in CHANGES
TLouf Apr 9, 2023
a652d3e
Pass multiline bool to `_pseudo_parse_arglist`
TLouf Apr 10, 2023
bcba5f6
Handle bracket notation for optional params in HTML
TLouf Apr 10, 2023
3243f9d
Rename `on_single_line` to `on_separate_line`
TLouf Apr 10, 2023
93bdb8d
Fix latex for sigs starting with optional param
TLouf Apr 10, 2023
417db1f
Cleanup of HTML
TLouf Apr 11, 2023
c4e9c62
Update LaTeX to handle optional parameters in one line per param context
jfbu Apr 11, 2023
57004e3
Use some LaTeX hack to handle comma placement vs bracket
jfbu Apr 11, 2023
88cfe5a
Mark latex strings as raw to ignore backslashes
TLouf Apr 11, 2023
9ce3e34
Add trailing comma in expected HTML of tests
TLouf Apr 11, 2023
3379337
Merge branch 'master' into signature-linebreaks
jfbu Apr 11, 2023
98f1803
Move `and` operator to line start
TLouf Apr 16, 2023
ef7adde
Handle optionals in text writer
TLouf Apr 16, 2023
1ad0981
Add `single-line-parameter-list` as JS object option
TLouf Apr 16, 2023
e36c811
Add essential JS tests
TLouf Apr 16, 2023
ea30441
Actually test for `multi_line_parameter_list` bool
TLouf Apr 16, 2023
2d604b9
Add JS tests with `maximum_signature_line_length`
TLouf Apr 16, 2023
71f5a8a
Remove (probably) forgotten c test template
TLouf Apr 16, 2023
e948563
Make test names consistent
TLouf Apr 16, 2023
d2b3459
Add newline after dd tags to make more readable HTML
TLouf Apr 16, 2023
bfc353e
Test for optional syntax in js and py domains
TLouf Apr 16, 2023
0c54703
latex: handle comma placement in writer
TLouf Apr 16, 2023
a1b031f
latex: follow-up refactoring after TeX black magic removal
jfbu Apr 16, 2023
a0f5d9b
latex: tidy-up some comment in sphinxlatexstyletext.sty
jfbu Apr 16, 2023
a8a9be9
Fix comma placement when `required_params_left`
TLouf Apr 16, 2023
3b34e30
Fix case of multiple params at same optional level
TLouf Apr 16, 2023
2c1f83a
lint: remove extra line between method defs
TLouf Apr 16, 2023
9c625f1
Conform test to new syntax
TLouf Apr 16, 2023
5866fe6
Test for multiple params in optional level
TLouf Apr 17, 2023
3bef7e4
Make HTML asserts more modular to ease debugging
TLouf Apr 18, 2023
30041a6
Test xelatex PDF build
TLouf Apr 21, 2023
8037a5b
Actually override xelatex' build conf
TLouf Apr 21, 2023
2a85b9e
Merge branch 'master' into signature-linebreaks
jfbu Apr 21, 2023
35424ea
Document the added \sphinxparam, \sphinxparamcomma...
jfbu Apr 21, 2023
db91327
Add a specific latex unit test
jfbu Apr 21, 2023
1a44553
Remove blank line in latex.rst
jfbu Apr 21, 2023
0c4fdb8
Simplify parametrize of latex build test
TLouf Apr 22, 2023
36183f5
Add tests for text output
TLouf Apr 22, 2023
f2d1574
Bump versionadded in doc/latex.rst to match expected release number
jfbu Apr 24, 2023
370cd0e
Bump again versionadded in doc/latex.rst to match expected release
jfbu Apr 30, 2023
50ac9a6
Merge branch 'master' into signature-linebreaks
AA-Turner May 11, 2023
585b2b9
misc updates (inc version bump)
AA-Turner May 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
36 changes: 36 additions & 0 deletions doc/usage/configuration.rst
Expand Up @@ -2903,6 +2903,18 @@ Options for the C domain

.. versionadded:: 4.0.3

.. confval:: c_maximum_signature_line_length
TLouf marked this conversation as resolved.
Show resolved Hide resolved

An integer representing the maximum number of characters that cannot be exceeded
by a C object's signature. When negative (the default), there is no maximum, no
line break will be introduced no matter how long the signature. When positive, all
objects whose signature exceed the given character limit will have each of their
arguments displayed on a separate, indented line. The directive
:rst:dir:`single-line-signature` allows to disable this behavior on specific
objects.

.. versionadded:: 6.x

.. _cpp-config:

Options for the C++ domain
Expand Down Expand Up @@ -2933,6 +2945,18 @@ Options for the C++ domain

.. versionadded:: 1.5

.. confval:: cpp_maximum_signature_line_length

An integer representing the maximum number of characters that cannot be exceeded
by a C++ object's signature. When negative (the default), there is no maximum, no
line break will be introduced no matter how long the signature. When positive, all
objects whose signature exceed the given character limit will have each of their
arguments displayed on a separate, indented line. The directive
:rst:dir:`single-line-signature` allows to disable this behavior on specific
objects.

.. versionadded:: 6.x

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

Expand All @@ -2945,6 +2969,18 @@ Options for the Python domain

.. note:: This configuration is still in experimental

.. confval:: python_maximum_signature_line_length

An integer representing the maximum number of characters that cannot be exceeded
by a Python object's signature. When negative (the default), there is no maximum,
no line break will be introduced no matter how long the signature. When positive,
all objects whose signature exceed the given character limit will have each of
their arguments displayed on a separate, indented line. The directive
:rst:dir:`single-line-signature` allows to disable this behavior on specific
objects.

.. versionadded:: 6.x
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've mixed feelings on this, how would you feel about just always breaking to one line per argument and having a global (boolean) option to preserve single lines?

A

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a potential user of this, I'd really like to be able to specify a threshold (and have Sphinx ship with a sensible default...)
I don't want simple functions with one or two args having their signatures split across lines, and I have an even enough mix that a boolean is going to cause an annoying amount of work no matter which way it is set....

Copy link
Contributor

@pradyunsg pradyunsg Jan 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to agree with @cjw296 -- something like https://installer.pypa.io/en/stable/api/__init__/ should stay on all-entries-on-one-line (instead of breaking that up) because they fit, while something like https://testfixtures.readthedocs.io/en/latest/api.html#testfixtures.compare should get split across multiple lines.

Using the number of characters in the signature is the natural piece for enabling that behaviour with a clear semantic; and the explicit opt-outs on a per-project and per-signature level are also useful to get the exact semantics that an author might want.

To answer the how I'd feel question: As a user, I'd be... annoyed/frustrated with a change as proposed by @AA-Turner; especially if the installer API example would get broken up into multiple lines after a Sphinx minor/patch update. As a theme author, I'd... implore you to, should you decide to change the default behaviour like that, to handle it like a backwards-incompatible change to a public Python API of this project. :)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re "When negative (the default), there is no maximum, no line break will be introduced no matter how long the signature".
I assume that if the html theme sets a maximum body width (either through a configuration parameter or hardcoded), then the negative default value will not override this, i.e. the signature will be wrapped based on html theme. Perhaps that could be noted/documented in the description of the python_maximum_signature_line_length parameter.
For example, see basic html theme's body_max_width parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emezh yes indeed, you can see the line breaks introduced by python_maximum_signature_line_length as hard-wrapping, while the soft-wrapping in the view (here the HTML render) can wrap lines independently. I did not make this clear at all in the doc though, so I clarified this point in my last commit (0bc3b68)


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

Expand Down
44 changes: 44 additions & 0 deletions doc/usage/restructuredtext/domains.rst
Expand Up @@ -231,6 +231,14 @@ 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`.


jfbu marked this conversation as resolved.
Show resolved Hide resolved
.. rst:directive:option:: single-line-signature
:type: no value

Forbids the potential introduction of line breaks between parameters of the
documented object, ignoring :confval:`python_maximum_signature_line_length`.


.. rst:directive:: .. py:data:: name

Describes global data in a module, including both variables and values used
Expand Down Expand Up @@ -329,6 +337,12 @@ 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-signature
:type: no value

Forbids the potential introduction of line breaks between parameters of the
documented object, ignoring :confval:`python_maximum_signature_line_length`.

.. rst:directive:: .. py:attribute:: name

Describes an object data attribute. The description should include
Expand Down Expand Up @@ -441,6 +455,12 @@ 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-signature
:type: no value

Forbids the potential introduction of line breaks between parameters of the
documented object, ignoring :confval:`python_maximum_signature_line_length`.

.. rst:directive:option:: staticmethod
:type: no value

Expand Down Expand Up @@ -494,6 +514,12 @@ 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-signature
:type: no value

Forbids the potential introduction of line breaks between parameters of the
documented object, ignoring :confval:`python_maximum_signature_line_length`.

.. rst:directive:: .. py:decoratormethod:: name
.. py:decoratormethod:: name(signature)

Expand Down Expand Up @@ -724,6 +750,12 @@ The C domain (name **c**) is suited for documentation of C API.
Note that you don't have to backslash-escape asterisks in the signature, as
it is not parsed by the reST inliner.

.. rst:directive:option:: single-line-signature
:type: no value

Forbids the potential introduction of line breaks between parameters of the
documented object, ignoring :confval:`c_maximum_signature_line_length`.

In the description of a function you can use the following info fields
(see also :ref:`info-field-lists`).

Expand Down Expand Up @@ -770,6 +802,12 @@ The C domain (name **c**) is suited for documentation of C API.
Describes a C macro, i.e., a C-language ``#define``, without the replacement
text.

.. rst:directive:option:: single-line-signature
:type: no value

Forbids the potential introduction of line breaks between parameters of the
documented object, ignoring :confval:`c_maximum_signature_line_length`.

In the description of a macro you can use the same info fields as for the
:rst:dir:`c:function` directive.

Expand Down Expand Up @@ -1126,6 +1164,12 @@ visibility statement (``public``, ``private`` or ``protected``).
.. cpp:function:: template<> \
void print(int i)

.. rst:directive:option:: single-line-signature
:type: no value

Forbids the potential introduction of line breaks between parameters of the
documented object, ignoring :confval:`cpp_maximum_signature_line_length`.

.. rst:directive:: .. cpp:member:: (member) variable declaration
.. cpp:var:: (member) variable declaration

Expand Down
23 changes: 19 additions & 4 deletions sphinx/addnodes.py
Expand Up @@ -150,7 +150,7 @@ class desc_signature(_desc_classes_injector, nodes.Part, nodes.Inline, nodes.Tex
"""Node for a single object signature.

As default the signature is a single-line signature.
Set ``is_multiline = True`` to describe a multi-line signature.
Set ``is_multi_line = True`` to describe a multi-line signature.
In that case all child nodes must be :py:class:`desc_signature_line` nodes.

This node always has the classes ``sig``, ``sig-object``, and the domain it belongs to.
Expand All @@ -160,7 +160,7 @@ class desc_signature(_desc_classes_injector, nodes.Part, nodes.Inline, nodes.Tex

@property
def child_text_separator(self):
if self.get('is_multiline'):
if self.get('is_multi_line'):
return ' '
else:
return super().child_text_separator
Expand All @@ -170,7 +170,7 @@ class desc_signature_line(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for a line in a multi-line object signature.

It should only be used as a child of a :py:class:`desc_signature`
with ``is_multiline`` set to ``True``.
with ``is_multi_line`` set to ``True``.
Set ``add_permalink = True`` for the line that should get the permalink.
"""
sphinx_line_type = ''
Expand Down Expand Up @@ -242,13 +242,28 @@ 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 ``is_multi_line = True`` to describe a multi-line parameter list. In that case
all child nodes must be :py:class:`desc_parameter_line` nodes, and each parameter
will then be written on its own, indented line.
"""
child_text_separator = ', '

def astext(self):
return f'({super().astext()})'


class desc_parameter_line(nodes.General, nodes.Element):
"""Node for a single, indented parameter line.

It should only be used as a child of a :py:class:`desc_parameterlist` with
``is_multi_line`` set to ``True``, and have a single :py:class:`desc_parameter`
child.
"""


class desc_parameter(nodes.Part, nodes.Inline, nodes.FixedTextElement):
"""Node for a single parameter."""

Expand Down
34 changes: 28 additions & 6 deletions sphinx/domains/c.py
Expand Up @@ -687,9 +687,15 @@ def describe_signature(self, signode: Any, mode: str,


class ASTParameters(ASTBase):
def __init__(self, args: list[ASTFunctionParameter], attrs: ASTAttributeList) -> None:
def __init__(
self,
args: list[ASTFunctionParameter],
attrs: ASTAttributeList,
multi_line: bool = False,
) -> None:
self.args = args
self.attrs = attrs
self.multi_line = multi_line

@property
def function_params(self) -> list[ASTFunctionParameter]:
Expand All @@ -713,13 +719,18 @@ 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 = self.multi_line
# only use the desc_parameterlist for the outer list, not for inner lists
if mode == 'lastIsName':
paramlist = addnodes.desc_parameterlist()
paramlist['is_multi_line'] = multi_line
for arg in self.args:
param_node = addnodes.desc_parameter_line() if multi_line else paramlist
param = addnodes.desc_parameter('', '', noemph=True)
arg.describe_signature(param, 'param', env, symbol=symbol)
paramlist += param
param_node += param
if multi_line:
paramlist += param_node
signode += paramlist
else:
signode += addnodes.desc_sig_punctuation('(', '(')
Expand Down Expand Up @@ -1457,7 +1468,7 @@ def describe_signature(self, signode: TextElement, mode: str,
assert self.symbol
# The caller of the domain added a desc_signature node.
# Always enable multiline:
signode['is_multiline'] = True
signode['is_multi_line'] = True
# Put each line in a desc_signature_line node.
mainDeclNode = addnodes.desc_signature_line()
mainDeclNode.sphinx_line_type = 'declarator'
Expand Down Expand Up @@ -2670,7 +2681,7 @@ def _parse_parameters(self, paramMode: str) -> ASTParameters | None:
'got "%s".' % self.current_char)

attrs = self._parse_attribute_list()
return ASTParameters(args, attrs)
return ASTParameters(args, attrs, multi_line=self.multi_line)

def _parse_decl_specs_simple(
self, outer: str | None, typed: bool
Expand Down Expand Up @@ -3149,6 +3160,7 @@ class CObject(ObjectDescription[ASTDeclaration]):
option_spec: OptionSpec = {
'noindexentry': directives.flag,
'nocontentsentry': directives.flag,
'single-line-signature': directives.flag,
}

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

parser = DefinitionParser(sig, location=signode, config=self.env.config)
max_len = self.env.config.c_maximum_signature_line_length
multi_line = (
max_len >= 0
and 'single-line-signature' not in self.options
and len(sig) > max_len
)
signode['is_multi_line'] = multi_line
parser = DefinitionParser(
sig, location=signode, config=self.env.config, multi_line=multi_line
)
try:
ast = self.parse_definition(parser)
parser.assert_end()
Expand Down Expand Up @@ -3870,11 +3891,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", -1, 'env')
app.add_post_transform(AliasTransform)

return {
'version': 'builtin',
'env_version': 2,
'env_version': 3,
'parallel_read_safe': True,
'parallel_write_safe': True,
}
29 changes: 23 additions & 6 deletions sphinx/domains/cpp.py
Expand Up @@ -2049,7 +2049,7 @@ def __init__(self, args: list[ASTFunctionParameter], volatile: bool, const: bool
refQual: str | None, exceptionSpec: ASTNoexceptSpec,
trailingReturn: ASTType,
override: bool, final: bool, attrs: ASTAttributeList,
initializer: str | None) -> None:
initializer: str | None, multi_line: bool = False) -> None:
self.args = args
self.volatile = volatile
self.const = const
Expand All @@ -2060,6 +2060,7 @@ def __init__(self, args: list[ASTFunctionParameter], volatile: bool, const: bool
self.final = final
self.attrs = attrs
self.initializer = initializer
self.multi_line = multi_line

@property
def function_params(self) -> list[ASTFunctionParameter]:
Expand Down Expand Up @@ -2129,13 +2130,18 @@ 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 = self.multi_line
# only use the desc_parameterlist for the outer list, not for inner lists
if mode == 'lastIsName':
paramlist = addnodes.desc_parameterlist()
paramlist['is_multi_line'] = multi_line
for arg in self.args:
param_node = addnodes.desc_parameter_line() if multi_line else paramlist
param = addnodes.desc_parameter('', '', noemph=True)
arg.describe_signature(param, 'param', env, symbol=symbol)
paramlist += param
param_node += param
if multi_line:
paramlist += param_node
signode += paramlist
else:
signode += addnodes.desc_sig_punctuation('(', '(')
Expand Down Expand Up @@ -4069,7 +4075,7 @@ def describe_signature(self, signode: desc_signature, mode: str,
assert self.symbol
# The caller of the domain added a desc_signature node.
# Always enable multiline:
signode['is_multiline'] = True
signode['is_multi_line'] = True
# Put each line in a desc_signature_line node.
mainDeclNode = addnodes.desc_signature_line()
mainDeclNode.sphinx_line_type = 'declarator'
Expand Down Expand Up @@ -6240,7 +6246,7 @@ def _parse_parameters_and_qualifiers(self, paramMode: str) -> ASTParametersQuali

return ASTParametersQualifiers(
args, volatile, const, refQual, exceptionSpec, trailingReturn,
override, final, attrs, initializer)
override, final, attrs, initializer, multi_line=self.multi_line)

def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimple:
"""Just parse the simple ones."""
Expand Down Expand Up @@ -7194,6 +7200,7 @@ class CPPObject(ObjectDescription[ASTDeclaration]):
'noindexentry': directives.flag,
'nocontentsentry': directives.flag,
'tparam-line-spec': directives.flag,
'single-line-signature': directives.flag,
}

def _add_enumerator_to_parent(self, ast: ASTDeclaration) -> None:
Expand Down Expand Up @@ -7350,7 +7357,16 @@ def run(self) -> list[Node]:
def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration:
parentSymbol: Symbol = self.env.temp_data['cpp:parent_symbol']

parser = DefinitionParser(sig, location=signode, config=self.env.config)
max_len = self.env.config.cpp_maximum_signature_line_length
multi_line = (
max_len >= 0
and 'single-line-signature' not in self.options
and len(sig) > max_len
)
signode['is_multi_line'] = multi_line
parser = DefinitionParser(
sig, location=signode, config=self.env.config, multi_line=multi_line
)
try:
ast = self.parse_definition(parser)
parser.assert_end()
Expand Down Expand Up @@ -8142,6 +8158,7 @@ def setup(app: Sphinx) -> dict[str, Any]:
app.add_config_value("cpp_index_common_prefix", [], 'env')
app.add_config_value("cpp_id_attributes", [], 'env')
app.add_config_value("cpp_paren_attributes", [], 'env')
app.add_config_value("cpp_maximum_signature_line_length", -1, 'env')
app.add_post_transform(AliasTransform)

# debug stuff
Expand All @@ -8156,7 +8173,7 @@ def initStuff(app):

return {
'version': 'builtin',
'env_version': 8,
'env_version': 9,
'parallel_read_safe': True,
'parallel_write_safe': True,
}