From 82f1b989499ae33aff6440eeb276d9dc2c4f0e08 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sat, 3 Dec 2022 19:09:22 +0100 Subject: [PATCH 001/103] ENH: enable multiline signatures when too long. --- sphinx/addnodes.py | 7 +++++++ sphinx/domains/python.py | 22 +++++++++++++++++----- sphinx/themes/basic/static/basic.css_t | 5 +++++ sphinx/writers/html5.py | 6 ++++++ 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index d5a92e656ad..8de2228143f 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -183,6 +183,13 @@ class desc_content(nodes.General, nodes.Element): """ +class desc_compact_content(nodes.General, nodes.Element): + """Node for compact object description content. + + Must be the last child node in a :py:class:`desc` node. + """ + + class desc_inline(_desc_classes_injector, nodes.Inline, nodes.TextElement): """Node for a signature fragment in inline text. diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 8801811cba6..ba87a896f0f 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -251,21 +251,22 @@ def _unparse_pep_604_annotation(node: ast.Subscript) -> list[Node]: def _parse_arglist( - arglist: str, env: BuildEnvironment | None = None + arglist: str, env: BuildEnvironment | None = None, multiline=False ) -> addnodes.desc_parameterlist: """Parse a list of arguments using AST parser""" params = addnodes.desc_parameterlist(arglist) sig = signature_from_str('(%s)' % arglist) last_kind = None for param in sig.parameters.values(): + param_node = addnodes.desc_compact_content() if multiline else params if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY: # PEP-570: Separator for Positional Only Parameter: / - params += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/')) + param_node += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/')) if param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD, param.POSITIONAL_ONLY, None): # PEP-3102: Separator for Keyword Only Parameter: * - params += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '*')) + param_node += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '*')) node = addnodes.desc_parameter() if param.kind == param.VAR_POSITIONAL: @@ -292,7 +293,9 @@ def _parse_arglist( node += nodes.inline('', param.default, classes=['default_value'], support_smartquotes=False) - params += node + param_node += node + if multiline: + params += param_node last_kind = param.kind if last_kind == Parameter.POSITIONAL_ONLY: @@ -452,6 +455,7 @@ class PyObject(ObjectDescription[Tuple[str, str]]): 'noindex': directives.flag, 'noindexentry': directives.flag, 'nocontentsentry': directives.flag, + 'singlelinesig': directives.flag, 'module': directives.unchanged, 'canonical': directives.unchanged, 'annotation': directives.unchanged, @@ -533,6 +537,13 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] signode['module'] = modname signode['class'] = classname signode['fullname'] = fullname + max_len = self.env.config.python_maximum_signature_line_length + multiline = ( + max_len >= 0 + and 'singlelinesig' not in self.options + and len(sig) > max_len + ) + signode['is_multiline'] = multiline sig_prefix = self.get_signature_prefix(sig) if sig_prefix: @@ -553,7 +564,7 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] signode += addnodes.desc_name(name, name) if arglist: try: - signode += _parse_arglist(arglist, self.env) + signode += _parse_arglist(arglist, self.env, multiline=multiline) except SyntaxError: # fallback to parse arglist original parser. # it supports to represent optional arguments (ex. "func(foo [, bar])") @@ -1510,6 +1521,7 @@ def setup(app: Sphinx) -> dict[str, Any]: app.add_domain(PythonDomain) app.add_config_value('python_use_unqualified_type_names', False, 'env') + app.add_config_value('python_maximum_signature_line_length', -1, 'env') app.connect('object-description-transform', filter_meta_fields) app.connect('missing-reference', builtin_resolver, priority=900) diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t index 9d5e4419d04..86056e0dd43 100644 --- a/sphinx/themes/basic/static/basic.css_t +++ b/sphinx/themes/basic/static/basic.css_t @@ -670,6 +670,11 @@ dd { margin-left: 30px; } +dd.compact { + margin-top: 0px; + margin-bottom: 0px; +} + dl > dd:last-child, dl > dd:last-child > :last-child { margin-bottom: 0; diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 7a62161b54e..4a8bccebe41 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -114,6 +114,12 @@ def visit_desc_content(self, node: Element) -> None: def depart_desc_content(self, node: Element) -> None: self.body.append('') + def visit_desc_compact_content(self, node: Element) -> None: + self.body.append(self.starttag(node, 'dd', '', classes=['compact'])) + + def depart_desc_compact_content(self, node: Element) -> None: + self.body.append('') + def visit_desc_inline(self, node: Element) -> None: self.body.append(self.starttag(node, 'span', '')) From 8d6dfbab28dcac40e9528d8eb2a9e13bad43d1f5 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sat, 17 Dec 2022 18:51:47 +0100 Subject: [PATCH 002/103] Make parameter nodes inherit from sig classes --- sphinx/addnodes.py | 16 +++++++++------- sphinx/domains/python.py | 4 ++-- sphinx/themes/basic/static/basic.css_t | 2 +- sphinx/writers/html5.py | 20 ++++++++++++++------ 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 8de2228143f..c922245b767 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -183,13 +183,6 @@ class desc_content(nodes.General, nodes.Element): """ -class desc_compact_content(nodes.General, nodes.Element): - """Node for compact object description content. - - Must be the last child node in a :py:class:`desc` node. - """ - - class desc_inline(_desc_classes_injector, nodes.Inline, nodes.TextElement): """Node for a signature fragment in inline text. @@ -256,6 +249,15 @@ def astext(self): return f'({super().astext()})' +class desc_multiline_parameterlist(nodes.Part, nodes.Inline, nodes.FixedTextElement): + """Node for a multiline parameter list. + """ + child_text_separator = ', ' + + def astext(self): + return f'({super().astext()})' + + class desc_parameter(nodes.Part, nodes.Inline, nodes.FixedTextElement): """Node for a single parameter.""" diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index ba87a896f0f..8affba28075 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -254,11 +254,11 @@ def _parse_arglist( arglist: str, env: BuildEnvironment | None = None, multiline=False ) -> addnodes.desc_parameterlist: """Parse a list of arguments using AST parser""" - params = addnodes.desc_parameterlist(arglist) + params = addnodes.desc_multiline_parameterlist(arglist) if multiline else addnodes.desc_parameterlist(arglist) sig = signature_from_str('(%s)' % arglist) last_kind = None for param in sig.parameters.values(): - param_node = addnodes.desc_compact_content() if multiline else params + param_node = addnodes.desc_content() if multiline else params if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY: # PEP-570: Separator for Positional Only Parameter: / param_node += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/')) diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t index 86056e0dd43..58a21409d56 100644 --- a/sphinx/themes/basic/static/basic.css_t +++ b/sphinx/themes/basic/static/basic.css_t @@ -670,7 +670,7 @@ dd { margin-left: 30px; } -dd.compact { +.sig dd { margin-top: 0px; margin-bottom: 0px; } diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 4a8bccebe41..eeac3d20996 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -114,12 +114,6 @@ def visit_desc_content(self, node: Element) -> None: def depart_desc_content(self, node: Element) -> None: self.body.append('') - def visit_desc_compact_content(self, node: Element) -> None: - self.body.append(self.starttag(node, 'dd', '', classes=['compact'])) - - def depart_desc_compact_content(self, node: Element) -> None: - self.body.append('') - def visit_desc_inline(self, node: Element) -> None: self.body.append(self.starttag(node, 'span', '')) @@ -167,6 +161,20 @@ def visit_desc_parameterlist(self, node: Element) -> None: def depart_desc_parameterlist(self, node: Element) -> None: self.body.append(')') + def visit_desc_multiline_parameterlist(self, node: Element) -> None: + self.body.append('(') + self.first_param = 1 + self.optional_param_level = 0 + # How many required parameters are left. + self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) + for c in node.children]) + self.param_separator = node.child_text_separator + self.body.append(self.starttag(node, 'dl')) + + def depart_desc_multiline_parameterlist(self, node: Element) -> None: + self.body.append('\n\n') + self.body.append(')') + # If required parameters are still to come, then put the comma after # the parameter. Otherwise, put the comma before. This ensures that # signatures like the following render correctly (see issue #1001): From 581433bd3e1fe571cbd0285e62af9fd8a7a41938 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sat, 17 Dec 2022 18:52:23 +0100 Subject: [PATCH 003/103] Fix permalink for multiline Python signatures --- sphinx/domains/python.py | 2 ++ sphinx/writers/html5.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 8affba28075..e013c18a5da 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -544,6 +544,8 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] and len(sig) > max_len ) signode['is_multiline'] = multiline + if multiline: + signode['add_permalink'] = True sig_prefix = self.get_signature_prefix(sig) if sig_prefix: diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index eeac3d20996..b401b9f150c 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -95,7 +95,7 @@ def visit_desc_signature(self, node: Element) -> None: def depart_desc_signature(self, node: Element) -> None: self.protect_literal_text -= 1 - if not node.get('is_multiline'): + if not node.get('is_multiline') or node.get('add_permalink'): self.add_permalink_ref(node, _('Permalink to this definition')) self.body.append('\n') From d3ec2336d8d7ca63a7bd6acfe190ff4f35af8d17 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sat, 17 Dec 2022 18:54:18 +0100 Subject: [PATCH 004/103] Enable multiline sig for C and C++ --- sphinx/domains/c.py | 29 ++++++++++++++++++++++++----- sphinx/domains/cpp.py | 24 +++++++++++++++++++----- sphinx/util/cfamily.py | 3 ++- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 3152a03234d..4eecaaeda5d 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -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, + multiline: bool = False, + ) -> None: self.args = args self.attrs = attrs + self.multiline = multiline @property def function_params(self) -> list[ASTFunctionParameter]: @@ -713,13 +719,17 @@ def _stringify(self, transform: StringifyTransform) -> str: def describe_signature(self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol) -> None: verify_description_mode(mode) + multiline = self.multiline # only use the desc_parameterlist for the outer list, not for inner lists if mode == 'lastIsName': - paramlist = addnodes.desc_parameterlist() + paramlist = addnodes.desc_multiline_parameterlist() if multiline else addnodes.desc_parameterlist() for arg in self.args: + param_node = addnodes.desc_content() if multiline else paramlist param = addnodes.desc_parameter('', '', noemph=True) arg.describe_signature(param, 'param', env, symbol=symbol) - paramlist += param + param_node += param + if multiline: + paramlist += param_node signode += paramlist else: signode += addnodes.desc_sig_punctuation('(', '(') @@ -2670,7 +2680,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, multiline=self.multiline) def _parse_decl_specs_simple( self, outer: str | None, typed: bool @@ -3149,6 +3159,7 @@ class CObject(ObjectDescription[ASTDeclaration]): option_spec: OptionSpec = { 'noindexentry': directives.flag, 'nocontentsentry': directives.flag, + 'singlelinesig': directives.flag, } def _add_enumerator_to_parent(self, ast: ASTDeclaration) -> None: @@ -3254,7 +3265,14 @@ 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 + multiline = ( + max_len >= 0 + and 'singlelinesig' not in self.options + and len(sig) > max_len + ) + signode['is_multiline'] = multiline + parser = DefinitionParser(sig, location=signode, config=self.env.config, multiline=multiline) try: ast = self.parse_definition(parser) parser.assert_end() @@ -3870,6 +3888,7 @@ 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 { diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 97964d82fd1..9037fc7639b 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -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, multiline: bool = False) -> None: self.args = args self.volatile = volatile self.const = const @@ -2060,6 +2060,7 @@ def __init__(self, args: list[ASTFunctionParameter], volatile: bool, const: bool self.final = final self.attrs = attrs self.initializer = initializer + self.multiline = multiline @property def function_params(self) -> list[ASTFunctionParameter]: @@ -2129,13 +2130,17 @@ def _stringify(self, transform: StringifyTransform) -> str: def describe_signature(self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol) -> None: verify_description_mode(mode) + multiline = self.multiline # only use the desc_parameterlist for the outer list, not for inner lists if mode == 'lastIsName': - paramlist = addnodes.desc_parameterlist() + paramlist = addnodes.desc_multiline_parameterlist() if multiline else addnodes.desc_parameterlist() for arg in self.args: + param_node = addnodes.desc_content() if multiline else paramlist param = addnodes.desc_parameter('', '', noemph=True) arg.describe_signature(param, 'param', env, symbol=symbol) - paramlist += param + param_node += param + if multiline: + paramlist += param_node signode += paramlist else: signode += addnodes.desc_sig_punctuation('(', '(') @@ -6240,7 +6245,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, multiline=self.multiline) def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimple: """Just parse the simple ones.""" @@ -7194,6 +7199,7 @@ class CPPObject(ObjectDescription[ASTDeclaration]): 'noindexentry': directives.flag, 'nocontentsentry': directives.flag, 'tparam-line-spec': directives.flag, + 'singlelinesig': directives.flag, } def _add_enumerator_to_parent(self, ast: ASTDeclaration) -> None: @@ -7350,7 +7356,14 @@ 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 + multiline = ( + max_len >= 0 + and 'singlelinesig' not in self.options + and len(sig) > max_len + ) + signode['is_multiline'] = multiline + parser = DefinitionParser(sig, location=signode, config=self.env.config, multiline=multiline) try: ast = self.parse_definition(parser) parser.assert_end() @@ -8142,6 +8155,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 diff --git a/sphinx/util/cfamily.py b/sphinx/util/cfamily.py index 4d79179d5bd..c1c5dfb8b2d 100644 --- a/sphinx/util/cfamily.py +++ b/sphinx/util/cfamily.py @@ -237,10 +237,11 @@ class DefinitionError(Exception): class BaseParser: def __init__(self, definition: str, *, location: nodes.Node | tuple[str, int] | str, - config: Config) -> None: + config: Config, multiline: bool = False) -> None: self.definition = definition.strip() self.location = location # for warnings self.config = config + self.multiline = multiline self.pos = 0 self.end = len(self.definition) From 1229b66ec90e0b1d83e739b2efcfac7c8e6b05e7 Mon Sep 17 00:00:00 2001 From: TLouf Date: Mon, 26 Dec 2022 17:46:38 +0100 Subject: [PATCH 005/103] Simplify `desc_parameterlist` into single class --- sphinx/addnodes.py | 11 +++-------- sphinx/domains/c.py | 2 +- sphinx/domains/cpp.py | 2 +- sphinx/domains/python.py | 2 +- sphinx/writers/html5.py | 18 ++++-------------- 5 files changed, 10 insertions(+), 25 deletions(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index c922245b767..07dd98c4cf2 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -245,14 +245,9 @@ class desc_parameterlist(nodes.Part, nodes.Inline, nodes.FixedTextElement): """Node for a general parameter list.""" child_text_separator = ', ' - def astext(self): - return f'({super().astext()})' - - -class desc_multiline_parameterlist(nodes.Part, nodes.Inline, nodes.FixedTextElement): - """Node for a multiline parameter list. - """ - child_text_separator = ', ' + def __init__(self, *args: Any, multiline: bool = False, **kwargs: Any) -> None: + super().__init__(*args, **kwargs) + self.multiline = multiline def astext(self): return f'({super().astext()})' diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 4eecaaeda5d..8a69e4a19cc 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -722,7 +722,7 @@ def describe_signature(self, signode: TextElement, mode: str, multiline = self.multiline # only use the desc_parameterlist for the outer list, not for inner lists if mode == 'lastIsName': - paramlist = addnodes.desc_multiline_parameterlist() if multiline else addnodes.desc_parameterlist() + paramlist = addnodes.desc_parameterlist(multiline=multiline) for arg in self.args: param_node = addnodes.desc_content() if multiline else paramlist param = addnodes.desc_parameter('', '', noemph=True) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 9037fc7639b..7dba6513f68 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -2133,7 +2133,7 @@ def describe_signature(self, signode: TextElement, mode: str, multiline = self.multiline # only use the desc_parameterlist for the outer list, not for inner lists if mode == 'lastIsName': - paramlist = addnodes.desc_multiline_parameterlist() if multiline else addnodes.desc_parameterlist() + paramlist = addnodes.desc_parameterlist(multiline=multiline) for arg in self.args: param_node = addnodes.desc_content() if multiline else paramlist param = addnodes.desc_parameter('', '', noemph=True) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index e013c18a5da..6958581894b 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -254,7 +254,7 @@ def _parse_arglist( arglist: str, env: BuildEnvironment | None = None, multiline=False ) -> addnodes.desc_parameterlist: """Parse a list of arguments using AST parser""" - params = addnodes.desc_multiline_parameterlist(arglist) if multiline else addnodes.desc_parameterlist(arglist) + params = addnodes.desc_parameterlist(multiline=multiline) sig = signature_from_str('(%s)' % arglist) last_kind = None for param in sig.parameters.values(): diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index b401b9f150c..e874720ec3a 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -157,22 +157,12 @@ def visit_desc_parameterlist(self, node: Element) -> None: self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) for c in node.children]) self.param_separator = node.child_text_separator + if node.multiline: + self.body.append(self.starttag(node, 'dl')) def depart_desc_parameterlist(self, node: Element) -> None: - self.body.append(')') - - def visit_desc_multiline_parameterlist(self, node: Element) -> None: - self.body.append('(') - self.first_param = 1 - self.optional_param_level = 0 - # How many required parameters are left. - self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) - for c in node.children]) - self.param_separator = node.child_text_separator - self.body.append(self.starttag(node, 'dl')) - - def depart_desc_multiline_parameterlist(self, node: Element) -> None: - self.body.append('\n\n') + if node.multiline: + self.body.append('\n\n') self.body.append(')') # If required parameters are still to come, then put the comma after From ec713e932f7abe96bc9ede031553db9849699fd2 Mon Sep 17 00:00:00 2001 From: TLouf Date: Mon, 26 Dec 2022 17:48:54 +0100 Subject: [PATCH 006/103] Fix flake8 errors --- sphinx/domains/c.py | 4 +++- sphinx/domains/cpp.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 8a69e4a19cc..7bd45721733 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -3272,7 +3272,9 @@ def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration: and len(sig) > max_len ) signode['is_multiline'] = multiline - parser = DefinitionParser(sig, location=signode, config=self.env.config, multiline=multiline) + parser = DefinitionParser( + sig, location=signode, config=self.env.config, multiline=multiline + ) try: ast = self.parse_definition(parser) parser.assert_end() diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 7dba6513f68..d9d5063b128 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -7363,7 +7363,9 @@ def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration: and len(sig) > max_len ) signode['is_multiline'] = multiline - parser = DefinitionParser(sig, location=signode, config=self.env.config, multiline=multiline) + parser = DefinitionParser( + sig, location=signode, config=self.env.config, multiline=multiline + ) try: ast = self.parse_definition(parser) parser.assert_end() From 85ff4930ffa1b26e5e2ea1354c95fffe373f7aae Mon Sep 17 00:00:00 2001 From: TLouf Date: Mon, 26 Dec 2022 18:04:05 +0100 Subject: [PATCH 007/103] Pass multiline as `desc_parameterlist` attribute --- sphinx/addnodes.py | 4 ---- sphinx/domains/c.py | 3 ++- sphinx/domains/cpp.py | 3 ++- sphinx/domains/python.py | 3 ++- sphinx/writers/html5.py | 4 ++-- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 07dd98c4cf2..d5a92e656ad 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -245,10 +245,6 @@ class desc_parameterlist(nodes.Part, nodes.Inline, nodes.FixedTextElement): """Node for a general parameter list.""" child_text_separator = ', ' - def __init__(self, *args: Any, multiline: bool = False, **kwargs: Any) -> None: - super().__init__(*args, **kwargs) - self.multiline = multiline - def astext(self): return f'({super().astext()})' diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 7bd45721733..f482d607a9b 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -722,7 +722,8 @@ def describe_signature(self, signode: TextElement, mode: str, multiline = self.multiline # only use the desc_parameterlist for the outer list, not for inner lists if mode == 'lastIsName': - paramlist = addnodes.desc_parameterlist(multiline=multiline) + paramlist = addnodes.desc_parameterlist() + paramlist['is_multiline'] = multiline for arg in self.args: param_node = addnodes.desc_content() if multiline else paramlist param = addnodes.desc_parameter('', '', noemph=True) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index d9d5063b128..b2730793773 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -2133,7 +2133,8 @@ def describe_signature(self, signode: TextElement, mode: str, multiline = self.multiline # only use the desc_parameterlist for the outer list, not for inner lists if mode == 'lastIsName': - paramlist = addnodes.desc_parameterlist(multiline=multiline) + paramlist = addnodes.desc_parameterlist() + paramlist['is_multiline'] = multiline for arg in self.args: param_node = addnodes.desc_content() if multiline else paramlist param = addnodes.desc_parameter('', '', noemph=True) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 6958581894b..a0759f07db0 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -254,7 +254,8 @@ def _parse_arglist( arglist: str, env: BuildEnvironment | None = None, multiline=False ) -> addnodes.desc_parameterlist: """Parse a list of arguments using AST parser""" - params = addnodes.desc_parameterlist(multiline=multiline) + params = addnodes.desc_parameterlist() + params['is_multiline'] = multiline sig = signature_from_str('(%s)' % arglist) last_kind = None for param in sig.parameters.values(): diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index e874720ec3a..bf817d9ca26 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -157,11 +157,11 @@ def visit_desc_parameterlist(self, node: Element) -> None: self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) for c in node.children]) self.param_separator = node.child_text_separator - if node.multiline: + if node.get('is_multiline'): self.body.append(self.starttag(node, 'dl')) def depart_desc_parameterlist(self, node: Element) -> None: - if node.multiline: + if node.get('is_multiline'): self.body.append('\n\n') self.body.append(')') From 8ca560dfd03a0a82fa57e4fe1b64e76dc0d4ac6e Mon Sep 17 00:00:00 2001 From: TLouf Date: Mon, 26 Dec 2022 18:04:14 +0100 Subject: [PATCH 008/103] Add type annotation --- sphinx/domains/python.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index a0759f07db0..5b30eda695d 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -251,7 +251,7 @@ def _unparse_pep_604_annotation(node: ast.Subscript) -> list[Node]: def _parse_arglist( - arglist: str, env: BuildEnvironment | None = None, multiline=False + arglist: str, env: BuildEnvironment | None = None, multiline: bool = False ) -> addnodes.desc_parameterlist: """Parse a list of arguments using AST parser""" params = addnodes.desc_parameterlist() From 9ec46983d8b07b18bca946be13f39a4b276dd912 Mon Sep 17 00:00:00 2001 From: TLouf Date: Wed, 28 Dec 2022 16:25:13 +0100 Subject: [PATCH 009/103] Use new desc_parameterline instead of desc_content --- sphinx/addnodes.py | 4 ++++ sphinx/domains/c.py | 2 +- sphinx/domains/cpp.py | 2 +- sphinx/domains/python.py | 2 +- sphinx/writers/html5.py | 6 ++++++ 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index d5a92e656ad..0269a087e34 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -249,6 +249,10 @@ def astext(self): return f'({super().astext()})' +class desc_parameterline(nodes.General, nodes.Element): + """Node for a parameter line.""" + + class desc_parameter(nodes.Part, nodes.Inline, nodes.FixedTextElement): """Node for a single parameter.""" diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index f482d607a9b..196dd1a0d23 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -725,7 +725,7 @@ def describe_signature(self, signode: TextElement, mode: str, paramlist = addnodes.desc_parameterlist() paramlist['is_multiline'] = multiline for arg in self.args: - param_node = addnodes.desc_content() if multiline else paramlist + param_node = addnodes.desc_parameterline() if multiline else paramlist param = addnodes.desc_parameter('', '', noemph=True) arg.describe_signature(param, 'param', env, symbol=symbol) param_node += param diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index b2730793773..0f3702db35c 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -2136,7 +2136,7 @@ def describe_signature(self, signode: TextElement, mode: str, paramlist = addnodes.desc_parameterlist() paramlist['is_multiline'] = multiline for arg in self.args: - param_node = addnodes.desc_content() if multiline else paramlist + param_node = addnodes.desc_parameterline() if multiline else paramlist param = addnodes.desc_parameter('', '', noemph=True) arg.describe_signature(param, 'param', env, symbol=symbol) param_node += param diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 5b30eda695d..d004a10136a 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -259,7 +259,7 @@ def _parse_arglist( sig = signature_from_str('(%s)' % arglist) last_kind = None for param in sig.parameters.values(): - param_node = addnodes.desc_content() if multiline else params + param_node = addnodes.desc_parameterline() if multiline else params if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY: # PEP-570: Separator for Positional Only Parameter: / param_node += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/')) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index bf817d9ca26..a74bd905ae9 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -165,6 +165,12 @@ def depart_desc_parameterlist(self, node: Element) -> None: self.body.append('\n\n') self.body.append(')') + def visit_desc_parameterline(self, node: Element) -> None: + self.body.append(self.starttag(node, 'dd', '')) + + def depart_desc_parameterline(self, node: Element) -> None: + self.body.append('') + # If required parameters are still to come, then put the comma after # the parameter. Otherwise, put the comma before. This ensures that # signatures like the following render correctly (see issue #1001): From b93ecf00b8c2533855aeb77c76852b4a686bc854 Mon Sep 17 00:00:00 2001 From: TLouf Date: Wed, 28 Dec 2022 22:07:10 +0100 Subject: [PATCH 010/103] Support HTML writer --- sphinx/writers/_html4.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sphinx/writers/_html4.py b/sphinx/writers/_html4.py index 3d36784cec7..befcb36a444 100644 --- a/sphinx/writers/_html4.py +++ b/sphinx/writers/_html4.py @@ -151,10 +151,20 @@ def visit_desc_parameterlist(self, node: Element) -> None: self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) for c in node.children]) self.param_separator = node.child_text_separator + if node.get('is_multiline'): + self.body.append(self.starttag(node, 'dl')) def depart_desc_parameterlist(self, node: Element) -> None: + if node.get('is_multiline'): + self.body.append('\n\n') self.body.append(')') + def visit_desc_parameterline(self, node: Element) -> None: + self.body.append(self.starttag(node, 'dd', '')) + + def depart_desc_parameterline(self, node: Element) -> None: + self.body.append('') + # If required parameters are still to come, then put the comma after # the parameter. Otherwise, put the comma before. This ensures that # signatures like the following render correctly (see issue #1001): From 3b110cefc5df272fbc925581f911d7a4e193c959 Mon Sep 17 00:00:00 2001 From: TLouf Date: Wed, 28 Dec 2022 22:07:22 +0100 Subject: [PATCH 011/103] Support text writer --- sphinx/writers/text.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index 7ac2359c8b4..149768ddbec 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -599,8 +599,15 @@ def visit_desc_parameterlist(self, node: Element) -> None: def depart_desc_parameterlist(self, node: Element) -> None: self.add_text(')') + def visit_desc_parameterline(self, node: Element) -> None: + self.new_state() + + def depart_desc_parameterline(self, node: Element) -> None: + self.add_text(',') + self.end_state(wrap=False, end=None) + def visit_desc_parameter(self, node: Element) -> None: - if not self.first_param: + if not self.first_param and not node.parent.parent.get('is_multiline'): self.add_text(', ') else: self.first_param = 0 From 234acd66eeb085d65c1a4b53b24277e1cded0465 Mon Sep 17 00:00:00 2001 From: TLouf Date: Thu, 29 Dec 2022 19:00:55 +0100 Subject: [PATCH 012/103] Increment environment versions --- sphinx/domains/c.py | 2 +- sphinx/domains/cpp.py | 2 +- sphinx/domains/python.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 196dd1a0d23..2c132a69ab1 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -3896,7 +3896,7 @@ def setup(app: Sphinx) -> dict[str, Any]: return { 'version': 'builtin', - 'env_version': 2, + 'env_version': 3, 'parallel_read_safe': True, 'parallel_write_safe': True, } diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 0f3702db35c..7b8c7820b18 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -8173,7 +8173,7 @@ def initStuff(app): return { 'version': 'builtin', - 'env_version': 8, + 'env_version': 9, 'parallel_read_safe': True, 'parallel_write_safe': True, } diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index d004a10136a..e8d086350dc 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -1530,7 +1530,7 @@ def setup(app: Sphinx) -> dict[str, Any]: return { 'version': 'builtin', - 'env_version': 3, + 'env_version': 4, 'parallel_read_safe': True, 'parallel_write_safe': True, } From 7c016c9fbca34f95dec8d85895d07d3ae6e5b584 Mon Sep 17 00:00:00 2001 From: TLouf Date: Fri, 30 Dec 2022 13:00:46 +0100 Subject: [PATCH 013/103] Better variable names --- sphinx/addnodes.py | 9 ++++----- sphinx/domains/c.py | 26 +++++++++++++------------- sphinx/domains/cpp.py | 26 +++++++++++++------------- sphinx/domains/python.py | 20 ++++++++++---------- sphinx/util/cfamily.py | 4 ++-- sphinx/writers/_html4.py | 10 +++++----- sphinx/writers/html5.py | 10 +++++----- sphinx/writers/text.py | 6 +++--- 8 files changed, 55 insertions(+), 56 deletions(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 0269a087e34..5d3858772b0 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -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. @@ -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 @@ -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 = '' @@ -249,8 +249,7 @@ def astext(self): return f'({super().astext()})' -class desc_parameterline(nodes.General, nodes.Element): - """Node for a parameter line.""" +class desc_parameter_line(nodes.General, nodes.Element): class desc_parameter(nodes.Part, nodes.Inline, nodes.FixedTextElement): diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 2c132a69ab1..ff84cf877c2 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -691,11 +691,11 @@ def __init__( self, args: list[ASTFunctionParameter], attrs: ASTAttributeList, - multiline: bool = False, + multi_line: bool = False, ) -> None: self.args = args self.attrs = attrs - self.multiline = multiline + self.multi_line = multi_line @property def function_params(self) -> list[ASTFunctionParameter]: @@ -719,17 +719,17 @@ def _stringify(self, transform: StringifyTransform) -> str: def describe_signature(self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol) -> None: verify_description_mode(mode) - multiline = self.multiline + 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_multiline'] = multiline + paramlist['is_multi_line'] = multi_line for arg in self.args: - param_node = addnodes.desc_parameterline() if multiline else paramlist + 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) param_node += param - if multiline: + if multi_line: paramlist += param_node signode += paramlist else: @@ -1468,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' @@ -2681,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, multiline=self.multiline) + return ASTParameters(args, attrs, multi_line=self.multi_line) def _parse_decl_specs_simple( self, outer: str | None, typed: bool @@ -3160,7 +3160,7 @@ class CObject(ObjectDescription[ASTDeclaration]): option_spec: OptionSpec = { 'noindexentry': directives.flag, 'nocontentsentry': directives.flag, - 'singlelinesig': directives.flag, + 'single-line-signature': directives.flag, } def _add_enumerator_to_parent(self, ast: ASTDeclaration) -> None: @@ -3267,14 +3267,14 @@ 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 - multiline = ( + multi_line = ( max_len >= 0 - and 'singlelinesig' not in self.options + and 'single-line-signature' not in self.options and len(sig) > max_len ) - signode['is_multiline'] = multiline + signode['is_multi_line'] = multi_line parser = DefinitionParser( - sig, location=signode, config=self.env.config, multiline=multiline + sig, location=signode, config=self.env.config, multi_line=multi_line ) try: ast = self.parse_definition(parser) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 7b8c7820b18..25f9c1c4273 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -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, multiline: bool = False) -> None: + initializer: str | None, multi_line: bool = False) -> None: self.args = args self.volatile = volatile self.const = const @@ -2060,7 +2060,7 @@ def __init__(self, args: list[ASTFunctionParameter], volatile: bool, const: bool self.final = final self.attrs = attrs self.initializer = initializer - self.multiline = multiline + self.multi_line = multi_line @property def function_params(self) -> list[ASTFunctionParameter]: @@ -2130,17 +2130,17 @@ def _stringify(self, transform: StringifyTransform) -> str: def describe_signature(self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol) -> None: verify_description_mode(mode) - multiline = self.multiline + 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_multiline'] = multiline + paramlist['is_multi_line'] = multi_line for arg in self.args: - param_node = addnodes.desc_parameterline() if multiline else paramlist + param_node = addnodes.desc_parameterline() if multi_line else paramlist param = addnodes.desc_parameter('', '', noemph=True) arg.describe_signature(param, 'param', env, symbol=symbol) param_node += param - if multiline: + if multi_line: paramlist += param_node signode += paramlist else: @@ -4075,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' @@ -6246,7 +6246,7 @@ def _parse_parameters_and_qualifiers(self, paramMode: str) -> ASTParametersQuali return ASTParametersQualifiers( args, volatile, const, refQual, exceptionSpec, trailingReturn, - override, final, attrs, initializer, multiline=self.multiline) + 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.""" @@ -7200,7 +7200,7 @@ class CPPObject(ObjectDescription[ASTDeclaration]): 'noindexentry': directives.flag, 'nocontentsentry': directives.flag, 'tparam-line-spec': directives.flag, - 'singlelinesig': directives.flag, + 'single-line-signature': directives.flag, } def _add_enumerator_to_parent(self, ast: ASTDeclaration) -> None: @@ -7358,14 +7358,14 @@ def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration: parentSymbol: Symbol = self.env.temp_data['cpp:parent_symbol'] max_len = self.env.config.cpp_maximum_signature_line_length - multiline = ( + multi_line = ( max_len >= 0 - and 'singlelinesig' not in self.options + and 'single-line-signature' not in self.options and len(sig) > max_len ) - signode['is_multiline'] = multiline + signode['is_multi_line'] = multi_line parser = DefinitionParser( - sig, location=signode, config=self.env.config, multiline=multiline + sig, location=signode, config=self.env.config, multi_line=multi_line ) try: ast = self.parse_definition(parser) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index e8d086350dc..ab06bafdeeb 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -251,15 +251,15 @@ def _unparse_pep_604_annotation(node: ast.Subscript) -> list[Node]: def _parse_arglist( - arglist: str, env: BuildEnvironment | None = None, multiline: bool = False + arglist: str, env: BuildEnvironment | None = None, multi_line: bool = False ) -> addnodes.desc_parameterlist: """Parse a list of arguments using AST parser""" params = addnodes.desc_parameterlist() - params['is_multiline'] = multiline + params['is_multi_line'] = multi_line sig = signature_from_str('(%s)' % arglist) last_kind = None for param in sig.parameters.values(): - param_node = addnodes.desc_parameterline() if multiline else params + param_node = addnodes.desc_parameter_line() if multi_line else params if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY: # PEP-570: Separator for Positional Only Parameter: / param_node += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/')) @@ -295,7 +295,7 @@ def _parse_arglist( support_smartquotes=False) param_node += node - if multiline: + if multi_line: params += param_node last_kind = param.kind @@ -456,7 +456,7 @@ class PyObject(ObjectDescription[Tuple[str, str]]): 'noindex': directives.flag, 'noindexentry': directives.flag, 'nocontentsentry': directives.flag, - 'singlelinesig': directives.flag, + 'single-line-signature': directives.flag, 'module': directives.unchanged, 'canonical': directives.unchanged, 'annotation': directives.unchanged, @@ -539,13 +539,13 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] signode['class'] = classname signode['fullname'] = fullname max_len = self.env.config.python_maximum_signature_line_length - multiline = ( + multi_line = ( max_len >= 0 - and 'singlelinesig' not in self.options + and 'single-line-signature' not in self.options and len(sig) > max_len ) - signode['is_multiline'] = multiline - if multiline: + signode['is_multi_line'] = multi_line + if multi_line: signode['add_permalink'] = True sig_prefix = self.get_signature_prefix(sig) @@ -567,7 +567,7 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] signode += addnodes.desc_name(name, name) if arglist: try: - signode += _parse_arglist(arglist, self.env, multiline=multiline) + signode += _parse_arglist(arglist, self.env, multi_line=multi_line) except SyntaxError: # fallback to parse arglist original parser. # it supports to represent optional arguments (ex. "func(foo [, bar])") diff --git a/sphinx/util/cfamily.py b/sphinx/util/cfamily.py index c1c5dfb8b2d..46b38a21ab4 100644 --- a/sphinx/util/cfamily.py +++ b/sphinx/util/cfamily.py @@ -237,11 +237,11 @@ class DefinitionError(Exception): class BaseParser: def __init__(self, definition: str, *, location: nodes.Node | tuple[str, int] | str, - config: Config, multiline: bool = False) -> None: + config: Config, multi_line: bool = False) -> None: self.definition = definition.strip() self.location = location # for warnings self.config = config - self.multiline = multiline + self.multi_line = multi_line self.pos = 0 self.end = len(self.definition) diff --git a/sphinx/writers/_html4.py b/sphinx/writers/_html4.py index befcb36a444..ba3fa3ff9c6 100644 --- a/sphinx/writers/_html4.py +++ b/sphinx/writers/_html4.py @@ -89,7 +89,7 @@ def visit_desc_signature(self, node: Element) -> None: def depart_desc_signature(self, node: Element) -> None: self.protect_literal_text -= 1 - if not node.get('is_multiline'): + if not node.get('is_multi_line'): self.add_permalink_ref(node, _('Permalink to this definition')) self.body.append('\n') @@ -151,18 +151,18 @@ def visit_desc_parameterlist(self, node: Element) -> None: self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) for c in node.children]) self.param_separator = node.child_text_separator - if node.get('is_multiline'): + if node.get('is_multi_line'): self.body.append(self.starttag(node, 'dl')) def depart_desc_parameterlist(self, node: Element) -> None: - if node.get('is_multiline'): + if node.get('is_multi_line'): self.body.append('\n\n') self.body.append(')') - def visit_desc_parameterline(self, node: Element) -> None: + def visit_desc_parameter_line(self, node: Element) -> None: self.body.append(self.starttag(node, 'dd', '')) - def depart_desc_parameterline(self, node: Element) -> None: + def depart_desc_parameter_line(self, node: Element) -> None: self.body.append('') # If required parameters are still to come, then put the comma after diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index a74bd905ae9..1c4e6149bb1 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -95,7 +95,7 @@ def visit_desc_signature(self, node: Element) -> None: def depart_desc_signature(self, node: Element) -> None: self.protect_literal_text -= 1 - if not node.get('is_multiline') or node.get('add_permalink'): + if not node.get('is_multi_line') or node.get('add_permalink'): self.add_permalink_ref(node, _('Permalink to this definition')) self.body.append('\n') @@ -157,18 +157,18 @@ def visit_desc_parameterlist(self, node: Element) -> None: self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) for c in node.children]) self.param_separator = node.child_text_separator - if node.get('is_multiline'): + if node.get('is_multi_line'): self.body.append(self.starttag(node, 'dl')) def depart_desc_parameterlist(self, node: Element) -> None: - if node.get('is_multiline'): + if node.get('is_multi_line'): self.body.append('\n\n') self.body.append(')') - def visit_desc_parameterline(self, node: Element) -> None: + def visit_desc_parameter_line(self, node: Element) -> None: self.body.append(self.starttag(node, 'dd', '')) - def depart_desc_parameterline(self, node: Element) -> None: + def depart_desc_parameter_line(self, node: Element) -> None: self.body.append('') # If required parameters are still to come, then put the comma after diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index 149768ddbec..1b0717dfdda 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -599,15 +599,15 @@ def visit_desc_parameterlist(self, node: Element) -> None: def depart_desc_parameterlist(self, node: Element) -> None: self.add_text(')') - def visit_desc_parameterline(self, node: Element) -> None: + def visit_desc_parameter_line(self, node: Element) -> None: self.new_state() - def depart_desc_parameterline(self, node: Element) -> None: + def depart_desc_parameter_line(self, node: Element) -> None: self.add_text(',') self.end_state(wrap=False, end=None) def visit_desc_parameter(self, node: Element) -> None: - if not self.first_param and not node.parent.parent.get('is_multiline'): + if not self.first_param and not node.parent.parent.get('is_multi_line'): self.add_text(', ') else: self.first_param = 0 From 5b8735d6d7e84d877ab8f35f30dbfafb287be590 Mon Sep 17 00:00:00 2001 From: TLouf Date: Fri, 30 Dec 2022 13:11:29 +0100 Subject: [PATCH 014/103] Expand docstrings of new nodes' classes --- sphinx/addnodes.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 5d3858772b0..393db7440a4 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -242,7 +242,13 @@ 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): @@ -250,6 +256,12 @@ def astext(self): 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): From 1ca68b0117041fd1662b516ae8cf1e674ca5af28 Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 3 Jan 2023 12:19:12 +0100 Subject: [PATCH 015/103] Fix oversight of param line name change in C++ --- sphinx/domains/cpp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 25f9c1c4273..f36d8066a0b 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -2136,7 +2136,7 @@ def describe_signature(self, signode: TextElement, mode: str, paramlist = addnodes.desc_parameterlist() paramlist['is_multi_line'] = multi_line for arg in self.args: - param_node = addnodes.desc_parameterline() if multi_line else paramlist + 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) param_node += param From 8e198f3e49f8d0efe2b32bdcc94124c58227b57e Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 3 Jan 2023 12:19:55 +0100 Subject: [PATCH 016/103] Add newlines to make param list HTML prettier --- sphinx/writers/_html4.py | 3 ++- sphinx/writers/html5.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sphinx/writers/_html4.py b/sphinx/writers/_html4.py index ba3fa3ff9c6..b2b345e110c 100644 --- a/sphinx/writers/_html4.py +++ b/sphinx/writers/_html4.py @@ -152,11 +152,12 @@ def visit_desc_parameterlist(self, node: Element) -> None: for c in node.children]) self.param_separator = node.child_text_separator if node.get('is_multi_line'): + self.body.append('\n\n') self.body.append(self.starttag(node, 'dl')) def depart_desc_parameterlist(self, node: Element) -> None: if node.get('is_multi_line'): - self.body.append('\n\n') + self.body.append('\n\n\n') self.body.append(')') def visit_desc_parameter_line(self, node: Element) -> None: diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 1c4e6149bb1..c490b79a1d1 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -158,11 +158,12 @@ def visit_desc_parameterlist(self, node: Element) -> None: for c in node.children]) self.param_separator = node.child_text_separator if node.get('is_multi_line'): + self.body.append('\n\n') self.body.append(self.starttag(node, 'dl')) def depart_desc_parameterlist(self, node: Element) -> None: if node.get('is_multi_line'): - self.body.append('\n\n') + self.body.append('\n\n\n') self.body.append(')') def visit_desc_parameter_line(self, node: Element) -> None: From 25ae353daf92420d22badc19b17519c758617773 Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 3 Jan 2023 12:20:46 +0100 Subject: [PATCH 017/103] Tweak EPUB's CSS to match HTML's --- sphinx/themes/epub/static/epub.css_t | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sphinx/themes/epub/static/epub.css_t b/sphinx/themes/epub/static/epub.css_t index 767d558be20..15938cdc54e 100644 --- a/sphinx/themes/epub/static/epub.css_t +++ b/sphinx/themes/epub/static/epub.css_t @@ -458,6 +458,11 @@ dl { margin-bottom: 15px; } +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + dd p { margin-top: 0px; } @@ -472,6 +477,11 @@ dd { margin-left: 30px; } +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + dt:target, .highlighted { background-color: #ddd; } From d6b2dac464e68680f481eda80a90083143adbc91 Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 3 Jan 2023 12:25:58 +0100 Subject: [PATCH 018/103] Add tests for python domain --- .../conf.py | 1 + .../index.rst | 4 + tests/test_domain_py.py | 77 +++++++++++++++++-- 3 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 tests/roots/test-domain-py-python_maximum_signature_line_length/conf.py create mode 100644 tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst diff --git a/tests/roots/test-domain-py-python_maximum_signature_line_length/conf.py b/tests/roots/test-domain-py-python_maximum_signature_line_length/conf.py new file mode 100644 index 00000000000..4d1d54d928c --- /dev/null +++ b/tests/roots/test-domain-py-python_maximum_signature_line_length/conf.py @@ -0,0 +1 @@ +python_maximum_signature_line_length = len("hello(name: str) -> str") - 1 diff --git a/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst b/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst new file mode 100644 index 00000000000..cc5e9e06a1b --- /dev/null +++ b/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst @@ -0,0 +1,4 @@ +domain-py-maximum_signature_line_length +======================================= + +.. py:function:: hello(name: str) -> str diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index 19fcfd36f5b..09bd0c9a83d 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -9,11 +9,10 @@ from sphinx import addnodes from sphinx.addnodes import (desc, desc_addname, desc_annotation, desc_content, desc_name, - desc_optional, desc_parameter, desc_parameterlist, desc_returns, - desc_sig_keyword, desc_sig_literal_number, - desc_sig_literal_string, desc_sig_name, desc_sig_operator, - desc_sig_punctuation, desc_sig_space, desc_signature, - pending_xref) + desc_optional, desc_parameter, desc_parameter_line, + desc_parameterlist, desc_returns, desc_sig_keyword, + desc_sig_literal_number, desc_sig_literal_string, + desc_sig_name, desc_sig_operator, desc_sig_punctuation, desc_sig_space, desc_signature, pending_xref) from sphinx.domains import IndexEntry from sphinx.domains.python import (PythonDomain, PythonModuleIndex, _parse_annotation, _pseudo_parse_arglist, py_sig_re) @@ -1439,3 +1438,71 @@ def test_signature_line_number(app, include_options): source, line = docutils.utils.get_source_line(xrefs[0]) assert 'index.rst' in source assert line == 1 + + +@pytest.mark.sphinx( + 'html', + confoverrides={'python_maximum_signature_line_length': len("hello(name: str) -> str")} +) +def test_pyfunction_signature_with_python_maximum_signature_line_length(app): + text = ".. py:function:: hello(name: str) -> str" + doctree = restructuredtext.parse(app, text) + expected_doctree = (addnodes.index, + [desc, ([desc_signature, ([desc_name, "hello"], + desc_parameterlist, + [desc_returns, pending_xref, "str"])], + desc_content)]) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="py", objtype="function", noindex=False) + signame_node = [desc_sig_name, "name"] + expected_sig = [desc_parameterlist, desc_parameter, (signame_node, + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"])] + assert_node(doctree[1][0][1], expected_sig) + + + text = (".. py:function:: hello(names: str) -> str\n" + " :single-line-signature:") + signame_node[1] = "names" + doctree = restructuredtext.parse(app, text) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="py", objtype="function", noindex=False) + assert_node(doctree[1][0][1], expected_sig) + + text = ".. py:function:: hello(names: str) -> str" + doctree = restructuredtext.parse(app, text) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="py", objtype="function", noindex=False) + expected_sig.insert(1, desc_parameter_line) + + assert_node(doctree[1][0][1], expected_sig) + + +@pytest.mark.sphinx( + 'html', testroot='domain-py-python_maximum_signature_line_length', +) +def test_python_python_maximum_signature_line_length(app, status, warning): + app.build() + content = (app.outdir / 'index.html').read_text(encoding='utf8') + expected = '\n'.join(( + '
', + ( + '
name' + ': ' + 'str,
' + ), + '
', + '', + ( + ') ' + ' ' + 'str' + '' + '' + ), + )) + assert expected in content From af77cadb63fd278ab57d353cf6f10756bb1058dd Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 3 Jan 2023 16:19:52 +0100 Subject: [PATCH 019/103] Add tests for C domain --- .../conf.py | 1 + .../index.rst | 4 ++ tests/test_domain_c.py | 71 ++++++++++++++++++- 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tests/roots/test-domain-c-c_maximum_signature_line_length/conf.py create mode 100644 tests/roots/test-domain-c-c_maximum_signature_line_length/index.rst diff --git a/tests/roots/test-domain-c-c_maximum_signature_line_length/conf.py b/tests/roots/test-domain-c-c_maximum_signature_line_length/conf.py new file mode 100644 index 00000000000..ba480ed2884 --- /dev/null +++ b/tests/roots/test-domain-c-c_maximum_signature_line_length/conf.py @@ -0,0 +1 @@ +c_maximum_signature_line_length = len("str hello(str name)") - 1 diff --git a/tests/roots/test-domain-c-c_maximum_signature_line_length/index.rst b/tests/roots/test-domain-c-c_maximum_signature_line_length/index.rst new file mode 100644 index 00000000000..be20940ec33 --- /dev/null +++ b/tests/roots/test-domain-c-c_maximum_signature_line_length/index.rst @@ -0,0 +1,4 @@ +domain-c-c_maximum_signature_line_length +======================================== + +.. c:function:: str hello(str name) diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index ac9142e687f..b657de73def 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -5,9 +5,13 @@ from xml.etree import ElementTree import pytest +from docutils import nodes from sphinx import addnodes -from sphinx.addnodes import desc +from sphinx.addnodes import (desc, desc_name, desc_content, desc_parameter, + desc_parameter_line, desc_parameterlist, desc_sig_name, + desc_sig_space, desc_signature, desc_signature_line, + pending_xref) from sphinx.domains.c import (DefinitionError, DefinitionParser, Symbol, _id_prefix, _macroKeywords, _max_id) from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping @@ -808,3 +812,68 @@ def test_domain_c_parse_noindexentry(app): assert_node(doctree, (addnodes.index, desc, addnodes.index, desc)) assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C function)', 'c.f', '', None)]) assert_node(doctree[2], addnodes.index, entries=[]) + + +@pytest.mark.sphinx( + 'html', + confoverrides={'c_maximum_signature_line_length': len("str hello(str name)")} +) +def test_cfunction_signature_with_c_maximum_signature_line_length(app): + text = ".. c:function:: str hello(str name)" + doctree = restructuredtext.parse(app, text) + expected_doctree = ( + addnodes.index, + [desc, ([desc_signature, ([desc_signature_line, (pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, "hello"]], + desc_parameterlist)])], + desc_content)] + ) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="c", objtype="function", noindex=False) + signame_node = [desc_sig_name, "name"] + expected_sig = [desc_parameterlist, desc_parameter, ([pending_xref, [desc_sig_name, "str"]], + desc_sig_space, + signame_node)] + assert_node(doctree[1][0][0][3], expected_sig) + + text = (".. c:function:: str hello(str names)\n" + " :single-line-signature:") + signame_node[1] = "names" + doctree = restructuredtext.parse(app, text) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="c", objtype="function", noindex=False) + assert_node(doctree[1][0][0][3], expected_sig) + + text = ".. c:function:: str hello(str names)" + doctree = restructuredtext.parse(app, text) + expected_sig.insert(1, desc_parameter_line) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="c", objtype="function", noindex=False) + assert_node(doctree[1][0][0][3], expected_sig) + + +@pytest.mark.sphinx( + 'html', testroot='domain-c-c_maximum_signature_line_length', +) +def test_domain_c_c_maximum_signature_line_length(app, status, warning): + app.build() + content = (app.outdir / 'index.html').read_text(encoding='utf8') + expected = '\n'.join(( + '
', + ( + '
str ' + 'name,
' + ), + '
', + '', + ( + ')' + '' + '
' + ), + )) + assert expected in content From ea89180c7c1dfaeb3f234cbede5ed81f313b51d0 Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 3 Jan 2023 16:21:13 +0100 Subject: [PATCH 020/103] Add tests for C++ domain --- .../conf.py | 1 + .../index.rst | 4 ++ tests/test_domain_cpp.py | 66 +++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 tests/roots/test-domain-cpp-cpp_maximum_signature_line_length/conf.py create mode 100644 tests/roots/test-domain-cpp-cpp_maximum_signature_line_length/index.rst diff --git a/tests/roots/test-domain-cpp-cpp_maximum_signature_line_length/conf.py b/tests/roots/test-domain-cpp-cpp_maximum_signature_line_length/conf.py new file mode 100644 index 00000000000..1eb3a64bfc4 --- /dev/null +++ b/tests/roots/test-domain-cpp-cpp_maximum_signature_line_length/conf.py @@ -0,0 +1 @@ +cpp_maximum_signature_line_length = len("str hello(str name)") - 1 diff --git a/tests/roots/test-domain-cpp-cpp_maximum_signature_line_length/index.rst b/tests/roots/test-domain-cpp-cpp_maximum_signature_line_length/index.rst new file mode 100644 index 00000000000..425908cb9b4 --- /dev/null +++ b/tests/roots/test-domain-cpp-cpp_maximum_signature_line_length/index.rst @@ -0,0 +1,4 @@ +domain-cpp-cpp_maximum_signature_line_length +============================================ + +.. cpp:function:: str hello(str name) diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 34d808a8218..f5974ccd7f5 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -5,9 +5,14 @@ import zlib import pytest +from docutils import nodes import sphinx.domains.cpp as cppDomain from sphinx import addnodes +from sphinx.addnodes import (desc, desc_name, desc_content, desc_parameter, + desc_parameter_line, desc_parameterlist, desc_sig_name, + desc_sig_space, desc_signature, desc_signature_line, + pending_xref) from sphinx.addnodes import desc from sphinx.domains.cpp import (DefinitionError, DefinitionParser, NoOldIdError, Symbol, _id_prefix, _max_id) @@ -1480,3 +1485,64 @@ def test_domain_cpp_normalize_unspecialized_template_args(make_app, app_params): ) warning = app2._warning.getvalue() assert 'Internal C++ domain error during symbol merging' not in warning + + +@pytest.mark.sphinx( + 'html', + confoverrides={'cpp_maximum_signature_line_length': len("str hello(str name)")} +) +def test_cfunction_signature_with_c_maximum_signature_line_length(app): + text = ".. cpp:function:: str hello(str name)" + doctree = restructuredtext.parse(app, text) + expected_doctree = ( + addnodes.index, + [desc, ([desc_signature, ([desc_signature_line, (pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, "hello"]], + desc_parameterlist)])], + desc_content)] + ) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="cpp", objtype="function", noindex=False) + signame_node = [desc_sig_name, "name"] + expected_sig = [desc_parameterlist, desc_parameter, ([pending_xref, [desc_sig_name, "str"]], + desc_sig_space, + signame_node)] + assert_node(doctree[1][0][0][3], expected_sig) + + text = (".. cpp:function:: str hello(str names)\n" + " :single-line-signature:") + signame_node[1] = "names" + doctree = restructuredtext.parse(app, text) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="cpp", objtype="function", noindex=False) + assert_node(doctree[1][0][0][3], expected_sig) + + text = ".. cpp:function:: str hello(str names)" + doctree = restructuredtext.parse(app, text) + expected_sig.insert(1, desc_parameter_line) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="cpp", objtype="function", noindex=False) + assert_node(doctree[1][0][0][3], expected_sig) + + +@pytest.mark.sphinx( + 'html', testroot='domain-cpp-cpp_maximum_signature_line_length', +) +def test_domain_cpp_cpp_maximum_signature_line_length(app, status, warning): + app.build() + content = (app.outdir / 'index.html').read_text(encoding='utf8') + expected = '\n'.join(( + '
', + ( + '
str ' + 'name,
' + ), + '
', + '', + ') None: + self.visit_definition(node) + + def depart_desc_parameter_line(self, node: Element) -> None: + self.depart_definition(node) + def visit_desc_parameter(self, node: Element) -> None: if not self.first_param: self.body.append(', ') diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index 1d72e4130c3..78d3d688af7 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -1469,6 +1469,12 @@ def visit_desc_parameterlist(self, node: Element) -> None: def depart_desc_parameterlist(self, node: Element) -> None: self.body.append(')') + def visit_desc_parameter_line(self, node: Element) -> None: + pass + + def depart_desc_parameter_line(self, node: Element) -> None: + pass + def visit_desc_parameter(self, node: Element) -> None: if not self.first_param: self.body.append(', ') From caff6551e54cf0fbe2d0609a4f244915b3714658 Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 3 Jan 2023 16:23:25 +0100 Subject: [PATCH 022/103] Python domain test cleanup --- tests/test_domain_py.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index 09bd0c9a83d..cc8165a1f45 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -9,7 +9,7 @@ from sphinx import addnodes from sphinx.addnodes import (desc, desc_addname, desc_annotation, desc_content, desc_name, - desc_optional, desc_parameter, desc_parameter_line, + desc_optional, desc_parameter, desc_parameter_line, desc_parameterlist, desc_returns, desc_sig_keyword, desc_sig_literal_number, desc_sig_literal_string, desc_sig_name, desc_sig_operator, desc_sig_punctuation, desc_sig_space, desc_signature, pending_xref) @@ -1462,7 +1462,6 @@ def test_pyfunction_signature_with_python_maximum_signature_line_length(app): [nodes.inline, pending_xref, "str"])] assert_node(doctree[1][0][1], expected_sig) - text = (".. py:function:: hello(names: str) -> str\n" " :single-line-signature:") signame_node[1] = "names" @@ -1474,11 +1473,10 @@ def test_pyfunction_signature_with_python_maximum_signature_line_length(app): text = ".. py:function:: hello(names: str) -> str" doctree = restructuredtext.parse(app, text) + expected_sig.insert(1, desc_parameter_line) assert_node(doctree, expected_doctree) assert_node(doctree[1], addnodes.desc, desctype="function", domain="py", objtype="function", noindex=False) - expected_sig.insert(1, desc_parameter_line) - assert_node(doctree[1][0][1], expected_sig) From b71b06d91d21b1a8ad369fc39d7c5a3f2c5811ac Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 3 Jan 2023 17:01:34 +0100 Subject: [PATCH 023/103] Make flake8 happy --- tests/test_domain_c.py | 5 ++--- tests/test_domain_cpp.py | 6 ++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index b657de73def..363c9153ae9 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -5,7 +5,6 @@ from xml.etree import ElementTree import pytest -from docutils import nodes from sphinx import addnodes from sphinx.addnodes import (desc, desc_name, desc_content, desc_parameter, @@ -744,7 +743,7 @@ def test_domain_c_build_intersphinx(tempdir, app, status, warning): inv_file.write_bytes(b'''\ # Sphinx inventory version 2 # Project: C Intersphinx Test -# Version: +# Version: # The remainder of this file is compressed using zlib. ''' + zlib.compress(b'''\ _enum c:enum 1 index.html#c.$ - @@ -837,7 +836,7 @@ def test_cfunction_signature_with_c_maximum_signature_line_length(app): desc_sig_space, signame_node)] assert_node(doctree[1][0][0][3], expected_sig) - + text = (".. c:function:: str hello(str names)\n" " :single-line-signature:") signame_node[1] = "names" diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index f5974ccd7f5..65af1f80853 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -5,7 +5,6 @@ import zlib import pytest -from docutils import nodes import sphinx.domains.cpp as cppDomain from sphinx import addnodes @@ -13,7 +12,6 @@ desc_parameter_line, desc_parameterlist, desc_sig_name, desc_sig_space, desc_signature, desc_signature_line, pending_xref) -from sphinx.addnodes import desc from sphinx.domains.cpp import (DefinitionError, DefinitionParser, NoOldIdError, Symbol, _id_prefix, _max_id) from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping @@ -1376,7 +1374,7 @@ def test_domain_cpp_build_intersphinx(tempdir, app, status, warning): inv_file.write_bytes(b'''\ # Sphinx inventory version 2 # Project: C Intersphinx Test -# Version: +# Version: # The remainder of this file is compressed using zlib. ''' + zlib.compress(b'''\ _class cpp:class 1 index.html#_CPPv46$ - @@ -1510,7 +1508,7 @@ def test_cfunction_signature_with_c_maximum_signature_line_length(app): desc_sig_space, signame_node)] assert_node(doctree[1][0][0][3], expected_sig) - + text = (".. cpp:function:: str hello(str names)\n" " :single-line-signature:") signame_node[1] = "names" From 88c2e09f52c93fccbd867d213792666e063ee3c8 Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 3 Jan 2023 17:06:36 +0100 Subject: [PATCH 024/103] Make isort happy --- tests/test_domain_c.py | 5 ++--- tests/test_domain_cpp.py | 5 ++--- tests/test_domain_py.py | 5 +++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 363c9153ae9..ee8a773ce1c 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -7,10 +7,9 @@ import pytest from sphinx import addnodes -from sphinx.addnodes import (desc, desc_name, desc_content, desc_parameter, +from sphinx.addnodes import (desc, desc_content, desc_name, desc_parameter, desc_parameter_line, desc_parameterlist, desc_sig_name, - desc_sig_space, desc_signature, desc_signature_line, - pending_xref) + desc_sig_space, desc_signature, desc_signature_line, pending_xref) from sphinx.domains.c import (DefinitionError, DefinitionParser, Symbol, _id_prefix, _macroKeywords, _max_id) from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 65af1f80853..d2fc4ffe15c 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -8,10 +8,9 @@ import sphinx.domains.cpp as cppDomain from sphinx import addnodes -from sphinx.addnodes import (desc, desc_name, desc_content, desc_parameter, +from sphinx.addnodes import (desc, desc_content, desc_name, desc_parameter, desc_parameter_line, desc_parameterlist, desc_sig_name, - desc_sig_space, desc_signature, desc_signature_line, - pending_xref) + desc_sig_space, desc_signature, desc_signature_line, pending_xref) from sphinx.domains.cpp import (DefinitionError, DefinitionParser, NoOldIdError, Symbol, _id_prefix, _max_id) from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index cc8165a1f45..80c3310d9b7 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -11,8 +11,9 @@ from sphinx.addnodes import (desc, desc_addname, desc_annotation, desc_content, desc_name, desc_optional, desc_parameter, desc_parameter_line, desc_parameterlist, desc_returns, desc_sig_keyword, - desc_sig_literal_number, desc_sig_literal_string, - desc_sig_name, desc_sig_operator, desc_sig_punctuation, desc_sig_space, desc_signature, pending_xref) + desc_sig_literal_number, desc_sig_literal_string, desc_sig_name, + desc_sig_operator, desc_sig_punctuation, desc_sig_space, + desc_signature, pending_xref) from sphinx.domains import IndexEntry from sphinx.domains.python import (PythonDomain, PythonModuleIndex, _parse_annotation, _pseudo_parse_arglist, py_sig_re) From 4258fd50b87175465ea1eb67353ec549bf267871 Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 3 Jan 2023 22:11:06 +0100 Subject: [PATCH 025/103] Add doc --- doc/usage/configuration.rst | 33 +++++++++++++++++++ doc/usage/restructuredtext/domains.rst | 44 ++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index f4a191a93cd..86d9f11a9f9 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -2903,6 +2903,17 @@ Options for the C domain .. versionadded:: 4.0.3 +.. confval:: c_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 + .. _cpp-config: Options for the C++ domain @@ -2933,6 +2944,17 @@ 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 ----------------------------- @@ -2945,6 +2967,17 @@ 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 + Example of configuration file ----------------------------- diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index 96e2bcc690b..c28f19bc98e 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -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`. + + .. 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 @@ -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 @@ -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 @@ -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) @@ -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`). @@ -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. @@ -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 From 6e6261cfeeee75fe172afea60d65e6aba8dbb553 Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 3 Jan 2023 22:18:05 +0100 Subject: [PATCH 026/103] Make docslint happy --- doc/usage/configuration.rst | 39 ++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index 86d9f11a9f9..fd5a1b7ac32 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -2905,12 +2905,13 @@ Options for the C domain .. confval:: c_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. + 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 @@ -2946,12 +2947,13 @@ Options for the C++ domain .. 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. + 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 @@ -2969,12 +2971,13 @@ Options for the Python domain .. 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. + 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 From 9c52eae5a0d114f00bdeb3628b3f1d31ddc76afd Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 8 Jan 2023 16:58:58 +0100 Subject: [PATCH 027/103] Add types for `maximum_signature_line_length` --- sphinx/domains/c.py | 2 +- sphinx/domains/cpp.py | 2 +- sphinx/domains/python.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index ff84cf877c2..e1ce9920e09 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -3891,7 +3891,7 @@ 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_config_value("c_maximum_signature_line_length", -1, 'env', types=[int]) app.add_post_transform(AliasTransform) return { diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index f36d8066a0b..7f3018938f6 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -8158,7 +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_config_value("cpp_maximum_signature_line_length", -1, 'env', types=[int]) app.add_post_transform(AliasTransform) # debug stuff diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index ab06bafdeeb..dd22f0aaaf7 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -1524,7 +1524,7 @@ def setup(app: Sphinx) -> dict[str, Any]: app.add_domain(PythonDomain) app.add_config_value('python_use_unqualified_type_names', False, 'env') - app.add_config_value('python_maximum_signature_line_length', -1, 'env') + app.add_config_value('python_maximum_signature_line_length', -1, 'env', types=[int]) app.connect('object-description-transform', filter_meta_fields) app.connect('missing-reference', builtin_resolver, priority=900) From 2d9ad80d2360e3ca8496bc02c9b592d17da515c4 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 8 Jan 2023 17:24:15 +0100 Subject: [PATCH 028/103] Fix typo --- tests/test_domain_cpp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index d2fc4ffe15c..96b2eaaed67 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -1488,7 +1488,7 @@ def test_domain_cpp_normalize_unspecialized_template_args(make_app, app_params): 'html', confoverrides={'cpp_maximum_signature_line_length': len("str hello(str name)")} ) -def test_cfunction_signature_with_c_maximum_signature_line_length(app): +def test_cppfunction_signature_with_cpp_maximum_signature_line_length(app): text = ".. cpp:function:: str hello(str name)" doctree = restructuredtext.parse(app, text) expected_doctree = ( From 0bc3b68d34c4d49f7cc71413a6d83dd74ef12f2d Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 8 Jan 2023 17:41:48 +0100 Subject: [PATCH 029/103] Add global `maximum_signature_line_length` config --- doc/usage/configuration.rst | 37 ++++++++++---------- sphinx/config.py | 2 +- sphinx/domains/c.py | 4 ++- sphinx/domains/cpp.py | 4 ++- sphinx/domains/python.py | 4 ++- tests/test_domain_c.py | 70 +++++++++++++++++++++++++++++++++++++ tests/test_domain_cpp.py | 70 +++++++++++++++++++++++++++++++++++++ tests/test_domain_py.py | 66 ++++++++++++++++++++++++++++++++++ 8 files changed, 235 insertions(+), 22 deletions(-) diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index fd5a1b7ac32..cef335c9429 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -666,6 +666,22 @@ 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 + + An integer representing the maximum number of characters that cannot be exceeded by + the signature of a Python, C or C++ object (which can be overridden with, + respectively, :confval:`python_maximum_signature_line_length`, + :confval:`c_maximum_signature_line_length` and + :confval:`cpp_maximum_signature_line_length`). 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. This introduces hard line + breaks, however, it does not override a potential soft wrapping introduced in + the format in which the documentation is built. The directive + :rst:dir:`single-line-signature` allows to disable this behavior on specific objects. + + .. versionadded:: 6.x + .. confval:: add_function_parentheses A boolean that decides whether parentheses are appended to function and @@ -2906,12 +2922,7 @@ Options for the C domain .. confval:: c_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. + by a C object's signature. Overrides :confval:`maximum_signature_line_length`. .. versionadded:: 6.x @@ -2948,12 +2959,7 @@ Options for the C++ domain .. 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. + by a C++ object's signature. Overrides :confval:`maximum_signature_line_length`. .. versionadded:: 6.x @@ -2972,12 +2978,7 @@ Options for the Python domain .. 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. + by a Python object's signature. Overrides :confval:`maximum_signature_line_length`. .. versionadded:: 6.x diff --git a/sphinx/config.py b/sphinx/config.py index 7eb361b1a8b..1beecfa74ee 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -133,7 +133,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': (-1, 'env', [int]), 'math_number_all': (False, 'env', []), 'math_eqref_format': (None, 'env', [str]), 'math_numfig': (True, 'env', []), diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index e1ce9920e09..d71f2fac676 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -3266,7 +3266,9 @@ 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 + c_max_len = self.env.config.c_maximum_signature_line_length + global_max_len = self.env.config.maximum_signature_line_length + max_len = c_max_len if c_max_len >= 0 else global_max_len multi_line = ( max_len >= 0 and 'single-line-signature' not in self.options diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 7f3018938f6..9098f5bd90e 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -7357,7 +7357,9 @@ def run(self) -> list[Node]: def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration: parentSymbol: Symbol = self.env.temp_data['cpp:parent_symbol'] - max_len = self.env.config.cpp_maximum_signature_line_length + cpp_max_len = self.env.config.cpp_maximum_signature_line_length + global_max_len = self.env.config.maximum_signature_line_length + max_len = cpp_max_len if cpp_max_len >= 0 else global_max_len multi_line = ( max_len >= 0 and 'single-line-signature' not in self.options diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index dd22f0aaaf7..832daa513b4 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -538,7 +538,9 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] signode['module'] = modname signode['class'] = classname signode['fullname'] = fullname - max_len = self.env.config.python_maximum_signature_line_length + py_max_len = self.env.config.python_maximum_signature_line_length + global_max_len = self.env.config.maximum_signature_line_length + max_len = py_max_len if py_max_len >= 0 else global_max_len multi_line = ( max_len >= 0 and 'single-line-signature' not in self.options diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index ee8a773ce1c..14990468799 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -854,6 +854,76 @@ def test_cfunction_signature_with_c_maximum_signature_line_length(app): assert_node(doctree[1][0][0][3], expected_sig) +@pytest.mark.sphinx( + 'html', + confoverrides={'maximum_signature_line_length': len("str hello(str name)")} +) +def test_cfunction_signature_with_maximum_signature_line_length(app): + text = ".. c:function:: str hello(str name)" + doctree = restructuredtext.parse(app, text) + expected_doctree = ( + addnodes.index, + [desc, ([desc_signature, ([desc_signature_line, (pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, "hello"]], + desc_parameterlist)])], + desc_content)] + ) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="c", objtype="function", noindex=False) + signame_node = [desc_sig_name, "name"] + expected_sig = [desc_parameterlist, desc_parameter, ([pending_xref, [desc_sig_name, "str"]], + desc_sig_space, + signame_node)] + assert_node(doctree[1][0][0][3], expected_sig) + + text = (".. c:function:: str hello(str names)\n" + " :single-line-signature:") + signame_node[1] = "names" + doctree = restructuredtext.parse(app, text) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="c", objtype="function", noindex=False) + assert_node(doctree[1][0][0][3], expected_sig) + + text = ".. c:function:: str hello(str names)" + doctree = restructuredtext.parse(app, text) + expected_sig.insert(1, desc_parameter_line) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="c", objtype="function", noindex=False) + assert_node(doctree[1][0][0][3], expected_sig) + + +@pytest.mark.sphinx( + 'html', + confoverrides={ + 'c_maximum_signature_line_length': len("str hello(str name)"), + 'maximum_signature_line_length': 1, + } +) +def test_c_maximum_signature_line_length_overrides_global(app): + text = ".. c:function:: str hello(str name)" + doctree = restructuredtext.parse(app, text) + expected_doctree = ( + addnodes.index, + [desc, ([desc_signature, ([desc_signature_line, (pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, "hello"]], + desc_parameterlist)])], + desc_content)] + ) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="c", objtype="function", noindex=False) + signame_node = [desc_sig_name, "name"] + expected_sig = [desc_parameterlist, desc_parameter, ([pending_xref, [desc_sig_name, "str"]], + desc_sig_space, + signame_node)] + assert_node(doctree[1][0][0][3], expected_sig) + + @pytest.mark.sphinx( 'html', testroot='domain-c-c_maximum_signature_line_length', ) diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 96b2eaaed67..b10af2abdf5 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -1526,6 +1526,76 @@ def test_cppfunction_signature_with_cpp_maximum_signature_line_length(app): assert_node(doctree[1][0][0][3], expected_sig) +@pytest.mark.sphinx( + 'html', + confoverrides={'maximum_signature_line_length': len("str hello(str name)")} +) +def test_cppfunction_signature_with_maximum_signature_line_length(app): + text = ".. cpp:function:: str hello(str name)" + doctree = restructuredtext.parse(app, text) + expected_doctree = ( + addnodes.index, + [desc, ([desc_signature, ([desc_signature_line, (pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, "hello"]], + desc_parameterlist)])], + desc_content)] + ) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="cpp", objtype="function", noindex=False) + signame_node = [desc_sig_name, "name"] + expected_sig = [desc_parameterlist, desc_parameter, ([pending_xref, [desc_sig_name, "str"]], + desc_sig_space, + signame_node)] + assert_node(doctree[1][0][0][3], expected_sig) + + text = (".. cpp:function:: str hello(str names)\n" + " :single-line-signature:") + signame_node[1] = "names" + doctree = restructuredtext.parse(app, text) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="cpp", objtype="function", noindex=False) + assert_node(doctree[1][0][0][3], expected_sig) + + text = ".. cpp:function:: str hello(str names)" + doctree = restructuredtext.parse(app, text) + expected_sig.insert(1, desc_parameter_line) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="cpp", objtype="function", noindex=False) + assert_node(doctree[1][0][0][3], expected_sig) + + +@pytest.mark.sphinx( + 'html', + confoverrides={ + 'cpp_maximum_signature_line_length': len("str hello(str name)"), + 'maximum_signature_line_length': 1, + } +) +def test_cpp_maximum_signature_line_length_overrides_global(app): + text = ".. cpp:function:: str hello(str name)" + doctree = restructuredtext.parse(app, text) + expected_doctree = ( + addnodes.index, + [desc, ([desc_signature, ([desc_signature_line, (pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, "hello"]], + desc_parameterlist)])], + desc_content)] + ) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="cpp", objtype="function", noindex=False) + signame_node = [desc_sig_name, "name"] + expected_sig = [desc_parameterlist, desc_parameter, ([pending_xref, [desc_sig_name, "str"]], + desc_sig_space, + signame_node)] + assert_node(doctree[1][0][0][3], expected_sig) + + @pytest.mark.sphinx( 'html', testroot='domain-cpp-cpp_maximum_signature_line_length', ) diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index 80c3310d9b7..c77baff539b 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -1481,6 +1481,72 @@ def test_pyfunction_signature_with_python_maximum_signature_line_length(app): assert_node(doctree[1][0][1], expected_sig) +@pytest.mark.sphinx( + 'html', + confoverrides={'maximum_signature_line_length': len("hello(name: str) -> str")} +) +def test_pyfunction_signature_with_maximum_signature_line_length(app): + text = ".. py:function:: hello(name: str) -> str" + doctree = restructuredtext.parse(app, text) + expected_doctree = (addnodes.index, + [desc, ([desc_signature, ([desc_name, "hello"], + desc_parameterlist, + [desc_returns, pending_xref, "str"])], + desc_content)]) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="py", objtype="function", noindex=False) + signame_node = [desc_sig_name, "name"] + expected_sig = [desc_parameterlist, desc_parameter, (signame_node, + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"])] + assert_node(doctree[1][0][1], expected_sig) + + text = (".. py:function:: hello(names: str) -> str\n" + " :single-line-signature:") + signame_node[1] = "names" + doctree = restructuredtext.parse(app, text) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="py", objtype="function", noindex=False) + assert_node(doctree[1][0][1], expected_sig) + + text = ".. py:function:: hello(names: str) -> str" + doctree = restructuredtext.parse(app, text) + expected_sig.insert(1, desc_parameter_line) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="py", objtype="function", noindex=False) + assert_node(doctree[1][0][1], expected_sig) + + +@pytest.mark.sphinx( + 'html', + confoverrides={ + 'python_maximum_signature_line_length': len("hello(name: str) -> str"), + 'maximum_signature_line_length': 1, + } +) +def test_python_maximum_signature_line_length_overrides_global(app): + text = ".. py:function:: hello(name: str) -> str" + doctree = restructuredtext.parse(app, text) + expected_doctree = (addnodes.index, + [desc, ([desc_signature, ([desc_name, "hello"], + desc_parameterlist, + [desc_returns, pending_xref, "str"])], + desc_content)]) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], addnodes.desc, desctype="function", + domain="py", objtype="function", noindex=False) + signame_node = [desc_sig_name, "name"] + expected_sig = [desc_parameterlist, desc_parameter, (signame_node, + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"])] + assert_node(doctree[1][0][1], expected_sig) + + @pytest.mark.sphinx( 'html', testroot='domain-py-python_maximum_signature_line_length', ) From 2c0da6a04204dd7692775d44587d0e4b7e69500f Mon Sep 17 00:00:00 2001 From: TLouf Date: Fri, 20 Jan 2023 13:24:23 +0100 Subject: [PATCH 030/103] Add tentative LaTeX implementation --- sphinx/domains/python.py | 1 - sphinx/texinputs/sphinxlatexobjects.sty | 16 ++++++++++++++++ sphinx/writers/latex.py | 15 ++++++++++++--- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 832daa513b4..b1968105f07 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -546,7 +546,6 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] and 'single-line-signature' not in self.options and len(sig) > max_len ) - signode['is_multi_line'] = multi_line if multi_line: signode['add_permalink'] = True diff --git a/sphinx/texinputs/sphinxlatexobjects.sty b/sphinx/texinputs/sphinxlatexobjects.sty index 1b497111d8f..0613f294dd1 100644 --- a/sphinx/texinputs/sphinxlatexobjects.sty +++ b/sphinx/texinputs/sphinxlatexobjects.sty @@ -146,6 +146,22 @@ \item[{#1\sphinxcode{(}\py@sigparams{#2}{#3}\strut}] \pysigadjustitemsep } +\newcommand{\pysigmultilinewithargsret}[3]{% + % as #1 may contain a footnote using \label we need to make \label + % a no-op here to avoid LaTeX complaining about duplicates +\let\spx@label\label\let\label\@gobble + \settowidth{\py@argswidth}{#1\sphinxcode{(}}% +\let\label\spx@label + \py@argswidth=\dimexpr\linewidth+\labelwidth-\py@argswidth\relax\relax + \item[#1\sphinxcode{(}] + + \@for\param:=#2\do{ + \param\sphinxcode{,} + + } + \item[\sphinxcode{)}#3\strut] + \pysigadjustitemsep +} \newcommand{\pysigadjustitemsep}{% % adjust \itemsep to control the separation with the next signature % sharing common description diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 7e78ea2ed8f..8d4f1d50eb9 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -705,7 +705,10 @@ def depart_desc(self, node: Element) -> None: def _visit_signature_line(self, node: Element) -> None: for child in node: if isinstance(child, addnodes.desc_parameterlist): - self.body.append(CR + r'\pysiglinewithargsret{') + if child.get('is_multi_line'): + self.body.append(CR + r'\pysigmultilinewithargsret{') + else: + self.body.append(CR + r'\pysiglinewithargsret{') break else: self.body.append(CR + r'\pysigline{') @@ -722,13 +725,13 @@ def visit_desc_signature(self, node: Element) -> None: if not self.in_desc_signature: self.in_desc_signature = True self.body.append(CR + r'\pysigstartsignatures') - if not node.get('is_multiline'): + if not node.get('is_multi_line'): self._visit_signature_line(node) else: self.body.append(CR + r'\pysigstartmultiline') def depart_desc_signature(self, node: Element) -> None: - if not node.get('is_multiline'): + if not node.get('is_multi_line'): self._depart_signature_line(node) else: self.body.append(CR + r'\pysigstopmultiline') @@ -793,6 +796,12 @@ def depart_desc_parameterlist(self, node: Element) -> None: # close parameterlist, open return annotation self.body.append('}{') + def visit_desc_parameter_line(self, node: Element) -> None: + pass + + def depart_desc_parameter_line(self, node: Element) -> None: + pass + def visit_desc_parameter(self, node: Element) -> None: if not self.first_param: self.body.append(', ') From d8df099e23cef21e10340a5e47ef38f9152e2e88 Mon Sep 17 00:00:00 2001 From: TLouf Date: Fri, 20 Jan 2023 13:46:59 +0100 Subject: [PATCH 031/103] Make docslint happy --- doc/usage/configuration.rst | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index cef335c9429..b9388c22d9e 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -668,17 +668,18 @@ General configuration .. confval:: maximum_signature_line_length - An integer representing the maximum number of characters that cannot be exceeded by - the signature of a Python, C or C++ object (which can be overridden with, + An integer representing the maximum number of characters that cannot be exceeded + by the signature of a Python, C or C++ object (which can be overridden with, respectively, :confval:`python_maximum_signature_line_length`, :confval:`c_maximum_signature_line_length` and - :confval:`cpp_maximum_signature_line_length`). 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. This introduces hard line - breaks, however, it does not override a potential soft wrapping introduced in - the format in which the documentation is built. The directive - :rst:dir:`single-line-signature` allows to disable this behavior on specific objects. + :confval:`cpp_maximum_signature_line_length`). 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. This + introduces hard line breaks, however, it does not override a potential soft + wrapping introduced in the format in which the documentation is built. The + directive :rst:dir:`single-line-signature` allows to disable this behavior on + specific objects. .. versionadded:: 6.x @@ -2978,7 +2979,8 @@ Options for the Python domain .. confval:: python_maximum_signature_line_length An integer representing the maximum number of characters that cannot be exceeded - by a Python object's signature. Overrides :confval:`maximum_signature_line_length`. + by a Python object's signature. Overrides + :confval:`maximum_signature_line_length`. .. versionadded:: 6.x From d803e687fab83b7bc84db2ee23f9a26938be5c87 Mon Sep 17 00:00:00 2001 From: TLouf <31036680+TLouf@users.noreply.github.com> Date: Mon, 23 Jan 2023 18:06:25 +0100 Subject: [PATCH 032/103] Cleanup latex style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jean-François B. <2589111+jfbu@users.noreply.github.com> --- sphinx/texinputs/sphinxlatexobjects.sty | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/sphinx/texinputs/sphinxlatexobjects.sty b/sphinx/texinputs/sphinxlatexobjects.sty index 0613f294dd1..c4526b5ceee 100644 --- a/sphinx/texinputs/sphinxlatexobjects.sty +++ b/sphinx/texinputs/sphinxlatexobjects.sty @@ -147,19 +147,18 @@ \pysigadjustitemsep } \newcommand{\pysigmultilinewithargsret}[3]{% - % as #1 may contain a footnote using \label we need to make \label - % a no-op here to avoid LaTeX complaining about duplicates -\let\spx@label\label\let\label\@gobble - \settowidth{\py@argswidth}{#1\sphinxcode{(}}% -\let\label\spx@label - \py@argswidth=\dimexpr\linewidth+\labelwidth-\py@argswidth\relax\relax - \item[#1\sphinxcode{(}] + % render each argument on its own line + \item[#1\sphinxcode{(}\strut] \@for\param:=#2\do{ \param\sphinxcode{,} } + \itemsep\z@skip \item[\sphinxcode{)}#3\strut] + % alternative to above two lines, which aligns the closing parenthesis + % with the arguments: + % \noindent\sphinxcode{)}{#3} \pysigadjustitemsep } \newcommand{\pysigadjustitemsep}{% From 3753e7e17043356cdb4b07559a1a5eeadff287b9 Mon Sep 17 00:00:00 2001 From: TLouf Date: Mon, 23 Jan 2023 18:09:27 +0100 Subject: [PATCH 033/103] Rename new latex command for clarity --- sphinx/texinputs/sphinxlatexobjects.sty | 2 +- sphinx/writers/latex.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/texinputs/sphinxlatexobjects.sty b/sphinx/texinputs/sphinxlatexobjects.sty index c4526b5ceee..8298eebd7b3 100644 --- a/sphinx/texinputs/sphinxlatexobjects.sty +++ b/sphinx/texinputs/sphinxlatexobjects.sty @@ -146,7 +146,7 @@ \item[{#1\sphinxcode{(}\py@sigparams{#2}{#3}\strut}] \pysigadjustitemsep } -\newcommand{\pysigmultilinewithargsret}[3]{% +\newcommand{\pysigwithmultilineargsret}[3]{% % render each argument on its own line \item[#1\sphinxcode{(}\strut] diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 564f4a33756..da131af7a6a 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -706,7 +706,7 @@ def _visit_signature_line(self, node: Element) -> None: for child in node: if isinstance(child, addnodes.desc_parameterlist): if child.get('is_multi_line'): - self.body.append(CR + r'\pysigmultilinewithargsret{') + self.body.append(CR + r'\pysigwithmultilineargsret{') else: self.body.append(CR + r'\pysiglinewithargsret{') break From eb63da65e9407142704474196f784937630afb63 Mon Sep 17 00:00:00 2001 From: TLouf Date: Mon, 23 Jan 2023 18:28:10 +0100 Subject: [PATCH 034/103] Force zero margins for dl tag --- sphinx/themes/basic/static/basic.css_t | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t index 58a21409d56..9ae18026720 100644 --- a/sphinx/themes/basic/static/basic.css_t +++ b/sphinx/themes/basic/static/basic.css_t @@ -675,6 +675,11 @@ dd { margin-bottom: 0px; } +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + dl > dd:last-child, dl > dd:last-child > :last-child { margin-bottom: 0; From 53e882f607b32d8ea9f0dd0800cbbeb0ef60b073 Mon Sep 17 00:00:00 2001 From: TLouf <31036680+TLouf@users.noreply.github.com> Date: Tue, 24 Jan 2023 09:56:49 +0100 Subject: [PATCH 035/103] latex: avoid page break above first function argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jean-François B. <2589111+jfbu@users.noreply.github.com> --- sphinx/texinputs/sphinxlatexobjects.sty | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sphinx/texinputs/sphinxlatexobjects.sty b/sphinx/texinputs/sphinxlatexobjects.sty index 8298eebd7b3..21627d9bb9d 100644 --- a/sphinx/texinputs/sphinxlatexobjects.sty +++ b/sphinx/texinputs/sphinxlatexobjects.sty @@ -149,7 +149,8 @@ \newcommand{\pysigwithmultilineargsret}[3]{% % render each argument on its own line \item[#1\sphinxcode{(}\strut] - + \leavevmode\par\nopagebreak + % this relies on \pysigstartsignatures having set \parskip to zero \@for\param:=#2\do{ \param\sphinxcode{,} From d7508a079490a824bc0d89ccb3d92df78f19ce1f Mon Sep 17 00:00:00 2001 From: TLouf <31036680+TLouf@users.noreply.github.com> Date: Tue, 24 Jan 2023 09:58:23 +0100 Subject: [PATCH 036/103] latex: match coding style of LaTeX macro files in for loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jean-François B. <2589111+jfbu@users.noreply.github.com> --- sphinx/texinputs/sphinxlatexobjects.sty | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sphinx/texinputs/sphinxlatexobjects.sty b/sphinx/texinputs/sphinxlatexobjects.sty index 21627d9bb9d..2acb5f9e7b1 100644 --- a/sphinx/texinputs/sphinxlatexobjects.sty +++ b/sphinx/texinputs/sphinxlatexobjects.sty @@ -151,10 +151,7 @@ \item[#1\sphinxcode{(}\strut] \leavevmode\par\nopagebreak % this relies on \pysigstartsignatures having set \parskip to zero - \@for\param:=#2\do{ - \param\sphinxcode{,} - - } + \@for\param:=#2\do{\param\sphinxcode{,}\par} \itemsep\z@skip \item[\sphinxcode{)}#3\strut] % alternative to above two lines, which aligns the closing parenthesis From b665647d43efe84cdf94e62074f5704516ac774a Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 24 Jan 2023 10:00:40 +0100 Subject: [PATCH 037/103] latex: rename command to `pysigwithonelineperarg` --- sphinx/texinputs/sphinxlatexobjects.sty | 2 +- sphinx/writers/latex.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/texinputs/sphinxlatexobjects.sty b/sphinx/texinputs/sphinxlatexobjects.sty index 2acb5f9e7b1..e7cbe2c9eb7 100644 --- a/sphinx/texinputs/sphinxlatexobjects.sty +++ b/sphinx/texinputs/sphinxlatexobjects.sty @@ -146,7 +146,7 @@ \item[{#1\sphinxcode{(}\py@sigparams{#2}{#3}\strut}] \pysigadjustitemsep } -\newcommand{\pysigwithmultilineargsret}[3]{% +\newcommand{\pysigwithonelineperarg}[3]{% % render each argument on its own line \item[#1\sphinxcode{(}\strut] \leavevmode\par\nopagebreak diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index da131af7a6a..e1150b94528 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -706,7 +706,7 @@ def _visit_signature_line(self, node: Element) -> None: for child in node: if isinstance(child, addnodes.desc_parameterlist): if child.get('is_multi_line'): - self.body.append(CR + r'\pysigwithmultilineargsret{') + self.body.append(CR + r'\pysigwithonelineperarg{') else: self.body.append(CR + r'\pysiglinewithargsret{') break From ada9d00deae9bd9ae68a69ec790c80043411d140 Mon Sep 17 00:00:00 2001 From: TLouf <31036680+TLouf@users.noreply.github.com> Date: Tue, 24 Jan 2023 18:48:57 +0100 Subject: [PATCH 038/103] latex: avoid page break before dedented closing brace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jean-François B. <2589111+jfbu@users.noreply.github.com> --- sphinx/texinputs/sphinxlatexobjects.sty | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sphinx/texinputs/sphinxlatexobjects.sty b/sphinx/texinputs/sphinxlatexobjects.sty index e7cbe2c9eb7..15306fada20 100644 --- a/sphinx/texinputs/sphinxlatexobjects.sty +++ b/sphinx/texinputs/sphinxlatexobjects.sty @@ -152,11 +152,8 @@ \leavevmode\par\nopagebreak % this relies on \pysigstartsignatures having set \parskip to zero \@for\param:=#2\do{\param\sphinxcode{,}\par} - \itemsep\z@skip - \item[\sphinxcode{)}#3\strut] - % alternative to above two lines, which aligns the closing parenthesis - % with the arguments: - % \noindent\sphinxcode{)}{#3} + % fulllineitems sets \labelwidth to be like \leftmargin + \nopagebreak\noindent\kern-\labelwidth\sphinxcode{)}{#3} \pysigadjustitemsep } \newcommand{\pysigadjustitemsep}{% From ed7fac8cb1def1f82e6893b88ea35bff3e6970bd Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 24 Jan 2023 18:59:54 +0100 Subject: [PATCH 039/103] Update versionadded --- doc/usage/configuration.rst | 8 ++++---- doc/usage/restructuredtext/domains.rst | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index 2a639265904..1c9bb81002e 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -681,7 +681,7 @@ General configuration directive :rst:dir:`single-line-signature` allows to disable this behavior on specific objects. - .. versionadded:: 6.x + .. versionadded:: 6.2.0 .. confval:: add_function_parentheses @@ -2925,7 +2925,7 @@ Options for the C domain An integer representing the maximum number of characters that cannot be exceeded by a C object's signature. Overrides :confval:`maximum_signature_line_length`. - .. versionadded:: 6.x + .. versionadded:: 6.2.0 .. _cpp-config: @@ -2962,7 +2962,7 @@ Options for the C++ domain An integer representing the maximum number of characters that cannot be exceeded by a C++ object's signature. Overrides :confval:`maximum_signature_line_length`. - .. versionadded:: 6.x + .. versionadded:: 6.2.0 Options for the Python domain ----------------------------- @@ -2982,7 +2982,7 @@ Options for the Python domain by a Python object's signature. Overrides :confval:`maximum_signature_line_length`. - .. versionadded:: 6.x + .. versionadded:: 6.2.0 Example of configuration file ----------------------------- diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index c28f19bc98e..3b3c11caa79 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -238,6 +238,8 @@ The following directives are provided for module and class contents: Forbids the potential introduction of line breaks between parameters of the documented object, ignoring :confval:`python_maximum_signature_line_length`. + .. versionadded:: 6.2.0 + .. rst:directive:: .. py:data:: name @@ -343,6 +345,8 @@ The following directives are provided for module and class contents: Forbids the potential introduction of line breaks between parameters of the documented object, ignoring :confval:`python_maximum_signature_line_length`. + .. versionadded:: 6.2.0 + .. rst:directive:: .. py:attribute:: name Describes an object data attribute. The description should include @@ -461,6 +465,8 @@ The following directives are provided for module and class contents: Forbids the potential introduction of line breaks between parameters of the documented object, ignoring :confval:`python_maximum_signature_line_length`. + .. versionadded:: 6.2.0 + .. rst:directive:option:: staticmethod :type: no value @@ -520,6 +526,8 @@ The following directives are provided for module and class contents: Forbids the potential introduction of line breaks between parameters of the documented object, ignoring :confval:`python_maximum_signature_line_length`. + .. versionadded:: 6.2.0 + .. rst:directive:: .. py:decoratormethod:: name .. py:decoratormethod:: name(signature) @@ -756,6 +764,8 @@ The C domain (name **c**) is suited for documentation of C API. Forbids the potential introduction of line breaks between parameters of the documented object, ignoring :confval:`c_maximum_signature_line_length`. + .. versionadded:: 6.2.0 + In the description of a function you can use the following info fields (see also :ref:`info-field-lists`). @@ -808,6 +818,8 @@ The C domain (name **c**) is suited for documentation of C API. Forbids the potential introduction of line breaks between parameters of the documented object, ignoring :confval:`c_maximum_signature_line_length`. + .. versionadded:: 6.2.0 + In the description of a macro you can use the same info fields as for the :rst:dir:`c:function` directive. @@ -1170,6 +1182,8 @@ visibility statement (``public``, ``private`` or ``protected``). Forbids the potential introduction of line breaks between parameters of the documented object, ignoring :confval:`cpp_maximum_signature_line_length`. + .. versionadded:: 6.2.0 + .. rst:directive:: .. cpp:member:: (member) variable declaration .. cpp:var:: (member) variable declaration From 1475f5f6df82f93593333d97200e69f7c59445c9 Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 24 Jan 2023 19:04:27 +0100 Subject: [PATCH 040/103] docs: fix linking to `single-line-signature` --- doc/usage/configuration.rst | 5 +++-- doc/usage/restructuredtext/domains.rst | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index 1c9bb81002e..ad24137d8b7 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -678,8 +678,9 @@ General configuration have each of their arguments displayed on a separate, indented line. This introduces hard line breaks, however, it does not override a potential soft wrapping introduced in the format in which the documentation is built. The - directive :rst:dir:`single-line-signature` allows to disable this behavior on - specific objects. + directive option :ref:`single-line-signature ` + (here for the example of the :rst:dir:`py:function` directive) allows to disable + this behavior on specific objects. .. versionadded:: 6.2.0 diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index 3b3c11caa79..ca18209e69b 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -232,6 +232,8 @@ The following directives are provided for module and class contents: the module specified by :rst:dir:`py:currentmodule`. + .. _pyfuncsinglelinesig: + .. rst:directive:option:: single-line-signature :type: no value From 9b9e694a6f100e918409030b40471555da3f4ba7 Mon Sep 17 00:00:00 2001 From: TLouf Date: Wed, 25 Jan 2023 10:29:34 +0100 Subject: [PATCH 041/103] docs: better linking to `single-line-signature` --- doc/usage/configuration.rst | 6 +++--- doc/usage/restructuredtext/domains.rst | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index ad24137d8b7..b494ca5f3ce 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -678,9 +678,9 @@ General configuration have each of their arguments displayed on a separate, indented line. This introduces hard line breaks, however, it does not override a potential soft wrapping introduced in the format in which the documentation is built. The - directive option :ref:`single-line-signature ` - (here for the example of the :rst:dir:`py:function` directive) allows to disable - this behavior on specific objects. + directive option :rst:dir:`py:function:single-line-signature` (here for the + example of the :rst:dir:`py:function` directive) allows to disable this behavior + on specific objects. .. versionadded:: 6.2.0 diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index ca18209e69b..79f34a34028 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -231,9 +231,6 @@ 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`. - - .. _pyfuncsinglelinesig: - .. rst:directive:option:: single-line-signature :type: no value From 4deb58c42f6be1c632780883dc076d2e5c6732a1 Mon Sep 17 00:00:00 2001 From: TLouf Date: Fri, 7 Apr 2023 09:51:58 +0200 Subject: [PATCH 042/103] Add trailing commas for ruff --- sphinx/domains/c.py | 2 +- sphinx/domains/cpp.py | 2 +- sphinx/domains/python.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 9ba4ae101d1..49a05e838d4 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -3280,7 +3280,7 @@ def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration: ) signode['is_multi_line'] = multi_line parser = DefinitionParser( - sig, location=signode, config=self.env.config, multi_line=multi_line + sig, location=signode, config=self.env.config, multi_line=multi_line, ) try: ast = self.parse_definition(parser) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index b6a11d0ce49..1f7da84c3a0 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -7365,7 +7365,7 @@ def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration: ) signode['is_multi_line'] = multi_line parser = DefinitionParser( - sig, location=signode, config=self.env.config, multi_line=multi_line + sig, location=signode, config=self.env.config, multi_line=multi_line, ) try: ast = self.parse_definition(parser) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index b430a33a6f4..d155de8638e 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -258,7 +258,7 @@ def _unparse_pep_604_annotation(node: ast.Subscript) -> list[Node]: def _parse_arglist( - arglist: str, env: BuildEnvironment | None = None, multi_line: bool = False + arglist: str, env: BuildEnvironment | None = None, multi_line: bool = False, ) -> addnodes.desc_parameterlist: """Parse a list of arguments using AST parser""" params = addnodes.desc_parameterlist() From 48d2ccb4507e5c8de3917732b838823d8f33f7f5 Mon Sep 17 00:00:00 2001 From: TLouf Date: Fri, 7 Apr 2023 09:56:46 +0200 Subject: [PATCH 043/103] Add trailing commas in tests --- tests/test_domain_c.py | 12 ++++++------ tests/test_domain_cpp.py | 12 ++++++------ tests/test_domain_py.py | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 7af3e91a089..56b94d663fb 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -830,7 +830,7 @@ def test_domain_c_parse_noindexentry(app): @pytest.mark.sphinx( 'html', - confoverrides={'c_maximum_signature_line_length': len("str hello(str name)")} + confoverrides={'c_maximum_signature_line_length': len("str hello(str name)")}, ) def test_cfunction_signature_with_c_maximum_signature_line_length(app): text = ".. c:function:: str hello(str name)" @@ -841,7 +841,7 @@ def test_cfunction_signature_with_c_maximum_signature_line_length(app): desc_sig_space, [desc_name, [desc_sig_name, "hello"]], desc_parameterlist)])], - desc_content)] + desc_content)], ) assert_node(doctree, expected_doctree) assert_node(doctree[1], addnodes.desc, desctype="function", @@ -872,7 +872,7 @@ def test_cfunction_signature_with_c_maximum_signature_line_length(app): @pytest.mark.sphinx( 'html', - confoverrides={'maximum_signature_line_length': len("str hello(str name)")} + confoverrides={'maximum_signature_line_length': len("str hello(str name)")}, ) def test_cfunction_signature_with_maximum_signature_line_length(app): text = ".. c:function:: str hello(str name)" @@ -883,7 +883,7 @@ def test_cfunction_signature_with_maximum_signature_line_length(app): desc_sig_space, [desc_name, [desc_sig_name, "hello"]], desc_parameterlist)])], - desc_content)] + desc_content)], ) assert_node(doctree, expected_doctree) assert_node(doctree[1], addnodes.desc, desctype="function", @@ -917,7 +917,7 @@ def test_cfunction_signature_with_maximum_signature_line_length(app): confoverrides={ 'c_maximum_signature_line_length': len("str hello(str name)"), 'maximum_signature_line_length': 1, - } + }, ) def test_c_maximum_signature_line_length_overrides_global(app): text = ".. c:function:: str hello(str name)" @@ -928,7 +928,7 @@ def test_c_maximum_signature_line_length_overrides_global(app): desc_sig_space, [desc_name, [desc_sig_name, "hello"]], desc_parameterlist)])], - desc_content)] + desc_content)], ) assert_node(doctree, expected_doctree) assert_node(doctree[1], addnodes.desc, desctype="function", diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 9a6f281b6a4..421ab2df55a 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -1502,7 +1502,7 @@ def test_domain_cpp_normalize_unspecialized_template_args(make_app, app_params): @pytest.mark.sphinx( 'html', - confoverrides={'cpp_maximum_signature_line_length': len("str hello(str name)")} + confoverrides={'cpp_maximum_signature_line_length': len("str hello(str name)")}, ) def test_cppfunction_signature_with_cpp_maximum_signature_line_length(app): text = ".. cpp:function:: str hello(str name)" @@ -1513,7 +1513,7 @@ def test_cppfunction_signature_with_cpp_maximum_signature_line_length(app): desc_sig_space, [desc_name, [desc_sig_name, "hello"]], desc_parameterlist)])], - desc_content)] + desc_content)], ) assert_node(doctree, expected_doctree) assert_node(doctree[1], addnodes.desc, desctype="function", @@ -1544,7 +1544,7 @@ def test_cppfunction_signature_with_cpp_maximum_signature_line_length(app): @pytest.mark.sphinx( 'html', - confoverrides={'maximum_signature_line_length': len("str hello(str name)")} + confoverrides={'maximum_signature_line_length': len("str hello(str name)")}, ) def test_cppfunction_signature_with_maximum_signature_line_length(app): text = ".. cpp:function:: str hello(str name)" @@ -1555,7 +1555,7 @@ def test_cppfunction_signature_with_maximum_signature_line_length(app): desc_sig_space, [desc_name, [desc_sig_name, "hello"]], desc_parameterlist)])], - desc_content)] + desc_content)], ) assert_node(doctree, expected_doctree) assert_node(doctree[1], addnodes.desc, desctype="function", @@ -1589,7 +1589,7 @@ def test_cppfunction_signature_with_maximum_signature_line_length(app): confoverrides={ 'cpp_maximum_signature_line_length': len("str hello(str name)"), 'maximum_signature_line_length': 1, - } + }, ) def test_cpp_maximum_signature_line_length_overrides_global(app): text = ".. cpp:function:: str hello(str name)" @@ -1600,7 +1600,7 @@ def test_cpp_maximum_signature_line_length_overrides_global(app): desc_sig_space, [desc_name, [desc_sig_name, "hello"]], desc_parameterlist)])], - desc_content)] + desc_content)], ) assert_node(doctree, expected_doctree) assert_node(doctree[1], addnodes.desc, desctype="function", diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index 207abec1461..400b098d557 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -1463,7 +1463,7 @@ def test_signature_line_number(app, include_options): @pytest.mark.sphinx( 'html', - confoverrides={'python_maximum_signature_line_length': len("hello(name: str) -> str")} + confoverrides={'python_maximum_signature_line_length': len("hello(name: str) -> str")}, ) def test_pyfunction_signature_with_python_maximum_signature_line_length(app): text = ".. py:function:: hello(name: str) -> str" @@ -1503,7 +1503,7 @@ def test_pyfunction_signature_with_python_maximum_signature_line_length(app): @pytest.mark.sphinx( 'html', - confoverrides={'maximum_signature_line_length': len("hello(name: str) -> str")} + confoverrides={'maximum_signature_line_length': len("hello(name: str) -> str")}, ) def test_pyfunction_signature_with_maximum_signature_line_length(app): text = ".. py:function:: hello(name: str) -> str" @@ -1546,7 +1546,7 @@ def test_pyfunction_signature_with_maximum_signature_line_length(app): confoverrides={ 'python_maximum_signature_line_length': len("hello(name: str) -> str"), 'maximum_signature_line_length': 1, - } + }, ) def test_python_maximum_signature_line_length_overrides_global(app): text = ".. py:function:: hello(name: str) -> str" From b065ab756e93edebc4a179cf85d14fde3b9df830 Mon Sep 17 00:00:00 2001 From: TLouf Date: Fri, 7 Apr 2023 10:12:07 +0200 Subject: [PATCH 044/103] Add `desc_parameter_line` to doc --- doc/extdev/nodes.rst | 1 + sphinx/addnodes.py | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/extdev/nodes.rst b/doc/extdev/nodes.rst index 5ef669468d7..f4d41d44c91 100644 --- a/doc/extdev/nodes.rst +++ b/doc/extdev/nodes.rst @@ -31,6 +31,7 @@ and in :py:class:`desc_signature_line` nodes. .. autoclass:: desc_type .. autoclass:: desc_returns .. autoclass:: desc_parameterlist +.. autoclass:: desc_parameter_line .. autoclass:: desc_parameter .. autoclass:: desc_optional .. autoclass:: desc_annotation diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 7e2f8c9a274..d895b49cdcf 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -547,6 +547,7 @@ def setup(app: Sphinx) -> dict[str, Any]: app.add_node(desc_type) app.add_node(desc_returns) app.add_node(desc_parameterlist) + app.add_node(desc_parameter_line) app.add_node(desc_parameter) app.add_node(desc_optional) app.add_node(desc_annotation) From 2b864fe47d1e392f42ed8ccf847213e0d33536a6 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 8 Apr 2023 02:48:56 +0100 Subject: [PATCH 045/103] Update config docs --- doc/usage/configuration.rst | 56 ++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index c9ff566ad19..608ab35fe9e 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -672,21 +672,25 @@ General configuration .. confval:: maximum_signature_line_length - An integer representing the maximum number of characters that cannot be exceeded - by the signature of a Python, C or C++ object (which can be overridden with, - respectively, :confval:`python_maximum_signature_line_length`, - :confval:`c_maximum_signature_line_length` and - :confval:`cpp_maximum_signature_line_length`). 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. This - introduces hard line breaks, however, it does not override a potential soft - wrapping introduced in the format in which the documentation is built. The - directive option :rst:dir:`py:function:single-line-signature` (here for the - example of the :rst:dir:`py:function` directive) allows to disable this behavior - on specific objects. - - .. versionadded:: 6.2.0 + 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. + ``py:function:single-line-parameter-list``). + + .. TODO: there's currently no way to cross-reference a directive option, + e.g. :rst:dir:opt:`py:function:single-line-parameter-list` + + .. versionadded:: 6.2 .. confval:: add_function_parentheses @@ -2927,10 +2931,11 @@ Options for the C domain .. confval:: c_maximum_signature_line_length - An integer representing the maximum number of characters that cannot be exceeded - by a C object's signature. Overrides :confval:`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:: 6.2.0 + .. versionadded:: 6.2 .. _cpp-config: @@ -2964,10 +2969,11 @@ Options for the C++ domain .. confval:: cpp_maximum_signature_line_length - An integer representing the maximum number of characters that cannot be exceeded - by a C++ object's signature. Overrides :confval:`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:: 6.2.0 + .. versionadded:: 6.2 Options for the Python domain ----------------------------- @@ -3013,11 +3019,11 @@ Options for the Python domain .. confval:: python_maximum_signature_line_length - An integer representing the maximum number of characters that cannot be exceeded - by a Python object's signature. Overrides - :confval:`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:: 6.2.0 + .. versionadded:: 6.2 Example of configuration file ----------------------------- From fa97a1530f41e297abc2e53edae7dd153d43be3d Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Fri, 7 Apr 2023 21:31:57 +0100 Subject: [PATCH 046/103] Update option docs --- doc/usage/restructuredtext/domains.rst | 63 ++++++++++++++------------ 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index 18c16204988..f527a04a67a 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -231,13 +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`. - .. rst:directive:option:: single-line-signature + .. rst:directive:option:: single-line-argument-list :type: no value - Forbids the potential introduction of line breaks between parameters of the - documented object, ignoring :confval:`python_maximum_signature_line_length`. + 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:: 6.2.0 + .. versionadded:: 6.2 .. rst:directive:: .. py:data:: name @@ -338,13 +339,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`. - .. rst:directive:option:: single-line-signature + .. rst:directive:option:: single-line-argument-list :type: no value - Forbids the potential introduction of line breaks between parameters of the - documented object, ignoring :confval:`python_maximum_signature_line_length`. + 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:: 6.2.0 + .. versionadded:: 6.2 .. rst:directive:: .. py:attribute:: name @@ -458,13 +460,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`. - .. rst:directive:option:: single-line-signature + .. rst:directive:option:: single-line-argument-list :type: no value - Forbids the potential introduction of line breaks between parameters of the - documented object, ignoring :confval:`python_maximum_signature_line_length`. + 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:: 6.2.0 + .. versionadded:: 6.2 .. rst:directive:option:: staticmethod :type: no value @@ -519,13 +522,14 @@ 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 + .. rst:directive:option:: single-line-argument-list :type: no value - Forbids the potential introduction of line breaks between parameters of the - documented object, ignoring :confval:`python_maximum_signature_line_length`. + 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:: 6.2.0 + .. versionadded:: 6.2 .. rst:directive:: .. py:decoratormethod:: name .. py:decoratormethod:: name(signature) @@ -757,13 +761,14 @@ 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 + .. rst:directive:option:: single-line-parameter-list :type: no value - Forbids the potential introduction of line breaks between parameters of the - documented object, ignoring :confval:`c_maximum_signature_line_length`. + 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:: 6.2.0 + .. versionadded:: 6.2 In the description of a function you can use the following info fields (see also :ref:`info-field-lists`). @@ -811,13 +816,14 @@ 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 + .. rst:directive:option:: single-line-parameter-list :type: no value - Forbids the potential introduction of line breaks between parameters of the - documented object, ignoring :confval:`c_maximum_signature_line_length`. + 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:: 6.2.0 + .. versionadded:: 6.2 In the description of a macro you can use the same info fields as for the :rst:dir:`c:function` directive. @@ -1175,13 +1181,14 @@ visibility statement (``public``, ``private`` or ``protected``). .. cpp:function:: template<> \ void print(int i) - .. rst:directive:option:: single-line-signature + .. rst:directive:option:: single-line-parameter-list :type: no value - Forbids the potential introduction of line breaks between parameters of the - documented object, ignoring :confval:`cpp_maximum_signature_line_length`. + Ensures that the macro's parameters will be emitted on a single logical + line, overriding :confval:`cpp_maximum_signature_line_length` and + :confval:`maximum_signature_line_length`. - .. versionadded:: 6.2.0 + .. versionadded:: 6.2 .. rst:directive:: .. cpp:member:: (member) variable declaration .. cpp:var:: (member) variable declaration From d787002c49f3517733832fef4b0dbdf4b658f975 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 8 Apr 2023 01:55:19 +0100 Subject: [PATCH 047/103] Move options to end --- doc/usage/restructuredtext/domains.rst | 30 +++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index f527a04a67a..a6bf4ef3578 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -761,15 +761,6 @@ 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-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:: 6.2 - In the description of a function you can use the following info fields (see also :ref:`info-field-lists`). @@ -809,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:: 6.2 + .. rst:directive:: .. c:macro:: name .. c:macro:: name(arg list) @@ -816,6 +816,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. + In the description of a macro you can use the same info fields as for the + :rst:dir:`c:function` directive. + + .. versionadded:: 3.0 + The function style variant. + .. rst:directive:option:: single-line-parameter-list :type: no value @@ -825,12 +831,6 @@ The C domain (name **c**) is suited for documentation of C API. .. versionadded:: 6.2 - In the description of a macro you can use the same info fields as for the - :rst:dir:`c:function` directive. - - .. versionadded:: 3.0 - The function style variant. - .. rst:directive:: .. c:struct:: name Describes a C struct. From 707650fdef6fcb1a531cb75ec01a6e3d7ffeb03a Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Fri, 7 Apr 2023 22:07:30 +0100 Subject: [PATCH 048/103] Revert changes to the frozen HTML 4 writer --- sphinx/writers/_html4.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/sphinx/writers/_html4.py b/sphinx/writers/_html4.py index 82efe5c74b5..b6bc61b0118 100644 --- a/sphinx/writers/_html4.py +++ b/sphinx/writers/_html4.py @@ -150,21 +150,10 @@ def visit_desc_parameterlist(self, node: Element) -> None: self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) for c in node.children]) self.param_separator = node.child_text_separator - if node.get('is_multi_line'): - self.body.append('\n\n') - self.body.append(self.starttag(node, 'dl')) def depart_desc_parameterlist(self, node: Element) -> None: - if node.get('is_multi_line'): - self.body.append('\n\n\n') self.body.append(')') - def visit_desc_parameter_line(self, node: Element) -> None: - self.body.append(self.starttag(node, 'dd', '')) - - def depart_desc_parameter_line(self, node: Element) -> None: - self.body.append('') - # If required parameters are still to come, then put the comma after # the parameter. Otherwise, put the comma before. This ensures that # signatures like the following render correctly (see issue #1001): From b1cb1b362e40307e5b2702942c930f816ae08ea0 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Fri, 7 Apr 2023 22:03:00 +0100 Subject: [PATCH 049/103] Revert ``is_multiline`` -> ```is_multi_line`` rename for ``desc_signature`` nodes The attribute is used in e.g. Breathe, better not to change. (though would be nice to) --- sphinx/addnodes.py | 6 +++--- sphinx/domains/c.py | 2 +- sphinx/domains/cpp.py | 2 +- sphinx/writers/_html4.py | 2 +- sphinx/writers/html5.py | 2 +- sphinx/writers/latex.py | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index d895b49cdcf..01cc53b5b2c 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -154,7 +154,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_multi_line = True`` to describe a multi-line signature. + Set ``is_multiline = 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. @@ -164,7 +164,7 @@ class desc_signature(_desc_classes_injector, nodes.Part, nodes.Inline, nodes.Tex @property def child_text_separator(self): - if self.get('is_multi_line'): + if self.get('is_multiline'): return ' ' else: return super().child_text_separator @@ -174,7 +174,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_multi_line`` set to ``True``. + with ``is_multiline`` set to ``True``. Set ``add_permalink = True`` for the line that should get the permalink. """ sphinx_line_type = '' diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 49a05e838d4..c60baa881c8 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -1482,7 +1482,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_multi_line'] = True + signode['is_multiline'] = True # Put each line in a desc_signature_line node. mainDeclNode = addnodes.desc_signature_line() mainDeclNode.sphinx_line_type = 'declarator' diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 1f7da84c3a0..856610623f2 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -4084,7 +4084,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_multi_line'] = True + signode['is_multiline'] = True # Put each line in a desc_signature_line node. mainDeclNode = addnodes.desc_signature_line() mainDeclNode.sphinx_line_type = 'declarator' diff --git a/sphinx/writers/_html4.py b/sphinx/writers/_html4.py index b6bc61b0118..0b670bf99db 100644 --- a/sphinx/writers/_html4.py +++ b/sphinx/writers/_html4.py @@ -88,7 +88,7 @@ def visit_desc_signature(self, node: Element) -> None: def depart_desc_signature(self, node: Element) -> None: self.protect_literal_text -= 1 - if not node.get('is_multi_line'): + if not node.get('is_multiline'): self.add_permalink_ref(node, _('Permalink to this definition')) self.body.append('\n') diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 13bbd895743..62d8a261a5b 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -94,7 +94,7 @@ def visit_desc_signature(self, node: Element) -> None: def depart_desc_signature(self, node: Element) -> None: self.protect_literal_text -= 1 - if not node.get('is_multi_line') or node.get('add_permalink'): + if not node.get('is_multiline') or node.get('add_permalink'): self.add_permalink_ref(node, _('Permalink to this definition')) self.body.append('\n') diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 0d0972faa1b..4cbfce7819a 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -732,13 +732,13 @@ def visit_desc_signature(self, node: Element) -> None: if not self.in_desc_signature: self.in_desc_signature = True self.body.append(CR + r'\pysigstartsignatures') - if not node.get('is_multi_line'): + if not node.get('is_multiline'): self._visit_signature_line(node) else: self.body.append(CR + r'\pysigstartmultiline') def depart_desc_signature(self, node: Element) -> None: - if not node.get('is_multi_line'): + if not node.get('is_multiline'): self._depart_signature_line(node) else: self.body.append(CR + r'\pysigstopmultiline') From 83087424ae99ff9418adfcc1bc46e4c56fd0f2c9 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Fri, 7 Apr 2023 21:59:58 +0100 Subject: [PATCH 050/103] ``maximum_signature_line_length`` type to be int | None --- sphinx/config.py | 2 +- sphinx/domains/c.py | 16 +++++++--------- sphinx/domains/cpp.py | 16 +++++++--------- sphinx/domains/python.py | 17 ++++++++--------- 4 files changed, 23 insertions(+), 28 deletions(-) diff --git a/sphinx/config.py b/sphinx/config.py index 37bd3565212..b56cd1b38a1 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -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': (-1, 'env', [int]), + 'maximum_signature_line_length': (None, 'env', {int, None}), 'math_number_all': (False, 'env', []), 'math_eqref_format': (None, 'env', [str]), 'math_numfig': (True, 'env', []), diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index c60baa881c8..782316be971 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -3270,14 +3270,12 @@ def run(self) -> list[Node]: def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration: parentSymbol: Symbol = self.env.temp_data['c:parent_symbol'] - c_max_len = self.env.config.c_maximum_signature_line_length - global_max_len = self.env.config.maximum_signature_line_length - max_len = c_max_len if c_max_len >= 0 else global_max_len - multi_line = ( - max_len >= 0 - and 'single-line-signature' not in self.options - and len(sig) > max_len - ) + max_len = (self.env.config.c_maximum_signature_line_length + or self.env.config.maximum_signature_line_length + or 0) + multi_line = ('single-line-signature' not in self.options + and (len(sig) > max_len > 0)) + signode['is_multi_line'] = multi_line parser = DefinitionParser( sig, location=signode, config=self.env.config, multi_line=multi_line, @@ -3897,7 +3895,7 @@ 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', types=[int]) + app.add_config_value("c_maximum_signature_line_length", None, 'env', types={int, None}) app.add_post_transform(AliasTransform) return { diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 856610623f2..093eae50074 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -7355,14 +7355,12 @@ def run(self) -> list[Node]: def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration: parentSymbol: Symbol = self.env.temp_data['cpp:parent_symbol'] - cpp_max_len = self.env.config.cpp_maximum_signature_line_length - global_max_len = self.env.config.maximum_signature_line_length - max_len = cpp_max_len if cpp_max_len >= 0 else global_max_len - multi_line = ( - max_len >= 0 - and 'single-line-signature' not in self.options - and len(sig) > max_len - ) + max_len = (self.env.config.cpp_maximum_signature_line_length + or self.env.config.maximum_signature_line_length + or 0) + multi_line = ('single-line-signature' not in self.options + and (len(sig) > max_len > 0)) + signode['is_multi_line'] = multi_line parser = DefinitionParser( sig, location=signode, config=self.env.config, multi_line=multi_line, @@ -8158,7 +8156,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', types=[int]) + app.add_config_value("cpp_maximum_signature_line_length", None, 'env', types={int, None}) app.add_post_transform(AliasTransform) # debug stuff diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index d155de8638e..70bb3f4f7da 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -545,14 +545,12 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] signode['module'] = modname signode['class'] = classname signode['fullname'] = fullname - py_max_len = self.env.config.python_maximum_signature_line_length - global_max_len = self.env.config.maximum_signature_line_length - max_len = py_max_len if py_max_len >= 0 else global_max_len - multi_line = ( - max_len >= 0 - and 'single-line-signature' not in self.options - and len(sig) > max_len - ) + + max_len = (self.env.config.python_maximum_signature_line_length + or self.env.config.maximum_signature_line_length + or 0) + multi_line = ('single-line-signature' not in self.options + and (len(sig) > max_len > 0)) if multi_line: signode['add_permalink'] = True @@ -1530,7 +1528,8 @@ def setup(app: Sphinx) -> dict[str, Any]: app.add_domain(PythonDomain) app.add_config_value('python_use_unqualified_type_names', False, 'env') - app.add_config_value('python_maximum_signature_line_length', -1, 'env', types=[int]) + app.add_config_value('python_maximum_signature_line_length', None, 'env', + types={int, None}) app.add_config_value('python_display_short_literal_types', False, 'env') app.connect('object-description-transform', filter_meta_fields) app.connect('missing-reference', builtin_resolver, priority=900) From e6ef3c2ea33d1a6a2b47dfe9265f3f33180c206f Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 8 Apr 2023 02:51:28 +0100 Subject: [PATCH 051/103] Rename ``single-line-signature`` to ``single-line-{parameter,argument}-list`` --- sphinx/domains/c.py | 4 ++-- sphinx/domains/cpp.py | 4 ++-- sphinx/domains/python.py | 4 ++-- tests/test_domain_c.py | 4 ++-- tests/test_domain_cpp.py | 4 ++-- tests/test_domain_py.py | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 782316be971..94e61b9581b 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -3164,7 +3164,7 @@ class CObject(ObjectDescription[ASTDeclaration]): option_spec: OptionSpec = { 'noindexentry': directives.flag, 'nocontentsentry': directives.flag, - 'single-line-signature': directives.flag, + 'single-line-parameter-list': directives.flag, } def _add_enumerator_to_parent(self, ast: ASTDeclaration) -> None: @@ -3273,7 +3273,7 @@ def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration: max_len = (self.env.config.c_maximum_signature_line_length or self.env.config.maximum_signature_line_length or 0) - multi_line = ('single-line-signature' not in self.options + multi_line = ('single-line-parameter-list' not in self.options and (len(sig) > max_len > 0)) signode['is_multi_line'] = multi_line diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 093eae50074..91034caa6c0 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -7198,7 +7198,7 @@ class CPPObject(ObjectDescription[ASTDeclaration]): 'noindexentry': directives.flag, 'nocontentsentry': directives.flag, 'tparam-line-spec': directives.flag, - 'single-line-signature': directives.flag, + 'single-line-parameter-list': directives.flag, } def _add_enumerator_to_parent(self, ast: ASTDeclaration) -> None: @@ -7358,7 +7358,7 @@ def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration: max_len = (self.env.config.cpp_maximum_signature_line_length or self.env.config.maximum_signature_line_length or 0) - multi_line = ('single-line-signature' not in self.options + multi_line = ('single-line-parameter-list' not in self.options and (len(sig) > max_len > 0)) signode['is_multi_line'] = multi_line diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 70bb3f4f7da..a16961f5a12 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -463,7 +463,7 @@ class PyObject(ObjectDescription[Tuple[str, str]]): 'noindex': directives.flag, 'noindexentry': directives.flag, 'nocontentsentry': directives.flag, - 'single-line-signature': directives.flag, + 'single-line-argument-list': directives.flag, 'module': directives.unchanged, 'canonical': directives.unchanged, 'annotation': directives.unchanged, @@ -549,7 +549,7 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] max_len = (self.env.config.python_maximum_signature_line_length or self.env.config.maximum_signature_line_length or 0) - multi_line = ('single-line-signature' not in self.options + multi_line = ('single-line-argument-list' not in self.options and (len(sig) > max_len > 0)) if multi_line: signode['add_permalink'] = True diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 56b94d663fb..7aa0935122f 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -853,7 +853,7 @@ def test_cfunction_signature_with_c_maximum_signature_line_length(app): assert_node(doctree[1][0][0][3], expected_sig) text = (".. c:function:: str hello(str names)\n" - " :single-line-signature:") + " :single-line-parameter-list:") signame_node[1] = "names" doctree = restructuredtext.parse(app, text) assert_node(doctree, expected_doctree) @@ -895,7 +895,7 @@ def test_cfunction_signature_with_maximum_signature_line_length(app): assert_node(doctree[1][0][0][3], expected_sig) text = (".. c:function:: str hello(str names)\n" - " :single-line-signature:") + " :single-line-parameter-list:") signame_node[1] = "names" doctree = restructuredtext.parse(app, text) assert_node(doctree, expected_doctree) diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 421ab2df55a..ad93ad85380 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -1525,7 +1525,7 @@ def test_cppfunction_signature_with_cpp_maximum_signature_line_length(app): assert_node(doctree[1][0][0][3], expected_sig) text = (".. cpp:function:: str hello(str names)\n" - " :single-line-signature:") + " :single-line-parameter-list:") signame_node[1] = "names" doctree = restructuredtext.parse(app, text) assert_node(doctree, expected_doctree) @@ -1567,7 +1567,7 @@ def test_cppfunction_signature_with_maximum_signature_line_length(app): assert_node(doctree[1][0][0][3], expected_sig) text = (".. cpp:function:: str hello(str names)\n" - " :single-line-signature:") + " :single-line-parameter-list:") signame_node[1] = "names" doctree = restructuredtext.parse(app, text) assert_node(doctree, expected_doctree) diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index 400b098d557..5124eaf084b 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -1484,7 +1484,7 @@ def test_pyfunction_signature_with_python_maximum_signature_line_length(app): assert_node(doctree[1][0][1], expected_sig) text = (".. py:function:: hello(names: str) -> str\n" - " :single-line-signature:") + " :single-line-argument-list:") signame_node[1] = "names" doctree = restructuredtext.parse(app, text) assert_node(doctree, expected_doctree) @@ -1524,7 +1524,7 @@ def test_pyfunction_signature_with_maximum_signature_line_length(app): assert_node(doctree[1][0][1], expected_sig) text = (".. py:function:: hello(names: str) -> str\n" - " :single-line-signature:") + " :single-line-argument-list:") signame_node[1] = "names" doctree = restructuredtext.parse(app, text) assert_node(doctree, expected_doctree) From dfd007595d51b26134f8f1aee09829b0e7a6d7ca Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 8 Apr 2023 02:32:06 +0100 Subject: [PATCH 052/103] Refactor tests If something fails, it is now easier to pinpoint where --- tests/test_domain_c.py | 273 ++++++++++++++++++++++++----------- tests/test_domain_cpp.py | 305 +++++++++++++++++++++++++-------------- tests/test_domain_py.py | 226 ++++++++++++++++++++--------- 3 files changed, 541 insertions(+), 263 deletions(-) diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 7aa0935122f..01f399b6538 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -828,136 +828,237 @@ def test_domain_c_parse_noindexentry(app): assert_node(doctree[2], addnodes.index, entries=[]) -@pytest.mark.sphinx( - 'html', - confoverrides={'c_maximum_signature_line_length': len("str hello(str name)")}, -) -def test_cfunction_signature_with_c_maximum_signature_line_length(app): +@pytest.mark.sphinx('html', confoverrides={ + 'c_maximum_signature_line_length': len("str hello(str name)"), +}) +def test_cfunction_signature_with_c_maximum_signature_line_length_equal(app): text = ".. c:function:: str hello(str name)" doctree = restructuredtext.parse(app, text) - expected_doctree = ( + assert_node(doctree, ( addnodes.index, - [desc, ([desc_signature, ([desc_signature_line, (pending_xref, - desc_sig_space, - [desc_name, [desc_sig_name, "hello"]], - desc_parameterlist)])], - desc_content)], - ) - assert_node(doctree, expected_doctree) + [desc, ( + [desc_signature, ( + [desc_signature_line, ( + pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, "hello"]], + desc_parameterlist, + )], + )], + desc_content, + )], + )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="c", objtype="function", noindex=False) - signame_node = [desc_sig_name, "name"] - expected_sig = [desc_parameterlist, desc_parameter, ([pending_xref, [desc_sig_name, "str"]], - desc_sig_space, - signame_node)] - assert_node(doctree[1][0][0][3], expected_sig) + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, ( + [pending_xref, [desc_sig_name, "str"]], + desc_sig_space, + [desc_sig_name, "name"], + )]) + +@pytest.mark.sphinx('html', confoverrides={ + 'c_maximum_signature_line_length': len("str hello(str name)"), +}) +def test_cfunction_signature_with_c_maximum_signature_line_length_force_single(app): text = (".. c:function:: str hello(str names)\n" " :single-line-parameter-list:") - signame_node[1] = "names" doctree = restructuredtext.parse(app, text) - assert_node(doctree, expected_doctree) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_signature_line, ( + pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, "hello"]], + desc_parameterlist, + )], + )], + desc_content, + )], + )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="c", objtype="function", noindex=False) - assert_node(doctree[1][0][0][3], expected_sig) + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, ( + [pending_xref, [desc_sig_name, "str"]], + desc_sig_space, + [desc_sig_name, "names"], + )]) + +@pytest.mark.sphinx('html', confoverrides={ + 'c_maximum_signature_line_length': len("str hello(str name)"), +}) +def test_cfunction_signature_with_c_maximum_signature_line_break(app): text = ".. c:function:: str hello(str names)" doctree = restructuredtext.parse(app, text) - expected_sig.insert(1, desc_parameter_line) - assert_node(doctree, expected_doctree) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_signature_line, ( + pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, "hello"]], + desc_parameterlist, + )], + )], + desc_content, + )], + )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="c", objtype="function", noindex=False) - assert_node(doctree[1][0][0][3], expected_sig) + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter_line, desc_parameter, ( + [pending_xref, [desc_sig_name, "str"]], + desc_sig_space, + [desc_sig_name, "names"], + )]) -@pytest.mark.sphinx( - 'html', - confoverrides={'maximum_signature_line_length': len("str hello(str name)")}, -) -def test_cfunction_signature_with_maximum_signature_line_length(app): +@pytest.mark.sphinx('html', confoverrides={ + 'maximum_signature_line_length': len("str hello(str name)"), +}) +def test_cfunction_signature_with_maximum_signature_line_length_equal(app): text = ".. c:function:: str hello(str name)" doctree = restructuredtext.parse(app, text) - expected_doctree = ( + assert_node(doctree, ( addnodes.index, - [desc, ([desc_signature, ([desc_signature_line, (pending_xref, - desc_sig_space, - [desc_name, [desc_sig_name, "hello"]], - desc_parameterlist)])], - desc_content)], - ) - assert_node(doctree, expected_doctree) + [desc, ( + [desc_signature, ( + [desc_signature_line, ( + pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, "hello"]], + desc_parameterlist, + )], + )], + desc_content, + )], + )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="c", objtype="function", noindex=False) - signame_node = [desc_sig_name, "name"] - expected_sig = [desc_parameterlist, desc_parameter, ([pending_xref, [desc_sig_name, "str"]], - desc_sig_space, - signame_node)] - assert_node(doctree[1][0][0][3], expected_sig) + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, ( + [pending_xref, [desc_sig_name, "str"]], + desc_sig_space, + [desc_sig_name, "name"], + )]) + +@pytest.mark.sphinx('html', confoverrides={ + 'maximum_signature_line_length': len("str hello(str name)"), +}) +def test_cfunction_signature_with_maximum_signature_line_length_force_single(app): text = (".. c:function:: str hello(str names)\n" " :single-line-parameter-list:") - signame_node[1] = "names" doctree = restructuredtext.parse(app, text) - assert_node(doctree, expected_doctree) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_signature_line, ( + pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, "hello"]], + desc_parameterlist, + )], + )], + desc_content, + )], + )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="c", objtype="function", noindex=False) - assert_node(doctree[1][0][0][3], expected_sig) + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, ( + [pending_xref, [desc_sig_name, "str"]], + desc_sig_space, + [desc_sig_name, "names"], + )]) + +@pytest.mark.sphinx('html', confoverrides={ + 'maximum_signature_line_length': len("str hello(str name)"), +}) +def test_cfunction_signature_with_maximum_signature_line_break(app): text = ".. c:function:: str hello(str names)" doctree = restructuredtext.parse(app, text) - expected_sig.insert(1, desc_parameter_line) - assert_node(doctree, expected_doctree) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_signature_line, ( + pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, "hello"]], + desc_parameterlist, + )], + )], + desc_content, + )], + )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="c", objtype="function", noindex=False) - assert_node(doctree[1][0][0][3], expected_sig) + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter_line, desc_parameter, ( + [pending_xref, [desc_sig_name, "str"]], + desc_sig_space, + [desc_sig_name, "names"], + )]) @pytest.mark.sphinx( 'html', - confoverrides={ - 'c_maximum_signature_line_length': len("str hello(str name)"), - 'maximum_signature_line_length': 1, - }, + confoverrides={'maximum_signature_line_length': len("str hello(str name)")}, ) +def test_cfunction_signature_with_maximum_signature_line_length(app): + ... + + +@pytest.mark.sphinx('html', confoverrides={ + 'c_maximum_signature_line_length': len('str hello(str name)'), + 'maximum_signature_line_length': 1, +}) def test_c_maximum_signature_line_length_overrides_global(app): - text = ".. c:function:: str hello(str name)" + text = '.. c:function:: str hello(str name)' doctree = restructuredtext.parse(app, text) - expected_doctree = ( + assert_node(doctree, ( addnodes.index, - [desc, ([desc_signature, ([desc_signature_line, (pending_xref, - desc_sig_space, - [desc_name, [desc_sig_name, "hello"]], - desc_parameterlist)])], - desc_content)], - ) - assert_node(doctree, expected_doctree) - assert_node(doctree[1], addnodes.desc, desctype="function", - domain="c", objtype="function", noindex=False) - signame_node = [desc_sig_name, "name"] - expected_sig = [desc_parameterlist, desc_parameter, ([pending_xref, [desc_sig_name, "str"]], - desc_sig_space, - signame_node)] - assert_node(doctree[1][0][0][3], expected_sig) + [desc, ( + [desc_signature, ( + [desc_signature_line, ( + pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, 'hello']], + desc_parameterlist, + )] + )], + desc_content, + )], + )) + assert_node(doctree[1], addnodes.desc, desctype='function', + domain='c', objtype='function', noindex=False) + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, ( + [pending_xref, [desc_sig_name, "str"]], + desc_sig_space, + [desc_sig_name, 'name'], + )]) -@pytest.mark.sphinx( - 'html', testroot='domain-c-c_maximum_signature_line_length', -) +@pytest.mark.sphinx('html', testroot='domain-c-c_maximum_signature_line_length') def test_domain_c_c_maximum_signature_line_length(app, status, warning): app.build() - content = (app.outdir / 'index.html').read_text(encoding='utf8') - expected = '\n'.join(( - '
', - ( - '
str ' - 'name,
' - ), - '
', - '', - ( - ')' - '
' - '
' - ), - )) + content = (app.outdir / 'index.html').read_text(encoding='utf-8') + expected = """\ + +
+
\ +str\ + \ +name, \ +
+
+ +)\ +\ +
\ + +""" assert expected in content diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index ad93ad85380..6fce78cbbfa 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -1500,132 +1500,221 @@ def test_domain_cpp_normalize_unspecialized_template_args(make_app, app_params): assert 'Internal C++ domain error during symbol merging' not in warning -@pytest.mark.sphinx( - 'html', - confoverrides={'cpp_maximum_signature_line_length': len("str hello(str name)")}, -) -def test_cppfunction_signature_with_cpp_maximum_signature_line_length(app): - text = ".. cpp:function:: str hello(str name)" +@pytest.mark.sphinx('html', confoverrides={ + 'cpp_maximum_signature_line_length': len('str hello(str name)'), +}) +def test_cpp_function_signature_with_cpp_maximum_signature_line_length_equal(app): + text = '.. cpp:function:: str hello(str name)' doctree = restructuredtext.parse(app, text) - expected_doctree = ( + assert_node(doctree, ( addnodes.index, - [desc, ([desc_signature, ([desc_signature_line, (pending_xref, - desc_sig_space, - [desc_name, [desc_sig_name, "hello"]], - desc_parameterlist)])], - desc_content)], - ) - assert_node(doctree, expected_doctree) - assert_node(doctree[1], addnodes.desc, desctype="function", - domain="cpp", objtype="function", noindex=False) - signame_node = [desc_sig_name, "name"] - expected_sig = [desc_parameterlist, desc_parameter, ([pending_xref, [desc_sig_name, "str"]], - desc_sig_space, - signame_node)] - assert_node(doctree[1][0][0][3], expected_sig) - - text = (".. cpp:function:: str hello(str names)\n" - " :single-line-parameter-list:") - signame_node[1] = "names" + [desc, ( + [desc_signature, ( + [desc_signature_line, ( + pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, 'hello']], + desc_parameterlist, + )], + )], + desc_content, + )], + )) + assert_node(doctree[1], addnodes.desc, desctype='function', + domain='cpp', objtype='function', noindex=False) + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, ( + [pending_xref, [desc_sig_name, 'str']], + desc_sig_space, + [desc_sig_name, 'name'], + )]) + + +@pytest.mark.sphinx('html', confoverrides={ + 'cpp_maximum_signature_line_length': len('str hello(str name)'), +}) +def test_cpp_function_signature_with_cpp_maximum_signature_line_length_force_single(app): + text = ('.. cpp:function:: str hello(str names)\n' + ' :single-line-parameter-list:') doctree = restructuredtext.parse(app, text) - assert_node(doctree, expected_doctree) - assert_node(doctree[1], addnodes.desc, desctype="function", - domain="cpp", objtype="function", noindex=False) - assert_node(doctree[1][0][0][3], expected_sig) - - text = ".. cpp:function:: str hello(str names)" + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_signature_line, ( + pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, 'hello']], + desc_parameterlist, + )], + )], + desc_content, + )], + )) + assert_node(doctree[1], addnodes.desc, desctype='function', + domain='cpp', objtype='function', noindex=False) + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, ( + [pending_xref, [desc_sig_name, 'str']], + desc_sig_space, + [desc_sig_name, 'names']), + ]) + + +@pytest.mark.sphinx('html', confoverrides={ + 'cpp_maximum_signature_line_length': len("str hello(str name)"), +}) +def test_cpp_function_signature_with_cpp_maximum_signature_line_length_break(app): + text = '.. cpp:function:: str hello(str names)' doctree = restructuredtext.parse(app, text) - expected_sig.insert(1, desc_parameter_line) - assert_node(doctree, expected_doctree) - assert_node(doctree[1], addnodes.desc, desctype="function", - domain="cpp", objtype="function", noindex=False) - assert_node(doctree[1][0][0][3], expected_sig) - - -@pytest.mark.sphinx( - 'html', - confoverrides={'maximum_signature_line_length': len("str hello(str name)")}, -) -def test_cppfunction_signature_with_maximum_signature_line_length(app): - text = ".. cpp:function:: str hello(str name)" + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_signature_line, ( + pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, 'hello']], + desc_parameterlist, + )], + )], + desc_content, + )], + )) + assert_node(doctree[1], addnodes.desc, desctype='function', + domain='cpp', objtype='function', noindex=False) + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter_line, desc_parameter, ( + [pending_xref, [desc_sig_name, 'str']], + desc_sig_space, + [desc_sig_name, 'names']), + ]) + + +@pytest.mark.sphinx('html', confoverrides={ + 'maximum_signature_line_length': len('str hello(str name)'), +}) +def test_cpp_function_signature_with_maximum_signature_line_length_equal(app): + text = '.. cpp:function:: str hello(str name)' doctree = restructuredtext.parse(app, text) - expected_doctree = ( + assert_node(doctree, ( addnodes.index, - [desc, ([desc_signature, ([desc_signature_line, (pending_xref, - desc_sig_space, - [desc_name, [desc_sig_name, "hello"]], - desc_parameterlist)])], - desc_content)], - ) - assert_node(doctree, expected_doctree) - assert_node(doctree[1], addnodes.desc, desctype="function", - domain="cpp", objtype="function", noindex=False) - signame_node = [desc_sig_name, "name"] - expected_sig = [desc_parameterlist, desc_parameter, ([pending_xref, [desc_sig_name, "str"]], - desc_sig_space, - signame_node)] - assert_node(doctree[1][0][0][3], expected_sig) - - text = (".. cpp:function:: str hello(str names)\n" - " :single-line-parameter-list:") - signame_node[1] = "names" + [desc, ( + [desc_signature, ( + [desc_signature_line, ( + pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, 'hello']], + desc_parameterlist, + )], + )], + desc_content, + )], + )) + assert_node(doctree[1], addnodes.desc, desctype='function', + domain='cpp', objtype='function', noindex=False) + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, ( + [pending_xref, [desc_sig_name, 'str']], + desc_sig_space, + [desc_sig_name, 'name'], + )]) + + +@pytest.mark.sphinx('html', confoverrides={ + 'maximum_signature_line_length': len('str hello(str name)'), +}) +def test_cpp_function_signature_with_maximum_signature_line_length_force_single(app): + text = ('.. cpp:function:: str hello(str names)\n' + ' :single-line-parameter-list:') doctree = restructuredtext.parse(app, text) - assert_node(doctree, expected_doctree) - assert_node(doctree[1], addnodes.desc, desctype="function", - domain="cpp", objtype="function", noindex=False) - assert_node(doctree[1][0][0][3], expected_sig) - - text = ".. cpp:function:: str hello(str names)" + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_signature_line, ( + pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, 'hello']], + desc_parameterlist, + )], + )], + desc_content, + )], + )) + assert_node(doctree[1], addnodes.desc, desctype='function', + domain='cpp', objtype='function', noindex=False) + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, ( + [pending_xref, [desc_sig_name, 'str']], + desc_sig_space, + [desc_sig_name, 'names']), + ]) + + +@pytest.mark.sphinx('html', confoverrides={ + 'maximum_signature_line_length': len("str hello(str name)"), +}) +def test_cpp_function_signature_with_maximum_signature_line_length_break(app): + text = '.. cpp:function:: str hello(str names)' doctree = restructuredtext.parse(app, text) - expected_sig.insert(1, desc_parameter_line) - assert_node(doctree, expected_doctree) - assert_node(doctree[1], addnodes.desc, desctype="function", - domain="cpp", objtype="function", noindex=False) - assert_node(doctree[1][0][0][3], expected_sig) - - -@pytest.mark.sphinx( - 'html', - confoverrides={ - 'cpp_maximum_signature_line_length': len("str hello(str name)"), - 'maximum_signature_line_length': 1, - }, -) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_signature_line, ( + pending_xref, + desc_sig_space, + [desc_name, [desc_sig_name, 'hello']], + desc_parameterlist, + )], + )], + desc_content, + )], + )) + assert_node(doctree[1], addnodes.desc, desctype='function', + domain='cpp', objtype='function', noindex=False) + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter_line, desc_parameter, ( + [pending_xref, [desc_sig_name, 'str']], + desc_sig_space, + [desc_sig_name, 'names']), + ]) + + +@pytest.mark.sphinx('html', confoverrides={ + 'cpp_maximum_signature_line_length': len('str hello(str name)'), + 'maximum_signature_line_length': 1, +}) def test_cpp_maximum_signature_line_length_overrides_global(app): - text = ".. cpp:function:: str hello(str name)" + text = '.. cpp:function:: str hello(str name)' doctree = restructuredtext.parse(app, text) - expected_doctree = ( + assert_node(doctree, ( addnodes.index, [desc, ([desc_signature, ([desc_signature_line, (pending_xref, desc_sig_space, [desc_name, [desc_sig_name, "hello"]], desc_parameterlist)])], desc_content)], - ) - assert_node(doctree, expected_doctree) - assert_node(doctree[1], addnodes.desc, desctype="function", - domain="cpp", objtype="function", noindex=False) - signame_node = [desc_sig_name, "name"] - expected_sig = [desc_parameterlist, desc_parameter, ([pending_xref, [desc_sig_name, "str"]], - desc_sig_space, - signame_node)] - assert_node(doctree[1][0][0][3], expected_sig) + )) + assert_node(doctree[1], addnodes.desc, desctype='function', + domain='cpp', objtype='function', noindex=False) + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, ( + [pending_xref, [desc_sig_name, 'str']], + desc_sig_space, + [desc_sig_name, 'name'], + )]) -@pytest.mark.sphinx( - 'html', testroot='domain-cpp-cpp_maximum_signature_line_length', -) +@pytest.mark.sphinx('html', testroot='domain-cpp-cpp_maximum_signature_line_length') def test_domain_cpp_cpp_maximum_signature_line_length(app, status, warning): app.build() - content = (app.outdir / 'index.html').read_text(encoding='utf8') - expected = '\n'.join(( - '
', - ( - '
str ' - 'name,
' - ), - '
', - '', - ') +
\ +str\ + \ +name, \ +
+ + +)\ +
str")}, -) -def test_pyfunction_signature_with_python_maximum_signature_line_length(app): +@pytest.mark.sphinx('html', confoverrides={ + 'python_maximum_signature_line_length': len("hello(name: str) -> str"), +}) +def test_pyfunction_signature_with_python_maximum_signature_line_length_equal(app): text = ".. py:function:: hello(name: str) -> str" doctree = restructuredtext.parse(app, text) - expected_doctree = (addnodes.index, - [desc, ([desc_signature, ([desc_name, "hello"], - desc_parameterlist, - [desc_returns, pending_xref, "str"])], - desc_content)]) - assert_node(doctree, expected_doctree) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_name, "hello"], + desc_parameterlist, + [desc_returns, pending_xref, "str"], + )], + desc_content, + )], + )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="py", objtype="function", noindex=False) - signame_node = [desc_sig_name, "name"] - expected_sig = [desc_parameterlist, desc_parameter, (signame_node, - [desc_sig_punctuation, ":"], - desc_sig_space, - [nodes.inline, pending_xref, "str"])] - assert_node(doctree[1][0][1], expected_sig) - + assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( + [desc_sig_name, "name"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"], + )]) + + +@pytest.mark.sphinx('html', confoverrides={ + 'python_maximum_signature_line_length': len("hello(name: str) -> str"), +}) +def test_pyfunction_signature_with_python_maximum_signature_line_length_force_single(app): text = (".. py:function:: hello(names: str) -> str\n" " :single-line-argument-list:") - signame_node[1] = "names" doctree = restructuredtext.parse(app, text) - assert_node(doctree, expected_doctree) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_name, "hello"], + desc_parameterlist, + [desc_returns, pending_xref, "str"], + )], + desc_content, + )], + )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="py", objtype="function", noindex=False) - assert_node(doctree[1][0][1], expected_sig) - + assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( + [desc_sig_name, "names"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"], + )]) + + +@pytest.mark.sphinx('html', confoverrides={ + 'python_maximum_signature_line_length': len("hello(name: str) -> str"), +}) +def test_pyfunction_signature_with_python_maximum_signature_line_length_break(app): text = ".. py:function:: hello(names: str) -> str" doctree = restructuredtext.parse(app, text) - expected_sig.insert(1, desc_parameter_line) - assert_node(doctree, expected_doctree) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_name, "hello"], + desc_parameterlist, + [desc_returns, pending_xref, "str"], + )], + desc_content, + )], + )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="py", objtype="function", noindex=False) - assert_node(doctree[1][0][1], expected_sig) - - -@pytest.mark.sphinx( - 'html', - confoverrides={'maximum_signature_line_length': len("hello(name: str) -> str")}, -) -def test_pyfunction_signature_with_maximum_signature_line_length(app): + assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter_line, desc_parameter, ( + [desc_sig_name, "names"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"], + )]) + + +@pytest.mark.sphinx('html', confoverrides={ + 'maximum_signature_line_length': len("hello(name: str) -> str"), +}) +def test_pyfunction_signature_with_maximum_signature_line_length_equal(app): text = ".. py:function:: hello(name: str) -> str" doctree = restructuredtext.parse(app, text) - expected_doctree = (addnodes.index, - [desc, ([desc_signature, ([desc_name, "hello"], - desc_parameterlist, - [desc_returns, pending_xref, "str"])], - desc_content)]) - assert_node(doctree, expected_doctree) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_name, "hello"], + desc_parameterlist, + [desc_returns, pending_xref, "str"], + )], + desc_content, + )], + )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="py", objtype="function", noindex=False) - signame_node = [desc_sig_name, "name"] - expected_sig = [desc_parameterlist, desc_parameter, (signame_node, - [desc_sig_punctuation, ":"], - desc_sig_space, - [nodes.inline, pending_xref, "str"])] - assert_node(doctree[1][0][1], expected_sig) - + assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( + [desc_sig_name, "name"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"], + )]) + + +@pytest.mark.sphinx('html', confoverrides={ + 'maximum_signature_line_length': len("hello(name: str) -> str"), +}) +def test_pyfunction_signature_with_maximum_signature_line_length_force_single(app): text = (".. py:function:: hello(names: str) -> str\n" " :single-line-argument-list:") - signame_node[1] = "names" doctree = restructuredtext.parse(app, text) - assert_node(doctree, expected_doctree) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_name, "hello"], + desc_parameterlist, + [desc_returns, pending_xref, "str"], + )], + desc_content, + )], + )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="py", objtype="function", noindex=False) - assert_node(doctree[1][0][1], expected_sig) - + assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( + [desc_sig_name, "names"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"], + )]) + + +@pytest.mark.sphinx('html', confoverrides={ + 'maximum_signature_line_length': len("hello(name: str) -> str"), +}) +def test_pyfunction_signature_with_maximum_signature_line_length_break(app): text = ".. py:function:: hello(names: str) -> str" doctree = restructuredtext.parse(app, text) - expected_sig.insert(1, desc_parameter_line) - assert_node(doctree, expected_doctree) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_name, "hello"], + desc_parameterlist, + [desc_returns, pending_xref, "str"], + )], + desc_content, + )], + )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="py", objtype="function", noindex=False) - assert_node(doctree[1][0][1], expected_sig) + assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter_line, desc_parameter, ( + [desc_sig_name, "names"], + [desc_sig_punctuation, ":"], + desc_sig_space, + [nodes.inline, pending_xref, "str"], + )]) @pytest.mark.sphinx( @@ -1573,23 +1657,27 @@ def test_python_maximum_signature_line_length_overrides_global(app): def test_python_python_maximum_signature_line_length(app, status, warning): app.build() content = (app.outdir / 'index.html').read_text(encoding='utf8') - expected = '\n'.join(( - '
', - ( - '
name' - ': ' - 'str,
' - ), - '
', - '', - ( - ') ' - ' ' - 'str' - '
' - '' - ), - )) + expected = """\ + +
+
\ +\ +name\ +:\ + \ +str\ +, \ +
+
+ +) \ +\ + \ +str\ +\ +\ +\ +""" assert expected in content From bfab7ea26f3d0d43126af49c9caad020119c0376 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Fri, 7 Apr 2023 22:56:31 +0100 Subject: [PATCH 053/103] Set and use ``multi_line_parameter_list`` on node --- sphinx/addnodes.py | 8 ++++---- sphinx/domains/c.py | 42 +++++++++++++++++++++------------------- sphinx/domains/cpp.py | 38 ++++++++++++++++++++++-------------- sphinx/domains/python.py | 36 +++++++++++++++++++++------------- sphinx/util/cfamily.py | 3 +-- sphinx/writers/html5.py | 4 ++-- sphinx/writers/latex.py | 2 +- sphinx/writers/text.py | 2 +- 8 files changed, 77 insertions(+), 58 deletions(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 01cc53b5b2c..3f9bc9fd36f 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -249,9 +249,9 @@ class desc_parameterlist(nodes.Part, nodes.Inline, nodes.FixedTextElement): """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. + Set ``multi_line_parameter_list = 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 = ', ' @@ -263,7 +263,7 @@ 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` + ``multi_line_parameter_list`` set to ``True``, and have a single :py:class:`desc_parameter` child. """ diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 94e61b9581b..18b99315791 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -701,15 +701,9 @@ def describe_signature(self, signode: Any, mode: str, class ASTParameters(ASTBase): - def __init__( - self, - args: list[ASTFunctionParameter], - attrs: ASTAttributeList, - multi_line: bool = False, - ) -> None: + def __init__(self, args: list[ASTFunctionParameter], attrs: ASTAttributeList) -> None: self.args = args self.attrs = attrs - self.multi_line = multi_line @property def function_params(self) -> list[ASTFunctionParameter]: @@ -733,18 +727,27 @@ 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 + 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['is_multi_line'] = multi_line + paramlist['multi_line_parameter_list'] = multi_line_parameter_list 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) - param_node += param - if multi_line: - paramlist += param_node + + if multi_line_parameter_list: + paramlist += addnodes.desc_parameter_line('', param) + else: + paramlist += param signode += paramlist else: signode += addnodes.desc_sig_punctuation('(', '(') @@ -2686,7 +2689,7 @@ def _parse_parameters(self, paramMode: str) -> ASTParameters | None: self.fail(f'Expecting "," or ")" in parameters, got "{self.current_char}".') attrs = self._parse_attribute_list() - return ASTParameters(args, attrs, multi_line=self.multi_line) + return ASTParameters(args, attrs) def _parse_decl_specs_simple( self, outer: str | None, typed: bool, @@ -3273,13 +3276,12 @@ def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration: max_len = (self.env.config.c_maximum_signature_line_length or self.env.config.maximum_signature_line_length or 0) - multi_line = ('single-line-parameter-list' not in self.options - and (len(sig) > max_len > 0)) - - signode['is_multi_line'] = multi_line - parser = DefinitionParser( - sig, location=signode, config=self.env.config, multi_line=multi_line, + 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) parser.assert_end() diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 91034caa6c0..fe77c65bd96 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -2062,7 +2062,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, multi_line: bool = False) -> None: + initializer: str | None) -> None: self.args = args self.volatile = volatile self.const = const @@ -2073,7 +2073,6 @@ 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]: @@ -2143,18 +2142,28 @@ 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 + 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['is_multi_line'] = multi_line + paramlist['multi_line_parameter_list'] = multi_line_parameter_list 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) - param_node += param - if multi_line: - paramlist += param_node + + if multi_line_parameter_list: + paramlist += addnodes.desc_parameter_line('', param) + else: + paramlist += param + signode += paramlist else: signode += addnodes.desc_sig_punctuation('(', '(') @@ -6248,7 +6257,7 @@ def _parse_parameters_and_qualifiers(self, paramMode: str) -> ASTParametersQuali return ASTParametersQualifiers( args, volatile, const, refQual, exceptionSpec, trailingReturn, - override, final, attrs, initializer, multi_line=self.multi_line) + override, final, attrs, initializer) def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimple: """Just parse the simple ones.""" @@ -7358,13 +7367,12 @@ def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration: max_len = (self.env.config.cpp_maximum_signature_line_length or self.env.config.maximum_signature_line_length or 0) - multi_line = ('single-line-parameter-list' not in self.options - and (len(sig) > max_len > 0)) - - signode['is_multi_line'] = multi_line - parser = DefinitionParser( - sig, location=signode, config=self.env.config, multi_line=multi_line, + 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) parser.assert_end() diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index a16961f5a12..fdd2489d957 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -258,25 +258,32 @@ def _unparse_pep_604_annotation(node: ast.Subscript) -> list[Node]: def _parse_arglist( - arglist: str, env: BuildEnvironment | None = None, multi_line: bool = False, + arglist: str, env: BuildEnvironment | None = None, multi_line_parameter_list: bool = False, ) -> addnodes.desc_parameterlist: """Parse a list of arguments using AST parser""" - params = addnodes.desc_parameterlist() - params['is_multi_line'] = multi_line + params = addnodes.desc_parameterlist(arglist) + params['multi_line_parameter_list'] = multi_line_parameter_list sig = signature_from_str('(%s)' % arglist) last_kind = None for param in sig.parameters.values(): - param_node = addnodes.desc_parameter_line() if multi_line else params + parameter_nodes = [] + if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY: # PEP-570: Separator for Positional Only Parameter: / - param_node += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/')) + parameter_nodes.append( + addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/')), + ) if param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD, param.POSITIONAL_ONLY, None): # PEP-3102: Separator for Keyword Only Parameter: * - param_node += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '*')) + parameter_nodes.append( + addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '*')), + ) node = addnodes.desc_parameter() + parameter_nodes.append(node) + if param.kind == param.VAR_POSITIONAL: node += addnodes.desc_sig_operator('', '*') node += addnodes.desc_sig_name('', param.name) @@ -301,9 +308,10 @@ def _parse_arglist( node += nodes.inline('', param.default, classes=['default_value'], support_smartquotes=False) - param_node += node - if multi_line: - params += param_node + if multi_line_parameter_list: + params.append(addnodes.desc_parameter_line('', *parameter_nodes)) + else: + params += parameter_nodes last_kind = param.kind if last_kind == Parameter.POSITIONAL_ONLY: @@ -549,9 +557,11 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] max_len = (self.env.config.python_maximum_signature_line_length or self.env.config.maximum_signature_line_length or 0) - multi_line = ('single-line-argument-list' not in self.options - and (len(sig) > max_len > 0)) - if multi_line: + multi_line_parameter_list = ( + 'single-line-argument-list' not in self.options + and (len(sig) > max_len > 0) + ) + if multi_line_parameter_list: signode['add_permalink'] = True sig_prefix = self.get_signature_prefix(sig) @@ -572,7 +582,7 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] signode += addnodes.desc_name(name, name) if arglist: try: - signode += _parse_arglist(arglist, self.env, multi_line=multi_line) + signode += _parse_arglist(arglist, self.env, multi_line_parameter_list) except SyntaxError: # fallback to parse arglist original parser. # it supports to represent optional arguments (ex. "func(foo [, bar])") diff --git a/sphinx/util/cfamily.py b/sphinx/util/cfamily.py index 7cde532e636..32984804b4f 100644 --- a/sphinx/util/cfamily.py +++ b/sphinx/util/cfamily.py @@ -237,11 +237,10 @@ class DefinitionError(Exception): class BaseParser: def __init__(self, definition: str, *, location: nodes.Node | tuple[str, int] | str, - config: Config, multi_line: bool = False) -> None: + config: Config) -> None: self.definition = definition.strip() self.location = location # for warnings self.config = config - self.multi_line = multi_line self.pos = 0 self.end = len(self.definition) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 62d8a261a5b..40c8a7c157d 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -156,12 +156,12 @@ def visit_desc_parameterlist(self, node: Element) -> None: self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) for c in node.children]) self.param_separator = node.child_text_separator - if node.get('is_multi_line'): + if node.get('multi_line_parameter_list'): self.body.append('\n\n') self.body.append(self.starttag(node, 'dl')) def depart_desc_parameterlist(self, node: Element) -> None: - if node.get('is_multi_line'): + if node.get('multi_line_parameter_list'): self.body.append('\n\n\n') self.body.append(')') diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 4cbfce7819a..6fd638447e5 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -712,7 +712,7 @@ def depart_desc(self, node: Element) -> None: def _visit_signature_line(self, node: Element) -> None: for child in node: if isinstance(child, addnodes.desc_parameterlist): - if child.get('is_multi_line'): + if child.get('multi_line_parameter_list'): self.body.append(CR + r'\pysigwithonelineperarg{') else: self.body.append(CR + r'\pysiglinewithargsret{') diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index 823b6e9803d..72d512e62e0 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -607,7 +607,7 @@ def depart_desc_parameter_line(self, node: Element) -> None: self.end_state(wrap=False, end=None) def visit_desc_parameter(self, node: Element) -> None: - if not self.first_param and not node.parent.parent.get('is_multi_line'): + if not self.first_param and not node.parent.parent.get('multi_line_parameter_list'): self.add_text(', ') else: self.first_param = 0 From 26551bc2fd21ef750b8ee8b8ff606b9663382125 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 8 Apr 2023 02:42:25 +0100 Subject: [PATCH 054/103] Remove ``desc_parameter_line`` --- doc/extdev/nodes.rst | 1 - sphinx/addnodes.py | 13 +------------ sphinx/domains/c.py | 6 +----- sphinx/domains/cpp.py | 7 +------ sphinx/domains/python.py | 5 +---- sphinx/writers/html5.py | 13 ++++++------- sphinx/writers/latex.py | 6 ------ sphinx/writers/manpage.py | 6 ------ sphinx/writers/texinfo.py | 6 ------ sphinx/writers/text.py | 13 ++++++------- tests/test_domain_c.py | 7 +++---- tests/test_domain_cpp.py | 7 +++---- tests/test_domain_py.py | 7 +++---- 13 files changed, 25 insertions(+), 72 deletions(-) diff --git a/doc/extdev/nodes.rst b/doc/extdev/nodes.rst index f4d41d44c91..5ef669468d7 100644 --- a/doc/extdev/nodes.rst +++ b/doc/extdev/nodes.rst @@ -31,7 +31,6 @@ and in :py:class:`desc_signature_line` nodes. .. autoclass:: desc_type .. autoclass:: desc_returns .. autoclass:: desc_parameterlist -.. autoclass:: desc_parameter_line .. autoclass:: desc_parameter .. autoclass:: desc_optional .. autoclass:: desc_annotation diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 3f9bc9fd36f..e92d32a0ef8 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -250,8 +250,7 @@ class desc_parameterlist(nodes.Part, nodes.Inline, nodes.FixedTextElement): 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 all child nodes must be :py:class:`desc_parameter_line` nodes, - and each parameter will then be written on its own, indented line. + In that case each parameter will then be written on its own, indented line. """ child_text_separator = ', ' @@ -259,15 +258,6 @@ 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 - ``multi_line_parameter_list`` 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.""" @@ -547,7 +537,6 @@ def setup(app: Sphinx) -> dict[str, Any]: app.add_node(desc_type) app.add_node(desc_returns) app.add_node(desc_parameterlist) - app.add_node(desc_parameter_line) app.add_node(desc_parameter) app.add_node(desc_optional) app.add_node(desc_annotation) diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 18b99315791..440e7197123 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -743,11 +743,7 @@ def describe_signature(self, signode: TextElement, mode: str, for arg in self.args: param = addnodes.desc_parameter('', '', noemph=True) arg.describe_signature(param, 'param', env, symbol=symbol) - - if multi_line_parameter_list: - paramlist += addnodes.desc_parameter_line('', param) - else: - paramlist += param + paramlist += param signode += paramlist else: signode += addnodes.desc_sig_punctuation('(', '(') diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index fe77c65bd96..41f2bd07682 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -2158,12 +2158,7 @@ def describe_signature(self, signode: TextElement, mode: str, for arg in self.args: param = addnodes.desc_parameter('', '', noemph=True) arg.describe_signature(param, 'param', env, symbol=symbol) - - if multi_line_parameter_list: - paramlist += addnodes.desc_parameter_line('', param) - else: - paramlist += param - + paramlist += param signode += paramlist else: signode += addnodes.desc_sig_punctuation('(', '(') diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index fdd2489d957..6b5123c89c4 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -308,10 +308,7 @@ def _parse_arglist( node += nodes.inline('', param.default, classes=['default_value'], support_smartquotes=False) - if multi_line_parameter_list: - params.append(addnodes.desc_parameter_line('', *parameter_nodes)) - else: - params += parameter_nodes + params += parameter_nodes last_kind = param.kind if last_kind == Parameter.POSITIONAL_ONLY: diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 40c8a7c157d..425652cd35b 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -156,7 +156,8 @@ def visit_desc_parameterlist(self, node: Element) -> None: self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) for c in node.children]) self.param_separator = node.child_text_separator - if node.get('multi_line_parameter_list'): + self.multi_line_parameter_list = node.get('multi_line_parameter_list', False) + if self.multi_line_parameter_list: self.body.append('\n\n') self.body.append(self.starttag(node, 'dl')) @@ -165,12 +166,6 @@ def depart_desc_parameterlist(self, node: Element) -> None: self.body.append('\n\n\n') self.body.append(')') - def visit_desc_parameter_line(self, node: Element) -> None: - self.body.append(self.starttag(node, 'dd', '')) - - def depart_desc_parameter_line(self, node: Element) -> None: - self.body.append('') - # If required parameters are still to come, then put the comma after # the parameter. Otherwise, put the comma before. This ensures that # signatures like the following render correctly (see issue #1001): @@ -178,6 +173,8 @@ def depart_desc_parameter_line(self, node: Element) -> None: # foo([a, ]b, c[, d]) # def visit_desc_parameter(self, node: Element) -> None: + if self.multi_line_parameter_list: + self.body.append(self.starttag(node, 'dd', '')) if self.first_param: self.first_param = 0 elif not self.required_params_left: @@ -192,6 +189,8 @@ def depart_desc_parameter(self, node: Element) -> None: self.body.append('') if self.required_params_left: self.body.append(self.param_separator) + if self.multi_line_parameter_list: + self.body.append('') def visit_desc_optional(self, node: Element) -> None: self.optional_param_level += 1 diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 6fd638447e5..6efb7459aa3 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -803,12 +803,6 @@ def depart_desc_parameterlist(self, node: Element) -> None: # close parameterlist, open return annotation self.body.append('}{') - def visit_desc_parameter_line(self, node: Element) -> None: - pass - - def depart_desc_parameter_line(self, node: Element) -> None: - pass - def visit_desc_parameter(self, node: Element) -> None: if not self.first_param: self.body.append(', ') diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py index a14e50a8d35..0731e972280 100644 --- a/sphinx/writers/manpage.py +++ b/sphinx/writers/manpage.py @@ -190,12 +190,6 @@ def visit_desc_parameterlist(self, node: Element) -> None: def depart_desc_parameterlist(self, node: Element) -> None: self.body.append(')') - def visit_desc_parameter_line(self, node: Element) -> None: - self.visit_definition(node) - - def depart_desc_parameter_line(self, node: Element) -> None: - self.depart_definition(node) - def visit_desc_parameter(self, node: Element) -> None: if not self.first_param: self.body.append(', ') diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index 0c612d45f4e..6979437686c 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -1468,12 +1468,6 @@ def visit_desc_parameterlist(self, node: Element) -> None: def depart_desc_parameterlist(self, node: Element) -> None: self.body.append(')') - def visit_desc_parameter_line(self, node: Element) -> None: - pass - - def depart_desc_parameter_line(self, node: Element) -> None: - pass - def visit_desc_parameter(self, node: Element) -> None: if not self.first_param: self.body.append(', ') diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index 72d512e62e0..27e746e1550 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -595,23 +595,22 @@ def depart_desc_returns(self, node: Element) -> None: def visit_desc_parameterlist(self, node: Element) -> None: self.add_text('(') self.first_param = 1 + self.multi_line_parameter_list = node.get('multi_line_parameter_list', False) def depart_desc_parameterlist(self, node: Element) -> None: self.add_text(')') - def visit_desc_parameter_line(self, node: Element) -> None: - self.new_state() - - def depart_desc_parameter_line(self, node: Element) -> None: - self.add_text(',') - self.end_state(wrap=False, end=None) - def visit_desc_parameter(self, node: Element) -> None: + if self.multi_line_parameter_list: + self.new_state() if not self.first_param and not node.parent.parent.get('multi_line_parameter_list'): self.add_text(', ') else: self.first_param = 0 self.add_text(node.astext()) + if self.multi_line_parameter_list: + self.add_text(',') + self.end_state(wrap=False, end=None) raise nodes.SkipNode def visit_desc_optional(self, node: Element) -> None: diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 01f399b6538..79593989ccb 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -12,7 +12,6 @@ desc_content, desc_name, desc_parameter, - desc_parameter_line, desc_parameterlist, desc_sig_name, desc_sig_space, @@ -909,7 +908,7 @@ def test_cfunction_signature_with_c_maximum_signature_line_break(app): )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="c", objtype="function", noindex=False) - assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter_line, desc_parameter, ( + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, ( [pending_xref, [desc_sig_name, "str"]], desc_sig_space, [desc_sig_name, "names"], @@ -997,7 +996,7 @@ def test_cfunction_signature_with_maximum_signature_line_break(app): )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="c", objtype="function", noindex=False) - assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter_line, desc_parameter, ( + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, ( [pending_xref, [desc_sig_name, "str"]], desc_sig_space, [desc_sig_name, "names"], @@ -1052,7 +1051,7 @@ def test_domain_c_c_maximum_signature_line_length(app, status, warning):
\ str\ \ -name, \ +name\
diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 6fce78cbbfa..0b2b0bb4e4b 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -13,7 +13,6 @@ desc_content, desc_name, desc_parameter, - desc_parameter_line, desc_parameterlist, desc_sig_name, desc_sig_space, @@ -1581,7 +1580,7 @@ def test_cpp_function_signature_with_cpp_maximum_signature_line_length_break(app )) assert_node(doctree[1], addnodes.desc, desctype='function', domain='cpp', objtype='function', noindex=False) - assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter_line, desc_parameter, ( + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, ( [pending_xref, [desc_sig_name, 'str']], desc_sig_space, [desc_sig_name, 'names']), @@ -1669,7 +1668,7 @@ def test_cpp_function_signature_with_maximum_signature_line_length_break(app): )) assert_node(doctree[1], addnodes.desc, desctype='function', domain='cpp', objtype='function', noindex=False) - assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter_line, desc_parameter, ( + assert_node(doctree[1][0][0][3], [desc_parameterlist, desc_parameter, ( [pending_xref, [desc_sig_name, 'str']], desc_sig_space, [desc_sig_name, 'names']), @@ -1710,7 +1709,7 @@ def test_domain_cpp_cpp_maximum_signature_line_length(app, status, warning):
\ str\ \ -name, \ +name\
diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index ed647c44c75..fa6c70a7d00 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -16,7 +16,6 @@ desc_name, desc_optional, desc_parameter, - desc_parameter_line, desc_parameterlist, desc_returns, desc_sig_keyword, @@ -1535,7 +1534,7 @@ def test_pyfunction_signature_with_python_maximum_signature_line_length_break(ap )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="py", objtype="function", noindex=False) - assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter_line, desc_parameter, ( + assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( [desc_sig_name, "names"], [desc_sig_punctuation, ":"], desc_sig_space, @@ -1617,7 +1616,7 @@ def test_pyfunction_signature_with_maximum_signature_line_length_break(app): )) assert_node(doctree[1], addnodes.desc, desctype="function", domain="py", objtype="function", noindex=False) - assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter_line, desc_parameter, ( + assert_node(doctree[1][0][1], [desc_parameterlist, desc_parameter, ( [desc_sig_name, "names"], [desc_sig_punctuation, ":"], desc_sig_space, @@ -1666,7 +1665,7 @@ def test_python_python_maximum_signature_line_length(app, status, warning): :\ \ str\ -, \ +\ From 9d03de0049d94d8d9ee137903fe28c2fe190b359 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 8 Apr 2023 03:43:25 +0100 Subject: [PATCH 055/103] options docs typo (macro -> function) --- doc/usage/restructuredtext/domains.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index a6bf4ef3578..461f09f04d3 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -1184,7 +1184,7 @@ visibility statement (``public``, ``private`` or ``protected``). .. rst:directive:option:: single-line-parameter-list :type: no value - Ensures that the macro's parameters will be emitted on a single logical + 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`. From b3e566278d084595190174086586101b756a8f9b Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 8 Apr 2023 03:43:33 +0100 Subject: [PATCH 056/103] CHANGES entry --- CHANGES | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES b/CHANGES index 8a77524fe4d..26f1b212132 100644 --- a/CHANGES +++ b/CHANGES @@ -69,6 +69,14 @@ Features added files. Patch by James Addison .. _officially recommended: https://jinja.palletsprojects.com/en/latest/templates/#template-file-extension +* #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 ``single-line-argument-list`` in :rst:dir:`py:function` + or ``single-line-parameter-list`` in :rst:dir:`cpp:function`. + Patch by Thomas Louf and Adam Turner. Bugs fixed ---------- From 592c86c75cb1bb4c4df0d54b4fc97d38bc334ff1 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 8 Apr 2023 03:46:30 +0100 Subject: [PATCH 057/103] fixup! Remove ``desc_parameter_line`` --- sphinx/domains/python.py | 10 +++------- sphinx/writers/text.py | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 6b5123c89c4..8fe65963439 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -270,16 +270,12 @@ def _parse_arglist( if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY: # PEP-570: Separator for Positional Only Parameter: / - parameter_nodes.append( - addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/')), - ) + params += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/')) if param.kind == param.KEYWORD_ONLY and last_kind in (param.POSITIONAL_OR_KEYWORD, param.POSITIONAL_ONLY, None): # PEP-3102: Separator for Keyword Only Parameter: * - parameter_nodes.append( - addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '*')), - ) + params += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '*')) node = addnodes.desc_parameter() parameter_nodes.append(node) @@ -308,7 +304,7 @@ def _parse_arglist( node += nodes.inline('', param.default, classes=['default_value'], support_smartquotes=False) - params += parameter_nodes + params += node last_kind = param.kind if last_kind == Parameter.POSITIONAL_ONLY: diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index 27e746e1550..7411cc83474 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -603,7 +603,7 @@ def depart_desc_parameterlist(self, node: Element) -> None: def visit_desc_parameter(self, node: Element) -> None: if self.multi_line_parameter_list: self.new_state() - if not self.first_param and not node.parent.parent.get('multi_line_parameter_list'): + if not self.first_param and not self.multi_line_parameter_list: self.add_text(', ') else: self.first_param = 0 From 1af1a2e85771fc75b546bb1eda542364d4dcbb97 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 8 Apr 2023 03:49:27 +0100 Subject: [PATCH 058/103] Restore stripped whitespace --- tests/test_domain_c.py | 2 +- tests/test_domain_cpp.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 79593989ccb..3575e15b2f0 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -757,7 +757,7 @@ def test_domain_c_build_intersphinx(tempdir, app, status, warning): inv_file.write_bytes(b'''\ # Sphinx inventory version 2 # Project: C Intersphinx Test -# Version: +# Version: # The remainder of this file is compressed using zlib. ''' + zlib.compress(b'''\ _enum c:enum 1 index.html#c.$ - diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 0b2b0bb4e4b..84828c29b03 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -1388,7 +1388,7 @@ def test_domain_cpp_build_intersphinx(tempdir, app, status, warning): inv_file.write_bytes(b'''\ # Sphinx inventory version 2 # Project: C Intersphinx Test -# Version: +# Version: # The remainder of this file is compressed using zlib. ''' + zlib.compress(b'''\ _class cpp:class 1 index.html#_CPPv46$ - From 69dba4b6491f2371de06a6a27638b6f2f69263ce Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 8 Apr 2023 04:10:02 +0100 Subject: [PATCH 059/103] fixup! (round 2) Remove ``desc_parameter_line`` --- sphinx/domains/python.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 8fe65963439..ce67d514a72 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -266,8 +266,6 @@ def _parse_arglist( sig = signature_from_str('(%s)' % arglist) last_kind = None for param in sig.parameters.values(): - parameter_nodes = [] - if param.kind != param.POSITIONAL_ONLY and last_kind == param.POSITIONAL_ONLY: # PEP-570: Separator for Positional Only Parameter: / params += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '/')) @@ -278,8 +276,6 @@ def _parse_arglist( params += addnodes.desc_parameter('', '', addnodes.desc_sig_operator('', '*')) node = addnodes.desc_parameter() - parameter_nodes.append(node) - if param.kind == param.VAR_POSITIONAL: node += addnodes.desc_sig_operator('', '*') node += addnodes.desc_sig_name('', param.name) From 2918995b05389ebde881b1b3132839481bb954e1 Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Sat, 8 Apr 2023 04:10:18 +0100 Subject: [PATCH 060/103] Remove unneeded py domain permalink logic --- sphinx/domains/python.py | 2 -- sphinx/writers/html5.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index ce67d514a72..3583a737abb 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -550,8 +550,6 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] 'single-line-argument-list' not in self.options and (len(sig) > max_len > 0) ) - if multi_line_parameter_list: - signode['add_permalink'] = True sig_prefix = self.get_signature_prefix(sig) if sig_prefix: diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 425652cd35b..f09233f4ea6 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -94,7 +94,7 @@ def visit_desc_signature(self, node: Element) -> None: def depart_desc_signature(self, node: Element) -> None: self.protect_literal_text -= 1 - if not node.get('is_multiline') or node.get('add_permalink'): + if not node.get('is_multiline'): self.add_permalink_ref(node, _('Permalink to this definition')) self.body.append('\n') From d58b63cda7d611fa8a3720d4af3338ab9931e546 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sat, 8 Apr 2023 21:44:04 +0200 Subject: [PATCH 061/103] Restore cross-reference to single line option --- doc/usage/configuration.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index 608ab35fe9e..127e2c19255 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -685,10 +685,7 @@ General configuration 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. - ``py:function:single-line-parameter-list``). - - .. TODO: there's currently no way to cross-reference a directive option, - e.g. :rst:dir:opt:`py:function:single-line-parameter-list` + :rst:dir:`py:function:single-line-argument-list`). .. versionadded:: 6.2 From b866b8847026275e675bd4ff3a10a79ff5ed5549 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sat, 8 Apr 2023 22:47:41 +0200 Subject: [PATCH 062/103] Add JS domain implentation Include in the docs as well, tests still missing --- doc/usage/configuration.rst | 11 +++++++++++ doc/usage/restructuredtext/domains.rst | 27 ++++++++++++++++++++++++++ sphinx/domains/javascript.py | 14 +++++++++++-- sphinx/domains/python.py | 5 ++++- 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index 127e2c19255..fb205a6aeee 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -3022,6 +3022,17 @@ Options for the Python domain .. versionadded:: 6.2 +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:: 6.2 + Example of configuration file ----------------------------- diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index 461f09f04d3..70334a899d4 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -1972,6 +1972,15 @@ 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:: 6.2 + .. rst:directive:: .. js:method:: name(signature) This directive is an alias for :rst:dir:`js:function`, however it describes @@ -1979,6 +1988,15 @@ The JavaScript domain (name **js**) provides the following directives: .. 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:: 6.2 + .. rst:directive:: .. js:class:: name Describes a constructor that creates an object. This is basically like a @@ -1997,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:: 6.2 + .. rst:directive:: .. js:data:: name Describes a global variable or constant. diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py index 7f07c20b5ec..b195bbf64c7 100644 --- a/sphinx/domains/javascript.py +++ b/sphinx/domains/javascript.py @@ -88,6 +88,14 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] signode['object'] = prefix signode['fullname'] = fullname + max_len = (self.env.config.javascript_maximum_signature_line_length + or self.env.config.maximum_signature_line_length + or 0) + multi_line_parameter_list = ( + 'single-line-parameter-list' not in self.options + and (len(sig) > max_len > 0) + ) + display_prefix = self.get_display_prefix() if display_prefix: signode += addnodes.desc_annotation('', '', *display_prefix) @@ -108,7 +116,7 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] if not arglist: signode += addnodes.desc_parameterlist() else: - _pseudo_parse_arglist(signode, arglist) + _pseudo_parse_arglist(signode, arglist, multi_line_parameter_list) return fullname, prefix def _object_hierarchy_parts(self, sig_node: desc_signature) -> tuple[str, ...]: @@ -489,7 +497,9 @@ def get_full_qualified_name(self, node: Element) -> str | None: def setup(app: Sphinx) -> dict[str, Any]: app.add_domain(JavaScriptDomain) - + app.add_config_value( + 'javascript_maximum_signature_line_length', None, 'env', types={int, None}, + ) return { 'version': 'builtin', 'env_version': 2, diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index 3583a737abb..d6d64b9ecbf 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -310,7 +310,9 @@ def _parse_arglist( return params -def _pseudo_parse_arglist(signode: desc_signature, arglist: str) -> None: +def _pseudo_parse_arglist( + signode: desc_signature, arglist: str, multi_line_parameter_list: bool = False, +) -> None: """"Parse" a list of arguments separated by commas. Arguments can have "optional" annotations given by enclosing them in @@ -318,6 +320,7 @@ def _pseudo_parse_arglist(signode: desc_signature, arglist: str) -> None: string literal (e.g. default argument value). """ paramlist = addnodes.desc_parameterlist() + paramlist['multi_line_parameter_list'] = multi_line_parameter_list stack: list[Element] = [paramlist] try: for argument in arglist.split(','): From c6cf460ae5f426520ea017ac7626a939ff25fe9a Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 9 Apr 2023 10:57:25 +0200 Subject: [PATCH 063/103] Mention @jfbu in CHANGES --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 26f1b212132..2bce86811ed 100644 --- a/CHANGES +++ b/CHANGES @@ -76,7 +76,7 @@ Features added This behaviour may also be controlled by options on object description directives, for example ``single-line-argument-list`` in :rst:dir:`py:function` or ``single-line-parameter-list`` in :rst:dir:`cpp:function`. - Patch by Thomas Louf and Adam Turner. + Patch by Thomas Louf, Adam Turner, and Jean-François B. Bugs fixed ---------- From b18c4812da16179289098f2e0bc4f4391ea6e2e2 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 9 Apr 2023 11:00:43 +0200 Subject: [PATCH 064/103] Unify directive option to `single-line-parameter-list` --- CHANGES | 3 +-- doc/usage/configuration.rst | 2 +- doc/usage/restructuredtext/domains.rst | 8 ++++---- sphinx/domains/python.py | 4 ++-- tests/test_domain_py.py | 4 ++-- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/CHANGES b/CHANGES index 2bce86811ed..3b81aff6178 100644 --- a/CHANGES +++ b/CHANGES @@ -74,8 +74,7 @@ Features added 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 ``single-line-argument-list`` in :rst:dir:`py:function` - or ``single-line-parameter-list`` in :rst:dir:`cpp:function`. + directives, for example ``single-line-parameter-list`` in :rst:dir:`py:function`. Patch by Thomas Louf, Adam Turner, and Jean-François B. Bugs fixed diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index fb205a6aeee..d2d992a9546 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -685,7 +685,7 @@ General configuration 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-argument-list`). + :rst:dir:`py:function:single-line-parameter-list`). .. versionadded:: 6.2 diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index 70334a899d4..74da2a87807 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -231,7 +231,7 @@ 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-argument-list + .. rst:directive:option:: single-line-parameter-list :type: no value Ensures that the function's arguments will be emitted on a single logical @@ -339,7 +339,7 @@ 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-argument-list + .. rst:directive:option:: single-line-parameter-list :type: no value Ensures that the class constructor's arguments will be emitted on a single @@ -460,7 +460,7 @@ 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-argument-list + .. rst:directive:option:: single-line-parameter-list :type: no value Ensures that the method's arguments will be emitted on a single logical @@ -522,7 +522,7 @@ 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-argument-list + .. rst:directive:option:: single-line-parameter-list :type: no value Ensures that the decorator's arguments will be emitted on a single logical diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index d6d64b9ecbf..fe8455835a5 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -463,7 +463,7 @@ class PyObject(ObjectDescription[Tuple[str, str]]): 'noindex': directives.flag, 'noindexentry': directives.flag, 'nocontentsentry': directives.flag, - 'single-line-argument-list': directives.flag, + 'single-line-parameter-list': directives.flag, 'module': directives.unchanged, 'canonical': directives.unchanged, 'annotation': directives.unchanged, @@ -550,7 +550,7 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] or self.env.config.maximum_signature_line_length or 0) multi_line_parameter_list = ( - 'single-line-argument-list' not in self.options + 'single-line-parameter-list' not in self.options and (len(sig) > max_len > 0) ) diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index fa6c70a7d00..985f1dad71e 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -1492,7 +1492,7 @@ def test_pyfunction_signature_with_python_maximum_signature_line_length_equal(ap }) def test_pyfunction_signature_with_python_maximum_signature_line_length_force_single(app): text = (".. py:function:: hello(names: str) -> str\n" - " :single-line-argument-list:") + " :single-line-parameter-list:") doctree = restructuredtext.parse(app, text) assert_node(doctree, ( addnodes.index, @@ -1574,7 +1574,7 @@ def test_pyfunction_signature_with_maximum_signature_line_length_equal(app): }) def test_pyfunction_signature_with_maximum_signature_line_length_force_single(app): text = (".. py:function:: hello(names: str) -> str\n" - " :single-line-argument-list:") + " :single-line-parameter-list:") doctree = restructuredtext.parse(app, text) assert_node(doctree, ( addnodes.index, From 4b47e794a6ce3276312a18f0339560f871a3530d Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 9 Apr 2023 11:03:19 +0200 Subject: [PATCH 065/103] Fix directive hyperlink in CHANGES --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 3b81aff6178..0da1aa35781 100644 --- a/CHANGES +++ b/CHANGES @@ -74,7 +74,7 @@ Features added 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 ``single-line-parameter-list`` in :rst:dir:`py:function`. + directives, for example :rst:dir:`py:function:single-line-parameter-list`. Patch by Thomas Louf, Adam Turner, and Jean-François B. Bugs fixed From a652d3e92ea98085e7d14fa5e7b1423f792d0681 Mon Sep 17 00:00:00 2001 From: TLouf Date: Mon, 10 Apr 2023 18:47:55 +0200 Subject: [PATCH 066/103] Pass multiline bool to `_pseudo_parse_arglist` --- sphinx/domains/python.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index fe8455835a5..40e794d5286 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -576,11 +576,11 @@ def handle_signature(self, sig: str, signode: desc_signature) -> tuple[str, str] except SyntaxError: # fallback to parse arglist original parser. # it supports to represent optional arguments (ex. "func(foo [, bar])") - _pseudo_parse_arglist(signode, arglist) + _pseudo_parse_arglist(signode, arglist, multi_line_parameter_list) except NotImplementedError as exc: logger.warning("could not parse arglist (%r): %s", arglist, exc, location=signode) - _pseudo_parse_arglist(signode, arglist) + _pseudo_parse_arglist(signode, arglist, multi_line_parameter_list) else: if self.needs_arglist(): # for callables, add an empty parameter list From bcba5f6c63135c977efa1e4790d31f97862aa032 Mon Sep 17 00:00:00 2001 From: TLouf Date: Mon, 10 Apr 2023 18:48:44 +0200 Subject: [PATCH 067/103] Handle bracket notation for optional params in HTML --- sphinx/writers/html5.py | 73 ++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index f09233f4ea6..d1b35dcd020 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -150,16 +150,21 @@ def depart_desc_returns(self, node: Element) -> None: def visit_desc_parameterlist(self, node: Element) -> None: self.body.append('(') - self.first_param = 1 + self.is_first_param = True self.optional_param_level = 0 + self.param_group_index = 0 + # Counts as what we call a parameter group are either a required parameter, or a + # set of contiguous optional ones. + self.list_is_required_param = [isinstance(c, addnodes.desc_parameter) + for c in node.children] # How many required parameters are left. - self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) - for c in node.children]) + self.required_params_left = sum(self.list_is_required_param) self.param_separator = node.child_text_separator self.multi_line_parameter_list = node.get('multi_line_parameter_list', False) if self.multi_line_parameter_list: self.body.append('\n\n') self.body.append(self.starttag(node, 'dl')) + self.param_separator = self.param_separator.rstrip() def depart_desc_parameterlist(self, node: Element) -> None: if node.get('multi_line_parameter_list'): @@ -173,11 +178,12 @@ def depart_desc_parameterlist(self, node: Element) -> None: # foo([a, ]b, c[, d]) # def visit_desc_parameter(self, node: Element) -> None: - if self.multi_line_parameter_list: + on_single_line = self.multi_line_parameter_list + if on_single_line and not (self.is_first_param and self.optional_param_level > 0): self.body.append(self.starttag(node, 'dd', '')) - if self.first_param: - self.first_param = 0 - elif not self.required_params_left: + if self.is_first_param: + self.is_first_param = False + elif not on_single_line and not self.required_params_left: self.body.append(self.param_separator) if self.optional_param_level == 0: self.required_params_left -= 1 @@ -187,18 +193,61 @@ def visit_desc_parameter(self, node: Element) -> None: def depart_desc_parameter(self, node: Element) -> None: if not node.hasattr('noemph'): self.body.append('') - if self.required_params_left: - self.body.append(self.param_separator) + is_required = self.list_is_required_param[self.param_group_index] if self.multi_line_parameter_list: - self.body.append('') + is_last_group = self.param_group_index + 1 == len(self.list_is_required_param) + next_is_required = ( + not is_last_group and + self.list_is_required_param[self.param_group_index + 1] + ) + if is_required and (is_last_group or next_is_required): + self.body.append(self.param_separator) + self.body.append('') + + elif self.required_params_left: + self.body.append(self.param_separator) + + if is_required: + self.param_group_index += 1 def visit_desc_optional(self, node: Element) -> None: self.optional_param_level += 1 - self.body.append('[') + self.max_optional_param_level = self.optional_param_level + if self.multi_line_parameter_list: + # If the first parameter is optional, start a new line and open the bracket. + if self.is_first_param: + self.body.append(self.starttag(node, 'dd', '')) + self.body.append('[') + # Else, open a new bracket, append the parameter separator, and end the + # line. + else: + self.body.append('[') + self.body.append(self.param_separator) + self.body.append('') + else: + self.body.append('[') def depart_desc_optional(self, node: Element) -> None: self.optional_param_level -= 1 - self.body.append(']') + # If it's the first time we go down one level, add the separator before the + # bracket. + if self.multi_line_parameter_list: + if self.optional_param_level == self.max_optional_param_level - 1: + self.body.append(self.param_separator) + self.body.append(']') + # End the line if multi-line and next is required. + is_last_group = self.param_group_index + 1 == len(self.list_is_required_param) + next_is_required = ( + not is_last_group and + self.list_is_required_param[self.param_group_index + 1] + ) + if self.optional_param_level == 0 and next_is_required: + self.body.append('') + + else: + self.body.append(']') + if self.optional_param_level == 0: + self.param_group_index += 1 def visit_desc_annotation(self, node: Element) -> None: self.body.append(self.starttag(node, 'em', '', CLASS='property')) From 3243f9de48d8ce19df258311db4ea227f1010409 Mon Sep 17 00:00:00 2001 From: TLouf Date: Mon, 10 Apr 2023 19:02:23 +0200 Subject: [PATCH 068/103] Rename `on_single_line` to `on_separate_line` --- sphinx/writers/html5.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index d1b35dcd020..76c6035b35d 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -178,12 +178,12 @@ def depart_desc_parameterlist(self, node: Element) -> None: # foo([a, ]b, c[, d]) # def visit_desc_parameter(self, node: Element) -> None: - on_single_line = self.multi_line_parameter_list - if on_single_line and not (self.is_first_param and self.optional_param_level > 0): + on_separate_line = self.multi_line_parameter_list + if on_separate_line and not (self.is_first_param and self.optional_param_level > 0): self.body.append(self.starttag(node, 'dd', '')) if self.is_first_param: self.is_first_param = False - elif not on_single_line and not self.required_params_left: + elif not on_separate_line and not self.required_params_left: self.body.append(self.param_separator) if self.optional_param_level == 0: self.required_params_left -= 1 From 93bdb8d7b7a82dc4be24078a0cdbf75668457c09 Mon Sep 17 00:00:00 2001 From: TLouf Date: Mon, 10 Apr 2023 20:05:09 +0200 Subject: [PATCH 069/103] Fix latex for sigs starting with optional param --- sphinx/writers/latex.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 6efb7459aa3..a3a91320547 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -798,27 +798,36 @@ def visit_desc_parameterlist(self, node: Element) -> None: # close name, open parameterlist self.body.append('}{') self.first_param = 1 + self.optional_param_level = 0 + self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) + for c in node.children]) def depart_desc_parameterlist(self, node: Element) -> None: # close parameterlist, open return annotation self.body.append('}{') def visit_desc_parameter(self, node: Element) -> None: - if not self.first_param: - self.body.append(', ') - else: + if self.first_param: self.first_param = 0 + elif not self.required_params_left: + self.body.append(', ') + if self.optional_param_level == 0: + self.required_params_left -= 1 if not node.hasattr('noemph'): self.body.append(r'\sphinxparam{') def depart_desc_parameter(self, node: Element) -> None: if not node.hasattr('noemph'): self.body.append('}') + if self.required_params_left: + self.body.append(', ') def visit_desc_optional(self, node: Element) -> None: + self.optional_param_level += 1 self.body.append(r'\sphinxoptional{') def depart_desc_optional(self, node: Element) -> None: + self.optional_param_level -= 1 self.body.append('}') def visit_desc_annotation(self, node: Element) -> None: From 417db1f59a2d2e25640e74a8c08e72b4da782f7b Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 11 Apr 2023 10:07:48 +0200 Subject: [PATCH 070/103] Cleanup of HTML --- sphinx/writers/html5.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 76c6035b35d..d5854544c06 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -153,7 +153,7 @@ def visit_desc_parameterlist(self, node: Element) -> None: self.is_first_param = True self.optional_param_level = 0 self.param_group_index = 0 - # Counts as what we call a parameter group are either a required parameter, or a + # Counts as what we call a parameter group either a required parameter, or a # set of contiguous optional ones. self.list_is_required_param = [isinstance(c, addnodes.desc_parameter) for c in node.children] @@ -229,21 +229,16 @@ def visit_desc_optional(self, node: Element) -> None: def depart_desc_optional(self, node: Element) -> None: self.optional_param_level -= 1 - # If it's the first time we go down one level, add the separator before the - # bracket. if self.multi_line_parameter_list: + # If it's the first time we go down one level, add the separator before the + # bracket. if self.optional_param_level == self.max_optional_param_level - 1: self.body.append(self.param_separator) self.body.append(']') - # End the line if multi-line and next is required. - is_last_group = self.param_group_index + 1 == len(self.list_is_required_param) - next_is_required = ( - not is_last_group and - self.list_is_required_param[self.param_group_index + 1] - ) - if self.optional_param_level == 0 and next_is_required: + # End the line if we have just closed the last bracket of this group of + # optional parameters. + if self.optional_param_level == 0: self.body.append('') - else: self.body.append(']') if self.optional_param_level == 0: From c4e9c62094dc07848987da738332f459594cb1a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Tue, 11 Apr 2023 10:58:26 +0200 Subject: [PATCH 071/103] Update LaTeX to handle optional parameters in one line per param context The internal LaTeX code is based on a different logic than formerly: now, it is the \sphinxparam which triggers a new line, not the commas. Added some mark-up which will make possible customizing the style of the commas. Signatures with one line per parameter use currently a specific \sphinxcode{,} which is customizable at user level. Others use standard commas, also customizable at user level. --- sphinx/texinputs/sphinxlatexobjects.sty | 10 +++++++++- sphinx/texinputs/sphinxlatexstyletext.sty | 6 ++++++ sphinx/writers/latex.py | 4 ++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/sphinx/texinputs/sphinxlatexobjects.sty b/sphinx/texinputs/sphinxlatexobjects.sty index d647ae1c3a0..a9baaf1650f 100644 --- a/sphinx/texinputs/sphinxlatexobjects.sty +++ b/sphinx/texinputs/sphinxlatexobjects.sty @@ -151,7 +151,15 @@ \item[#1\sphinxcode{(}\strut] \leavevmode\par\nopagebreak % this relies on \pysigstartsignatures having set \parskip to zero - \@for\param:=#2\do{\param\sphinxcode{,}\par} + \begingroup + \let\sphinxparamcomma\sphinxparamcommaoneperline + % The very first \sphinxparam should not emit a \par hence a complication + % with a group and global definition here as it may occur in a \sphinxoptional + \global\let\spx@sphinxparam\sphinxparam + \gdef\sphinxparam{\gdef\sphinxparam{\par\spx@sphinxparam}\spx@sphinxparam}% + #2\sphinxparamcomma,\par + \endgroup + \global\let\sphinxparam\spx@sphinxparam % fulllineitems sets \labelwidth to be like \leftmargin \nopagebreak\noindent\kern-\labelwidth\sphinxcode{)}{#3} \pysigadjustitemsep diff --git a/sphinx/texinputs/sphinxlatexstyletext.sty b/sphinx/texinputs/sphinxlatexstyletext.sty index 913bc8210a6..1c70ab4f5d1 100644 --- a/sphinx/texinputs/sphinxlatexstyletext.sty +++ b/sphinx/texinputs/sphinxlatexstyletext.sty @@ -112,6 +112,12 @@ % Special characters % +% Modified internally when rendering signatures with one line per param +% If redefining this, mind that it is followed by "," in input. +\let\sphinxparamcomma\empty +% Mind the "," delimiter +\def\sphinxparamcommaoneperline,{\sphinxcode{,}} +% % The \kern\z@ is to prevent en-dash and em-dash TeX ligatures. % A linebreak can occur after the dash in regular text (this is % normal behaviour of "-" in TeX, it is not related to \kern\z@). diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index a3a91320547..983fcaba657 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -810,7 +810,7 @@ def visit_desc_parameter(self, node: Element) -> None: if self.first_param: self.first_param = 0 elif not self.required_params_left: - self.body.append(', ') + self.body.append('\sphinxparamcomma, ') if self.optional_param_level == 0: self.required_params_left -= 1 if not node.hasattr('noemph'): @@ -820,7 +820,7 @@ def depart_desc_parameter(self, node: Element) -> None: if not node.hasattr('noemph'): self.body.append('}') if self.required_params_left: - self.body.append(', ') + self.body.append('\sphinxparamcomma, ') def visit_desc_optional(self, node: Element) -> None: self.optional_param_level += 1 From 57004e3382f97063d66b0988e05b48d3ddbe87ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Tue, 11 Apr 2023 12:00:27 +0200 Subject: [PATCH 072/103] Use some LaTeX hack to handle comma placement vs bracket But this should probably be handled by sphinx.writers.latex to have easier mark-up to work with at LaTeX level. --- sphinx/texinputs/sphinxlatexobjects.sty | 19 +++++++++++++++++-- sphinx/texinputs/sphinxlatexstyletext.sty | 9 +++++---- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/sphinx/texinputs/sphinxlatexobjects.sty b/sphinx/texinputs/sphinxlatexobjects.sty index a9baaf1650f..6e416f834eb 100644 --- a/sphinx/texinputs/sphinxlatexobjects.sty +++ b/sphinx/texinputs/sphinxlatexobjects.sty @@ -146,18 +146,33 @@ \item[{#1\sphinxcode{(}\py@sigparams{#2}{#3}\strut}] \pysigadjustitemsep } + +\def\spx@pysigwithonelineperarg@a{% + \ifx\spx@token\sphinxoptional + \expandafter\spx@pysigwithonelineperarg@b + \else\sphinxparamcommaoneperline + \fi +} +\def\spx@pysigwithonelineperarg@b\sphinxoptional#1{% + \sphinxoptional{\sphinxparamcommaoneperline#1}% +} +\def\sphinxoptionalextraspace{0.5mm} \newcommand{\pysigwithonelineperarg}[3]{% % render each argument on its own line \item[#1\sphinxcode{(}\strut] \leavevmode\par\nopagebreak % this relies on \pysigstartsignatures having set \parskip to zero \begingroup - \let\sphinxparamcomma\sphinxparamcommaoneperline + \def\sphinxoptionalhook{\ifvmode\else\kern\sphinxoptionalextraspace\relax\fi}% + % FIXME: handle this at sphinx.writers.latex level for suitable + % mark-up, rather than doing this very hacky TeX. + \@firstofone{\def\sphinxparamcomma,} {\futurelet\spx@token\spx@pysigwithonelineperarg@a}% % The very first \sphinxparam should not emit a \par hence a complication % with a group and global definition here as it may occur in a \sphinxoptional \global\let\spx@sphinxparam\sphinxparam \gdef\sphinxparam{\gdef\sphinxparam{\par\spx@sphinxparam}\spx@sphinxparam}% - #2\sphinxparamcomma,\par + #2\sphinxparamcomma, \par % <-- attention to leave space token before \par + % due to above hack (but see FIXME) \endgroup \global\let\sphinxparam\spx@sphinxparam % fulllineitems sets \labelwidth to be like \leftmargin diff --git a/sphinx/texinputs/sphinxlatexstyletext.sty b/sphinx/texinputs/sphinxlatexstyletext.sty index 1c70ab4f5d1..a4defc96cad 100644 --- a/sphinx/texinputs/sphinxlatexstyletext.sty +++ b/sphinx/texinputs/sphinxlatexstyletext.sty @@ -58,7 +58,8 @@ \protected\def\sphinxparam#1{\emph{#1}} % \optional is used for ``[, arg]``, i.e. desc_optional nodes. \long\protected\def\sphinxoptional#1{% - {\textnormal{\Large[}}{#1}\hspace{0.5mm}{\textnormal{\Large]}}} + {\sphinxoptionalhook\textnormal{\Large[}}{#1}\hspace{0.5mm}{\textnormal{\Large]}}} +\let\sphinxoptionalhook\empty % additional customizable styling \def\sphinxstyleindexentry #1{\texttt{#1}} @@ -113,10 +114,10 @@ % Special characters % % Modified internally when rendering signatures with one line per param -% If redefining this, mind that it is followed by "," in input. +% If redefining this, mind that it is followed by explicit "," in input. \let\sphinxparamcomma\empty -% Mind the "," delimiter -\def\sphinxparamcommaoneperline,{\sphinxcode{,}} +% The "," in mark-up will have been swallowed if this is executed. +\def\sphinxparamcommaoneperline{\sphinxcode{,}} % % The \kern\z@ is to prevent en-dash and em-dash TeX ligatures. % A linebreak can occur after the dash in regular text (this is From 88cfe5ad980f6dc5cd38700178bc9d26224d0973 Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 11 Apr 2023 19:33:03 +0200 Subject: [PATCH 073/103] Mark latex strings as raw to ignore backslashes --- sphinx/writers/latex.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 983fcaba657..e14b30f6e16 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -810,7 +810,7 @@ def visit_desc_parameter(self, node: Element) -> None: if self.first_param: self.first_param = 0 elif not self.required_params_left: - self.body.append('\sphinxparamcomma, ') + self.body.append(r'\sphinxparamcomma, ') if self.optional_param_level == 0: self.required_params_left -= 1 if not node.hasattr('noemph'): @@ -820,7 +820,7 @@ def depart_desc_parameter(self, node: Element) -> None: if not node.hasattr('noemph'): self.body.append('}') if self.required_params_left: - self.body.append('\sphinxparamcomma, ') + self.body.append(r'\sphinxparamcomma, ') def visit_desc_optional(self, node: Element) -> None: self.optional_param_level += 1 From 9ce3e3470302548c67c35643c2e3dd9f44a2048f Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 11 Apr 2023 19:44:38 +0200 Subject: [PATCH 074/103] Add trailing comma in expected HTML of tests --- tests/test_domain_c.py | 2 +- tests/test_domain_cpp.py | 2 +- tests/test_domain_py.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 3575e15b2f0..a06dfd544e7 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -1051,7 +1051,7 @@ def test_domain_c_c_maximum_signature_line_length(app, status, warning):
\ str\ \ -name\ +name,\
diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 84828c29b03..0e7e0a9f056 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -1709,7 +1709,7 @@ def test_domain_cpp_cpp_maximum_signature_line_length(app, status, warning):
\ str\ \ -name\ +name,\
diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index 985f1dad71e..9195fbd4660 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -1665,7 +1665,7 @@ def test_python_python_maximum_signature_line_length(app, status, warning): :\ \ str\ -\ +,\ From 98f1803b94544719a4f0e61e9be24fc9bac92934 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 16 Apr 2023 12:59:37 +0200 Subject: [PATCH 075/103] Move `and` operator to line start --- sphinx/writers/html5.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index d5854544c06..5060197c7e5 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -197,8 +197,8 @@ def depart_desc_parameter(self, node: Element) -> None: if self.multi_line_parameter_list: is_last_group = self.param_group_index + 1 == len(self.list_is_required_param) next_is_required = ( - not is_last_group and - self.list_is_required_param[self.param_group_index + 1] + not is_last_group + and self.list_is_required_param[self.param_group_index + 1] ) if is_required and (is_last_group or next_is_required): self.body.append(self.param_separator) From ef7adde80dcd87afd89114e6e928a1d90afd88b8 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 16 Apr 2023 13:04:10 +0200 Subject: [PATCH 076/103] Handle optionals in text writer --- sphinx/writers/text.py | 79 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 11 deletions(-) diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index 7411cc83474..3a90939006a 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -594,30 +594,87 @@ def depart_desc_returns(self, node: Element) -> None: def visit_desc_parameterlist(self, node: Element) -> None: self.add_text('(') - self.first_param = 1 + self.is_first_param = True + self.optional_param_level = 0 + self.param_group_index = 0 + # Counts as what we call a parameter group are either a required parameter, or a + # set of contiguous optional ones. + self.list_is_required_param = [isinstance(c, addnodes.desc_parameter) + for c in node.children] + self.required_params_left = sum(self.list_is_required_param) + self.param_separator = ', ' self.multi_line_parameter_list = node.get('multi_line_parameter_list', False) + if self.multi_line_parameter_list: + self.param_separator = self.param_separator.rstrip() def depart_desc_parameterlist(self, node: Element) -> None: self.add_text(')') def visit_desc_parameter(self, node: Element) -> None: - if self.multi_line_parameter_list: + on_separate_line = self.multi_line_parameter_list + if on_separate_line and not (self.is_first_param and self.optional_param_level > 0): self.new_state() - if not self.first_param and not self.multi_line_parameter_list: - self.add_text(', ') - else: - self.first_param = 0 + if self.is_first_param: + self.is_first_param = False + elif not on_separate_line and not self.required_params_left: + self.add_text(self.param_separator) + if self.optional_param_level == 0: + self.required_params_left -= 1 + self.add_text(node.astext()) - if self.multi_line_parameter_list: - self.add_text(',') - self.end_state(wrap=False, end=None) + + is_required = self.list_is_required_param[self.param_group_index] + if on_separate_line: + is_last_group = self.param_group_index + 1 == len(self.list_is_required_param) + next_is_required = ( + not is_last_group + and self.list_is_required_param[self.param_group_index + 1] + ) + if is_required and (is_last_group or next_is_required): + self.add_text(self.param_separator) + self.end_state(wrap=False, end=None) + + elif self.required_params_left: + self.add_text(self.param_separator) + + if is_required: + self.param_group_index += 1 raise nodes.SkipNode def visit_desc_optional(self, node: Element) -> None: - self.add_text('[') + self.optional_param_level += 1 + self.max_optional_param_level = self.optional_param_level + if self.multi_line_parameter_list: + # If the first parameter is optional, start a new line and open the bracket. + if self.is_first_param: + self.new_state() + self.add_text('[') + # Else, open a new bracket, append the parameter separator, and end the + # line. + else: + self.add_text('[') + self.add_text(self.param_separator) + self.end_state(wrap=False, end=None) + else: + self.add_text('[') def depart_desc_optional(self, node: Element) -> None: - self.add_text(']') + self.optional_param_level -= 1 + if self.multi_line_parameter_list: + # If it's the first time we go down one level, add the separator before the + # bracket. + if self.optional_param_level == self.max_optional_param_level - 1: + self.add_text(self.param_separator) + self.add_text(']') + # End the line if we have just closed the last bracket of this group of + # optional parameters. + if self.optional_param_level == 0: + self.end_state(wrap=False, end=None) + + else: + self.add_text(']') + if self.optional_param_level == 0: + self.param_group_index += 1 def visit_desc_annotation(self, node: Element) -> None: pass From 1ad09815625971fe3d470ae81bf7d26c7cd3ceb7 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 16 Apr 2023 13:40:19 +0200 Subject: [PATCH 077/103] Add `single-line-parameter-list` as JS object option --- sphinx/domains/javascript.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py index b195bbf64c7..3b620f51408 100644 --- a/sphinx/domains/javascript.py +++ b/sphinx/domains/javascript.py @@ -43,6 +43,7 @@ class JSObject(ObjectDescription[Tuple[str, str]]): 'noindex': directives.flag, 'noindexentry': directives.flag, 'nocontentsentry': directives.flag, + 'single-line-parameter-list': directives.flag, } def get_display_prefix(self) -> list[Node]: From e36c8110f4bb82f011a45aa3cadcc8c6962d1f1a Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 16 Apr 2023 14:10:03 +0200 Subject: [PATCH 078/103] Add essential JS tests --- .../conf.py | 1 + .../index.rst | 4 + tests/test_domain_js.py | 115 ++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 tests/roots/test-domain-js-javascript_maximum_signature_line_length/conf.py create mode 100644 tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst diff --git a/tests/roots/test-domain-js-javascript_maximum_signature_line_length/conf.py b/tests/roots/test-domain-js-javascript_maximum_signature_line_length/conf.py new file mode 100644 index 00000000000..a27d8d7c271 --- /dev/null +++ b/tests/roots/test-domain-js-javascript_maximum_signature_line_length/conf.py @@ -0,0 +1 @@ +javascript_maximum_signature_line_length = len("hello(name)") - 1 diff --git a/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst b/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst new file mode 100644 index 00000000000..29561d55f13 --- /dev/null +++ b/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst @@ -0,0 +1,4 @@ +domain-js-maximum_signature_line_length +======================================= + +.. js:function:: hello(name) diff --git a/tests/test_domain_js.py b/tests/test_domain_js.py index 634b02b378e..89bee235064 100644 --- a/tests/test_domain_js.py +++ b/tests/test_domain_js.py @@ -242,3 +242,118 @@ def test_module_content_line_number(app): source, line = docutils.utils.get_source_line(xrefs[0]) assert 'index.rst' in source assert line == 3 + + +@pytest.mark.sphinx('html', confoverrides={ + 'javascript_maximum_signature_line_length': len("hello(name)"), +}) +def test_jsfunction_signature_with_javascript_maximum_signature_line_length_equal(app): + text = ".. js:function:: hello(name)" + doctree = restructuredtext.parse(app, text) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_name, ([desc_sig_name, "hello"])], + desc_parameterlist, + )], + desc_content, + )], + )) + assert_node(doctree[1], desc, desctype="function", + domain="js", objtype="function", noindex=False) + assert_node(doctree[1][0][1], + [desc_parameterlist, desc_parameter, ([desc_sig_name, "name"])]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) + + +@pytest.mark.sphinx('html', confoverrides={ + 'javascript_maximum_signature_line_length': len("hello(name)"), +}) +def test_jsfunction_signature_with_javascript_maximum_signature_line_length_force_single(app): + text = (".. js:function:: hello(names)\n" + " :single-line-parameter-list:") + doctree = restructuredtext.parse(app, text) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_name, ([desc_sig_name, "hello"])], + desc_parameterlist, + )], + desc_content, + )], + )) + assert_node(doctree[1], desc, desctype="function", + domain="js", objtype="function", noindex=False) + assert_node(doctree[1][0][1], + [desc_parameterlist, desc_parameter, ([desc_sig_name, "names"])]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) + + +@pytest.mark.sphinx('html', confoverrides={ + 'javascript_maximum_signature_line_length': len("hello(name)"), +}) +def test_jsfunction_signature_with_javascript_maximum_signature_line_length_break(app): + text = ".. js:function:: hello(names)" + doctree = restructuredtext.parse(app, text) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_name, ([desc_sig_name, "hello"])], + desc_parameterlist, + )], + desc_content, + )], + )) + assert_node(doctree[1], desc, desctype="function", + domain="js", objtype="function", noindex=False) + assert_node(doctree[1][0][1], + [desc_parameterlist, desc_parameter, ([desc_sig_name, "names"])]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=True) + + +@pytest.mark.sphinx( + 'html', + confoverrides={ + 'javascript_maximum_signature_line_length': len("hello(name)"), + 'maximum_signature_line_length': 1, + }, +) +def test_javascript_maximum_signature_line_length_overrides_global(app): + text = ".. js:function:: hello(name)" + doctree = restructuredtext.parse(app, text) + expected_doctree = (addnodes.index, + [desc, ([desc_signature, ([desc_name, ([desc_sig_name, "hello"])], + desc_parameterlist)], + desc_content)]) + assert_node(doctree, expected_doctree) + assert_node(doctree[1], desc, desctype="function", + domain="js", objtype="function", noindex=False) + expected_sig = [desc_parameterlist, desc_parameter, [desc_sig_name, "name"]] + assert_node(doctree[1][0][1], expected_sig) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) + + +@pytest.mark.sphinx( + 'html', testroot='domain-js-javascript_maximum_signature_line_length', +) +def test_javascript_javascript_maximum_signature_line_length(app, status, warning): + app.build() + content = (app.outdir / 'index.html').read_text(encoding='utf8') + expected = """\ + +
+
\ +\ +name\ +,\ +
+
+ +)\ +\ +\ +""" + assert expected in content From ea304417721bc6687f9b34d785996f36c5705c8c Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 16 Apr 2023 14:18:24 +0200 Subject: [PATCH 079/103] Actually test for `multi_line_parameter_list` bool --- tests/test_domain_c.py | 7 +++++++ tests/test_domain_cpp.py | 7 +++++++ tests/test_domain_py.py | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index a06dfd544e7..39edf66c74c 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -854,6 +854,7 @@ def test_cfunction_signature_with_c_maximum_signature_line_length_equal(app): desc_sig_space, [desc_sig_name, "name"], )]) + assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False) @pytest.mark.sphinx('html', confoverrides={ @@ -884,6 +885,7 @@ def test_cfunction_signature_with_c_maximum_signature_line_length_force_single(a desc_sig_space, [desc_sig_name, "names"], )]) + assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False) @pytest.mark.sphinx('html', confoverrides={ @@ -913,6 +915,7 @@ def test_cfunction_signature_with_c_maximum_signature_line_break(app): desc_sig_space, [desc_sig_name, "names"], )]) + assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=True) @pytest.mark.sphinx('html', confoverrides={ @@ -942,6 +945,7 @@ def test_cfunction_signature_with_maximum_signature_line_length_equal(app): desc_sig_space, [desc_sig_name, "name"], )]) + assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False) @pytest.mark.sphinx('html', confoverrides={ @@ -972,6 +976,7 @@ def test_cfunction_signature_with_maximum_signature_line_length_force_single(app desc_sig_space, [desc_sig_name, "names"], )]) + assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False) @pytest.mark.sphinx('html', confoverrides={ @@ -1001,6 +1006,7 @@ def test_cfunction_signature_with_maximum_signature_line_break(app): desc_sig_space, [desc_sig_name, "names"], )]) + assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=True) @pytest.mark.sphinx( @@ -1039,6 +1045,7 @@ def test_c_maximum_signature_line_length_overrides_global(app): desc_sig_space, [desc_sig_name, 'name'], )]) + assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False) @pytest.mark.sphinx('html', testroot='domain-c-c_maximum_signature_line_length') diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 0e7e0a9f056..5dea971b483 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -1526,6 +1526,7 @@ def test_cpp_function_signature_with_cpp_maximum_signature_line_length_equal(app desc_sig_space, [desc_sig_name, 'name'], )]) + assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False) @pytest.mark.sphinx('html', confoverrides={ @@ -1556,6 +1557,7 @@ def test_cpp_function_signature_with_cpp_maximum_signature_line_length_force_sin desc_sig_space, [desc_sig_name, 'names']), ]) + assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False) @pytest.mark.sphinx('html', confoverrides={ @@ -1585,6 +1587,7 @@ def test_cpp_function_signature_with_cpp_maximum_signature_line_length_break(app desc_sig_space, [desc_sig_name, 'names']), ]) + assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=True) @pytest.mark.sphinx('html', confoverrides={ @@ -1614,6 +1617,7 @@ def test_cpp_function_signature_with_maximum_signature_line_length_equal(app): desc_sig_space, [desc_sig_name, 'name'], )]) + assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False) @pytest.mark.sphinx('html', confoverrides={ @@ -1644,6 +1648,7 @@ def test_cpp_function_signature_with_maximum_signature_line_length_force_single( desc_sig_space, [desc_sig_name, 'names']), ]) + assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False) @pytest.mark.sphinx('html', confoverrides={ @@ -1673,6 +1678,7 @@ def test_cpp_function_signature_with_maximum_signature_line_length_break(app): desc_sig_space, [desc_sig_name, 'names']), ]) + assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=True) @pytest.mark.sphinx('html', confoverrides={ @@ -1697,6 +1703,7 @@ def test_cpp_maximum_signature_line_length_overrides_global(app): desc_sig_space, [desc_sig_name, 'name'], )]) + assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=False) @pytest.mark.sphinx('html', testroot='domain-cpp-cpp_maximum_signature_line_length') diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index 9195fbd4660..80365b3609a 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -1485,6 +1485,7 @@ def test_pyfunction_signature_with_python_maximum_signature_line_length_equal(ap desc_sig_space, [nodes.inline, pending_xref, "str"], )]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) @pytest.mark.sphinx('html', confoverrides={ @@ -1513,6 +1514,7 @@ def test_pyfunction_signature_with_python_maximum_signature_line_length_force_si desc_sig_space, [nodes.inline, pending_xref, "str"], )]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) @pytest.mark.sphinx('html', confoverrides={ @@ -1540,6 +1542,7 @@ def test_pyfunction_signature_with_python_maximum_signature_line_length_break(ap desc_sig_space, [nodes.inline, pending_xref, "str"], )]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=True) @pytest.mark.sphinx('html', confoverrides={ @@ -1567,6 +1570,7 @@ def test_pyfunction_signature_with_maximum_signature_line_length_equal(app): desc_sig_space, [nodes.inline, pending_xref, "str"], )]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) @pytest.mark.sphinx('html', confoverrides={ @@ -1595,6 +1599,7 @@ def test_pyfunction_signature_with_maximum_signature_line_length_force_single(ap desc_sig_space, [nodes.inline, pending_xref, "str"], )]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) @pytest.mark.sphinx('html', confoverrides={ @@ -1622,6 +1627,7 @@ def test_pyfunction_signature_with_maximum_signature_line_length_break(app): desc_sig_space, [nodes.inline, pending_xref, "str"], )]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=True) @pytest.mark.sphinx( @@ -1648,6 +1654,7 @@ def test_python_maximum_signature_line_length_overrides_global(app): desc_sig_space, [nodes.inline, pending_xref, "str"])] assert_node(doctree[1][0][1], expected_sig) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) @pytest.mark.sphinx( From 2d604b9f15006cdacbcc9ef59fd0a37c6023f83e Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 16 Apr 2023 14:22:24 +0200 Subject: [PATCH 080/103] Add JS tests with `maximum_signature_line_length` --- tests/test_domain_js.py | 70 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/test_domain_js.py b/tests/test_domain_js.py index 89bee235064..fab35a5f6ac 100644 --- a/tests/test_domain_js.py +++ b/tests/test_domain_js.py @@ -314,6 +314,76 @@ def test_jsfunction_signature_with_javascript_maximum_signature_line_length_brea assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=True) +@pytest.mark.sphinx('html', confoverrides={ + 'maximum_signature_line_length': len("hello(name)"), +}) +def test_jsfunction_signature_with_maximum_signature_line_length_equal(app): + text = ".. js:function:: hello(name)" + doctree = restructuredtext.parse(app, text) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_name, ([desc_sig_name, "hello"])], + desc_parameterlist, + )], + desc_content, + )], + )) + assert_node(doctree[1], desc, desctype="function", + domain="js", objtype="function", noindex=False) + assert_node(doctree[1][0][1], + [desc_parameterlist, desc_parameter, ([desc_sig_name, "name"])]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) + + +@pytest.mark.sphinx('html', confoverrides={ + 'maximum_signature_line_length': len("hello(name)"), +}) +def test_jsfunction_signature_with_maximum_signature_line_length_force_single(app): + text = (".. js:function:: hello(names)\n" + " :single-line-parameter-list:") + doctree = restructuredtext.parse(app, text) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_name, ([desc_sig_name, "hello"])], + desc_parameterlist, + )], + desc_content, + )], + )) + assert_node(doctree[1], desc, desctype="function", + domain="js", objtype="function", noindex=False) + assert_node(doctree[1][0][1], + [desc_parameterlist, desc_parameter, ([desc_sig_name, "names"])]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=False) + + +@pytest.mark.sphinx('html', confoverrides={ + 'maximum_signature_line_length': len("hello(name)"), +}) +def test_jsfunction_signature_with_maximum_signature_line_length_break(app): + text = ".. js:function:: hello(names)" + doctree = restructuredtext.parse(app, text) + assert_node(doctree, ( + addnodes.index, + [desc, ( + [desc_signature, ( + [desc_name, ([desc_sig_name, "hello"])], + desc_parameterlist, + )], + desc_content, + )], + )) + assert_node(doctree[1], desc, desctype="function", + domain="js", objtype="function", noindex=False) + assert_node(doctree[1][0][1], + [desc_parameterlist, desc_parameter, ([desc_sig_name, "names"])]) + assert_node(doctree[1][0][1], desc_parameterlist, multi_line_parameter_list=True) + + @pytest.mark.sphinx( 'html', confoverrides={ From 71f5a8abfaa2bf74525cb8386d6bc657f3a7affe Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 16 Apr 2023 14:29:23 +0200 Subject: [PATCH 081/103] Remove (probably) forgotten c test template --- tests/test_domain_c.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 39edf66c74c..eda58d30138 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -1009,14 +1009,6 @@ def test_cfunction_signature_with_maximum_signature_line_break(app): assert_node(doctree[1][0][0][3], desc_parameterlist, multi_line_parameter_list=True) -@pytest.mark.sphinx( - 'html', - confoverrides={'maximum_signature_line_length': len("str hello(str name)")}, -) -def test_cfunction_signature_with_maximum_signature_line_length(app): - ... - - @pytest.mark.sphinx('html', confoverrides={ 'c_maximum_signature_line_length': len('str hello(str name)'), 'maximum_signature_line_length': 1, From e9485639ed54a4c8ff6a4aa20e85a207485be85c Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 16 Apr 2023 14:31:13 +0200 Subject: [PATCH 082/103] Make test names consistent --- tests/test_domain_c.py | 4 ++-- tests/test_domain_js.py | 2 +- tests/test_domain_py.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index eda58d30138..109b0a65fe2 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -891,7 +891,7 @@ def test_cfunction_signature_with_c_maximum_signature_line_length_force_single(a @pytest.mark.sphinx('html', confoverrides={ 'c_maximum_signature_line_length': len("str hello(str name)"), }) -def test_cfunction_signature_with_c_maximum_signature_line_break(app): +def test_cfunction_signature_with_c_maximum_signature_line_length_break(app): text = ".. c:function:: str hello(str names)" doctree = restructuredtext.parse(app, text) assert_node(doctree, ( @@ -982,7 +982,7 @@ def test_cfunction_signature_with_maximum_signature_line_length_force_single(app @pytest.mark.sphinx('html', confoverrides={ 'maximum_signature_line_length': len("str hello(str name)"), }) -def test_cfunction_signature_with_maximum_signature_line_break(app): +def test_cfunction_signature_with_maximum_signature_line_length_break(app): text = ".. c:function:: str hello(str names)" doctree = restructuredtext.parse(app, text) assert_node(doctree, ( diff --git a/tests/test_domain_js.py b/tests/test_domain_js.py index fab35a5f6ac..256f9046d8e 100644 --- a/tests/test_domain_js.py +++ b/tests/test_domain_js.py @@ -409,7 +409,7 @@ def test_javascript_maximum_signature_line_length_overrides_global(app): @pytest.mark.sphinx( 'html', testroot='domain-js-javascript_maximum_signature_line_length', ) -def test_javascript_javascript_maximum_signature_line_length(app, status, warning): +def test_domain_js_javascript_maximum_signature_line_length(app, status, warning): app.build() content = (app.outdir / 'index.html').read_text(encoding='utf8') expected = """\ diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index 80365b3609a..b6eecfc33aa 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -1660,7 +1660,7 @@ def test_python_maximum_signature_line_length_overrides_global(app): @pytest.mark.sphinx( 'html', testroot='domain-py-python_maximum_signature_line_length', ) -def test_python_python_maximum_signature_line_length(app, status, warning): +def test_domain_py_python_maximum_signature_line_length(app, status, warning): app.build() content = (app.outdir / 'index.html').read_text(encoding='utf8') expected = """\ From d2b3459a2ece647ccc66d0076207fa1cef701aea Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 16 Apr 2023 15:26:48 +0200 Subject: [PATCH 083/103] Add newline after dd tags to make more readable HTML --- sphinx/writers/html5.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 5060197c7e5..b7dd2a8f6a4 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -168,7 +168,7 @@ def visit_desc_parameterlist(self, node: Element) -> None: def depart_desc_parameterlist(self, node: Element) -> None: if node.get('multi_line_parameter_list'): - self.body.append('\n\n\n') + self.body.append('\n\n') self.body.append(')') # If required parameters are still to come, then put the comma after @@ -202,7 +202,7 @@ def depart_desc_parameter(self, node: Element) -> None: ) if is_required and (is_last_group or next_is_required): self.body.append(self.param_separator) - self.body.append('') + self.body.append('\n') elif self.required_params_left: self.body.append(self.param_separator) @@ -223,7 +223,7 @@ def visit_desc_optional(self, node: Element) -> None: else: self.body.append('[') self.body.append(self.param_separator) - self.body.append('') + self.body.append('\n') else: self.body.append('[') @@ -238,7 +238,7 @@ def depart_desc_optional(self, node: Element) -> None: # End the line if we have just closed the last bracket of this group of # optional parameters. if self.optional_param_level == 0: - self.body.append('') + self.body.append('\n') else: self.body.append(']') if self.optional_param_level == 0: From bfc353eb3f3595564a879c805bb48f2628eb8838 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 16 Apr 2023 15:47:32 +0200 Subject: [PATCH 084/103] Test for optional syntax in js and py domains --- .../conf.py | 2 +- .../index.rst | 2 + .../conf.py | 2 +- .../index.rst | 2 + tests/test_domain_js.py | 45 ++++++++++++++++++- tests/test_domain_py.py | 45 ++++++++++++++++++- 6 files changed, 92 insertions(+), 6 deletions(-) diff --git a/tests/roots/test-domain-js-javascript_maximum_signature_line_length/conf.py b/tests/roots/test-domain-js-javascript_maximum_signature_line_length/conf.py index a27d8d7c271..d7c9331bd83 100644 --- a/tests/roots/test-domain-js-javascript_maximum_signature_line_length/conf.py +++ b/tests/roots/test-domain-js-javascript_maximum_signature_line_length/conf.py @@ -1 +1 @@ -javascript_maximum_signature_line_length = len("hello(name)") - 1 +javascript_maximum_signature_line_length = 1 diff --git a/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst b/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst index 29561d55f13..6bedae06040 100644 --- a/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst +++ b/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst @@ -2,3 +2,5 @@ domain-js-maximum_signature_line_length ======================================= .. js:function:: hello(name) + +.. js:function:: foo([a[, b, ]]c, d[, f]) diff --git a/tests/roots/test-domain-py-python_maximum_signature_line_length/conf.py b/tests/roots/test-domain-py-python_maximum_signature_line_length/conf.py index 4d1d54d928c..45f620db423 100644 --- a/tests/roots/test-domain-py-python_maximum_signature_line_length/conf.py +++ b/tests/roots/test-domain-py-python_maximum_signature_line_length/conf.py @@ -1 +1 @@ -python_maximum_signature_line_length = len("hello(name: str) -> str") - 1 +python_maximum_signature_line_length = 1 diff --git a/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst b/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst index cc5e9e06a1b..dc1ce850f09 100644 --- a/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst +++ b/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst @@ -2,3 +2,5 @@ domain-py-maximum_signature_line_length ======================================= .. py:function:: hello(name: str) -> str + +.. py:function:: foo([a[, b, ]]c, d[, f]) diff --git a/tests/test_domain_js.py b/tests/test_domain_js.py index 256f9046d8e..aa67ba42536 100644 --- a/tests/test_domain_js.py +++ b/tests/test_domain_js.py @@ -412,7 +412,7 @@ def test_javascript_maximum_signature_line_length_overrides_global(app): def test_domain_js_javascript_maximum_signature_line_length(app, status, warning): app.build() content = (app.outdir / 'index.html').read_text(encoding='utf8') - expected = """\ + expected_parameter_list_hello = """\
\ @@ -426,4 +426,45 @@ def test_domain_js_javascript_maximum_signature_line_length(app, status, warning \ \ """ - assert expected in content + assert expected_parameter_list_hello in content + + expected_parameter_list_foo = """\ + +
+
\ +[\ +\ +a\ +\ +[,\ +
+
\ +\ +b\ +,\ +]]\ +
+
\ +\ +c\ +,\ +
+
\ +\ +d\ +\ +[,\ +
+
\ +\ +f\ +,\ +]\ +
+
+ +)\ +\ +\ +""" + assert expected_parameter_list_foo in content diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index b6eecfc33aa..e7e2dfb51da 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -1663,7 +1663,7 @@ def test_python_maximum_signature_line_length_overrides_global(app): def test_domain_py_python_maximum_signature_line_length(app, status, warning): app.build() content = (app.outdir / 'index.html').read_text(encoding='utf8') - expected = """\ + expected_parameter_list_hello = """\
\ @@ -1684,7 +1684,48 @@ def test_domain_py_python_maximum_signature_line_length(app, status, warning): \ \ """ - assert expected in content + assert expected_parameter_list_hello in content + + expected_parameter_list_foo = """\ + +
+
\ +[\ +\ +a\ +\ +[,\ +
+
\ +\ +b\ +,\ +]]\ +
+
\ +\ +c\ +,\ +
+
\ +\ +d\ +\ +[,\ +
+
\ +\ +f\ +,\ +]\ +
+
+ +)\ +\ +\ +""" + assert expected_parameter_list_foo in content def test_module_content_line_number(app): From 0c5470336d7b30d465a0f0e296930bcf27da0629 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 16 Apr 2023 18:12:06 +0200 Subject: [PATCH 085/103] latex: handle comma placement in writer Use smilar logic as in other writers, and remove TeX black magic. --- sphinx/texinputs/sphinxlatexobjects.sty | 6 +-- sphinx/writers/latex.py | 50 ++++++++++++++++++++----- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/sphinx/texinputs/sphinxlatexobjects.sty b/sphinx/texinputs/sphinxlatexobjects.sty index 6e416f834eb..a72000b891b 100644 --- a/sphinx/texinputs/sphinxlatexobjects.sty +++ b/sphinx/texinputs/sphinxlatexobjects.sty @@ -164,15 +164,11 @@ % this relies on \pysigstartsignatures having set \parskip to zero \begingroup \def\sphinxoptionalhook{\ifvmode\else\kern\sphinxoptionalextraspace\relax\fi}% - % FIXME: handle this at sphinx.writers.latex level for suitable - % mark-up, rather than doing this very hacky TeX. - \@firstofone{\def\sphinxparamcomma,} {\futurelet\spx@token\spx@pysigwithonelineperarg@a}% % The very first \sphinxparam should not emit a \par hence a complication % with a group and global definition here as it may occur in a \sphinxoptional \global\let\spx@sphinxparam\sphinxparam \gdef\sphinxparam{\gdef\sphinxparam{\par\spx@sphinxparam}\spx@sphinxparam}% - #2\sphinxparamcomma, \par % <-- attention to leave space token before \par - % due to above hack (but see FIXME) + #2\par \endgroup \global\let\sphinxparam\spx@sphinxparam % fulllineitems sets \labelwidth to be like \leftmargin diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index e14b30f6e16..92c1052d6d2 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -797,20 +797,29 @@ def depart_desc_returns(self, node: Element) -> None: def visit_desc_parameterlist(self, node: Element) -> None: # close name, open parameterlist self.body.append('}{') - self.first_param = 1 + self.is_first_param = True self.optional_param_level = 0 - self.required_params_left = sum([isinstance(c, addnodes.desc_parameter) - for c in node.children]) + self.param_group_index = 0 + # Counts as what we call a parameter group either a required parameter, or a + # set of contiguous optional ones. + self.list_is_required_param = [isinstance(c, addnodes.desc_parameter) + for c in node.children] + # How many required parameters are left. + self.required_params_left = sum(self.list_is_required_param) + self.param_separator = r'\sphinxparamcomma, ' + self.multi_line_parameter_list = node.get('multi_line_parameter_list', False) + if self.multi_line_parameter_list: + self.param_separator = self.param_separator.rstrip() def depart_desc_parameterlist(self, node: Element) -> None: # close parameterlist, open return annotation self.body.append('}{') def visit_desc_parameter(self, node: Element) -> None: - if self.first_param: - self.first_param = 0 - elif not self.required_params_left: - self.body.append(r'\sphinxparamcomma, ') + if self.is_first_param: + self.is_first_param = False + elif not self.multi_line_parameter_list and not self.required_params_left: + self.body.append(self.param_separator) if self.optional_param_level == 0: self.required_params_left -= 1 if not node.hasattr('noemph'): @@ -819,16 +828,39 @@ def visit_desc_parameter(self, node: Element) -> None: def depart_desc_parameter(self, node: Element) -> None: if not node.hasattr('noemph'): self.body.append('}') - if self.required_params_left: - self.body.append(r'\sphinxparamcomma, ') + is_required = self.list_is_required_param[self.param_group_index] + if self.multi_line_parameter_list: + is_last_group = self.param_group_index + 1 == len(self.list_is_required_param) + next_is_required = ( + not is_last_group + and self.list_is_required_param[self.param_group_index + 1] + ) + if is_required and (is_last_group or next_is_required): + self.body.append(self.param_separator) + + elif self.required_params_left: + self.body.append(self.param_separator) + + if is_required: + self.param_group_index += 1 def visit_desc_optional(self, node: Element) -> None: self.optional_param_level += 1 + self.max_optional_param_level = self.optional_param_level self.body.append(r'\sphinxoptional{') + if self.multi_line_parameter_list and not self.is_first_param: + self.body.append(self.param_separator) def depart_desc_optional(self, node: Element) -> None: self.optional_param_level -= 1 + if self.multi_line_parameter_list: + # If it's the first time we go down one level, add the separator before the + # bracket. + if self.optional_param_level == self.max_optional_param_level - 1: + self.body.append(self.param_separator) self.body.append('}') + if self.optional_param_level == 0: + self.param_group_index += 1 def visit_desc_annotation(self, node: Element) -> None: self.body.append(r'\sphinxbfcode{\sphinxupquote{') From a1b031f0f4a7471aaf00d8752334fd741b85f6d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Sun, 16 Apr 2023 18:51:14 +0200 Subject: [PATCH 086/103] latex: follow-up refactoring after TeX black magic removal --- sphinx/texinputs/sphinxlatexobjects.sty | 10 +--------- sphinx/texinputs/sphinxlatexstyletext.sty | 9 ++++----- sphinx/writers/latex.py | 4 +--- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/sphinx/texinputs/sphinxlatexobjects.sty b/sphinx/texinputs/sphinxlatexobjects.sty index a72000b891b..a2038a9f160 100644 --- a/sphinx/texinputs/sphinxlatexobjects.sty +++ b/sphinx/texinputs/sphinxlatexobjects.sty @@ -147,15 +147,6 @@ \pysigadjustitemsep } -\def\spx@pysigwithonelineperarg@a{% - \ifx\spx@token\sphinxoptional - \expandafter\spx@pysigwithonelineperarg@b - \else\sphinxparamcommaoneperline - \fi -} -\def\spx@pysigwithonelineperarg@b\sphinxoptional#1{% - \sphinxoptional{\sphinxparamcommaoneperline#1}% -} \def\sphinxoptionalextraspace{0.5mm} \newcommand{\pysigwithonelineperarg}[3]{% % render each argument on its own line @@ -163,6 +154,7 @@ \leavevmode\par\nopagebreak % this relies on \pysigstartsignatures having set \parskip to zero \begingroup + \let\sphinxparamcomma\sphinxparamcommaoneperline \def\sphinxoptionalhook{\ifvmode\else\kern\sphinxoptionalextraspace\relax\fi}% % The very first \sphinxparam should not emit a \par hence a complication % with a group and global definition here as it may occur in a \sphinxoptional diff --git a/sphinx/texinputs/sphinxlatexstyletext.sty b/sphinx/texinputs/sphinxlatexstyletext.sty index a4defc96cad..211acacb6ef 100644 --- a/sphinx/texinputs/sphinxlatexstyletext.sty +++ b/sphinx/texinputs/sphinxlatexstyletext.sty @@ -113,11 +113,10 @@ % Special characters % -% Modified internally when rendering signatures with one line per param -% If redefining this, mind that it is followed by explicit "," in input. -\let\sphinxparamcomma\empty -% The "," in mark-up will have been swallowed if this is executed. -\def\sphinxparamcommaoneperline{\sphinxcode{,}} +\def\sphinxparamcomma{, }% by default separae parameters with comma + space +% if signature is rendered with one line per param, this is used instead +% (the \texttt makes the comma slightly more distinctive) +\def\sphinxparamcommaoneperline{\texttt{,}} % % The \kern\z@ is to prevent en-dash and em-dash TeX ligatures. % A linebreak can occur after the dash in regular text (this is diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 92c1052d6d2..cd2e1c513a2 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -806,10 +806,8 @@ def visit_desc_parameterlist(self, node: Element) -> None: for c in node.children] # How many required parameters are left. self.required_params_left = sum(self.list_is_required_param) - self.param_separator = r'\sphinxparamcomma, ' + self.param_separator = r'\sphinxparamcomma ' self.multi_line_parameter_list = node.get('multi_line_parameter_list', False) - if self.multi_line_parameter_list: - self.param_separator = self.param_separator.rstrip() def depart_desc_parameterlist(self, node: Element) -> None: # close parameterlist, open return annotation From a0f5d9ba8b160de649a2a3521bb09f7cae1b3dad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Sun, 16 Apr 2023 19:11:11 +0200 Subject: [PATCH 087/103] latex: tidy-up some comment in sphinxlatexstyletext.sty --- sphinx/texinputs/sphinxlatexstyletext.sty | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sphinx/texinputs/sphinxlatexstyletext.sty b/sphinx/texinputs/sphinxlatexstyletext.sty index 211acacb6ef..292facc9132 100644 --- a/sphinx/texinputs/sphinxlatexstyletext.sty +++ b/sphinx/texinputs/sphinxlatexstyletext.sty @@ -113,9 +113,9 @@ % Special characters % -\def\sphinxparamcomma{, }% by default separae parameters with comma + space -% if signature is rendered with one line per param, this is used instead -% (the \texttt makes the comma slightly more distinctive) +\def\sphinxparamcomma{, }% by default separate parameters with comma + space +% If the signature is rendered with one line per param, this wil be used +% instead (this \texttt makes the comma slightly more distinctive). \def\sphinxparamcommaoneperline{\texttt{,}} % % The \kern\z@ is to prevent en-dash and em-dash TeX ligatures. From a8a9be9ba357faf4a70c8446225a7a4071d6f593 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 16 Apr 2023 20:11:47 +0200 Subject: [PATCH 088/103] Fix comma placement when `required_params_left` --- sphinx/writers/html5.py | 6 ++++++ sphinx/writers/latex.py | 15 ++++++++++++--- sphinx/writers/text.py | 6 ++++++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index b7dd2a8f6a4..c44402f2057 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -218,6 +218,12 @@ def visit_desc_optional(self, node: Element) -> None: if self.is_first_param: self.body.append(self.starttag(node, 'dd', '')) self.body.append('[') + # Else, if there remains at least one required parameter, append the + # parameter separator, open a new bracket, and end the line. + elif self.required_params_left: + self.body.append(self.param_separator) + self.body.append('[') + self.body.append('
\n') # Else, open a new bracket, append the parameter separator, and end the # line. else: diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index cd2e1c513a2..ec6802fbee3 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -845,9 +845,18 @@ def depart_desc_parameter(self, node: Element) -> None: def visit_desc_optional(self, node: Element) -> None: self.optional_param_level += 1 self.max_optional_param_level = self.optional_param_level - self.body.append(r'\sphinxoptional{') - if self.multi_line_parameter_list and not self.is_first_param: - self.body.append(self.param_separator) + if self.multi_line_parameter_list: + if self.is_first_param: + self.body.append(r'\sphinxoptional{') + elif self.required_params_left: + self.body.append(self.param_separator) + self.body.append(r'\sphinxoptional{') + else: + self.body.append(r'\sphinxoptional{') + self.body.append(self.param_separator) + else: + self.body.append(r'\sphinxoptional{') + def depart_desc_optional(self, node: Element) -> None: self.optional_param_level -= 1 diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index 3a90939006a..e57aa071858 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -649,6 +649,12 @@ def visit_desc_optional(self, node: Element) -> None: if self.is_first_param: self.new_state() self.add_text('[') + # Else, if there remains at least one required parameter, append the + # parameter separator, open a new bracket, and end the line. + elif self.required_params_left: + self.add_text(self.param_separator) + self.add_text('[') + self.end_state(wrap=False, end=None) # Else, open a new bracket, append the parameter separator, and end the # line. else: From 3b34e30a2e16872cb11585786f038089972ba0c9 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 16 Apr 2023 20:42:25 +0200 Subject: [PATCH 089/103] Fix case of multiple params at same optional level --- sphinx/writers/html5.py | 8 +++++++- sphinx/writers/latex.py | 8 +++++++- sphinx/writers/text.py | 8 +++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index c44402f2057..fedb2ba918b 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -152,6 +152,7 @@ def visit_desc_parameterlist(self, node: Element) -> None: self.body.append('(') self.is_first_param = True self.optional_param_level = 0 + self.params_left_at_level = 0 self.param_group_index = 0 # Counts as what we call a parameter group either a required parameter, or a # set of contiguous optional ones. @@ -187,6 +188,8 @@ def visit_desc_parameter(self, node: Element) -> None: self.body.append(self.param_separator) if self.optional_param_level == 0: self.required_params_left -= 1 + else: + self.params_left_at_level -= 1 if not node.hasattr('noemph'): self.body.append('') @@ -200,7 +203,8 @@ def depart_desc_parameter(self, node: Element) -> None: not is_last_group and self.list_is_required_param[self.param_group_index + 1] ) - if is_required and (is_last_group or next_is_required): + opt_param_left_at_level = self.params_left_at_level > 0 + if opt_param_left_at_level or is_required and (is_last_group or next_is_required): self.body.append(self.param_separator) self.body.append('
\n') @@ -211,6 +215,8 @@ def depart_desc_parameter(self, node: Element) -> None: self.param_group_index += 1 def visit_desc_optional(self, node: Element) -> None: + self.params_left_at_level = sum([isinstance(c, addnodes.desc_parameter) + for c in node.children]) self.optional_param_level += 1 self.max_optional_param_level = self.optional_param_level if self.multi_line_parameter_list: diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index ec6802fbee3..aab0bea92d2 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -799,6 +799,7 @@ def visit_desc_parameterlist(self, node: Element) -> None: self.body.append('}{') self.is_first_param = True self.optional_param_level = 0 + self.params_left_at_level = 0 self.param_group_index = 0 # Counts as what we call a parameter group either a required parameter, or a # set of contiguous optional ones. @@ -820,6 +821,8 @@ def visit_desc_parameter(self, node: Element) -> None: self.body.append(self.param_separator) if self.optional_param_level == 0: self.required_params_left -= 1 + else: + self.params_left_at_level -= 1 if not node.hasattr('noemph'): self.body.append(r'\sphinxparam{') @@ -833,7 +836,8 @@ def depart_desc_parameter(self, node: Element) -> None: not is_last_group and self.list_is_required_param[self.param_group_index + 1] ) - if is_required and (is_last_group or next_is_required): + opt_param_left_at_level = self.params_left_at_level > 0 + if opt_param_left_at_level or is_required and (is_last_group or next_is_required): self.body.append(self.param_separator) elif self.required_params_left: @@ -843,6 +847,8 @@ def depart_desc_parameter(self, node: Element) -> None: self.param_group_index += 1 def visit_desc_optional(self, node: Element) -> None: + self.params_left_at_level = sum([isinstance(c, addnodes.desc_parameter) + for c in node.children]) self.optional_param_level += 1 self.max_optional_param_level = self.optional_param_level if self.multi_line_parameter_list: diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index e57aa071858..b1b278f146f 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -596,6 +596,7 @@ def visit_desc_parameterlist(self, node: Element) -> None: self.add_text('(') self.is_first_param = True self.optional_param_level = 0 + self.params_left_at_level = 0 self.param_group_index = 0 # Counts as what we call a parameter group are either a required parameter, or a # set of contiguous optional ones. @@ -620,6 +621,8 @@ def visit_desc_parameter(self, node: Element) -> None: self.add_text(self.param_separator) if self.optional_param_level == 0: self.required_params_left -= 1 + else: + self.params_left_at_level -= 1 self.add_text(node.astext()) @@ -630,7 +633,8 @@ def visit_desc_parameter(self, node: Element) -> None: not is_last_group and self.list_is_required_param[self.param_group_index + 1] ) - if is_required and (is_last_group or next_is_required): + opt_param_left_at_level = self.params_left_at_level > 0 + if opt_param_left_at_level or is_required and (is_last_group or next_is_required): self.add_text(self.param_separator) self.end_state(wrap=False, end=None) @@ -642,6 +646,8 @@ def visit_desc_parameter(self, node: Element) -> None: raise nodes.SkipNode def visit_desc_optional(self, node: Element) -> None: + self.params_left_at_level = sum([isinstance(c, addnodes.desc_parameter) + for c in node.children]) self.optional_param_level += 1 self.max_optional_param_level = self.optional_param_level if self.multi_line_parameter_list: From 2c1f83ad07c148a7eba271ec7d2c75772ac777b0 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 16 Apr 2023 20:47:57 +0200 Subject: [PATCH 090/103] lint: remove extra line between method defs --- sphinx/writers/latex.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index aab0bea92d2..ccdfbbd6e5e 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -863,7 +863,6 @@ def visit_desc_optional(self, node: Element) -> None: else: self.body.append(r'\sphinxoptional{') - def depart_desc_optional(self, node: Element) -> None: self.optional_param_level -= 1 if self.multi_line_parameter_list: From 9c625f111d281d6ccfadd47678f70c8ed49be32a Mon Sep 17 00:00:00 2001 From: TLouf Date: Sun, 16 Apr 2023 20:54:36 +0200 Subject: [PATCH 091/103] Conform test to new syntax --- .../index.rst | 2 +- .../index.rst | 2 +- tests/test_domain_js.py | 4 ++-- tests/test_domain_py.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst b/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst index 6bedae06040..7ccde73bf18 100644 --- a/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst +++ b/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst @@ -3,4 +3,4 @@ domain-js-maximum_signature_line_length .. js:function:: hello(name) -.. js:function:: foo([a[, b, ]]c, d[, f]) +.. js:function:: foo([a, [b, ]]c, d[, f]) diff --git a/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst b/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst index dc1ce850f09..fb218c65178 100644 --- a/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst +++ b/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst @@ -3,4 +3,4 @@ domain-py-maximum_signature_line_length .. py:function:: hello(name: str) -> str -.. py:function:: foo([a[, b, ]]c, d[, f]) +.. py:function:: foo([a, [b, ]]c, d[, f]) diff --git a/tests/test_domain_js.py b/tests/test_domain_js.py index aa67ba42536..c60bb0e4b94 100644 --- a/tests/test_domain_js.py +++ b/tests/test_domain_js.py @@ -435,8 +435,8 @@ def test_domain_js_javascript_maximum_signature_line_length(app, status, warning [\ \ a\ -\ -[,\ +,\ +[\
\ \ diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index e7e2dfb51da..f96a058ac9c 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -1693,8 +1693,8 @@ def test_domain_py_python_maximum_signature_line_length(app, status, warning): [\ \ a\ -\ -[,\ +,\ +[\
\ \ From 5866fe6935aeb50b5182f5d1777ff2766a1a7af3 Mon Sep 17 00:00:00 2001 From: TLouf Date: Mon, 17 Apr 2023 17:52:27 +0200 Subject: [PATCH 092/103] Test for multiple params in optional level --- .../index.rst | 2 +- .../index.rst | 2 +- tests/test_domain_js.py | 5 +++++ tests/test_domain_py.py | 5 +++++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst b/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst index 7ccde73bf18..3d093b4decb 100644 --- a/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst +++ b/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst @@ -3,4 +3,4 @@ domain-js-maximum_signature_line_length .. js:function:: hello(name) -.. js:function:: foo([a, [b, ]]c, d[, f]) +.. js:function:: foo([a, [b, ]]c, d[, f, g]) diff --git a/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst b/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst index fb218c65178..a23c9410ab5 100644 --- a/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst +++ b/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst @@ -3,4 +3,4 @@ domain-py-maximum_signature_line_length .. py:function:: hello(name: str) -> str -.. py:function:: foo([a, [b, ]]c, d[, f]) +.. py:function:: foo([a, [b, ]]c, d[, f, g]) diff --git a/tests/test_domain_js.py b/tests/test_domain_js.py index c60bb0e4b94..a09e091c4b2 100644 --- a/tests/test_domain_js.py +++ b/tests/test_domain_js.py @@ -459,6 +459,11 @@ def test_domain_js_javascript_maximum_signature_line_length(app, status, warning \ f\ ,\ +
+
\ +\ +g\ +,\ ]\
diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index f96a058ac9c..f88e5401245 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -1717,6 +1717,11 @@ def test_domain_py_python_maximum_signature_line_length(app, status, warning): \ f\ ,\ + +
\ +\ +g\ +,\ ]\
From 3bef7e497821f25f99163dd710c4e92ce711af30 Mon Sep 17 00:00:00 2001 From: TLouf Date: Tue, 18 Apr 2023 18:24:07 +0200 Subject: [PATCH 093/103] Make HTML asserts more modular to ease debugging --- .../index.rst | 2 +- .../index.rst | 2 +- tests/test_domain_js.py | 67 +++++++++---------- tests/test_domain_py.py | 67 +++++++++---------- 4 files changed, 62 insertions(+), 76 deletions(-) diff --git a/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst b/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst index 3d093b4decb..b79fc1a8fdf 100644 --- a/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst +++ b/tests/roots/test-domain-js-javascript_maximum_signature_line_length/index.rst @@ -3,4 +3,4 @@ domain-js-maximum_signature_line_length .. js:function:: hello(name) -.. js:function:: foo([a, [b, ]]c, d[, f, g]) +.. js:function:: foo([a, [b, ]]c, d[, e, f]) diff --git a/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst b/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst index a23c9410ab5..75e46830512 100644 --- a/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst +++ b/tests/roots/test-domain-py-python_maximum_signature_line_length/index.rst @@ -3,4 +3,4 @@ domain-py-maximum_signature_line_length .. py:function:: hello(name: str) -> str -.. py:function:: foo([a, [b, ]]c, d[, f, g]) +.. py:function:: foo([a, [b, ]]c, d[, e, f]) diff --git a/tests/test_domain_js.py b/tests/test_domain_js.py index a09e091c4b2..f41c98c2527 100644 --- a/tests/test_domain_js.py +++ b/tests/test_domain_js.py @@ -428,48 +428,41 @@ def test_domain_js_javascript_maximum_signature_line_length(app, status, warning """ assert expected_parameter_list_hello in content + param_line_fmt = '
{}
\n' + param_name_fmt = ( + '{}' + ) + optional_fmt = '{}' + + expected_a = param_line_fmt.format( + optional_fmt.format("[") + param_name_fmt.format("a") + "," + optional_fmt.format("["), + ) + assert expected_a in content + + expected_b = param_line_fmt.format( + param_name_fmt.format("b") + "," + optional_fmt.format("]") + optional_fmt.format("]"), + ) + assert expected_b in content + + expected_c = param_line_fmt.format(param_name_fmt.format("c") + ",") + assert expected_c in content + + expected_d = param_line_fmt.format(param_name_fmt.format("d") + optional_fmt.format("[") + ",") + assert expected_d in content + + expected_e = param_line_fmt.format(param_name_fmt.format("e") + ",") + assert expected_e in content + + expected_f = param_line_fmt.format(param_name_fmt.format("f") + "," + optional_fmt.format("]")) + assert expected_f in content + expected_parameter_list_foo = """\
-
\ -[\ -\ -a\ -,\ -[\ -
-
\ -\ -b\ -,\ -]]\ -
-
\ -\ -c\ -,\ -
-
\ -\ -d\ -\ -[,\ -
-
\ -\ -f\ -,\ -
-
\ -\ -g\ -,\ -]\ -
-
+{}{}{}{}{}{} )\ \ \ -""" +""".format(expected_a, expected_b, expected_c, expected_d, expected_e, expected_f) assert expected_parameter_list_foo in content diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index f88e5401245..bd2cea98a3c 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -1686,50 +1686,43 @@ def test_domain_py_python_maximum_signature_line_length(app, status, warning): """ assert expected_parameter_list_hello in content + param_line_fmt = '
{}
\n' + param_name_fmt = ( + '{}' + ) + optional_fmt = '{}' + + expected_a = param_line_fmt.format( + optional_fmt.format("[") + param_name_fmt.format("a") + "," + optional_fmt.format("["), + ) + assert expected_a in content + + expected_b = param_line_fmt.format( + param_name_fmt.format("b") + "," + optional_fmt.format("]") + optional_fmt.format("]"), + ) + assert expected_b in content + + expected_c = param_line_fmt.format(param_name_fmt.format("c") + ",") + assert expected_c in content + + expected_d = param_line_fmt.format(param_name_fmt.format("d") + optional_fmt.format("[") + ",") + assert expected_d in content + + expected_e = param_line_fmt.format(param_name_fmt.format("e") + ",") + assert expected_e in content + + expected_f = param_line_fmt.format(param_name_fmt.format("f") + "," + optional_fmt.format("]")) + assert expected_f in content + expected_parameter_list_foo = """\
-
\ -[\ -\ -a\ -,\ -[\ -
-
\ -\ -b\ -,\ -]]\ -
-
\ -\ -c\ -,\ -
-
\ -\ -d\ -\ -[,\ -
-
\ -\ -f\ -,\ -
-
\ -\ -g\ -,\ -]\ -
-
+{}{}{}{}{}{} )\ \ \ -""" +""".format(expected_a, expected_b, expected_c, expected_d, expected_e, expected_f) assert expected_parameter_list_foo in content From 30041a6ae70c7662e7b1af5a8e29f711124abf5d Mon Sep 17 00:00:00 2001 From: TLouf Date: Fri, 21 Apr 2023 13:04:15 +0200 Subject: [PATCH 094/103] Test xelatex PDF build Skip other engines to reduce testing time. --- tests/test_build_latex.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 8c6ecb96a30..05d7511947b 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -91,12 +91,15 @@ def skip_if_stylefiles_notfound(testfunc): @skip_if_requested @skip_if_stylefiles_notfound +# Only running test with `python_maximum_signature_line_length` not None with xelatex to +# reduce testing time. @pytest.mark.parametrize( - "engine,docclass", - product(LATEX_ENGINES, DOCCLASSES), + "engine,docclass,python_maximum_signature_line_length", + list(product(LATEX_ENGINES, DOCCLASSES, [None])) + list(product(['xelatex'], DOCCLASSES, [1])), ) @pytest.mark.sphinx('latex') -def test_build_latex_doc(app, status, warning, engine, docclass): +def test_build_latex_doc(app, status, warning, engine, docclass, python_maximum_signature_line_length): + app.config.python_maximum_signature_line_length = python_maximum_signature_line_length app.config.intersphinx_mapping = { 'sphinx': ('https://www.sphinx-doc.org/en/master/', None), } From 8037a5b605810293c8ae0724d48a7fc027994ae7 Mon Sep 17 00:00:00 2001 From: TLouf Date: Fri, 21 Apr 2023 16:54:47 +0200 Subject: [PATCH 095/103] Actually override xelatex' build conf --- tests/test_build_latex.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 05d7511947b..1dc872ede19 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -91,15 +91,25 @@ def skip_if_stylefiles_notfound(testfunc): @skip_if_requested @skip_if_stylefiles_notfound -# Only running test with `python_maximum_signature_line_length` not None with xelatex to -# reduce testing time. @pytest.mark.parametrize( - "engine,docclass,python_maximum_signature_line_length", - list(product(LATEX_ENGINES, DOCCLASSES, [None])) + list(product(['xelatex'], DOCCLASSES, [1])), + "engine,docclass", + # Only running test with `python_maximum_signature_line_length` not None with last + # LaTeX engine to reduce testing time, as if this configuration does not fail with + # one engine, it's almost impossible it would fail with another. + [ + pytest.param( + engine, + doc_class, + marks=pytest.mark.sphinx( + 'latex', freshenv=True, confoverrides={'python_maximum_signature_line_length': 1}, + ), + ) + if engine == LATEX_ENGINES[0] + else pytest.param(engine, doc_class, marks=pytest.mark.sphinx('latex', freshenv=True)) + for engine, doc_class in product(LATEX_ENGINES, DOCCLASSES) + ], ) -@pytest.mark.sphinx('latex') -def test_build_latex_doc(app, status, warning, engine, docclass, python_maximum_signature_line_length): - app.config.python_maximum_signature_line_length = python_maximum_signature_line_length +def test_build_latex_doc(app, status, warning, engine, docclass): app.config.intersphinx_mapping = { 'sphinx': ('https://www.sphinx-doc.org/en/master/', None), } @@ -113,7 +123,6 @@ def test_build_latex_doc(app, status, warning, engine, docclass, python_maximum_ normalize_intersphinx_mapping(app, app.config) load_mappings(app) app.builder.init() - LaTeXTranslator.ignore_missing_images = True app.builder.build_all() From 35424eaaced012bd0e18dbd37590623abea0e330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Fri, 21 Apr 2023 21:29:31 +0200 Subject: [PATCH 096/103] Document the added \sphinxparam, \sphinxparamcomma... --- doc/latex.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/latex.rst b/doc/latex.rst index 596e7a18c05..6c7a4a95771 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -1477,7 +1477,12 @@ Macros ``\sphinxkeyboard`` .. versionadded:: 6.2.0 - ``\sphinxparam``, ``\sphinxsamedocref`` + + ``\sphinxparam``, ``\sphinxsamedocref``. There is also + ``\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: From db913279bdf27252f6d81cd46280c49ad93c3ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Fri, 21 Apr 2023 21:30:25 +0200 Subject: [PATCH 097/103] Add a specific latex unit test I hesitated: - whether to reuse as here a test source which was prepared for Python domain and used there already, - in place of this, rather add a check of the sphinxtests.tex contents as output from the test_build_latex_doc for pdflatex engine. --- tests/test_build_latex.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 16465218892..fde5b15cdae 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -1750,3 +1750,16 @@ def count_label(name): # ensure that we did not forget any label to check # and if so, report them nicely in case of failure assert sorted(tested_labels) == sorted(output_labels) + + +@pytest.mark.sphinx('latex', testroot='domain-py-python_maximum_signature_line_length', + confoverrides={'python_maximum_signature_line_length': 23}) +def test_one_parameter_per_line(app, status, warning): + app.builder.build_all() + result = (app.outdir / 'python.tex').read_text(encoding='utf8') + + # TODO: should these asserts check presence or absence of a final \sphinxparamcomma? + # signature of 23 characters is too short to trigger one-param-per-line mark-up + assert ('\\pysiglinewithargsret{\\sphinxbfcode{\\sphinxupquote{hello}}}' in result) + + assert ('\\pysigwithonelineperarg{\\sphinxbfcode{\\sphinxupquote{foo}}}' in result) From 1a4455341ff139fa583581a3c0992ace64a848e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Fri, 21 Apr 2023 21:46:21 +0200 Subject: [PATCH 098/103] Remove blank line in latex.rst --- doc/latex.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/latex.rst b/doc/latex.rst index 6c7a4a95771..9ba0313c8db 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -1477,7 +1477,6 @@ Macros ``\sphinxkeyboard`` .. versionadded:: 6.2.0 - ``\sphinxparam``, ``\sphinxsamedocref``. There is also ``\sphinxparamcomma`` which defaults to a comma followed by a space and ``\sphinxparamcommaoneperline`` which is used for one-parameter-per-line From 0c4fdb8cf49f36cf62b3b5dd48933eb87b46e22a Mon Sep 17 00:00:00 2001 From: TLouf Date: Sat, 22 Apr 2023 12:18:50 +0200 Subject: [PATCH 099/103] Simplify parametrize of latex build test --- tests/test_build_latex.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index fde5b15cdae..f0c73db6cb4 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -3,7 +3,7 @@ import os import re import subprocess -from itertools import product +from itertools import chain, product from pathlib import Path from shutil import copyfile from subprocess import CalledProcessError @@ -95,24 +95,18 @@ def skip_if_stylefiles_notfound(testfunc): @skip_if_requested @skip_if_stylefiles_notfound @pytest.mark.parametrize( - "engine,docclass", + "engine,docclass,python_maximum_signature_line_length", # Only running test with `python_maximum_signature_line_length` not None with last # LaTeX engine to reduce testing time, as if this configuration does not fail with # one engine, it's almost impossible it would fail with another. - [ - pytest.param( - engine, - doc_class, - marks=pytest.mark.sphinx( - 'latex', freshenv=True, confoverrides={'python_maximum_signature_line_length': 1}, - ), - ) - if engine == LATEX_ENGINES[0] - else pytest.param(engine, doc_class, marks=pytest.mark.sphinx('latex', freshenv=True)) - for engine, doc_class in product(LATEX_ENGINES, DOCCLASSES) - ], + chain( + product(LATEX_ENGINES[:-1], DOCCLASSES, [None]), + product([LATEX_ENGINES[-1]], DOCCLASSES, [1]), + ), ) -def test_build_latex_doc(app, status, warning, engine, docclass): +@pytest.mark.sphinx('latex', freshenv=True) +def test_build_latex_doc(app, status, warning, engine, docclass, python_maximum_signature_line_length): + app.config.python_maximum_signature_line_length = python_maximum_signature_line_length app.config.intersphinx_mapping = { 'sphinx': ('https://www.sphinx-doc.org/en/master/', None), } From 36183f5bfb965d03f8232ca462a6995eca8f6ec6 Mon Sep 17 00:00:00 2001 From: TLouf Date: Sat, 22 Apr 2023 12:35:26 +0200 Subject: [PATCH 100/103] Add tests for text output --- tests/test_domain_c.py | 16 +++++++++++++++- tests/test_domain_cpp.py | 16 +++++++++++++++- tests/test_domain_js.py | 39 ++++++++++++++++++++++++++++++++++++++- tests/test_domain_py.py | 39 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 106 insertions(+), 4 deletions(-) diff --git a/tests/test_domain_c.py b/tests/test_domain_c.py index 109b0a65fe2..a6316484533 100644 --- a/tests/test_domain_c.py +++ b/tests/test_domain_c.py @@ -30,6 +30,7 @@ from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping from sphinx.testing import restructuredtext from sphinx.testing.util import assert_node +from sphinx.writers.text import STDINDENT class Config: @@ -1041,7 +1042,7 @@ def test_c_maximum_signature_line_length_overrides_global(app): @pytest.mark.sphinx('html', testroot='domain-c-c_maximum_signature_line_length') -def test_domain_c_c_maximum_signature_line_length(app, status, warning): +def test_domain_c_c_maximum_signature_line_length_in_html(app, status, warning): app.build() content = (app.outdir / 'index.html').read_text(encoding='utf-8') expected = """\ @@ -1060,3 +1061,16 @@ def test_domain_c_c_maximum_signature_line_length(app, status, warning): """ assert expected in content + + +@pytest.mark.sphinx( + 'text', testroot='domain-c-c_maximum_signature_line_length', +) +def test_domain_c_c_maximum_signature_line_length_in_text(app, status, warning): + app.build() + content = (app.outdir / 'index.txt').read_text(encoding='utf8') + param_line_fmt = STDINDENT * " " + "{}\n" + + expected_parameter_list_hello = "(\n{})".format(param_line_fmt.format("str name,")) + + assert expected_parameter_list_hello in content diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 5dea971b483..49b0c22e9ba 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -31,6 +31,7 @@ from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping from sphinx.testing import restructuredtext from sphinx.testing.util import assert_node +from sphinx.writers.text import STDINDENT def parse(name, string): @@ -1707,7 +1708,7 @@ def test_cpp_maximum_signature_line_length_overrides_global(app): @pytest.mark.sphinx('html', testroot='domain-cpp-cpp_maximum_signature_line_length') -def test_domain_cpp_cpp_maximum_signature_line_length(app, status, warning): +def test_domain_cpp_cpp_maximum_signature_line_length_in_html(app, status, warning): app.build() content = (app.outdir / 'index.html').read_text(encoding='utf-8') expected = """\ @@ -1724,3 +1725,16 @@ def test_domain_cpp_cpp_maximum_signature_line_length(app, status, warning): \ """.format(expected_a, expected_b, expected_c, expected_d, expected_e, expected_f) assert expected_parameter_list_foo in content + + +@pytest.mark.sphinx( + 'text', testroot='domain-js-javascript_maximum_signature_line_length', +) +def test_domain_js_javascript_maximum_signature_line_length_in_text(app, status, warning): + app.build() + content = (app.outdir / 'index.txt').read_text(encoding='utf8') + param_line_fmt = STDINDENT * " " + "{}\n" + + expected_parameter_list_hello = "(\n{})".format(param_line_fmt.format("name,")) + + assert expected_parameter_list_hello in content + + expected_a = param_line_fmt.format("[a,[") + assert expected_a in content + + expected_b = param_line_fmt.format("b,]]") + assert expected_b in content + + expected_c = param_line_fmt.format("c,") + assert expected_c in content + + expected_d = param_line_fmt.format("d[,") + assert expected_d in content + + expected_e = param_line_fmt.format("e,") + assert expected_e in content + + expected_f = param_line_fmt.format("f,]") + assert expected_f in content + + expected_parameter_list_foo = "(\n{}{}{}{}{}{})".format( + expected_a, expected_b, expected_c, expected_d, expected_e, expected_f, + ) + assert expected_parameter_list_foo in content diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index bd2cea98a3c..2b84f01c00d 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -38,6 +38,7 @@ ) from sphinx.testing import restructuredtext from sphinx.testing.util import assert_node +from sphinx.writers.text import STDINDENT def parse(sig): @@ -1660,7 +1661,7 @@ def test_python_maximum_signature_line_length_overrides_global(app): @pytest.mark.sphinx( 'html', testroot='domain-py-python_maximum_signature_line_length', ) -def test_domain_py_python_maximum_signature_line_length(app, status, warning): +def test_domain_py_python_maximum_signature_line_length_in_html(app, status, warning): app.build() content = (app.outdir / 'index.html').read_text(encoding='utf8') expected_parameter_list_hello = """\ @@ -1726,6 +1727,42 @@ def test_domain_py_python_maximum_signature_line_length(app, status, warning): assert expected_parameter_list_foo in content +@pytest.mark.sphinx( + 'text', testroot='domain-py-python_maximum_signature_line_length', +) +def test_domain_py_python_maximum_signature_line_length_in_text(app, status, warning): + app.build() + content = (app.outdir / 'index.txt').read_text(encoding='utf8') + param_line_fmt = STDINDENT * " " + "{}\n" + + expected_parameter_list_hello = "(\n{}) -> str".format(param_line_fmt.format("name: str,")) + + assert expected_parameter_list_hello in content + + expected_a = param_line_fmt.format("[a,[") + assert expected_a in content + + expected_b = param_line_fmt.format("b,]]") + assert expected_b in content + + expected_c = param_line_fmt.format("c,") + assert expected_c in content + + expected_d = param_line_fmt.format("d[,") + assert expected_d in content + + expected_e = param_line_fmt.format("e,") + assert expected_e in content + + expected_f = param_line_fmt.format("f,]") + assert expected_f in content + + expected_parameter_list_foo = "(\n{}{}{}{}{}{})".format( + expected_a, expected_b, expected_c, expected_d, expected_e, expected_f, + ) + assert expected_parameter_list_foo in content + + def test_module_content_line_number(app): text = (".. py:module:: foo\n" + "\n" + From f2d1574afaf02ad7faab36cd427f1445a4577a5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Mon, 24 Apr 2023 14:17:09 +0200 Subject: [PATCH 101/103] Bump versionadded in doc/latex.rst to match expected release number --- doc/latex.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/latex.rst b/doc/latex.rst index 9ba0313c8db..a13b92b5c3a 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -1477,7 +1477,9 @@ Macros ``\sphinxkeyboard`` .. versionadded:: 6.2.0 - ``\sphinxparam``, ``\sphinxsamedocref``. There is also + ``\sphinxparam``, ``\sphinxsamedocref`` + + .. versionadded:: 6.3.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 From 370cd0e8d89d1434e95b6b797ba9e85be66fd8bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20B?= <2589111+jfbu@users.noreply.github.com> Date: Sun, 30 Apr 2023 09:44:25 +0200 Subject: [PATCH 102/103] Bump again versionadded in doc/latex.rst to match expected release --- doc/latex.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/latex.rst b/doc/latex.rst index a13b92b5c3a..3f94ae7910a 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -1479,7 +1479,7 @@ Macros .. versionadded:: 6.2.0 ``\sphinxparam``, ``\sphinxsamedocref`` - .. versionadded:: 6.3.0 + .. 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 From 585b2b9088c64dfb9a604bcb225c31c8eedd171e Mon Sep 17 00:00:00 2001 From: Adam Turner <9087854+aa-turner@users.noreply.github.com> Date: Thu, 11 May 2023 13:57:08 +0100 Subject: [PATCH 103/103] misc updates (inc version bump) --- CHANGES | 2 +- doc/usage/configuration.rst | 10 +++++----- doc/usage/restructuredtext/domains.rst | 20 ++++++++++---------- sphinx/domains/javascript.py | 2 +- sphinx/writers/html5.py | 12 ++++++------ 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/CHANGES b/CHANGES index c59014d42e1..07bc8157ef1 100644 --- a/CHANGES +++ b/CHANGES @@ -32,7 +32,7 @@ Features added 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 B. + Patch by Thomas Louf, Adam Turner, and Jean-François Burnol. Bugs fixed ---------- diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index 3bbc75d88ba..1fc4c674b4c 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -692,7 +692,7 @@ General configuration object directive, such as seen in the C, C++, and Python domains (e.g. :rst:dir:`py:function:single-line-parameter-list`). - .. versionadded:: 6.2 + .. versionadded:: 7.1 .. confval:: add_function_parentheses @@ -2937,7 +2937,7 @@ Options for the C domain parameter will be displayed on an individual logical line. This is a domain-specific setting, overriding :confval:`maximum_signature_line_length`. - .. versionadded:: 6.2 + .. versionadded:: 7.1 .. _cpp-config: @@ -2975,7 +2975,7 @@ Options for the C++ domain parameter will be displayed on an individual logical line. This is a domain-specific setting, overriding :confval:`maximum_signature_line_length`. - .. versionadded:: 6.2 + .. versionadded:: 7.1 Options for the Python domain ----------------------------- @@ -3025,7 +3025,7 @@ Options for the Python domain argument will be displayed on an individual logical line. This is a domain-specific setting, overriding :confval:`maximum_signature_line_length`. - .. versionadded:: 6.2 + .. versionadded:: 7.1 Options for the Javascript domain --------------------------------- @@ -3036,7 +3036,7 @@ Options for the Javascript domain parameter will be displayed on an individual logical line. This is a domain-specific setting, overriding :confval:`maximum_signature_line_length`. - .. versionadded:: 6.2 + .. versionadded:: 7.1 Example of configuration file ----------------------------- diff --git a/doc/usage/restructuredtext/domains.rst b/doc/usage/restructuredtext/domains.rst index 74da2a87807..cbece86e826 100644 --- a/doc/usage/restructuredtext/domains.rst +++ b/doc/usage/restructuredtext/domains.rst @@ -238,7 +238,7 @@ The following directives are provided for module and class contents: line, overriding :confval:`python_maximum_signature_line_length` and :confval:`maximum_signature_line_length`. - .. versionadded:: 6.2 + .. versionadded:: 7.1 .. rst:directive:: .. py:data:: name @@ -346,7 +346,7 @@ The following directives are provided for module and class contents: logical line, overriding :confval:`python_maximum_signature_line_length` and :confval:`maximum_signature_line_length`. - .. versionadded:: 6.2 + .. versionadded:: 7.1 .. rst:directive:: .. py:attribute:: name @@ -467,7 +467,7 @@ The following directives are provided for module and class contents: line, overriding :confval:`python_maximum_signature_line_length` and :confval:`maximum_signature_line_length`. - .. versionadded:: 6.2 + .. versionadded:: 7.1 .. rst:directive:option:: staticmethod :type: no value @@ -529,7 +529,7 @@ The following directives are provided for module and class contents: line, overriding :confval:`python_maximum_signature_line_length` and :confval:`maximum_signature_line_length`. - .. versionadded:: 6.2 + .. versionadded:: 7.1 .. rst:directive:: .. py:decoratormethod:: name .. py:decoratormethod:: name(signature) @@ -807,7 +807,7 @@ The C domain (name **c**) is suited for documentation of C API. line, overriding :confval:`c_maximum_signature_line_length` and :confval:`maximum_signature_line_length`. - .. versionadded:: 6.2 + .. versionadded:: 7.1 .. rst:directive:: .. c:macro:: name @@ -829,7 +829,7 @@ The C domain (name **c**) is suited for documentation of C API. line, overriding :confval:`c_maximum_signature_line_length` and :confval:`maximum_signature_line_length`. - .. versionadded:: 6.2 + .. versionadded:: 7.1 .. rst:directive:: .. c:struct:: name @@ -1188,7 +1188,7 @@ visibility statement (``public``, ``private`` or ``protected``). line, overriding :confval:`cpp_maximum_signature_line_length` and :confval:`maximum_signature_line_length`. - .. versionadded:: 6.2 + .. versionadded:: 7.1 .. rst:directive:: .. cpp:member:: (member) variable declaration .. cpp:var:: (member) variable declaration @@ -1979,7 +1979,7 @@ The JavaScript domain (name **js**) provides the following directives: line, overriding :confval:`javascript_maximum_signature_line_length` and :confval:`maximum_signature_line_length`. - .. versionadded:: 6.2 + .. versionadded:: 7.1 .. rst:directive:: .. js:method:: name(signature) @@ -1995,7 +1995,7 @@ The JavaScript domain (name **js**) provides the following directives: line, overriding :confval:`javascript_maximum_signature_line_length` and :confval:`maximum_signature_line_length`. - .. versionadded:: 6.2 + .. versionadded:: 7.1 .. rst:directive:: .. js:class:: name @@ -2022,7 +2022,7 @@ The JavaScript domain (name **js**) provides the following directives: line, overriding :confval:`javascript_maximum_signature_line_length` and :confval:`maximum_signature_line_length`. - .. versionadded:: 6.2 + .. versionadded:: 7.1 .. rst:directive:: .. js:data:: name diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py index 787d59907fc..c6baab8a9ee 100644 --- a/sphinx/domains/javascript.py +++ b/sphinx/domains/javascript.py @@ -487,7 +487,7 @@ def setup(app: Sphinx) -> dict[str, Any]: ) return { 'version': 'builtin', - 'env_version': 2, + 'env_version': 3, 'parallel_read_safe': True, 'parallel_write_safe': True, } diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index fedb2ba918b..e7d932286c5 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -230,8 +230,8 @@ def visit_desc_optional(self, node: Element) -> None: self.body.append(self.param_separator) self.body.append('[') self.body.append('\n') - # Else, open a new bracket, append the parameter separator, and end the - # line. + # Else, open a new bracket, append the parameter separator, + # and end the line. else: self.body.append('[') self.body.append(self.param_separator) @@ -242,13 +242,13 @@ def visit_desc_optional(self, node: Element) -> None: def depart_desc_optional(self, node: Element) -> None: self.optional_param_level -= 1 if self.multi_line_parameter_list: - # If it's the first time we go down one level, add the separator before the - # bracket. + # If it's the first time we go down one level, add the separator + # before the bracket. if self.optional_param_level == self.max_optional_param_level - 1: self.body.append(self.param_separator) self.body.append(']') - # End the line if we have just closed the last bracket of this group of - # optional parameters. + # End the line if we have just closed the last bracket of this + # optional parameter group. if self.optional_param_level == 0: self.body.append('\n') else: