From 02016c0ef25f6a521aae789727487e0bbc71014a Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 3 Apr 2021 23:18:06 +0900 Subject: [PATCH 01/29] Close #8829: doc: Update description of paralle-read-safe --- doc/extdev/index.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/extdev/index.rst b/doc/extdev/index.rst index ad04951f3..d9c04f413 100644 --- a/doc/extdev/index.rst +++ b/doc/extdev/index.rst @@ -171,10 +171,26 @@ as metadata of the extension. Metadata keys currently recognized are: source files can be used when the extension is loaded. It defaults to ``False``, i.e. you have to explicitly specify your extension to be parallel-read-safe after checking that it is. + + .. note:: The *parallel-read-safe* extension must satisfy the following + conditions: + + * The core logic of the extension is parallely executable during + the reading phase. + * It has event handlers for :event:`env-merge-info` and + :event:`env-purge-doc` events if it stores dataa to the build + environment object (env) during the reading phase. + * ``'parallel_write_safe'``: a boolean that specifies if parallel writing of output files can be used when the extension is loaded. Since extensions usually don't negatively influence the process, this defaults to ``True``. + .. note:: The *parallel-write-safe* extension must satisfy the following + conditions: + + * The core logic of the extension is parallely executable during + the writing phase. + APIs used for writing extensions -------------------------------- From d09747f225e4960636a126b98b0d4cb1c79bf288 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 11 Apr 2021 21:35:47 +0900 Subject: [PATCH 02/29] doc: Add docs for napoleon_preprocess_types --- doc/usage/extensions/napoleon.rst | 13 ++++++++++++- sphinx/ext/napoleon/__init__.py | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/doc/usage/extensions/napoleon.rst b/doc/usage/extensions/napoleon.rst index 066c56e2d..bd2bcad3f 100644 --- a/doc/usage/extensions/napoleon.rst +++ b/doc/usage/extensions/napoleon.rst @@ -301,6 +301,7 @@ sure that "sphinx.ext.napoleon" is enabled in `conf.py`:: napoleon_use_ivar = False napoleon_use_param = True napoleon_use_rtype = True + napoleon_preprocess_types = False napoleon_type_aliases = None napoleon_attr_annotations = True @@ -510,6 +511,16 @@ sure that "sphinx.ext.napoleon" is enabled in `conf.py`:: :returns: *bool* -- True if successful, False otherwise +.. confval:: napoleon_preprocess_types + + True to convert the type definitions in the docstrings as references. + Defaults to *True*. + + .. versionadded:: 3.2.1 + .. versionchanged:: 3.5 + + Do preprocess the Google style docstrings also. + .. confval:: napoleon_type_aliases A mapping to translate type names to other names or references. Works @@ -570,4 +581,4 @@ sure that "sphinx.ext.napoleon" is enabled in `conf.py`:: .. versionadded:: 1.8 .. versionchanged:: 3.5 - Support ``params_style`` and ``returns_style`` \ No newline at end of file + Support ``params_style`` and ``returns_style`` diff --git a/sphinx/ext/napoleon/__init__.py b/sphinx/ext/napoleon/__init__.py index ba8f62a18..8e513a9d0 100644 --- a/sphinx/ext/napoleon/__init__.py +++ b/sphinx/ext/napoleon/__init__.py @@ -240,7 +240,7 @@ class Config: :returns: *bool* -- True if successful, False otherwise napoleon_preprocess_types : :obj:`bool` (Defaults to False) - Enable the type preprocessor for numpy style docstrings. + Enable the type preprocessor. napoleon_type_aliases : :obj:`dict` (Defaults to None) Add a mapping of strings to string, translating types in numpy From d59e96c8b4df3672e9213253b30724eb3157dfdf Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 11 Apr 2021 22:25:48 +0900 Subject: [PATCH 03/29] docs: Add versionchanged tag to extlinks (refs: #8898) --- doc/usage/extensions/extlinks.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/usage/extensions/extlinks.rst b/doc/usage/extensions/extlinks.rst index 221b79d99..c345a7c82 100644 --- a/doc/usage/extensions/extlinks.rst +++ b/doc/usage/extensions/extlinks.rst @@ -51,6 +51,10 @@ The extension adds a config value: that generate links, i.e. ``:issue:`this issue <123>```. In this case, the *caption* is not relevant. + .. versionchanged:: 4.0 + + Support to substitute by '%s' in the caption. + .. note:: Since links are generated from the role in the reading stage, they appear as From 14f7d243bd9509d0dab9cace0d9a18fe34b58b92 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Tue, 16 Mar 2021 20:02:26 +0100 Subject: [PATCH 04/29] Decl styling, more nodes and C++ conversion --- sphinx/addnodes.py | 67 ++- sphinx/domains/cpp.py | 495 +++++++++++------- sphinx/themes/basic/static/basic.css_t | 42 +- sphinx/transforms/post_transforms/__init__.py | 6 +- tests/test_domain_cpp.py | 4 + tests/test_ext_intersphinx.py | 4 +- 6 files changed, 381 insertions(+), 237 deletions(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 5645ac91b..3200e5e47 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -154,6 +154,10 @@ class desc_signature_line(nodes.Part, nodes.Inline, nodes.FixedTextElement): # nodes to use within a desc_signature or desc_signature_line +class desc_name(nodes.Part, nodes.Inline, nodes.FixedTextElement): + """Node for the main object name.""" + + class desc_addname(nodes.Part, nodes.Inline, nodes.FixedTextElement): """Node for additional name parts (module name, class name).""" @@ -168,14 +172,11 @@ class desc_type(nodes.Part, nodes.Inline, nodes.FixedTextElement): class desc_returns(desc_type): """Node for a "returns" annotation (a la -> in Python).""" + def astext(self) -> str: return ' -> ' + super().astext() -class desc_name(nodes.Part, nodes.Inline, nodes.FixedTextElement): - """Node for the main object name.""" - - class desc_parameterlist(nodes.Part, nodes.Inline, nodes.FixedTextElement): """Node for a general parameter list.""" child_text_separator = ', ' @@ -207,6 +208,10 @@ class desc_content(nodes.General, nodes.Element): """ +# Signature text elements, generally translated to node.inline +# in SigElementFallbackTransform. +# When adding a new one, add it to SIG_ELEMENTS. + class desc_sig_element(nodes.inline): """Common parent class of nodes for inline text of a signature.""" classes: List[str] = [] @@ -217,8 +222,19 @@ class desc_sig_element(nodes.inline): self['classes'].extend(self.classes) +# to not reinvent the wheel, the classes in the following desc_sig classes +# are based on those used in Pygments + +class desc_sig_space(desc_sig_element): + """Node for a space in a signature.""" + classes = ["w"] + + def __init__(self) -> None: + super().__init__(' ', ' ') + + class desc_sig_name(desc_sig_element): - """Node for a name in a signature.""" + """Node for an identifier in a signature.""" classes = ["n"] @@ -228,10 +244,43 @@ class desc_sig_operator(desc_sig_element): class desc_sig_punctuation(desc_sig_element): - """Node for a punctuation in a signature.""" + """Node for punctuation in a signature.""" classes = ["p"] +class desc_sig_keyword(desc_sig_element): + """Node for a general keyword in a signature.""" + classes = ["k"] + + +class desc_sig_keyword_type(desc_sig_element): + """Node for a keyword which is a built-in type in a signature.""" + classes = ["kt"] + + +class desc_sig_literal_number(desc_sig_element): + """Node for a numeric literal in a signature.""" + classes = ["m"] + + +class desc_sig_literal_string(desc_sig_element): + """Node for a string literal in a signature.""" + classes = ["s"] + + +class desc_sig_literal_char(desc_sig_element): + """Node for a character literal in a signature.""" + classes = ["sc"] + + +SIG_ELEMENTS = [desc_sig_space, + desc_sig_name, + desc_sig_operator, + desc_sig_punctuation, + desc_sig_keyword, desc_sig_keyword_type, + desc_sig_literal_number, desc_sig_literal_string, desc_sig_literal_char] + + # new admonition-like constructs class versionmodified(nodes.Admonition, nodes.TextElement): @@ -336,6 +385,7 @@ class pending_xref(nodes.Inline, nodes.Element): These nodes are resolved before writing output, in BuildEnvironment.resolve_references. """ + child_text_separator = '' class pending_xref_condition(nodes.Inline, nodes.TextElement): @@ -424,9 +474,8 @@ def setup(app: "Sphinx") -> Dict[str, Any]: app.add_node(desc_optional) app.add_node(desc_annotation) app.add_node(desc_content) - app.add_node(desc_sig_name) - app.add_node(desc_sig_operator) - app.add_node(desc_sig_punctuation) + for n in SIG_ELEMENTS: + app.add_node(n) app.add_node(versionmodified) app.add_node(seealso) app.add_node(productionlist) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index abe961791..bb11138d6 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -604,6 +604,10 @@ class ASTIdentifier(ASTBase): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", prefix: str, templateArgs: str, symbol: "Symbol") -> None: verify_description_mode(mode) + if self.is_anon(): + node = addnodes.desc_sig_name(text="[anonymous]") + else: + node = addnodes.desc_sig_name(self.identifier, self.identifier) if mode == 'markType': targetText = prefix + self.identifier + templateArgs pnode = addnodes.pending_xref('', refdomain='cpp', @@ -611,21 +615,17 @@ class ASTIdentifier(ASTBase): reftarget=targetText, modname=None, classname=None) pnode['cpp:parent_key'] = symbol.get_lookup_key() - if self.is_anon(): - pnode += nodes.strong(text="[anonymous]") - else: - pnode += nodes.Text(self.identifier) + pnode += node signode += pnode elif mode == 'lastIsName': - if self.is_anon(): - signode += nodes.strong(text="[anonymous]") - else: - signode += addnodes.desc_name(self.identifier, self.identifier) + nameNode = addnodes.desc_name() + nameNode += node + signode += nameNode elif mode == 'noneIsName': - if self.is_anon(): - signode += nodes.strong(text="[anonymous]") - else: - signode += nodes.Text(self.identifier) + signode += node + elif mode == 'param': + node['classes'].append('sig-param') + signode += node elif mode == 'udl': # the target is 'operator""id' instead of just 'id' assert len(prefix) == 0 @@ -637,7 +637,7 @@ class ASTIdentifier(ASTBase): reftarget=targetText, modname=None, classname=None) pnode['cpp:parent_key'] = symbol.get_lookup_key() - pnode += nodes.Text(self.identifier) + pnode += node signode += pnode else: raise Exception('Unknown description mode: %s' % mode) @@ -669,7 +669,7 @@ class ASTNestedNameElement(ASTBase): tArgs = str(self.templateArgs) if self.templateArgs is not None else '' self.identOrOp.describe_signature(signode, mode, env, prefix, tArgs, symbol) if self.templateArgs is not None: - self.templateArgs.describe_signature(signode, mode, env, symbol) + self.templateArgs.describe_signature(signode, 'markType', env, symbol) class ASTNestedName(ASTBase): @@ -730,12 +730,15 @@ class ASTNestedName(ASTBase): # just print the name part, with template args, not template params if mode == 'noneIsName': if self.rooted: + assert False, "Can this happen?" # TODO signode += nodes.Text('::') for i in range(len(self.names)): if i != 0: - signode += nodes.Text('::') + assert False, "Can this happen?" # TODO + signode += nodes.Text('::blah') n = self.names[i] if self.templates[i]: + assert False, "Can this happen?" # TODO signode += nodes.Text("template") signode += nodes.Text(" ") n.describe_signature(signode, mode, env, '', symbol) @@ -743,9 +746,7 @@ class ASTNestedName(ASTBase): assert not self.rooted, str(self) assert len(self.names) == 1 assert not self.templates[0] - node = nodes.emphasis() - self.names[0].describe_signature(node, 'noneIsName', env, '', symbol) - signode += node + self.names[0].describe_signature(signode, 'param', env, '', symbol) elif mode == 'markType' or mode == 'lastIsName' or mode == 'markName': # Each element should be a pending xref targeting the complete # prefix. however, only the identifier part should be a link, such @@ -771,17 +772,18 @@ class ASTNestedName(ASTBase): if self.rooted: prefix += '::' if mode == 'lastIsName' and len(names) == 0: - signode += nodes.Text('::') + signode += addnodes.desc_sig_punctuation('::', '::') else: - dest += nodes.Text('::') + dest += addnodes.desc_sig_punctuation('::', '::') for i in range(len(names)): nne = names[i] template = self.templates[i] if not first: - dest += nodes.Text('::') + dest += addnodes.desc_sig_punctuation('::', '::') prefix += '::' if template: - dest += nodes.Text("template ") + dest += addnodes.desc_sig_keyword('template', 'template') + dest += addnodes.desc_sig_space() first = False txt_nne = str(nne) if txt_nne != '': @@ -793,10 +795,11 @@ class ASTNestedName(ASTBase): prefix += txt_nne if mode == 'lastIsName': if len(self.names) > 1: - dest += addnodes.desc_addname('::', '::') + dest += addnodes.desc_sig_punctuation('::', '::') signode += dest if self.templates[-1]: - signode += nodes.Text("template ") + signode += addnodes.desc_sig_keyword('template', 'template') + signode += addnodes.desc_sig_space() self.names[-1].describe_signature(signode, mode, env, '', symbol) else: raise Exception('Unknown description mode: %s' % mode) @@ -831,7 +834,7 @@ class ASTPointerLiteral(ASTLiteral): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('nullptr')) + signode += addnodes.desc_sig_keyword('nullptr', 'nullptr') class ASTBooleanLiteral(ASTLiteral): @@ -852,7 +855,7 @@ class ASTBooleanLiteral(ASTLiteral): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text(str(self))) + signode += addnodes.desc_sig_keyword(str(self), str(self)) class ASTNumberLiteral(ASTLiteral): @@ -868,8 +871,7 @@ class ASTNumberLiteral(ASTLiteral): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - txt = str(self) - signode.append(nodes.Text(txt, txt)) + signode += addnodes.desc_sig_literal_number(self.data, self.data) class ASTStringLiteral(ASTLiteral): @@ -885,8 +887,7 @@ class ASTStringLiteral(ASTLiteral): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - txt = str(self) - signode.append(nodes.Text(txt, txt)) + signode += addnodes.desc_sig_literal_string(self.data, self.data) class ASTCharLiteral(ASTLiteral): @@ -913,8 +914,10 @@ class ASTCharLiteral(ASTLiteral): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - txt = str(self) - signode.append(nodes.Text(txt, txt)) + if self.prefix is not None: + signode += addnodes.desc_sig_keyword(self.prefix, self.prefix) + txt = "'" + self.data + "'" + signode += addnodes.desc_sig_literal_char(txt, txt) class ASTUserDefinedLiteral(ASTLiteral): @@ -946,7 +949,7 @@ class ASTThisLiteral(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text("this")) + signode += addnodes.desc_sig_keyword('this', 'this') class ASTFoldExpr(ASTExpression): @@ -996,19 +999,19 @@ class ASTFoldExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('(')) + signode += addnodes.desc_sig_punctuation('(', '(') if self.leftExpr: self.leftExpr.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(' ')) - signode.append(nodes.Text(self.op)) - signode.append(nodes.Text(' ')) - signode.append(nodes.Text('...')) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_operator(self.op, self.op) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation('...', '...') if self.rightExpr: - signode.append(nodes.Text(' ')) - signode.append(nodes.Text(self.op)) - signode.append(nodes.Text(' ')) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_operator(self.op, self.op) + signode += addnodes.desc_sig_space() self.rightExpr.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) + signode += addnodes.desc_sig_punctuation(')', ')') class ASTParenExpr(ASTExpression): @@ -1023,9 +1026,9 @@ class ASTParenExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('(', '(')) + signode += addnodes.desc_sig_punctuation('(', '(') self.expr.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')', ')')) + signode += addnodes.desc_sig_punctuation(')', ')') class ASTIdExpression(ASTExpression): @@ -1068,9 +1071,9 @@ class ASTPostfixArray(ASTPostfixOp): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('[')) + signode += addnodes.desc_sig_punctuation('[', '[') self.expr.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(']')) + signode += addnodes.desc_sig_punctuation(']', ']') class ASTPostfixMember(ASTPostfixOp): @@ -1085,7 +1088,7 @@ class ASTPostfixMember(ASTPostfixOp): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('.')) + signode += addnodes.desc_sig_punctuation('.', '.') self.name.describe_signature(signode, 'noneIsName', env, symbol) @@ -1101,7 +1104,7 @@ class ASTPostfixMemberOfPointer(ASTPostfixOp): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('->')) + signode += addnodes.desc_sig_operator('->', '->') self.name.describe_signature(signode, 'noneIsName', env, symbol) @@ -1114,7 +1117,7 @@ class ASTPostfixInc(ASTPostfixOp): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('++')) + signode += addnodes.desc_sig_operator('++', '++') class ASTPostfixDec(ASTPostfixOp): @@ -1126,7 +1129,7 @@ class ASTPostfixDec(ASTPostfixOp): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('--')) + signode += addnodes.desc_sig_operator('--', '--') class ASTPostfixCallExpr(ASTPostfixOp): @@ -1195,13 +1198,13 @@ class ASTExplicitCast(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text(self.cast)) - signode.append(nodes.Text('<')) + signode += addnodes.desc_sig_keyword(self.cast, self.cast) + signode += addnodes.desc_sig_punctuation('<', '<') self.typ.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text('>')) - signode.append(nodes.Text('(')) + signode += addnodes.desc_sig_punctuation('>', '>') + signode += addnodes.desc_sig_punctuation('(', '(') self.expr.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) + signode += addnodes.desc_sig_punctuation(')', ')') class ASTTypeId(ASTExpression): @@ -1218,10 +1221,10 @@ class ASTTypeId(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('typeid')) - signode.append(nodes.Text('(')) + signode += addnodes.desc_sig_keyword('typeid', 'typeid') + signode += addnodes.desc_sig_punctuation('(', '(') self.typeOrExpr.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) + signode += addnodes.desc_sig_punctuation(')', ')') # Unary expressions @@ -1243,9 +1246,9 @@ class ASTUnaryOpExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text(self.op)) + signode += addnodes.desc_sig_operator(self.op, self.op) if self.op[0] in 'cn': - signode.append(nodes.Text(' ')) + signode += addnodes.desc_sig_space() self.expr.describe_signature(signode, mode, env, symbol) @@ -1261,10 +1264,12 @@ class ASTSizeofParamPack(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('sizeof...(')) + signode += addnodes.desc_sig_keyword('sizeof', 'sizeof') + signode += addnodes.desc_sig_punctuation('...', '...') + signode += addnodes.desc_sig_punctuation('(', '(') self.identifier.describe_signature(signode, 'markType', env, symbol=symbol, prefix="", templateArgs="") - signode.append(nodes.Text(')')) + signode += addnodes.desc_sig_punctuation(')', ')') class ASTSizeofType(ASTExpression): @@ -1279,9 +1284,10 @@ class ASTSizeofType(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('sizeof(')) + signode += addnodes.desc_sig_keyword('sizeof', 'sizeof') + signode += addnodes.desc_sig_punctuation('(', '(') self.typ.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) + signode += addnodes.desc_sig_punctuation(')', ')') class ASTSizeofExpr(ASTExpression): @@ -1296,7 +1302,8 @@ class ASTSizeofExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('sizeof ')) + signode += addnodes.desc_sig_keyword('sizeof', 'sizeof') + signode += addnodes.desc_sig_space() self.expr.describe_signature(signode, mode, env, symbol) @@ -1312,9 +1319,10 @@ class ASTAlignofExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('alignof(')) + signode += addnodes.desc_sig_keyword('alignof', 'alignof') + signode += addnodes.desc_sig_punctuation('(', '(') self.typ.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) + signode += addnodes.desc_sig_punctuation(')', ')') class ASTNoexceptExpr(ASTExpression): @@ -1329,9 +1337,10 @@ class ASTNoexceptExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('noexcept(')) + signode += addnodes.desc_sig_keyword('noexcept', 'noexcept') + signode += addnodes.desc_sig_punctuation('(', '(') self.expr.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) + signode += addnodes.desc_sig_punctuation(')', ')') class ASTNewExpr(ASTExpression): @@ -1371,8 +1380,9 @@ class ASTNewExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: if self.rooted: - signode.append(nodes.Text('::')) - signode.append(nodes.Text('new ')) + signode += addnodes.desc_sig_punctuation('::', '::') + signode += addnodes.desc_sig_keyword('new', 'new') + signode += addnodes.desc_sig_space() # TODO: placement if self.isNewTypeId: self.typ.describe_signature(signode, mode, env, symbol) @@ -1408,10 +1418,12 @@ class ASTDeleteExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: if self.rooted: - signode.append(nodes.Text('::')) - signode.append(nodes.Text('delete ')) + signode += addnodes.desc_sig_punctuation('::', '::') + signode += addnodes.desc_sig_keyword('delete', 'delete') + signode += addnodes.desc_sig_space() if self.array: - signode.append(nodes.Text('[] ')) + signode += addnodes.desc_sig_punctuation('[]', '[]') + signode += addnodes.desc_sig_space() self.expr.describe_signature(signode, mode, env, symbol) @@ -1435,9 +1447,9 @@ class ASTCastExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('(')) + signode += addnodes.desc_sig_punctuation('(', '(') self.typ.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) + signode += addnodes.desc_sig_punctuation(')', ')') self.expr.describe_signature(signode, mode, env, symbol) @@ -1471,9 +1483,9 @@ class ASTBinOpExpr(ASTExpression): env: "BuildEnvironment", symbol: "Symbol") -> None: self.exprs[0].describe_signature(signode, mode, env, symbol) for i in range(1, len(self.exprs)): - signode.append(nodes.Text(' ')) - signode.append(nodes.Text(self.ops[i - 1])) - signode.append(nodes.Text(' ')) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_operator(self.ops[i - 1], self.ops[i - 1]) + signode += addnodes.desc_sig_space() self.exprs[i].describe_signature(signode, mode, env, symbol) @@ -1494,17 +1506,18 @@ class ASTBracedInitList(ASTBase): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) - signode.append(nodes.Text('{')) + signode += addnodes.desc_sig_punctuation('{', '{') first = True for e in self.exprs: if not first: - signode.append(nodes.Text(', ')) + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() else: first = False e.describe_signature(signode, mode, env, symbol) if self.trailingComma: - signode.append(nodes.Text(',')) - signode.append(nodes.Text('}')) + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_punctuation('}', '}') class ASTAssignmentExpr(ASTExpression): @@ -1536,9 +1549,9 @@ class ASTAssignmentExpr(ASTExpression): env: "BuildEnvironment", symbol: "Symbol") -> None: self.exprs[0].describe_signature(signode, mode, env, symbol) for i in range(1, len(self.exprs)): - signode.append(nodes.Text(' ')) - signode.append(nodes.Text(self.ops[i - 1])) - signode.append(nodes.Text(' ')) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_operator(self.ops[i - 1], self.ops[i - 1]) + signode += addnodes.desc_sig_space() self.exprs[i].describe_signature(signode, mode, env, symbol) @@ -1563,7 +1576,8 @@ class ASTCommaExpr(ASTExpression): env: "BuildEnvironment", symbol: "Symbol") -> None: self.exprs[0].describe_signature(signode, mode, env, symbol) for i in range(1, len(self.exprs)): - signode.append(nodes.Text(', ')) + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() self.exprs[i].describe_signature(signode, mode, env, symbol) @@ -1579,7 +1593,7 @@ class ASTFallbackExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode += nodes.Text(self.expr) + signode += nodes.literal(self.expr, self.expr) ################################################################################ @@ -1599,24 +1613,39 @@ class ASTOperator(ASTBase): def get_id(self, version: int) -> str: raise NotImplementedError() + def _describe_identifier(self, signode: TextElement, identnode: TextElement, + env: "BuildEnvironment", symbol: "Symbol") -> None: + """Render the prefix into signode, and the last part into identnode.""" + raise NotImplementedError() + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", prefix: str, templateArgs: str, symbol: "Symbol") -> None: verify_description_mode(mode) - identifier = str(self) if mode == 'lastIsName': - signode += addnodes.desc_name(identifier, identifier) + identnode = addnodes.desc_name() + self._describe_identifier(identnode, identnode, env, symbol) + signode += identnode elif mode == 'markType': - targetText = prefix + identifier + templateArgs + targetText = prefix + str(self) + templateArgs pnode = addnodes.pending_xref('', refdomain='cpp', reftype='identifier', reftarget=targetText, modname=None, classname=None) pnode['cpp:parent_key'] = symbol.get_lookup_key() - pnode += nodes.Text(identifier) + # Render the identifier part, but collapse it into a string + # and make that the a link to this operator. + # E.g., if it is 'operator SomeType', then 'SomeType' becomes + # a link to the operator, not to 'SomeType'. + identnode = nodes.literal() + self._describe_identifier(signode, identnode, env, symbol) + txt = identnode.astext() + pnode += addnodes.desc_name(txt, txt) signode += pnode else: - signode += addnodes.desc_addname(identifier, identifier) + identnode = addnodes.desc_addname() + self._describe_identifier(identnode, identnode, env, symbol) + signode += identnode class ASTOperatorBuildIn(ASTOperator): @@ -1641,6 +1670,13 @@ class ASTOperatorBuildIn(ASTOperator): else: return 'operator' + self.op + def _describe_identifier(self, signode: TextElement, identnode: TextElement, + env: "BuildEnvironment", symbol: "Symbol") -> None: + signode += addnodes.desc_sig_keyword('operator', 'operator') + if self.op in ('new', 'new[]', 'delete', 'delete[]') or self.op[0] in "abcnox": + signode += addnodes.desc_sig_space() + identnode += addnodes.desc_sig_operator(self.op, self.op) + class ASTOperatorLiteral(ASTOperator): def __init__(self, identifier: ASTIdentifier) -> None: @@ -1655,6 +1691,12 @@ class ASTOperatorLiteral(ASTOperator): def _stringify(self, transform: StringifyTransform) -> str: return 'operator""' + transform(self.identifier) + def _describe_identifier(self, signode: TextElement, identnode: TextElement, + env: "BuildEnvironment", symbol: "Symbol") -> None: + signode += addnodes.desc_sig_keyword('operator', 'operator') + signode += addnodes.desc_sig_literal_string('""', '""') + self.identifier.describe_signature(identnode, 'markType', env, '', '', symbol) + class ASTOperatorType(ASTOperator): def __init__(self, type: "ASTType") -> None: @@ -1672,6 +1714,12 @@ class ASTOperatorType(ASTOperator): def get_name_no_template(self) -> str: return str(self) + def _describe_identifier(self, signode: TextElement, identnode: TextElement, + env: "BuildEnvironment", symbol: "Symbol") -> None: + signode += addnodes.desc_sig_keyword('operator', 'operator') + signode += addnodes.desc_sig_space() + self.type.describe_signature(identnode, 'markType', env, symbol) + class ASTTemplateArgConstant(ASTBase): def __init__(self, value: ASTExpression) -> None: @@ -1730,16 +1778,17 @@ class ASTTemplateArgs(ASTBase): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) - signode += nodes.Text('<') + signode += addnodes.desc_sig_punctuation('<', '<') first = True for a in self.args: if not first: - signode += nodes.Text(', ') + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() first = False a.describe_signature(signode, 'markType', env, symbol=symbol) if self.packExpansion: - signode += nodes.Text('...') - signode += nodes.Text('>') + signode += addnodes.desc_sig_punctuation('...', '...') + signode += addnodes.desc_sig_punctuation('>', '>') # Main part of declarations @@ -1780,7 +1829,7 @@ class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode += nodes.Text(str(self.name)) + signode += addnodes.desc_sig_keyword_type(self.name, self.name) class ASTTrailingTypeSpecDecltypeAuto(ASTTrailingTypeSpec): @@ -1794,7 +1843,10 @@ class ASTTrailingTypeSpecDecltypeAuto(ASTTrailingTypeSpec): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text(str(self))) + signode += addnodes.desc_sig_keyword('decltype', 'decltype') + signode += addnodes.desc_sig_punctuation('(', '(') + signode += addnodes.desc_sig_keyword('auto', 'auto') + signode += addnodes.desc_sig_punctuation(')', ')') class ASTTrailingTypeSpecDecltype(ASTTrailingTypeSpec): @@ -1811,9 +1863,10 @@ class ASTTrailingTypeSpecDecltype(ASTTrailingTypeSpec): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('decltype(')) + signode += addnodes.desc_sig_keyword('decltype', 'decltype') + signode += addnodes.desc_sig_punctuation('(', '(') self.expr.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) + signode += addnodes.desc_sig_punctuation(')', ')') class ASTTrailingTypeSpecName(ASTTrailingTypeSpec): @@ -1839,8 +1892,8 @@ class ASTTrailingTypeSpecName(ASTTrailingTypeSpec): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: if self.prefix: - signode += addnodes.desc_annotation(self.prefix, self.prefix) - signode += nodes.Text(' ') + signode += addnodes.desc_sig_keyword(self.prefix, self.prefix) + signode += addnodes.desc_sig_space() self.nestedName.describe_signature(signode, mode, env, symbol=symbol) @@ -1872,7 +1925,7 @@ class ASTFunctionParameter(ASTBase): env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) if self.ellipsis: - signode += nodes.Text('...') + signode += addnodes.desc_sig_punctuation('...', '...') else: self.arg.describe_signature(signode, mode, env, symbol=symbol) @@ -1888,18 +1941,19 @@ class ASTNoexceptSpec(ASTBase): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode += addnodes.desc_annotation('noexcept', 'noexcept') + signode += addnodes.desc_sig_keyword('noexcept', 'noexcept') if self.expr: - signode.append(nodes.Text('(')) - self.expr.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) + signode += addnodes.desc_sig_punctuation('(', '(') + self.expr.describe_signature(signode, 'markType', env, symbol) + signode += addnodes.desc_sig_punctuation(')', ')') class ASTParametersQualifiers(ASTBase): def __init__(self, args: List[ASTFunctionParameter], volatile: bool, const: bool, - refQual: str, exceptionSpec: ASTNoexceptSpec, trailingReturn: "ASTType", + refQual: Optional[str], exceptionSpec: ASTNoexceptSpec, + trailingReturn: "ASTType", override: bool, final: bool, attrs: List[ASTAttribute], - initializer: str) -> None: + initializer: Optional[str]) -> None: self.args = args self.volatile = volatile self.const = const @@ -1988,43 +2042,51 @@ class ASTParametersQualifiers(ASTBase): paramlist += param signode += paramlist else: - signode += nodes.Text('(', '(') + signode += addnodes.desc_sig_punctuation('(', '(') first = True for arg in self.args: if not first: - signode += nodes.Text(', ', ', ') + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() first = False arg.describe_signature(signode, 'markType', env, symbol=symbol) - signode += nodes.Text(')', ')') + signode += addnodes.desc_sig_punctuation(')', ')') def _add_anno(signode: TextElement, text: str) -> None: - signode += nodes.Text(' ') - signode += addnodes.desc_annotation(text, text) - - def _add_text(signode: TextElement, text: str) -> None: - signode += nodes.Text(' ' + text) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_keyword(text, text) if self.volatile: _add_anno(signode, 'volatile') if self.const: _add_anno(signode, 'const') if self.refQual: - _add_text(signode, self.refQual) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation(self.refQual, self.refQual) if self.exceptionSpec: - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() self.exceptionSpec.describe_signature(signode, mode, env, symbol) if self.trailingReturn: - signode += nodes.Text(' -> ') + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_operator('->', '->') + signode += addnodes.desc_sig_space() self.trailingReturn.describe_signature(signode, mode, env, symbol) if self.final: _add_anno(signode, 'final') if self.override: _add_anno(signode, 'override') for attr in self.attrs: - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() attr.describe_signature(signode) if self.initializer: - _add_text(signode, '= ' + str(self.initializer)) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation('=', '=') + signode += addnodes.desc_sig_space() + assert self.initializer in ('0', 'delete', 'default') + if self.initializer == '0': + signode += addnodes.desc_sig_literal_number('0', '0') + else: + signode += addnodes.desc_sig_keyword(self.initializer, self.initializer) class ASTDeclSpecsSimple(ASTBase): @@ -2083,14 +2145,14 @@ class ASTDeclSpecsSimple(ASTBase): addSpace = False for attr in self.attrs: if addSpace: - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() addSpace = True attr.describe_signature(signode) def _add(signode: TextElement, text: str) -> bool: if addSpace: - signode += nodes.Text(' ') - signode += addnodes.desc_annotation(text, text) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_keyword(text, text) return True if self.storage: @@ -2168,7 +2230,7 @@ class ASTDeclSpecs(ASTBase): if self.trailingTypeSpec: if addSpace: - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() numChildren = len(signode) self.trailingTypeSpec.describe_signature(signode, mode, env, symbol=symbol) @@ -2176,7 +2238,7 @@ class ASTDeclSpecs(ASTBase): if len(str(self.rightSpecs)) > 0: if addSpace: - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() self.rightSpecs.describe_signature(signode) @@ -2209,10 +2271,10 @@ class ASTArray(ASTBase): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) - signode.append(nodes.Text("[")) + signode += addnodes.desc_sig_punctuation('[', '[') if self.size: self.size.describe_signature(signode, 'markType', env, symbol) - signode.append(nodes.Text("]")) + signode += addnodes.desc_sig_punctuation(']', ']') class ASTDeclarator(ASTBase): @@ -2375,7 +2437,9 @@ class ASTDeclaratorNameBitField(ASTDeclarator): verify_description_mode(mode) if self.declId: self.declId.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(' : ', ' : ')) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation(':', ':') + signode += addnodes.desc_sig_space() self.size.describe_signature(signode, mode, env, symbol) @@ -2461,23 +2525,23 @@ class ASTDeclaratorPtr(ASTDeclarator): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) - signode += nodes.Text("*") + signode += addnodes.desc_sig_punctuation('*', '*') for a in self.attrs: a.describe_signature(signode) if len(self.attrs) > 0 and (self.volatile or self.const): - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() def _add_anno(signode: TextElement, text: str) -> None: - signode += addnodes.desc_annotation(text, text) + signode += addnodes.desc_sig_keyword(text, text) if self.volatile: _add_anno(signode, 'volatile') if self.const: if self.volatile: - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() _add_anno(signode, 'const') if self.const or self.volatile or len(self.attrs) > 0: if self.next.require_space_after_declSpecs(): - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() self.next.describe_signature(signode, mode, env, symbol) @@ -2538,11 +2602,11 @@ class ASTDeclaratorRef(ASTDeclarator): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) - signode += nodes.Text("&") + signode += addnodes.desc_sig_punctuation('&', '&') for a in self.attrs: a.describe_signature(signode) if len(self.attrs) > 0 and self.next.require_space_after_declSpecs(): - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() self.next.describe_signature(signode, mode, env, symbol) @@ -2595,9 +2659,9 @@ class ASTDeclaratorParamPack(ASTDeclarator): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) - signode += nodes.Text("...") + signode += addnodes.desc_sig_punctuation('...', '...') if self.next.name: - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() self.next.describe_signature(signode, mode, env, symbol) @@ -2680,18 +2744,19 @@ class ASTDeclaratorMemPtr(ASTDeclarator): env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) self.className.describe_signature(signode, 'markType', env, symbol) - signode += nodes.Text('::*') + signode += addnodes.desc_sig_punctuation('::', '::') + signode += addnodes.desc_sig_punctuation('*', '*') def _add_anno(signode: TextElement, text: str) -> None: - signode += addnodes.desc_annotation(text, text) + signode += addnodes.desc_sig_keyword(text, text) if self.volatile: _add_anno(signode, 'volatile') if self.const: if self.volatile: - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() _add_anno(signode, 'const') if self.next.require_space_after_declSpecs(): - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() self.next.describe_signature(signode, mode, env, symbol) @@ -2752,9 +2817,9 @@ class ASTDeclaratorParen(ASTDeclarator): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) - signode += nodes.Text('(') + signode += addnodes.desc_sig_punctuation('(', '(') self.inner.describe_signature(signode, mode, env, symbol) - signode += nodes.Text(')') + signode += addnodes.desc_sig_punctuation(')', ')') self.next.describe_signature(signode, "noneIsName", env, symbol) @@ -2775,7 +2840,7 @@ class ASTPackExpansionExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: self.expr.describe_signature(signode, mode, env, symbol) - signode += nodes.Text('...') + signode += addnodes.desc_sig_punctuation('...', '...') class ASTParenExprList(ASTBaseParenExprList): @@ -2792,15 +2857,16 @@ class ASTParenExprList(ASTBaseParenExprList): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) - signode.append(nodes.Text('(')) + signode += addnodes.desc_sig_punctuation('(', '(') first = True for e in self.exprs: if not first: - signode.append(nodes.Text(', ')) + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() else: first = False e.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) + signode += addnodes.desc_sig_punctuation(')', ')') class ASTInitializer(ASTBase): @@ -2820,7 +2886,9 @@ class ASTInitializer(ASTBase): env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) if self.hasAssign: - signode.append(nodes.Text(' = ')) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation('=', '=') + signode += addnodes.desc_sig_space() self.value.describe_signature(signode, 'markType', env, symbol) @@ -2924,7 +2992,7 @@ class ASTType(ASTBase): self.declSpecs.describe_signature(signode, 'markType', env, symbol) if (self.decl.require_space_after_declSpecs() and len(str(self.declSpecs)) > 0): - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() # for parameters that don't really declare new names we get 'markType', # this should not be propagated, but be 'noneIsName'. if mode == 'markType': @@ -2966,7 +3034,9 @@ class ASTTemplateParamConstrainedTypeWithInit(ASTBase): env: "BuildEnvironment", symbol: "Symbol") -> None: self.type.describe_signature(signode, mode, env, symbol) if self.init: - signode += nodes.Text(" = ") + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation('=', '=') + signode += addnodes.desc_sig_space() self.init.describe_signature(signode, mode, env, symbol) @@ -3034,7 +3104,9 @@ class ASTTypeUsing(ASTBase): verify_description_mode(mode) self.name.describe_signature(signode, mode, env, symbol=symbol) if self.type: - signode += nodes.Text(' = ') + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation('=', '=') + signode += addnodes.desc_sig_space() self.type.describe_signature(signode, 'markType', env, symbol=symbol) @@ -3079,7 +3151,6 @@ class ASTBaseClass(ASTBase): def _stringify(self, transform: StringifyTransform) -> str: res = [] - if self.visibility is not None: res.append(self.visibility) res.append(' ') @@ -3094,15 +3165,15 @@ class ASTBaseClass(ASTBase): env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) if self.visibility is not None: - signode += addnodes.desc_annotation(self.visibility, - self.visibility) - signode += nodes.Text(' ') + signode += addnodes.desc_sig_keyword(self.visibility, + self.visibility) + signode += addnodes.desc_sig_space() if self.virtual: - signode += addnodes.desc_annotation('virtual', 'virtual') - signode += nodes.Text(' ') + signode += addnodes.desc_sig_keyword('virtual', 'virtual') + signode += addnodes.desc_sig_space() self.name.describe_signature(signode, 'markType', env, symbol=symbol) if self.pack: - signode += nodes.Text('...') + signode += addnodes.desc_sig_punctuation('...', '...') class ASTClass(ASTBase): @@ -3134,13 +3205,17 @@ class ASTClass(ASTBase): verify_description_mode(mode) self.name.describe_signature(signode, mode, env, symbol=symbol) if self.final: - signode += nodes.Text(' ') - signode += addnodes.desc_annotation('final', 'final') + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_keyword('final', 'final') if len(self.bases) > 0: - signode += nodes.Text(' : ') + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation(':', ':') + signode += addnodes.desc_sig_space() for b in self.bases: b.describe_signature(signode, mode, env, symbol=symbol) - signode += nodes.Text(', ') + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() + signode.pop() signode.pop() @@ -3191,7 +3266,9 @@ class ASTEnum(ASTBase): # self.scoped has been done by the CPPEnumObject self.name.describe_signature(signode, mode, env, symbol=symbol) if self.underlyingType: - signode += nodes.Text(' : ') + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation(':', ':') + signode += addnodes.desc_sig_space() self.underlyingType.describe_signature(signode, 'noneIsName', env, symbol=symbol) @@ -3281,17 +3358,19 @@ class ASTTemplateKeyParamPackIdDefault(ASTTemplateParam): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode += nodes.Text(self.key) + signode += addnodes.desc_sig_keyword(self.key, self.key) if self.parameterPack: if self.identifier: - signode += nodes.Text(' ') - signode += nodes.Text('...') + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation('...', '...') if self.identifier: if not self.parameterPack: - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() self.identifier.describe_signature(signode, mode, env, '', '', symbol) if self.default: - signode += nodes.Text(' = ') + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation('=', '=') + signode += addnodes.desc_sig_space() self.default.describe_signature(signode, 'markType', env, symbol) @@ -3364,7 +3443,7 @@ class ASTTemplateParamTemplateType(ASTTemplateParam): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: self.nestedParams.describe_signature(signode, 'noneIsName', env, symbol) - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() self.data.describe_signature(signode, mode, env, symbol) @@ -3436,14 +3515,16 @@ class ASTTemplateParams(ASTBase): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode += nodes.Text("template<") + signode += addnodes.desc_sig_keyword('template', 'template') + signode += addnodes.desc_sig_punctuation('<', '<') first = True for param in self.params: if not first: - signode += nodes.Text(", ") + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() first = False param.describe_signature(signode, mode, env, symbol) - signode += nodes.Text(">") + signode += addnodes.desc_sig_punctuation('>', '>') def describe_signature_as_introducer( self, parentNode: desc_signature, mode: str, env: "BuildEnvironment", @@ -3454,18 +3535,20 @@ class ASTTemplateParams(ASTBase): signode.sphinx_line_type = 'templateParams' return signode lineNode = makeLine(parentNode) - lineNode += nodes.Text("template<") + lineNode += addnodes.desc_sig_keyword('template', 'template') + lineNode += addnodes.desc_sig_punctuation('<', '<') first = True for param in self.params: if not first: - lineNode += nodes.Text(", ") + lineNode += addnodes.desc_sig_punctuation(',', ',') + lineNode += addnodes.desc_sig_space() first = False if lineSpec: lineNode = makeLine(parentNode) param.describe_signature(lineNode, mode, env, symbol) if lineSpec and not first: lineNode = makeLine(parentNode) - lineNode += nodes.Text(">") + lineNode += addnodes.desc_sig_punctuation('>', '>') # Template introducers @@ -3519,7 +3602,7 @@ class ASTTemplateIntroductionParameter(ASTBase): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: if self.parameterPack: - signode += nodes.Text('...') + signode += addnodes.desc_sig_punctuation('...', '...') self.identifier.describe_signature(signode, mode, env, '', '', symbol) @@ -3564,14 +3647,15 @@ class ASTTemplateIntroduction(ASTBase): parentNode += signode signode.sphinx_line_type = 'templateIntroduction' self.concept.describe_signature(signode, 'markType', env, symbol) - signode += nodes.Text('{') + signode += addnodes.desc_sig_punctuation('{', '{') first = True for param in self.params: if not first: - signode += nodes.Text(', ') + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() first = False param.describe_signature(signode, mode, env, symbol) - signode += nodes.Text('}') + signode += addnodes.desc_sig_punctuation('}', '}') ################################################################################ @@ -3613,7 +3697,8 @@ class ASTRequiresClause(ASTBase): def describe_signature(self, signode: addnodes.desc_signature_line, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode += nodes.Text('requires ', 'requires ') + signode += addnodes.desc_sig_keyword('requires', 'requires') + signode += addnodes.desc_sig_space() self.expr.describe_signature(signode, mode, env, symbol) @@ -3733,39 +3818,42 @@ class ASTDeclaration(ASTBase): self.requiresClause.describe_signature(reqNode, 'markType', env, self.symbol) signode += mainDeclNode if self.visibility and self.visibility != "public": - mainDeclNode += addnodes.desc_annotation(self.visibility + " ", - self.visibility + " ") + mainDeclNode += addnodes.desc_sig_keyword(self.visibility, self.visibility) + mainDeclNode += addnodes.desc_sig_space() if self.objectType == 'type': prefix = self.declaration.get_type_declaration_prefix() - prefix += ' ' - mainDeclNode += addnodes.desc_annotation(prefix, prefix) + mainDeclNode += addnodes.desc_sig_keyword(prefix, prefix) + mainDeclNode += addnodes.desc_sig_space() elif self.objectType == 'concept': - mainDeclNode += addnodes.desc_annotation('concept ', 'concept ') + mainDeclNode += addnodes.desc_sig_keyword('concept', 'concept') + mainDeclNode += addnodes.desc_sig_space() elif self.objectType == 'member': pass elif self.objectType == 'function': pass elif self.objectType == 'class': assert self.directiveType in ('class', 'struct') - prefix = self.directiveType + ' ' - mainDeclNode += addnodes.desc_annotation(prefix, prefix) + mainDeclNode += addnodes.desc_sig_keyword(self.directiveType, self.directiveType) + mainDeclNode += addnodes.desc_sig_space() elif self.objectType == 'union': - mainDeclNode += addnodes.desc_annotation('union ', 'union ') + mainDeclNode += addnodes.desc_sig_keyword('union', 'union') + mainDeclNode += addnodes.desc_sig_space() elif self.objectType == 'enum': - if self.directiveType == 'enum': - prefix = 'enum ' - elif self.directiveType == 'enum-class': - prefix = 'enum class ' + mainDeclNode += addnodes.desc_sig_keyword('enum', 'enum') + mainDeclNode += addnodes.desc_sig_space() + if self.directiveType == 'enum-class': + mainDeclNode += addnodes.desc_sig_keyword('class', 'class') + mainDeclNode += addnodes.desc_sig_space() elif self.directiveType == 'enum-struct': - prefix = 'enum struct ' + mainDeclNode += addnodes.desc_sig_keyword('struct', 'struct') + mainDeclNode += addnodes.desc_sig_space() else: - assert False # wrong directiveType used - mainDeclNode += addnodes.desc_annotation(prefix, prefix) + assert self.directiveType == 'enum', self.directiveType elif self.objectType == 'enumerator': - mainDeclNode += addnodes.desc_annotation('enumerator ', 'enumerator ') + mainDeclNode += addnodes.desc_sig_keyword('enumerator', 'enumerator') + mainDeclNode += addnodes.desc_sig_space() else: - print(self.objectType) - assert False + assert False, self.objectType self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol) lastDeclNode = mainDeclNode if self.trailingRequiresClause: @@ -3776,7 +3864,7 @@ class ASTDeclaration(ASTBase): self.trailingRequiresClause.describe_signature( trailingReqNode, 'markType', env, self.symbol) if self.semicolon: - lastDeclNode += nodes.Text(';') + lastDeclNode += addnodes.desc_sig_punctuation(';', ';') class ASTNamespace(ASTBase): @@ -7604,9 +7692,10 @@ class CPPDomain(Domain): title += '()' * addParen # and reconstruct the title again contnode += nodes.Text(title) - return make_refnode(builder, fromdocname, docname, + res = make_refnode(builder, fromdocname, docname, declaration.get_newest_id(), contnode, displayName ), declaration.objectType + return res def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder, typ: str, target: str, node: pending_xref, contnode: Element diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t index 6264f20d2..4af85bea9 100644 --- a/sphinx/themes/basic/static/basic.css_t +++ b/sphinx/themes/basic/static/basic.css_t @@ -508,6 +508,30 @@ table.hlist td { vertical-align: top; } +/* -- object description styles --------------------------------------------- */ + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + /* -- other body styles ----------------------------------------------------- */ @@ -634,14 +658,6 @@ dl.glossary dt { font-size: 1.1em; } -.optional { - font-size: 1.3em; -} - -.sig-paren { - font-size: larger; -} - .versionmodified { font-style: italic; } @@ -786,16 +802,6 @@ div.literal-block-wrapper { margin: 1em 0; } -code.descname { - background-color: transparent; - font-weight: bold; - font-size: 1.2em; -} - -code.descclassname { - background-color: transparent; -} - code.xref, a code { background-color: transparent; font-weight: bold; diff --git a/sphinx/transforms/post_transforms/__init__.py b/sphinx/transforms/post_transforms/__init__.py index 653097cd8..76a709f5f 100644 --- a/sphinx/transforms/post_transforms/__init__.py +++ b/sphinx/transforms/post_transforms/__init__.py @@ -209,10 +209,6 @@ class SigElementFallbackTransform(SphinxPostTransform): """Fallback desc_sig_element nodes to inline if translator does not supported them.""" default_priority = 200 - SIG_ELEMENTS = [addnodes.desc_sig_name, - addnodes.desc_sig_operator, - addnodes.desc_sig_punctuation] - def run(self, **kwargs: Any) -> None: def has_visitor(translator: Type[nodes.NodeVisitor], node: Type[Element]) -> bool: return hasattr(translator, "visit_%s" % node.__name__) @@ -222,7 +218,7 @@ class SigElementFallbackTransform(SphinxPostTransform): # subclass of SphinxTranslator supports desc_sig_element nodes automatically. return - if all(has_visitor(translator, node) for node in self.SIG_ELEMENTS): + if all(has_visitor(translator, node) for node in addnodes.SIG_ELEMENTS): # the translator supports all desc_sig_element nodes return else: diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index edd5ea5bc..7ecf89f54 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -73,6 +73,7 @@ def _check(name, input, idDict, output, key, asTextOutput): print("Input: ", input) print("astext(): ", resAsText) print("Expected: ", outputAsText) + print("Node:", parentNode) raise DefinitionError("") idExpected = [None] @@ -743,6 +744,9 @@ def test_anon_definitions(): check('class', '@1', {3: "Ut1_1"}, asTextOutput='class [anonymous]') check('class', '@a::A', {3: "NUt1_a1AE"}, asTextOutput='class [anonymous]::A') + check('function', 'int f(int @a)', {1: 'f__i', 2: '1fi'}, + asTextOutput='int f(int [anonymous])') + def test_templates(): check('class', "A", {2: "IE1AI1TE"}, output="template<> {key}A") diff --git a/tests/test_ext_intersphinx.py b/tests/test_ext_intersphinx.py index 523ed2acc..785faed62 100644 --- a/tests/test_ext_intersphinx.py +++ b/tests/test_ext_intersphinx.py @@ -258,10 +258,10 @@ def test_missing_reference_cppdomain(tempdir, app, status, warning): 'Bar' in html) assert ('foons' in html) + ' title="(in foo v2.0)">foons' in html) assert ('bartype' in html) + ' title="(in foo v2.0)">bartype' in html) def test_missing_reference_jsdomain(tempdir, app, status, warning): From 3c9a74cb0b9e5ca90adfb1ee55b2262717fae223 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 20 Mar 2021 16:16:14 +0100 Subject: [PATCH 05/29] Decl styling, docs and restructuring --- doc/extdev/nodes.rst | 17 +++++++++-- sphinx/addnodes.py | 63 ++++++++++++++++++++++++++++------------- sphinx/writers/html5.py | 22 ++++++++++---- 3 files changed, 74 insertions(+), 28 deletions(-) diff --git a/doc/extdev/nodes.rst b/doc/extdev/nodes.rst index 3976de4c7..d327e1cb0 100644 --- a/doc/extdev/nodes.rst +++ b/doc/extdev/nodes.rst @@ -8,18 +8,31 @@ Doctree node classes added by Sphinx Nodes for domain-specific object descriptions --------------------------------------------- +Top-level nodes +............... + +These nodes form the top-most levels of object descriptions. + .. autoclass:: desc .. autoclass:: desc_signature .. autoclass:: desc_signature_line +.. autoclass:: desc_content + +Nodes for high-level structure in signatures +............................................ + +These nodes occur in in non-multiline :py:class:`desc_signature` nodes +and in :py:class:`desc_signature_line` nodes. + +.. autoclass:: desc_name .. autoclass:: desc_addname + .. autoclass:: desc_type .. autoclass:: desc_returns -.. autoclass:: desc_name .. autoclass:: desc_parameterlist .. autoclass:: desc_parameter .. autoclass:: desc_optional .. autoclass:: desc_annotation -.. autoclass:: desc_content New admonition-like constructs ------------------------------ diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 3200e5e47..f818e496d 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -115,24 +115,32 @@ class toctree(nodes.General, nodes.Element, translatable): return messages -# domain-specific object descriptions (class, function etc.) +############################################################# +# Domain-specific object descriptions (class, function etc.) +############################################################# + +# Top-level nodes +################# class desc(nodes.Admonition, nodes.Element): - """Node for object descriptions. + """Node for a list of object signatures and a common description of them. - This node is similar to a "definition list" with one definition. It - contains one or more ``desc_signature`` and a ``desc_content``. + Contains one or more :py:class:`desc_signature` nodes + and then a single :py:class:`desc_content` node. + + This node always has two classes: + + - The name of the domain it belongs to, e.g., ``py`` or ``cpp``. + - The name of the object type in the domain, e.g., ``function``. """ class desc_signature(nodes.Part, nodes.Inline, nodes.TextElement): - """Node for object signatures. + """Node for a single object signature. - The "term" part of the custom Sphinx definition list. - - As default the signature is a single line signature, - but set ``is_multiline = True`` to describe a multi-line signature. - In that case all child nodes must be ``desc_signature_line`` nodes. + As default the signature is a single-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. """ @property @@ -144,22 +152,40 @@ class desc_signature(nodes.Part, nodes.Inline, nodes.TextElement): class desc_signature_line(nodes.Part, nodes.Inline, nodes.FixedTextElement): - """Node for a line in a multi-line object signatures. + """Node for a line in a multi-line object signature. - It should only be used in a ``desc_signature`` with ``is_multiline`` set. + It should only be used as a child of a :py:class:`desc_signature` + with ``is_multiline`` set to ``True``. Set ``add_permalink = True`` for the line that should get the permalink. """ sphinx_line_type = '' +class desc_content(nodes.General, nodes.Element): + """Node for object description content. + + Must be the last child node in a :py:class:`desc` node. + """ + +# Nodes for high-level structure in signatures +############################################## + # nodes to use within a desc_signature or desc_signature_line class desc_name(nodes.Part, nodes.Inline, nodes.FixedTextElement): - """Node for the main object name.""" + """Node for the main object name. + + For example, in the declaration of a Python class ``MyModule.MyClass``, + the main name is ``MyClass``. + """ class desc_addname(nodes.Part, nodes.Inline, nodes.FixedTextElement): - """Node for additional name parts (module name, class name).""" + """Node for additional name parts for an object. + + For example, in the declaration of a Python class ``MyModule.MyClass``, + the additional name part is ``MyModule.``. + """ # compatibility alias @@ -201,12 +227,8 @@ class desc_annotation(nodes.Part, nodes.Inline, nodes.FixedTextElement): """Node for signature annotations (not Python 3-style annotations).""" -class desc_content(nodes.General, nodes.Element): - """Node for object description content. - - This is the "definition" part of the custom Sphinx definition list. - """ - +# Leaf nodes for markup of text fragments +######################################### # Signature text elements, generally translated to node.inline # in SigElementFallbackTransform. @@ -281,6 +303,7 @@ SIG_ELEMENTS = [desc_sig_space, desc_sig_literal_number, desc_sig_literal_string, desc_sig_literal_char] +############################################################### # new admonition-like constructs class versionmodified(nodes.Admonition, nodes.TextElement): diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index fa7360c4e..745e76b38 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -78,6 +78,13 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): def depart_start_of_file(self, node: Element) -> None: self.docnames.pop() + ############################################################# + # Domain-specific object descriptions + ############################################################# + + # Top-level nodes for descriptions + ################################## + def visit_desc(self, node: Element) -> None: self.body.append(self.starttag(node, 'dl', CLASS=node['objtype'])) @@ -104,6 +111,15 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): self.add_permalink_ref(node.parent, _('Permalink to this definition')) self.body.append('
') + # Nodes for high-level structure in signatures + ############################################## + + def visit_desc_name(self, node: Element) -> None: + self.body.append(self.starttag(node, 'code', '', CLASS='sig-name descname')) + + def depart_desc_name(self, node: Element) -> None: + self.body.append('') + def visit_desc_addname(self, node: Element) -> None: self.body.append(self.starttag(node, 'code', '', CLASS='sig-prename descclassname')) @@ -122,12 +138,6 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): def depart_desc_returns(self, node: Element) -> None: pass - def visit_desc_name(self, node: Element) -> None: - self.body.append(self.starttag(node, 'code', '', CLASS='sig-name descname')) - - def depart_desc_name(self, node: Element) -> None: - self.body.append('') - def visit_desc_parameterlist(self, node: Element) -> None: self.body.append('(') self.first_param = 1 From d131ec7acbc2026c0cc05c58fa39f3d845f3e6ba Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 20 Mar 2021 16:25:09 +0100 Subject: [PATCH 06/29] Decl styling, move desc dynamic classes to domain base class --- sphinx/addnodes.py | 3 +++ sphinx/directives/__init__.py | 1 + sphinx/writers/html.py | 24 ++++++++++++++++++------ sphinx/writers/html5.py | 14 ++++++++------ 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index f818e496d..ac41dd951 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -134,6 +134,9 @@ class desc(nodes.Admonition, nodes.Element): - The name of the object type in the domain, e.g., ``function``. """ + # TODO: can we introduce a constructor + # that forces the specification of the domain and objtyp? + class desc_signature(nodes.Part, nodes.Inline, nodes.TextElement): """Node for a single object signature. diff --git a/sphinx/directives/__init__.py b/sphinx/directives/__init__.py index e1ccc8be7..8e8d65f03 100644 --- a/sphinx/directives/__init__.py +++ b/sphinx/directives/__init__.py @@ -172,6 +172,7 @@ class ObjectDescription(SphinxDirective, Generic[T]): node['noindex'] = noindex = ('noindex' in self.options) if self.domain: node['classes'].append(self.domain) + node['classes'].append(node['objtype']) self.names: List[T] = [] signatures = self.get_signatures() diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index a92927cab..73e762c42 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -107,8 +107,15 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): def depart_start_of_file(self, node: Element) -> None: self.docnames.pop() + ############################################################# + # Domain-specific object descriptions + ############################################################# + + # Top-level nodes for descriptions + ################################## + def visit_desc(self, node: Element) -> None: - self.body.append(self.starttag(node, 'dl', CLASS=node['objtype'])) + self.body.append(self.starttag(node, 'dl')) def depart_desc(self, node: Element) -> None: self.body.append('\n\n') @@ -133,6 +140,15 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): self.add_permalink_ref(node.parent, _('Permalink to this definition')) self.body.append('
') + def visit_desc_content(self, node: Element) -> None: + self.body.append(self.starttag(node, 'dd', '')) + + def depart_desc_content(self, node: Element) -> None: + self.body.append('') + + # Nodes for high-level structure in signatures + ############################################## + def visit_desc_addname(self, node: Element) -> None: self.body.append(self.starttag(node, 'code', '', CLASS='descclassname')) @@ -205,11 +221,7 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): def depart_desc_annotation(self, node: Element) -> None: self.body.append('') - def visit_desc_content(self, node: Element) -> None: - self.body.append(self.starttag(node, 'dd', '')) - - def depart_desc_content(self, node: Element) -> None: - self.body.append('') + ############################################## def visit_versionmodified(self, node: Element) -> None: self.body.append(self.starttag(node, 'div', CLASS=node['type'])) diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 745e76b38..c3c082967 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -86,7 +86,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): ################################## def visit_desc(self, node: Element) -> None: - self.body.append(self.starttag(node, 'dl', CLASS=node['objtype'])) + self.body.append(self.starttag(node, 'dl')) def depart_desc(self, node: Element) -> None: self.body.append('\n\n') @@ -111,6 +111,12 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): self.add_permalink_ref(node.parent, _('Permalink to this definition')) self.body.append('
') + def visit_desc_content(self, node: Element) -> None: + self.body.append(self.starttag(node, 'dd', '')) + + def depart_desc_content(self, node: Element) -> None: + self.body.append('') + # Nodes for high-level structure in signatures ############################################## @@ -186,11 +192,7 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): def depart_desc_annotation(self, node: Element) -> None: self.body.append('') - def visit_desc_content(self, node: Element) -> None: - self.body.append(self.starttag(node, 'dd', '')) - - def depart_desc_content(self, node: Element) -> None: - self.body.append('') + ############################################## def visit_versionmodified(self, node: Element) -> None: self.body.append(self.starttag(node, 'div', CLASS=node['type'])) From e012c93f1b69d943fe481982dce6d9dcf317ed51 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 20 Mar 2021 16:42:00 +0100 Subject: [PATCH 07/29] Decl styling, move static classes to addnodes from HTML5 writer --- sphinx/addnodes.py | 25 ++++++++++++++++++++++--- sphinx/writers/html.py | 14 +++++++------- sphinx/writers/html5.py | 4 ++-- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index ac41dd951..17f579c0e 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -119,6 +119,19 @@ class toctree(nodes.General, nodes.Element, translatable): # Domain-specific object descriptions (class, function etc.) ############################################################# +class _desc_classes_injector: + """Helper base class for injecting a fixes list of classes. + + Use as the first base class. + """ + + classes = [] # type: List[str] + + def __init__(self, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self['classes'].extend(self.classes) + + # Top-level nodes ################# @@ -175,20 +188,26 @@ class desc_content(nodes.General, nodes.Element): # nodes to use within a desc_signature or desc_signature_line -class desc_name(nodes.Part, nodes.Inline, nodes.FixedTextElement): +class desc_name(_desc_classes_injector, nodes.Part, nodes.Inline, nodes.FixedTextElement): """Node for the main object name. For example, in the declaration of a Python class ``MyModule.MyClass``, the main name is ``MyClass``. + + This node always has the class ``sig-name``. """ + classes = ['sig-name', 'descname'] # 'descname' is for backwards compatibility -class desc_addname(nodes.Part, nodes.Inline, nodes.FixedTextElement): +class desc_addname(_desc_classes_injector, nodes.Part, nodes.Inline, nodes.FixedTextElement): """Node for additional name parts for an object. For example, in the declaration of a Python class ``MyModule.MyClass``, the additional name part is ``MyModule.``. + + This node always has the class ``sig-prename``. """ + classes = ['sig-prename', 'descclassname'] # 'descclassname' is for backwards compatibility # compatibility alias @@ -237,7 +256,7 @@ class desc_annotation(nodes.Part, nodes.Inline, nodes.FixedTextElement): # in SigElementFallbackTransform. # When adding a new one, add it to SIG_ELEMENTS. -class desc_sig_element(nodes.inline): +class desc_sig_element(nodes.inline, _desc_classes_injector): """Common parent class of nodes for inline text of a signature.""" classes: List[str] = [] diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 73e762c42..54d7311ea 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -149,8 +149,14 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): # Nodes for high-level structure in signatures ############################################## + def visit_desc_name(self, node: Element) -> None: + self.body.append(self.starttag(node, 'code', '')) + + def depart_desc_name(self, node: Element) -> None: + self.body.append('') + def visit_desc_addname(self, node: Element) -> None: - self.body.append(self.starttag(node, 'code', '', CLASS='descclassname')) + self.body.append(self.starttag(node, 'code', '')) def depart_desc_addname(self, node: Element) -> None: self.body.append('') @@ -167,12 +173,6 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): def depart_desc_returns(self, node: Element) -> None: pass - def visit_desc_name(self, node: Element) -> None: - self.body.append(self.starttag(node, 'code', '', CLASS='descname')) - - def depart_desc_name(self, node: Element) -> None: - self.body.append('') - def visit_desc_parameterlist(self, node: Element) -> None: self.body.append('(') self.first_param = 1 diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index c3c082967..e3a28f85d 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -121,13 +121,13 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): ############################################## def visit_desc_name(self, node: Element) -> None: - self.body.append(self.starttag(node, 'code', '', CLASS='sig-name descname')) + self.body.append(self.starttag(node, 'code', '')) def depart_desc_name(self, node: Element) -> None: self.body.append('') def visit_desc_addname(self, node: Element) -> None: - self.body.append(self.starttag(node, 'code', '', CLASS='sig-prename descclassname')) + self.body.append(self.starttag(node, 'code', '')) def depart_desc_addname(self, node: Element) -> None: self.body.append('') From 98800be904eeb4aca33acaac60a06e42b181d0f3 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 20 Mar 2021 17:19:34 +0100 Subject: [PATCH 08/29] Decl styling, make desc_inline node Use the new node for cpp:expr --- CHANGES | 3 +++ doc/extdev/nodes.rst | 1 + sphinx/addnodes.py | 30 +++++++++++++++++++++++--- sphinx/domains/cpp.py | 7 ++---- sphinx/themes/basic/static/basic.css_t | 6 ++++++ sphinx/writers/html5.py | 6 ++++++ 6 files changed, 45 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 2743788ac..fe87bf9b9 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,9 @@ Dependencies Incompatible changes -------------------- +* #9023: Change the CSS classes on :rst:role:`cpp:expr` and + :rst:role:`cpp:texpr`. + Deprecated ---------- diff --git a/doc/extdev/nodes.rst b/doc/extdev/nodes.rst index d327e1cb0..77872df40 100644 --- a/doc/extdev/nodes.rst +++ b/doc/extdev/nodes.rst @@ -17,6 +17,7 @@ These nodes form the top-most levels of object descriptions. .. autoclass:: desc_signature .. autoclass:: desc_signature_line .. autoclass:: desc_content +.. autoclass:: desc_inline Nodes for high-level structure in signatures ............................................ diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 17f579c0e..2d117d7a9 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -151,13 +151,16 @@ class desc(nodes.Admonition, nodes.Element): # that forces the specification of the domain and objtyp? -class desc_signature(nodes.Part, nodes.Inline, nodes.TextElement): +class desc_signature(_desc_classes_injector, nodes.Part, nodes.Inline, nodes.TextElement): """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. In that case all child nodes must be :py:class:`desc_signature_line` nodes. + + This node always has the classes ``sig`` and ``sig-object``. """ + classes = ['sig', 'sig-object'] @property def child_text_separator(self): @@ -183,6 +186,22 @@ class desc_content(nodes.General, nodes.Element): 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. + + This is for example used for roles like :rst:role:`cpp:expr`. + + This node always has the classes ``sig``, ``sig-inline``, + and the name of the domain it belongs to. + """ + classes = ['sig', 'sig-inline'] + + def __init__(self, domain: str, *args, **kwargs): + super().__init__(*args, **kwargs) + self['classes'].append(domain) + + # Nodes for high-level structure in signatures ############################################## @@ -507,20 +526,25 @@ class manpage(nodes.Inline, nodes.FixedTextElement): def setup(app: "Sphinx") -> Dict[str, Any]: app.add_node(toctree) + app.add_node(desc) app.add_node(desc_signature) app.add_node(desc_signature_line) + app.add_node(desc_content) + app.add_node(desc_inline) + + app.add_node(desc_name) app.add_node(desc_addname) app.add_node(desc_type) app.add_node(desc_returns) - app.add_node(desc_name) app.add_node(desc_parameterlist) app.add_node(desc_parameter) app.add_node(desc_optional) app.add_node(desc_annotation) - app.add_node(desc_content) + for n in SIG_ELEMENTS: app.add_node(n) + app.add_node(versionmodified) app.add_node(seealso) app.add_node(productionlist) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index bb11138d6..51cdd3946 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -7395,11 +7395,9 @@ class CPPExprRole(SphinxRole): if asCode: # render the expression as inline code self.class_type = 'cpp-expr' - self.node_type: Type[TextElement] = nodes.literal else: # render the expression as inline text self.class_type = 'cpp-texpr' - self.node_type = nodes.inline def run(self) -> Tuple[List[Node], List[system_message]]: text = self.text.replace('\n', ' ') @@ -7407,20 +7405,19 @@ class CPPExprRole(SphinxRole): location=self.get_source_info(), config=self.config) # attempt to mimic XRefRole classes, except that... - classes = ['xref', 'cpp', self.class_type] try: ast = parser.parse_expression() except DefinitionError as ex: logger.warning('Unparseable C++ expression: %r\n%s', text, ex, location=self.get_source_info()) # see below - return [self.node_type(text, text, classes=classes)], [] + return [addnodes.desc_inline('cpp', text, text, classes=[self.class_type])], [] parentSymbol = self.env.temp_data.get('cpp:parent_symbol', None) if parentSymbol is None: parentSymbol = self.env.domaindata['cpp']['root_symbol'] # ...most if not all of these classes should really apply to the individual references, # not the container node - signode = self.node_type(classes=classes) + signode = addnodes.desc_inline('cpp', classes=[self.class_type]) ast.describe_signature(signode, 'markType', self.env, parentSymbol) return [signode], [] diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t index 4af85bea9..f0ce51e4b 100644 --- a/sphinx/themes/basic/static/basic.css_t +++ b/sphinx/themes/basic/static/basic.css_t @@ -532,6 +532,12 @@ table.hlist td { font-style: italic; } +/* C++ specific styling */ + +.sig-inline.cpp-expr { + font-family: monospace; +} + /* -- other body styles ----------------------------------------------------- */ diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index e3a28f85d..1c81584e3 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -117,6 +117,12 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): def depart_desc_content(self, node: Element) -> None: self.body.append('') + def visit_desc_inline(self, node: Element) -> None: + self.body.append(self.starttag(node, 'span', '')) + + def depart_desc_inline(self, node: Element) -> None: + self.body.append('') + # Nodes for high-level structure in signatures ############################################## From b0a2e5cf040ab3ddb64ebc0ee4f60b59fa30eef9 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 20 Mar 2021 17:42:26 +0100 Subject: [PATCH 09/29] Decl styling, consistent monospace --- CHANGES | 2 ++ sphinx/themes/basic/static/basic.css_t | 8 ++++++-- sphinx/writers/html5.py | 8 ++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index fe87bf9b9..28e89863f 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,8 @@ Deprecated Features added -------------- +* #9023: More CSS classes on domain descriptions, see :ref:`nodes` for details. + Bugs fixed ---------- diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t index f0ce51e4b..de7651e17 100644 --- a/sphinx/themes/basic/static/basic.css_t +++ b/sphinx/themes/basic/static/basic.css_t @@ -510,6 +510,10 @@ table.hlist td { /* -- object description styles --------------------------------------------- */ +.sig { + font-family: monospace; +} + .sig-name, code.descname { background-color: transparent; font-weight: bold; @@ -534,8 +538,8 @@ table.hlist td { /* C++ specific styling */ -.sig-inline.cpp-expr { - font-family: monospace; +.sig-inline.cpp-texpr { + font-family: unset; } diff --git a/sphinx/writers/html5.py b/sphinx/writers/html5.py index 1c81584e3..469642591 100644 --- a/sphinx/writers/html5.py +++ b/sphinx/writers/html5.py @@ -127,16 +127,16 @@ class HTML5Translator(SphinxTranslator, BaseTranslator): ############################################## def visit_desc_name(self, node: Element) -> None: - self.body.append(self.starttag(node, 'code', '')) + self.body.append(self.starttag(node, 'span', '')) def depart_desc_name(self, node: Element) -> None: - self.body.append('') + self.body.append('') def visit_desc_addname(self, node: Element) -> None: - self.body.append(self.starttag(node, 'code', '')) + self.body.append(self.starttag(node, 'span', '')) def depart_desc_addname(self, node: Element) -> None: - self.body.append('') + self.body.append('') def visit_desc_type(self, node: Element) -> None: pass From 86eeee5031008bb0da73332cd8dd28bd3c9e5aed Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 20 Mar 2021 17:53:11 +0100 Subject: [PATCH 10/29] Fix flake8 and mypy violations --- sphinx/addnodes.py | 9 +++++---- sphinx/domains/cpp.py | 24 ++++++++++++------------ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 2d117d7a9..69d11eb53 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -119,7 +119,7 @@ class toctree(nodes.General, nodes.Element, translatable): # Domain-specific object descriptions (class, function etc.) ############################################################# -class _desc_classes_injector: +class _desc_classes_injector(nodes.TextElement): """Helper base class for injecting a fixes list of classes. Use as the first base class. @@ -127,7 +127,7 @@ class _desc_classes_injector: classes = [] # type: List[str] - def __init__(self, *args, **kwargs) -> None: + def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self['classes'].extend(self.classes) @@ -197,7 +197,7 @@ class desc_inline(_desc_classes_injector, nodes.Inline, nodes.TextElement): """ classes = ['sig', 'sig-inline'] - def __init__(self, domain: str, *args, **kwargs): + def __init__(self, domain: str, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self['classes'].append(domain) @@ -226,7 +226,8 @@ class desc_addname(_desc_classes_injector, nodes.Part, nodes.Inline, nodes.Fixed This node always has the class ``sig-prename``. """ - classes = ['sig-prename', 'descclassname'] # 'descclassname' is for backwards compatibility + # 'descclassname' is for backwards compatibility + classes = ['sig-prename', 'descclassname'] # compatibility alias diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 51cdd3946..aa88838f4 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -9,7 +9,7 @@ """ import re -from typing import (Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, Type, +from typing import (Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, TypeVar, Union, cast) from docutils import nodes @@ -1623,9 +1623,9 @@ class ASTOperator(ASTBase): symbol: "Symbol") -> None: verify_description_mode(mode) if mode == 'lastIsName': - identnode = addnodes.desc_name() - self._describe_identifier(identnode, identnode, env, symbol) - signode += identnode + mainName = addnodes.desc_name() + self._describe_identifier(mainName, mainName, env, symbol) + signode += mainName elif mode == 'markType': targetText = prefix + str(self) + templateArgs pnode = addnodes.pending_xref('', refdomain='cpp', @@ -1637,15 +1637,15 @@ class ASTOperator(ASTBase): # and make that the a link to this operator. # E.g., if it is 'operator SomeType', then 'SomeType' becomes # a link to the operator, not to 'SomeType'. - identnode = nodes.literal() - self._describe_identifier(signode, identnode, env, symbol) - txt = identnode.astext() + container = nodes.literal() + self._describe_identifier(signode, container, env, symbol) + txt = container.astext() pnode += addnodes.desc_name(txt, txt) signode += pnode else: - identnode = addnodes.desc_addname() - self._describe_identifier(identnode, identnode, env, symbol) - signode += identnode + addName = addnodes.desc_addname() + self._describe_identifier(addName, addName, env, symbol) + signode += addName class ASTOperatorBuildIn(ASTOperator): @@ -7690,8 +7690,8 @@ class CPPDomain(Domain): # and reconstruct the title again contnode += nodes.Text(title) res = make_refnode(builder, fromdocname, docname, - declaration.get_newest_id(), contnode, displayName - ), declaration.objectType + declaration.get_newest_id(), contnode, displayName + ), declaration.objectType return res def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder, From c1da955df2f14ffc5a686b8724d46369abf847d4 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 20 Mar 2021 18:02:07 +0100 Subject: [PATCH 11/29] Decl styling, use a post-transform to get the domain name in the desc_signature nodes --- sphinx/addnodes.py | 3 ++- sphinx/transforms/post_transforms/__init__.py | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 69d11eb53..23cf0acb8 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -158,8 +158,9 @@ class desc_signature(_desc_classes_injector, nodes.Part, nodes.Inline, nodes.Tex 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`` and ``sig-object``. + This node always has the classes ``sig``, ``sig-object``, and the domain it belongs to. """ + # Note: the domain name is being added through a post-transform DescSigAddDomainAsClass classes = ['sig', 'sig-object'] @property diff --git a/sphinx/transforms/post_transforms/__init__.py b/sphinx/transforms/post_transforms/__init__.py index 76a709f5f..b70677e4f 100644 --- a/sphinx/transforms/post_transforms/__init__.py +++ b/sphinx/transforms/post_transforms/__init__.py @@ -232,10 +232,20 @@ class SigElementFallbackTransform(SphinxPostTransform): node.replace_self(newnode) +class DescSigAddDomainAsClass(SphinxPostTransform): + """Add the domain name of the parent node as a class in each desc_signature node.""" + default_priority = 200 + + def run(self, **kwargs: Any) -> None: + for node in self.document.traverse(addnodes.desc_signature): + node['classes'].append(node.parent['domain']) + + def setup(app: Sphinx) -> Dict[str, Any]: app.add_post_transform(ReferencesResolver) app.add_post_transform(OnlyNodeTransform) app.add_post_transform(SigElementFallbackTransform) + app.add_post_transform(DescSigAddDomainAsClass) return { 'version': 'builtin', From 70708e4b24f70aa84b7c46c0b83175eaba32baf6 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 20 Mar 2021 18:07:11 +0100 Subject: [PATCH 12/29] Decl styling, add CSS for the C++ domain --- sphinx/themes/basic/static/basic.css_t | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t index de7651e17..7f505f6b1 100644 --- a/sphinx/themes/basic/static/basic.css_t +++ b/sphinx/themes/basic/static/basic.css_t @@ -542,6 +542,18 @@ table.hlist td { font-family: unset; } +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.cpp .m { + color: #1750EB; +} + +.sig.cpp .s { + color: #067D17; +} + /* -- other body styles ----------------------------------------------------- */ From 87414faa92fe987b70dc2d4cb61d08a609a5e56c Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 20 Mar 2021 19:27:13 +0100 Subject: [PATCH 13/29] Decl styling, fix cpp tests --- tests/test_domain_cpp.py | 5 ++--- tests/test_ext_intersphinx.py | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 7ecf89f54..52aaad850 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -1210,7 +1210,7 @@ not found in `{test}` cpp_any_role = RoleClasses('cpp-any', 'a', ['code']) # NYI: consistent looks # texpr_role = RoleClasses('cpp-texpr', 'span', ['a', 'code']) - expr_role = RoleClasses('cpp-expr', 'code', ['a']) + expr_role = RoleClasses('cpp-expr', 'span', ['a']) texpr_role = RoleClasses('cpp-texpr', 'span', ['a', 'span']) # XRefRole-style classes @@ -1227,8 +1227,7 @@ not found in `{test}` for role in (expr_role, texpr_role): name = role.name expect = '`{name}` puts the domain and role classes at its root'.format(name=name) - # NYI: xref should go in the references - assert {'xref', 'cpp', name} <= role.classes, expect + assert {'sig', 'sig-inline', 'cpp', name} <= role.classes, expect # reference classes diff --git a/tests/test_ext_intersphinx.py b/tests/test_ext_intersphinx.py index 785faed62..62456a3f4 100644 --- a/tests/test_ext_intersphinx.py +++ b/tests/test_ext_intersphinx.py @@ -258,10 +258,10 @@ def test_missing_reference_cppdomain(tempdir, app, status, warning): 'Bar' in html) assert ('foons' in html) + ' title="(in foo v2.0)">foons' in html) assert ('bartype' in html) + ' title="(in foo v2.0)">bartype' in html) def test_missing_reference_jsdomain(tempdir, app, status, warning): From 3fdc9bcf9fca90e30246b972651cbb29756c56fd Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 20 Mar 2021 19:43:41 +0100 Subject: [PATCH 14/29] Fix desc_sig_space --- sphinx/addnodes.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 23cf0acb8..5d26643ed 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -294,8 +294,9 @@ class desc_sig_space(desc_sig_element): """Node for a space in a signature.""" classes = ["w"] - def __init__(self) -> None: - super().__init__(' ', ' ') + def __init__(self, rawsource: str = '', text: str = ' ', + *children: Element, **attributes: Any) -> None: + super().__init__(rawsource, text, *children, **attributes) class desc_sig_name(desc_sig_element): From f769dde254e6f25f5b8e6e29718b90b425c3f235 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 20 Mar 2021 19:48:54 +0100 Subject: [PATCH 15/29] Decl styling, fix html test --- tests/test_build_html.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_build_html.py b/tests/test_build_html.py index b01d6f4eb..c74552d9e 100644 --- a/tests/test_build_html.py +++ b/tests/test_build_html.py @@ -285,10 +285,10 @@ def test_html4_output(app, status, warning): 'objects.html': [ (".//dt[@id='mod.Cls.meth1']", ''), (".//dt[@id='errmod.Error']", ''), - (".//dt/code/span", r'long\(parameter,'), - (".//dt/code/span", r'list\)'), - (".//dt/code/span", 'another'), - (".//dt/code/span", 'one'), + (".//dt/span[@class='sig-name descname']/span[@class='pre']", r'long\(parameter,'), + (".//dt/span[@class='sig-name descname']/span[@class='pre']", r'list\)'), + (".//dt/span[@class='sig-name descname']/span[@class='pre']", 'another'), + (".//dt/span[@class='sig-name descname']/span[@class='pre']", 'one'), (".//a[@href='#mod.Cls'][@class='reference internal']", ''), (".//dl[@class='std userdesc']", ''), (".//dt[@id='userdesc-myobj']", ''), From c0ef6a92656cbada6702d0102b19d58c881763ed Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sat, 20 Mar 2021 20:08:26 +0100 Subject: [PATCH 16/29] Fix isort violation --- sphinx/domains/cpp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index aa88838f4..057cc2037 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -9,8 +9,8 @@ """ import re -from typing import (Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, - TypeVar, Union, cast) +from typing import (Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, TypeVar, + Union, cast) from docutils import nodes from docutils.nodes import Element, Node, TextElement, system_message From 7126503eeb2b13bba35dc18c87631c6e728bf6f0 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Thu, 25 Mar 2021 19:42:50 +0100 Subject: [PATCH 17/29] Decl styling, update writers Add test objects so all builders gets exposed to the new nodes. Make the fallback node post-transform change desc_inline as well. Make the html4, latex, and text writers handle desc_inline. --- sphinx/transforms/post_transforms/__init__.py | 17 +++---- sphinx/writers/html.py | 6 +++ sphinx/writers/latex.py | 48 +++++++++++++------ sphinx/writers/manpage.py | 34 ++++++++----- sphinx/writers/text.py | 30 +++++++++--- tests/roots/test-root/objects.txt | 6 +++ 6 files changed, 101 insertions(+), 40 deletions(-) diff --git a/sphinx/transforms/post_transforms/__init__.py b/sphinx/transforms/post_transforms/__init__.py index b70677e4f..bcc9caf00 100644 --- a/sphinx/transforms/post_transforms/__init__.py +++ b/sphinx/transforms/post_transforms/__init__.py @@ -206,7 +206,7 @@ class OnlyNodeTransform(SphinxPostTransform): class SigElementFallbackTransform(SphinxPostTransform): - """Fallback desc_sig_element nodes to inline if translator does not supported them.""" + """Fallback various desc_* nodes to inline if translator does not supported them.""" default_priority = 200 def run(self, **kwargs: Any) -> None: @@ -218,14 +218,15 @@ class SigElementFallbackTransform(SphinxPostTransform): # subclass of SphinxTranslator supports desc_sig_element nodes automatically. return - if all(has_visitor(translator, node) for node in addnodes.SIG_ELEMENTS): - # the translator supports all desc_sig_element nodes - return - else: - self.fallback() + # for the leaf elements (desc_sig_element), the translator should support _all_ + if not all(has_visitor(translator, node) for node in addnodes.SIG_ELEMENTS): + self.fallback(addnodes.desc_sig_element) - def fallback(self) -> None: - for node in self.document.traverse(addnodes.desc_sig_element): + if not has_visitor(translator, addnodes.desc_inline): + self.fallback(addnodes.desc_inline) + + def fallback(self, nodeType: Any) -> None: + for node in self.document.traverse(nodeType): newnode = nodes.inline() newnode.update_all_atts(node) newnode.extend(node) diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py index 54d7311ea..d633f07e8 100644 --- a/sphinx/writers/html.py +++ b/sphinx/writers/html.py @@ -146,6 +146,12 @@ class HTMLTranslator(SphinxTranslator, BaseTranslator): def depart_desc_content(self, node: Element) -> None: self.body.append('') + def visit_desc_inline(self, node: Element) -> None: + self.body.append(self.starttag(node, 'span', '')) + + def depart_desc_inline(self, node: Element) -> None: + self.body.append('') + # Nodes for high-level structure in signatures ############################################## diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index edef5417f..546db9e31 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -698,6 +698,13 @@ class LaTeXTranslator(SphinxTranslator): def depart_subtitle(self, node: Element) -> None: self.body.append(self.context.pop()) + ############################################################# + # Domain-specific object descriptions + ############################################################# + + # Top-level nodes for descriptions + ################################## + def visit_desc(self, node: Element) -> None: if self.config.latex_show_urls == 'footnote': self.body.append(BLANKLINE) @@ -750,6 +757,31 @@ class LaTeXTranslator(SphinxTranslator): def depart_desc_signature_line(self, node: Element) -> None: self._depart_signature_line(node) + def visit_desc_content(self, node: Element) -> None: + if node.children and not isinstance(node.children[0], nodes.paragraph): + # avoid empty desc environment which causes a formatting bug + self.body.append('~') + + def depart_desc_content(self, node: Element) -> None: + pass + + def visit_desc_inline(self, node: Element) -> None: + self.body.append(r'\sphinxcode{\sphinxupquote{') + + def depart_desc_inline(self, node: Element) -> None: + self.body.append('}}') + + # Nodes for high-level structure in signatures + ############################################## + + def visit_desc_name(self, node: Element) -> None: + self.body.append(r'\sphinxbfcode{\sphinxupquote{') + self.literal_whitespace += 1 + + def depart_desc_name(self, node: Element) -> None: + self.body.append('}}') + self.literal_whitespace -= 1 + def visit_desc_addname(self, node: Element) -> None: self.body.append(r'\sphinxcode{\sphinxupquote{') self.literal_whitespace += 1 @@ -770,14 +802,6 @@ class LaTeXTranslator(SphinxTranslator): def depart_desc_returns(self, node: Element) -> None: self.body.append(r'}') - def visit_desc_name(self, node: Element) -> None: - self.body.append(r'\sphinxbfcode{\sphinxupquote{') - self.literal_whitespace += 1 - - def depart_desc_name(self, node: Element) -> None: - self.body.append('}}') - self.literal_whitespace -= 1 - def visit_desc_parameterlist(self, node: Element) -> None: # close name, open parameterlist self.body.append('}{') @@ -811,13 +835,7 @@ class LaTeXTranslator(SphinxTranslator): def depart_desc_annotation(self, node: Element) -> None: self.body.append('}}') - def visit_desc_content(self, node: Element) -> None: - if node.children and not isinstance(node.children[0], nodes.paragraph): - # avoid empty desc environment which causes a formatting bug - self.body.append('~') - - def depart_desc_content(self, node: Element) -> None: - pass + ############################################## def visit_seealso(self, node: Element) -> None: self.body.append(BLANKLINE) diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py index c11c49892..a43f117ae 100644 --- a/sphinx/writers/manpage.py +++ b/sphinx/writers/manpage.py @@ -120,6 +120,13 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator): def depart_start_of_file(self, node: Element) -> None: pass + ############################################################# + # Domain-specific object descriptions + ############################################################# + + # Top-level nodes for descriptions + ################################## + def visit_desc(self, node: Element) -> None: self.visit_definition_list(node) @@ -139,6 +146,21 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator): def depart_desc_signature_line(self, node: Element) -> None: self.body.append(' ') + def visit_desc_content(self, node: Element) -> None: + self.visit_definition(node) + + def depart_desc_content(self, node: Element) -> None: + self.depart_definition(node) + + # Nodes for high-level structure in signatures + ############################################## + + def visit_desc_name(self, node: Element) -> None: + pass + + def depart_desc_name(self, node: Element) -> None: + pass + def visit_desc_addname(self, node: Element) -> None: pass @@ -157,12 +179,6 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator): def depart_desc_returns(self, node: Element) -> None: pass - def visit_desc_name(self, node: Element) -> None: - pass - - def depart_desc_name(self, node: Element) -> None: - pass - def visit_desc_parameterlist(self, node: Element) -> None: self.body.append('(') self.first_param = 1 @@ -191,11 +207,7 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator): def depart_desc_annotation(self, node: Element) -> None: pass - def visit_desc_content(self, node: Element) -> None: - self.visit_definition(node) - - def depart_desc_content(self, node: Element) -> None: - self.depart_definition(node) + ############################################## def visit_versionmodified(self, node: Element) -> None: self.visit_paragraph(node) diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py index 5bd96de76..d8dab9181 100644 --- a/sphinx/writers/text.py +++ b/sphinx/writers/text.py @@ -536,6 +536,13 @@ class TextTranslator(SphinxTranslator): def depart_attribution(self, node: Element) -> None: pass + ############################################################# + # Domain-specific object descriptions + ############################################################# + + # Top-level nodes + ################# + def visit_desc(self, node: Element) -> None: pass @@ -555,6 +562,22 @@ class TextTranslator(SphinxTranslator): def depart_desc_signature_line(self, node: Element) -> None: self.add_text('\n') + def visit_desc_content(self, node: Element) -> None: + self.new_state() + self.add_text(self.nl) + + def depart_desc_content(self, node: Element) -> None: + self.end_state() + + def visit_desc_inline(self, node: Element) -> None: + pass + + def depart_desc_inline(self, node: Element) -> None: + pass + + # Nodes for high-level structure in signatures + ############################################## + def visit_desc_name(self, node: Element) -> None: pass @@ -606,12 +629,7 @@ class TextTranslator(SphinxTranslator): def depart_desc_annotation(self, node: Element) -> None: pass - def visit_desc_content(self, node: Element) -> None: - self.new_state() - self.add_text(self.nl) - - def depart_desc_content(self, node: Element) -> None: - self.end_state() + ############################################## def visit_figure(self, node: Element) -> None: self.new_state() diff --git a/tests/roots/test-root/objects.txt b/tests/roots/test-root/objects.txt index adb090a44..d5ec2a486 100644 --- a/tests/roots/test-root/objects.txt +++ b/tests/roots/test-root/objects.txt @@ -213,3 +213,9 @@ CPP domain .. cpp:function:: T& operator[]( unsigned j ) const T& operator[]( unsigned j ) const + +.. cpp:function:: template \ + requires A \ + void f() + +- :cpp:expr:`a + b` From a1ac0fd1ec97edf783a93cfc9fdd5c65621e4ff5 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Tue, 30 Mar 2021 11:50:36 +0200 Subject: [PATCH 18/29] Decl styling, C++, fix alternate spellings of operators --- sphinx/domains/cpp.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 057cc2037..1c876081b 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -1246,10 +1246,12 @@ class ASTUnaryOpExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode += addnodes.desc_sig_operator(self.op, self.op) if self.op[0] in 'cn': + signode += addnodes.desc_sig_keyword(self.op, self.op) signode += addnodes.desc_sig_space() - self.expr.describe_signature(signode, mode, env, symbol) + else: + signode += addnodes.desc_sig_operator(self.op, self.op) + self.expr.describe_signature(signode, mode, env, symbol) class ASTSizeofParamPack(ASTExpression): @@ -1484,7 +1486,11 @@ class ASTBinOpExpr(ASTExpression): self.exprs[0].describe_signature(signode, mode, env, symbol) for i in range(1, len(self.exprs)): signode += addnodes.desc_sig_space() - signode += addnodes.desc_sig_operator(self.ops[i - 1], self.ops[i - 1]) + op = self.ops[i - 1] + if ord(op[0]) >= ord('a') and ord(op[0]) <= ord('z'): + signode += addnodes.desc_sig_keyword(op, op) + else: + signode += addnodes.desc_sig_operator(op, op) signode += addnodes.desc_sig_space() self.exprs[i].describe_signature(signode, mode, env, symbol) @@ -1550,7 +1556,11 @@ class ASTAssignmentExpr(ASTExpression): self.exprs[0].describe_signature(signode, mode, env, symbol) for i in range(1, len(self.exprs)): signode += addnodes.desc_sig_space() - signode += addnodes.desc_sig_operator(self.ops[i - 1], self.ops[i - 1]) + op = self.ops[i - 1] + if ord(op[0]) >= ord('a') and ord(op[0]) <= ord('z'): + signode += addnodes.desc_sig_keyword(op, op) + else: + signode += addnodes.desc_sig_operator(op, op) signode += addnodes.desc_sig_space() self.exprs[i].describe_signature(signode, mode, env, symbol) From 9cd9e124ff45553bf5a4385d37eeca6be47f9a80 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Tue, 30 Mar 2021 14:12:56 +0200 Subject: [PATCH 19/29] Decl styling, convert C --- sphinx/domains/c.py | 223 +++++++++++++++++++++++--------------------- 1 file changed, 118 insertions(+), 105 deletions(-) diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 72b9746ac..d3aad012b 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -9,8 +9,7 @@ """ import re -from typing import (Any, Callable, Dict, Generator, Iterator, List, Tuple, Type, TypeVar, - Union, cast) +from typing import Any, Callable, Dict, Generator, Iterator, List, Tuple, TypeVar, Union, cast from docutils import nodes from docutils.nodes import Element, Node, TextElement, system_message @@ -132,6 +131,10 @@ class ASTIdentifier(ASTBaseBase): prefix: str, symbol: "Symbol") -> None: # note: slightly different signature of describe_signature due to the prefix verify_description_mode(mode) + if self.is_anon(): + node = addnodes.desc_sig_name(text="[anonymous]") + else: + node = addnodes.desc_sig_name(self.identifier, self.identifier) if mode == 'markType': targetText = prefix + self.identifier pnode = addnodes.pending_xref('', refdomain='c', @@ -139,21 +142,14 @@ class ASTIdentifier(ASTBaseBase): reftarget=targetText, modname=None, classname=None) pnode['c:parent_key'] = symbol.get_lookup_key() - if self.is_anon(): - pnode += nodes.strong(text="[anonymous]") - else: - pnode += nodes.Text(self.identifier) + pnode += node signode += pnode elif mode == 'lastIsName': - if self.is_anon(): - signode += nodes.strong(text="[anonymous]") - else: - signode += addnodes.desc_name(self.identifier, self.identifier) + nameNode = addnodes.desc_name() + nameNode += node + signode += nameNode elif mode == 'noneIsName': - if self.is_anon(): - signode += nodes.strong(text="[anonymous]") - else: - signode += nodes.Text(self.identifier) + signode += node else: raise Exception('Unknown description mode: %s' % mode) @@ -184,18 +180,18 @@ class ASTNestedName(ASTBase): # just print the name part, with template args, not template params if mode == 'noneIsName': if self.rooted: + assert False, "Can this happen?" # TODO signode += nodes.Text('.') for i in range(len(self.names)): if i != 0: + assert False, "Can this happen?" # TODO signode += nodes.Text('.') n = self.names[i] n.describe_signature(signode, mode, env, '', symbol) elif mode == 'param': assert not self.rooted, str(self) assert len(self.names) == 1 - node = nodes.emphasis() - self.names[0].describe_signature(node, 'noneIsName', env, '', symbol) - signode += node + self.names[0].describe_signature(signode, 'noneIsName', env, '', symbol) elif mode == 'markType' or mode == 'lastIsName' or mode == 'markName': # Each element should be a pending xref targeting the complete # prefix. @@ -213,13 +209,13 @@ class ASTNestedName(ASTBase): if self.rooted: prefix += '.' if mode == 'lastIsName' and len(names) == 0: - signode += nodes.Text('.') + signode += addnodes.desc_sig_punctuation('.', '.') else: - dest += nodes.Text('.') + dest += addnodes.desc_sig_punctuation('.', '.') for i in range(len(names)): ident = names[i] if not first: - dest += nodes.Text('.') + dest += addnodes.desc_sig_punctuation('.', '.') prefix += '.' first = False txt_ident = str(ident) @@ -228,7 +224,7 @@ class ASTNestedName(ASTBase): prefix += txt_ident if mode == 'lastIsName': if len(self.names) > 1: - dest += addnodes.desc_addname('.', '.') + dest += addnodes.desc_sig_punctuation('.', '.') signode += dest self.names[-1].describe_signature(signode, mode, env, '', symbol) else: @@ -262,7 +258,8 @@ class ASTBooleanLiteral(ASTLiteral): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text(str(self))) + txt = str(self) + signode += addnodes.desc_sig_keyword(txt, txt) class ASTNumberLiteral(ASTLiteral): @@ -275,7 +272,7 @@ class ASTNumberLiteral(ASTLiteral): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: txt = str(self) - signode.append(nodes.Text(txt, txt)) + signode += addnodes.desc_sig_literal_number(txt, txt) class ASTCharLiteral(ASTLiteral): @@ -297,7 +294,7 @@ class ASTCharLiteral(ASTLiteral): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: txt = str(self) - signode.append(nodes.Text(txt, txt)) + signode += addnodes.desc_sig_literal_char(txt, txt) class ASTStringLiteral(ASTLiteral): @@ -310,7 +307,7 @@ class ASTStringLiteral(ASTLiteral): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: txt = str(self) - signode.append(nodes.Text(txt, txt)) + signode += addnodes.desc_sig_literal_string(txt, txt) class ASTIdExpression(ASTExpression): @@ -341,9 +338,9 @@ class ASTParenExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('(', '(')) + signode += addnodes.desc_sig_punctuation('(', '(') self.expr.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')', ')')) + signode += addnodes.desc_sig_punctuation(')', ')') # Postfix expressions @@ -374,9 +371,9 @@ class ASTPostfixArray(ASTPostfixOp): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('[')) + signode += addnodes.desc_sig_punctuation('[', '[') self.expr.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(']')) + signode += addnodes.desc_sig_punctuation(']', ']') class ASTPostfixInc(ASTPostfixOp): @@ -385,7 +382,7 @@ class ASTPostfixInc(ASTPostfixOp): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('++')) + signode += addnodes.desc_sig_operator('++', '++') class ASTPostfixDec(ASTPostfixOp): @@ -394,7 +391,7 @@ class ASTPostfixDec(ASTPostfixOp): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('--')) + signode += addnodes.desc_sig_operator('--', '--') class ASTPostfixMemberOfPointer(ASTPostfixOp): @@ -406,7 +403,7 @@ class ASTPostfixMemberOfPointer(ASTPostfixOp): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('->')) + signode += addnodes.desc_sig_operator('->', '->') self.name.describe_signature(signode, 'noneIsName', env, symbol) @@ -444,10 +441,12 @@ class ASTUnaryOpExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text(self.op)) if self.op[0] in 'cn': - signode.append(nodes.Text(" ")) - self.expr.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_keyword(self.op, self.op) + signode += addnodes.desc_sig_space() + else: + signode += addnodes.desc_sig_operator(self.op, self.op) + self.expr.describe_signature(signode, mode, env, symbol) class ASTSizeofType(ASTExpression): @@ -459,9 +458,10 @@ class ASTSizeofType(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('sizeof(')) + signode += addnodes.desc_sig_keyword('sizeof', 'sizeof') + signode += addnodes.desc_sig_punctuation('(', '(') self.typ.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) + signode += addnodes.desc_sig_punctuation(')', ')') class ASTSizeofExpr(ASTExpression): @@ -473,7 +473,8 @@ class ASTSizeofExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('sizeof ')) + signode += addnodes.desc_sig_keyword('sizeof', 'sizeof') + signode += addnodes.desc_sig_space() self.expr.describe_signature(signode, mode, env, symbol) @@ -486,9 +487,10 @@ class ASTAlignofExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('alignof(')) + signode += addnodes.desc_sig_keyword('alignof', 'alignof') + signode += addnodes.desc_sig_punctuation('(', '(') self.typ.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) + signode += addnodes.desc_sig_punctuation(')', ')') # Other expressions @@ -508,9 +510,9 @@ class ASTCastExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode.append(nodes.Text('(')) + signode += addnodes.desc_sig_punctuation('(', '(') self.typ.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) + signode += addnodes.desc_sig_punctuation(')', ')') self.expr.describe_signature(signode, mode, env, symbol) @@ -535,9 +537,13 @@ class ASTBinOpExpr(ASTBase): env: "BuildEnvironment", symbol: "Symbol") -> None: self.exprs[0].describe_signature(signode, mode, env, symbol) for i in range(1, len(self.exprs)): - signode.append(nodes.Text(' ')) - signode.append(nodes.Text(self.ops[i - 1])) - signode.append(nodes.Text(' ')) + signode += addnodes.desc_sig_space() + op = self.ops[i - 1] + if ord(op[0]) >= ord('a') and ord(op[0]) <= ord('z'): + signode += addnodes.desc_sig_keyword(op, op) + else: + signode += addnodes.desc_sig_operator(op, op) + signode += addnodes.desc_sig_space() self.exprs[i].describe_signature(signode, mode, env, symbol) @@ -562,9 +568,13 @@ class ASTAssignmentExpr(ASTExpression): env: "BuildEnvironment", symbol: "Symbol") -> None: self.exprs[0].describe_signature(signode, mode, env, symbol) for i in range(1, len(self.exprs)): - signode.append(nodes.Text(' ')) - signode.append(nodes.Text(self.ops[i - 1])) - signode.append(nodes.Text(' ')) + signode += addnodes.desc_sig_space() + op = self.ops[i - 1] + if ord(op[0]) >= ord('a') and ord(op[0]) <= ord('z'): + signode += addnodes.desc_sig_keyword(op, op) + else: + signode += addnodes.desc_sig_operator(op, op) + signode += addnodes.desc_sig_space() self.exprs[i].describe_signature(signode, mode, env, symbol) @@ -580,7 +590,7 @@ class ASTFallbackExpr(ASTExpression): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode += nodes.Text(self.expr) + signode += nodes.literal(self.expr, self.expr) ################################################################################ @@ -600,7 +610,7 @@ class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - signode += nodes.Text(str(self.name)) + signode += addnodes.desc_sig_keyword_type(self.name, self.name) class ASTTrailingTypeSpecName(ASTTrailingTypeSpec): @@ -623,8 +633,8 @@ class ASTTrailingTypeSpecName(ASTTrailingTypeSpec): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: if self.prefix: - signode += addnodes.desc_annotation(self.prefix, self.prefix) - signode += nodes.Text(' ') + signode += addnodes.desc_sig_keyword(self.prefix, self.prefix) + signode += addnodes.desc_sig_space() self.nestedName.describe_signature(signode, mode, env, symbol=symbol) @@ -647,7 +657,7 @@ class ASTFunctionParameter(ASTBase): env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) if self.ellipsis: - signode += nodes.Text('...') + signode += addnodes.desc_sig_punctuation('...', '...') else: self.arg.describe_signature(signode, mode, env, symbol=symbol) @@ -688,17 +698,18 @@ class ASTParameters(ASTBase): paramlist += param signode += paramlist else: - signode += nodes.Text('(', '(') + signode += addnodes.desc_sig_punctuation('(', '(') first = True for arg in self.args: if not first: - signode += nodes.Text(', ', ', ') + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() first = False arg.describe_signature(signode, 'markType', env, symbol=symbol) - signode += nodes.Text(')', ')') + signode += addnodes.desc_sig_punctuation(')', ')') for attr in self.attrs: - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() attr.describe_signature(signode) @@ -744,12 +755,12 @@ class ASTDeclSpecsSimple(ASTBaseBase): def describe_signature(self, modifiers: List[Node]) -> None: def _add(modifiers: List[Node], text: str) -> None: if len(modifiers) > 0: - modifiers.append(nodes.Text(' ')) - modifiers.append(addnodes.desc_annotation(text, text)) + modifiers.append(addnodes.desc_sig_space()) + modifiers.append(addnodes.desc_sig_keyword(text, text)) for attr in self.attrs: if len(modifiers) > 0: - modifiers.append(nodes.Text(' ')) + modifiers.append(addnodes.desc_sig_space()) modifiers.append(attr.describe_signature(modifiers)) if self.storage: _add(modifiers, self.storage) @@ -799,24 +810,19 @@ class ASTDeclSpecs(ASTBase): verify_description_mode(mode) modifiers: List[Node] = [] - def _add(modifiers: List[Node], text: str) -> None: - if len(modifiers) > 0: - modifiers.append(nodes.Text(' ')) - modifiers.append(addnodes.desc_annotation(text, text)) - self.leftSpecs.describe_signature(modifiers) for m in modifiers: signode += m if self.trailingTypeSpec: if len(modifiers) > 0: - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() self.trailingTypeSpec.describe_signature(signode, mode, env, symbol=symbol) modifiers = [] self.rightSpecs.describe_signature(modifiers) if len(modifiers) > 0: - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() for m in modifiers: signode += m @@ -857,13 +863,13 @@ class ASTArray(ASTBase): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) - signode.append(nodes.Text("[")) + signode += addnodes.desc_sig_punctuation('[', '[') addSpace = False def _add(signode: TextElement, text: str) -> bool: if addSpace: - signode += nodes.Text(' ') - signode += addnodes.desc_annotation(text, text) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_keyword(text, text) return True if self.static: @@ -875,12 +881,12 @@ class ASTArray(ASTBase): if self.const: addSpace = _add(signode, 'const') if self.vla: - signode.append(nodes.Text('*')) + signode += addnodes.desc_sig_punctuation('*', '*') elif self.size: if addSpace: - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() self.size.describe_signature(signode, 'markType', env, symbol) - signode.append(nodes.Text("]")) + signode += addnodes.desc_sig_punctuation(']', ']') class ASTDeclarator(ASTBase): @@ -964,7 +970,9 @@ class ASTDeclaratorNameBitField(ASTDeclarator): verify_description_mode(mode) if self.declId: self.declId.describe_signature(signode, mode, env, symbol) - signode += nodes.Text(' : ', ' : ') + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation(':', ':') + signode += addnodes.desc_sig_space() self.size.describe_signature(signode, mode, env, symbol) @@ -1016,28 +1024,28 @@ class ASTDeclaratorPtr(ASTDeclarator): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) - signode += nodes.Text("*") + signode += addnodes.desc_sig_punctuation('*', '*') for a in self.attrs: a.describe_signature(signode) if len(self.attrs) > 0 and (self.restrict or self.volatile or self.const): - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() def _add_anno(signode: TextElement, text: str) -> None: - signode += addnodes.desc_annotation(text, text) + signode += addnodes.desc_sig_keyword(text, text) if self.restrict: _add_anno(signode, 'restrict') if self.volatile: if self.restrict: - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() _add_anno(signode, 'volatile') if self.const: if self.restrict or self.volatile: - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() _add_anno(signode, 'const') if self.const or self.volatile or self.restrict or len(self.attrs) > 0: if self.next.require_space_after_declSpecs(): - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() self.next.describe_signature(signode, mode, env, symbol) @@ -1070,9 +1078,9 @@ class ASTDeclaratorParen(ASTDeclarator): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) - signode += nodes.Text('(') + signode += addnodes.desc_sig_punctuation('(', '(') self.inner.describe_signature(signode, mode, env, symbol) - signode += nodes.Text(')') + signode += addnodes.desc_sig_punctuation(')', ')') self.next.describe_signature(signode, "noneIsName", env, symbol) @@ -1090,15 +1098,16 @@ class ASTParenExprList(ASTBaseParenExprList): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) - signode.append(nodes.Text('(')) + signode += addnodes.desc_sig_punctuation('(', '(') first = True for e in self.exprs: if not first: - signode.append(nodes.Text(', ')) + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() else: first = False e.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) + signode += addnodes.desc_sig_punctuation(')', ')') class ASTBracedInitList(ASTBase): @@ -1114,17 +1123,18 @@ class ASTBracedInitList(ASTBase): def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) - signode.append(nodes.Text('{')) + signode += addnodes.desc_sig_punctuation('{', '{') first = True for e in self.exprs: if not first: - signode.append(nodes.Text(', ')) + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() else: first = False e.describe_signature(signode, mode, env, symbol) if self.trailingComma: - signode.append(nodes.Text(',')) - signode.append(nodes.Text('}')) + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_punctuation('}', '}') class ASTInitializer(ASTBase): @@ -1144,7 +1154,9 @@ class ASTInitializer(ASTBase): env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) if self.hasAssign: - signode.append(nodes.Text(' = ')) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation('=', '=') + signode += addnodes.desc_sig_space() self.value.describe_signature(signode, 'markType', env, symbol) @@ -1187,7 +1199,7 @@ class ASTType(ASTBase): self.declSpecs.describe_signature(signode, 'markType', env, symbol) if (self.decl.require_space_after_declSpecs() and len(str(self.declSpecs)) > 0): - signode += nodes.Text(' ') + signode += addnodes.desc_sig_space() # for parameters that don't really declare new names we get 'markType', # this should not be propagated, but be 'noneIsName'. if mode == 'markType': @@ -1241,10 +1253,10 @@ class ASTMacroParameter(ASTBase): env: "BuildEnvironment", symbol: "Symbol") -> None: verify_description_mode(mode) if self.ellipsis: - signode += nodes.Text('...') + signode += addnodes.desc_sig_punctuation('...', '...') elif self.variadic: name = str(self) - signode += nodes.emphasis(name, name) + signode += addnodes.desc_sig_name(name, name) else: self.arg.describe_signature(signode, mode, env, symbol=symbol) @@ -1427,23 +1439,27 @@ class ASTDeclaration(ASTBaseBase): elif self.objectType == 'macro': pass elif self.objectType == 'struct': - mainDeclNode += addnodes.desc_annotation('struct ', 'struct ') + mainDeclNode += addnodes.desc_sig_keyword('struct', 'struct') + mainDeclNode += addnodes.desc_sig_space() elif self.objectType == 'union': - mainDeclNode += addnodes.desc_annotation('union ', 'union ') + mainDeclNode += addnodes.desc_sig_keyword('union', 'union') + mainDeclNode += addnodes.desc_sig_space() elif self.objectType == 'enum': - mainDeclNode += addnodes.desc_annotation('enum ', 'enum ') + mainDeclNode += addnodes.desc_sig_keyword('enum', 'enum') + mainDeclNode += addnodes.desc_sig_space() elif self.objectType == 'enumerator': - mainDeclNode += addnodes.desc_annotation('enumerator ', 'enumerator ') + mainDeclNode += addnodes.desc_sig_keyword('enumerator', 'enumerator') + mainDeclNode += addnodes.desc_sig_space() elif self.objectType == 'type': decl = cast(ASTType, self.declaration) prefix = decl.get_type_declaration_prefix() - prefix += ' ' - mainDeclNode += addnodes.desc_annotation(prefix, prefix) + mainDeclNode += addnodes.desc_sig_keyword(prefix, prefix) + mainDeclNode += addnodes.desc_sig_space() else: assert False self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol) if self.semicolon: - mainDeclNode += nodes.Text(';') + mainDeclNode += addnodes.desc_sig_punctuation(';', ';') class SymbolLookupResult: @@ -3661,31 +3677,28 @@ class CExprRole(SphinxRole): if asCode: # render the expression as inline code self.class_type = 'c-expr' - self.node_type: Type[TextElement] = nodes.literal else: # render the expression as inline text self.class_type = 'c-texpr' - self.node_type = nodes.inline def run(self) -> Tuple[List[Node], List[system_message]]: text = self.text.replace('\n', ' ') parser = DefinitionParser(text, location=self.get_source_info(), config=self.env.config) # attempt to mimic XRefRole classes, except that... - classes = ['xref', 'c', self.class_type] try: ast = parser.parse_expression() except DefinitionError as ex: logger.warning('Unparseable C expression: %r\n%s', text, ex, location=self.get_source_info()) # see below - return [self.node_type(text, text, classes=classes)], [] + return [addnodes.desc_inline('c', text, text, classes=[self.class_type])], [] parentSymbol = self.env.temp_data.get('c:parent_symbol', None) if parentSymbol is None: parentSymbol = self.env.domaindata['c']['root_symbol'] # ...most if not all of these classes should really apply to the individual references, # not the container node - signode = self.node_type(classes=classes) + signode = addnodes.desc_inline('c', classes=[self.class_type]) ast.describe_signature(signode, 'markType', self.env, parentSymbol) return [signode], [] From f1d9d0aac61ad40fba8bfd82b97352782aa11a45 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Tue, 30 Mar 2021 14:13:17 +0200 Subject: [PATCH 20/29] Decl styling, C styling and C++ char literal styling --- sphinx/themes/basic/static/basic.css_t | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t index 7f505f6b1..450b92ead 100644 --- a/sphinx/themes/basic/static/basic.css_t +++ b/sphinx/themes/basic/static/basic.css_t @@ -538,19 +538,23 @@ table.hlist td { /* C++ specific styling */ +.sig-inline.c-texpr, .sig-inline.cpp-texpr { font-family: unset; } +.sig.c .k, .sig.c .kt, .sig.cpp .k, .sig.cpp .kt { color: #0033B3; } +.sig.c .m, .sig.cpp .m { color: #1750EB; } -.sig.cpp .s { +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { color: #067D17; } From a840b9e4d6fcf4c9144ac7ca7e15ac66cf5545df Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Tue, 30 Mar 2021 16:18:00 +0200 Subject: [PATCH 21/29] C, C++, fix unary op --- sphinx/domains/c.py | 2 +- sphinx/domains/cpp.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index d3aad012b..1eeaf65cf 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -446,7 +446,7 @@ class ASTUnaryOpExpr(ASTExpression): signode += addnodes.desc_sig_space() else: signode += addnodes.desc_sig_operator(self.op, self.op) - self.expr.describe_signature(signode, mode, env, symbol) + self.expr.describe_signature(signode, mode, env, symbol) class ASTSizeofType(ASTExpression): diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 1c876081b..4a36f6131 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -1251,7 +1251,7 @@ class ASTUnaryOpExpr(ASTExpression): signode += addnodes.desc_sig_space() else: signode += addnodes.desc_sig_operator(self.op, self.op) - self.expr.describe_signature(signode, mode, env, symbol) + self.expr.describe_signature(signode, mode, env, symbol) class ASTSizeofParamPack(ASTExpression): From da5b8e41e337064028fb5b321155a8933c91f819 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Wed, 7 Apr 2021 20:37:44 +0200 Subject: [PATCH 22/29] Decl styling, fixes from review --- sphinx/addnodes.py | 4 ++-- sphinx/transforms/post_transforms/__init__.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index 5d26643ed..a2fb776e4 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -119,13 +119,13 @@ class toctree(nodes.General, nodes.Element, translatable): # Domain-specific object descriptions (class, function etc.) ############################################################# -class _desc_classes_injector(nodes.TextElement): +class _desc_classes_injector(nodes.Element): """Helper base class for injecting a fixes list of classes. Use as the first base class. """ - classes = [] # type: List[str] + classes: List[str] = [] def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) diff --git a/sphinx/transforms/post_transforms/__init__.py b/sphinx/transforms/post_transforms/__init__.py index bcc9caf00..e2899d994 100644 --- a/sphinx/transforms/post_transforms/__init__.py +++ b/sphinx/transforms/post_transforms/__init__.py @@ -233,7 +233,7 @@ class SigElementFallbackTransform(SphinxPostTransform): node.replace_self(newnode) -class DescSigAddDomainAsClass(SphinxPostTransform): +class PropagateDescDomain(SphinxPostTransform): """Add the domain name of the parent node as a class in each desc_signature node.""" default_priority = 200 @@ -246,7 +246,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: app.add_post_transform(ReferencesResolver) app.add_post_transform(OnlyNodeTransform) app.add_post_transform(SigElementFallbackTransform) - app.add_post_transform(DescSigAddDomainAsClass) + app.add_post_transform(PropagateDescDomain) return { 'version': 'builtin', From cb21eb2283b0489adf10cc6799de3be088eb3fe1 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sun, 11 Apr 2021 19:55:03 +0200 Subject: [PATCH 23/29] Decl styling, handle desc_inline in manpage and texinfo --- sphinx/writers/manpage.py | 6 ++++++ sphinx/writers/texinfo.py | 28 ++++++++++++++++++++++------ 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py index a43f117ae..3f0eea5eb 100644 --- a/sphinx/writers/manpage.py +++ b/sphinx/writers/manpage.py @@ -152,6 +152,12 @@ class ManualPageTranslator(SphinxTranslator, BaseTranslator): def depart_desc_content(self, node: Element) -> None: self.depart_definition(node) + def visit_desc_inline(self, node: Element) -> None: + pass + + def depart_desc_inline(self, node: Element) -> None: + pass + # Nodes for high-level structure in signatures ############################################## diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py index cad6f3685..6df558323 100644 --- a/sphinx/writers/texinfo.py +++ b/sphinx/writers/texinfo.py @@ -1367,7 +1367,12 @@ class TexinfoTranslator(SphinxTranslator): self.body.append('\n\n') raise nodes.SkipNode - # -- Desc + ############################################################# + # Domain-specific object descriptions + ############################################################# + + # Top-level nodes for descriptions + ################################## def visit_desc(self, node: addnodes.desc) -> None: self.descs.append(node) @@ -1408,6 +1413,21 @@ class TexinfoTranslator(SphinxTranslator): def depart_desc_signature_line(self, node: Element) -> None: pass + def visit_desc_content(self, node: Element) -> None: + pass + + def depart_desc_content(self, node: Element) -> None: + pass + + def visit_desc_inline(self, node: Element) -> None: + pass + + def depart_desc_inline(self, node: Element) -> None: + pass + + # Nodes for high-level structure in signatures + ############################################## + def visit_desc_name(self, node: Element) -> None: pass @@ -1470,11 +1490,7 @@ class TexinfoTranslator(SphinxTranslator): def depart_desc_annotation(self, node: Element) -> None: pass - def visit_desc_content(self, node: Element) -> None: - pass - - def depart_desc_content(self, node: Element) -> None: - pass + ############################################## def visit_inline(self, node: Element) -> None: pass From 9b3b8a49fb0c2c0059577ccd8993fb6ae64deb97 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Mon, 12 Apr 2021 20:24:11 +0200 Subject: [PATCH 24/29] Decl styling, fix font size in basic --- sphinx/themes/basic/static/basic.css_t | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sphinx/themes/basic/static/basic.css_t b/sphinx/themes/basic/static/basic.css_t index 450b92ead..25ab688e4 100644 --- a/sphinx/themes/basic/static/basic.css_t +++ b/sphinx/themes/basic/static/basic.css_t @@ -511,12 +511,19 @@ table.hlist td { /* -- object description styles --------------------------------------------- */ .sig { - font-family: monospace; + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; } .sig-name, code.descname { background-color: transparent; font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { font-size: 1.2em; } From 709339ec53430691593b3a6059bf4ffc040dfe73 Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Mon, 12 Apr 2021 20:40:08 +0200 Subject: [PATCH 25/29] Decl styling, disable smart quoting in sigs --- sphinx/addnodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py index a2fb776e4..8a020b02e 100644 --- a/sphinx/addnodes.py +++ b/sphinx/addnodes.py @@ -119,7 +119,7 @@ class toctree(nodes.General, nodes.Element, translatable): # Domain-specific object descriptions (class, function etc.) ############################################################# -class _desc_classes_injector(nodes.Element): +class _desc_classes_injector(nodes.Element, not_smartquotable): """Helper base class for injecting a fixes list of classes. Use as the first base class. From c7b169c5a95f9d84583afcfe18773742a67cdef0 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 17 Apr 2021 02:06:57 +0900 Subject: [PATCH 26/29] Fix #8127: py domain: Ellipsis in info-field-list causes nit-picky warning On parsing the types, the leading dot of the ellipsis (...) is considered as a reference name. And its first dot is considered as a notation for relative type reference (ex. ".ClassName"). As a result, it was converted double dots unexpectedly. This changes the parsing rule to treat the ellipsis as a symbol, not a name. --- CHANGES | 1 + sphinx/domains/python.py | 2 +- tests/test_domain_py.py | 25 ++++++++++++++++++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 28e89863f..581be506e 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,7 @@ Deprecated Features added -------------- +* #8127: py domain: Ellipsis in info-field-list causes nit-picky warning * #9023: More CSS classes on domain descriptions, see :ref:`nodes` for details. Bugs fixed diff --git a/sphinx/domains/python.py b/sphinx/domains/python.py index d0c5f7118..dbb315e6e 100644 --- a/sphinx/domains/python.py +++ b/sphinx/domains/python.py @@ -304,7 +304,7 @@ class PyXrefMixin: def make_xrefs(self, rolename: str, domain: str, target: str, innernode: Type[TextlikeNode] = nodes.emphasis, contnode: Node = None, env: BuildEnvironment = None) -> List[Node]: - delims = r'(\s*[\[\]\(\),](?:\s*or\s)?\s*|\s+or\s+)' + delims = r'(\s*[\[\]\(\),](?:\s*or\s)?\s*|\s+or\s+|\.\.\.)' delims_re = re.compile(delims) sub_targets = re.split(delims, target) diff --git a/tests/test_domain_py.py b/tests/test_domain_py.py index f5df9084b..214cb58a0 100644 --- a/tests/test_domain_py.py +++ b/tests/test_domain_py.py @@ -876,7 +876,9 @@ def test_info_field_list(app): "\n" " :param str name: blah blah\n" " :param age: blah blah\n" - " :type age: int\n") + " :type age: int\n" + " :param items: blah blah\n" + " :type items: Tuple[str, ...]\n") doctree = restructuredtext.parse(app, text) print(doctree) @@ -890,6 +892,7 @@ def test_info_field_list(app): assert_node(doctree[3][1][0][0], ([nodes.field_name, "Parameters"], [nodes.field_body, nodes.bullet_list, ([nodes.list_item, nodes.paragraph], + [nodes.list_item, nodes.paragraph], [nodes.list_item, nodes.paragraph])])) # :param str name: @@ -916,6 +919,26 @@ def test_info_field_list(app): refdomain="py", reftype="class", reftarget="int", **{"py:module": "example", "py:class": "Class"}) + # :param items: + :type items: + assert_node(doctree[3][1][0][0][1][0][2][0], + ([addnodes.literal_strong, "items"], + " (", + [pending_xref, addnodes.literal_emphasis, "Tuple"], + [addnodes.literal_emphasis, "["], + [pending_xref, addnodes.literal_emphasis, "str"], + [addnodes.literal_emphasis, ", "], + [addnodes.literal_emphasis, "..."], + [addnodes.literal_emphasis, "]"], + ")", + " -- ", + "blah blah")) + assert_node(doctree[3][1][0][0][1][0][2][0][2], pending_xref, + refdomain="py", reftype="class", reftarget="Tuple", + **{"py:module": "example", "py:class": "Class"}) + assert_node(doctree[3][1][0][0][1][0][2][0][4], pending_xref, + refdomain="py", reftype="class", reftarget="str", + **{"py:module": "example", "py:class": "Class"}) + def test_info_field_list_var(app): text = (".. py:class:: Class\n" From 5f56d4146e5ca67887d19db7df1dbe6b1af863b3 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 17 Apr 2021 16:51:19 +0900 Subject: [PATCH 27/29] Fix #9103: LaTeX: imgconverter: conversion runs even if not needed The imgconverter unexpectedly goes to convert even if the given image is supported by the target builder when the image globbing is not used. This enables format guess-ing on not globbed. --- CHANGES | 1 + sphinx/transforms/post_transforms/images.py | 2 +- tests/roots/test-ext-imgconverter/img.pdf | Bin 0 -> 141783 bytes tests/roots/test-ext-imgconverter/index.rst | 1 + tests/test_ext_imgconverter.py | 5 +++++ 5 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 tests/roots/test-ext-imgconverter/img.pdf diff --git a/CHANGES b/CHANGES index 28e89863f..b61fdd58b 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,7 @@ Deprecated Features added -------------- +* #9103: LaTeX: imgconverter: conversion runs even if not needed * #9023: More CSS classes on domain descriptions, see :ref:`nodes` for details. Bugs fixed diff --git a/sphinx/transforms/post_transforms/images.py b/sphinx/transforms/post_transforms/images.py index f9b78837f..c9ed572b8 100644 --- a/sphinx/transforms/post_transforms/images.py +++ b/sphinx/transforms/post_transforms/images.py @@ -197,7 +197,7 @@ class ImageConverter(BaseImageConverter): def match(self, node: nodes.image) -> bool: if not self.app.builder.supported_image_types: return False - elif set(node['candidates']) & set(self.app.builder.supported_image_types): + elif set(self.guess_mimetypes(node)) & set(self.app.builder.supported_image_types): # builder supports the image; no need to convert return False elif self.available is None: diff --git a/tests/roots/test-ext-imgconverter/img.pdf b/tests/roots/test-ext-imgconverter/img.pdf new file mode 100644 index 0000000000000000000000000000000000000000..cacbd855df4ffdb1c9acd5dbd22fad2fce51b532 GIT binary patch literal 141783 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

P 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 Y+uk4b>tlj<)Hl%MId|^COJkn@7dPrOEC2ui literal 0 HcmV?d00001 diff --git a/tests/roots/test-ext-imgconverter/index.rst b/tests/roots/test-ext-imgconverter/index.rst index 786c92e8d..f8ef1d6e2 100644 --- a/tests/roots/test-ext-imgconverter/index.rst +++ b/tests/roots/test-ext-imgconverter/index.rst @@ -2,3 +2,4 @@ test-ext-imgconverter ===================== .. image:: svgimg.svg +.. image:: img.pdf diff --git a/tests/test_ext_imgconverter.py b/tests/test_ext_imgconverter.py index 5f6f236cf..b4fd46d25 100644 --- a/tests/test_ext_imgconverter.py +++ b/tests/test_ext_imgconverter.py @@ -19,6 +19,11 @@ def test_ext_imgconverter(app, status, warning): app.builder.build_all() content = (app.outdir / 'python.tex').read_text() + + # supported image (not converted) + assert '\\sphinxincludegraphics{{img}.pdf}' in content + + # non supported image (converted) assert '\\sphinxincludegraphics{{svgimg}.png}' in content assert not (app.outdir / 'svgimg.svg').exists() assert (app.outdir / 'svgimg.png').exists() From c2c2b81f9156c82df6d7795614736f99187195da Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sat, 17 Apr 2021 23:54:44 +0900 Subject: [PATCH 28/29] Fix #8818: autodoc: Super class having ``Any`` arguments causes nit-picky warning On generating the base class information, unexpected nit-picky warning for ``typing.Any`` was emitted. This fixes it by using `~` prefix on generating a cross-reference to make it valid. --- CHANGES | 1 + sphinx/util/typing.py | 55 +++++++++++++++++--------- tests/test_ext_autodoc.py | 4 +- tests/test_ext_autodoc_autoclass.py | 3 +- tests/test_util_typing.py | 60 ++++++++++++++++++----------- 5 files changed, 78 insertions(+), 45 deletions(-) diff --git a/CHANGES b/CHANGES index 581be506e..654500aaf 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,7 @@ Deprecated Features added -------------- +* #8818: autodoc: Super class having ``Any`` arguments causes nit-picky warning * #8127: py domain: Ellipsis in info-field-list causes nit-picky warning * #9023: More CSS classes on domain descriptions, see :ref:`nodes` for details. diff --git a/sphinx/util/typing.py b/sphinx/util/typing.py index fcecb8bb1..957f8a332 100644 --- a/sphinx/util/typing.py +++ b/sphinx/util/typing.py @@ -138,16 +138,16 @@ def _restify_py37(cls: Optional[Type]) -> str: if len(cls.__args__) > 1 and cls.__args__[-1] is NoneType: if len(cls.__args__) > 2: args = ', '.join(restify(a) for a in cls.__args__[:-1]) - return ':obj:`Optional`\\ [:obj:`Union`\\ [%s]]' % args + return ':obj:`~typing.Optional`\\ [:obj:`~typing.Union`\\ [%s]]' % args else: - return ':obj:`Optional`\\ [%s]' % restify(cls.__args__[0]) + return ':obj:`~typing.Optional`\\ [%s]' % restify(cls.__args__[0]) else: args = ', '.join(restify(a) for a in cls.__args__) - return ':obj:`Union`\\ [%s]' % args + return ':obj:`~typing.Union`\\ [%s]' % args elif inspect.isgenericalias(cls): if getattr(cls, '_name', None): if cls.__module__ == 'typing': - text = ':class:`%s`' % cls._name + text = ':class:`~%s.%s`' % (cls.__module__, cls._name) else: text = ':class:`%s.%s`' % (cls.__module__, cls._name) else: @@ -167,20 +167,23 @@ def _restify_py37(cls: Optional[Type]) -> str: return text elif hasattr(cls, '__qualname__'): if cls.__module__ == 'typing': - return ':class:`%s`' % cls.__qualname__ + return ':class:`~%s.%s`' % (cls.__module__, cls.__qualname__) else: return ':class:`%s.%s`' % (cls.__module__, cls.__qualname__) elif hasattr(cls, '_name'): # SpecialForm if cls.__module__ == 'typing': - return ':obj:`%s`' % cls._name + return ':obj:`~%s.%s`' % (cls.__module__, cls._name) else: return ':obj:`%s.%s`' % (cls.__module__, cls._name) elif isinstance(cls, ForwardRef): return ':class:`%s`' % cls.__forward_arg__ else: # not a class (ex. TypeVar) - return ':obj:`%s.%s`' % (cls.__module__, cls.__name__) + if cls.__module__ == 'typing': + return ':obj:`~%s.%s`' % (cls.__module__, cls.__name__) + else: + return ':obj:`%s.%s`' % (cls.__module__, cls.__name__) def _restify_py36(cls: Optional[Type]) -> str: @@ -203,13 +206,23 @@ def _restify_py36(cls: Optional[Type]) -> str: if (isinstance(cls, typing.TupleMeta) and # type: ignore not hasattr(cls, '__tuple_params__')): + if module == 'typing': + reftext = ':class:`~typing.%s`' % qualname + else: + reftext = ':class:`%s`' % qualname + params = cls.__args__ if params: param_str = ', '.join(restify(p) for p in params) - return ':class:`%s`\\ [%s]' % (qualname, param_str) + return reftext + '\\ [%s]' % param_str else: - return ':class:`%s`' % qualname + return reftext elif isinstance(cls, typing.GenericMeta): + if module == 'typing': + reftext = ':class:`~typing.%s`' % qualname + else: + reftext = ':class:`%s`' % qualname + if cls.__args__ is None or len(cls.__args__) <= 2: # type: ignore # NOQA params = cls.__args__ # type: ignore elif cls.__origin__ == Generator: # type: ignore @@ -217,13 +230,13 @@ def _restify_py36(cls: Optional[Type]) -> str: else: # typing.Callable args = ', '.join(restify(arg) for arg in cls.__args__[:-1]) # type: ignore result = restify(cls.__args__[-1]) # type: ignore - return ':class:`%s`\\ [[%s], %s]' % (qualname, args, result) + return reftext + '\\ [[%s], %s]' % (args, result) if params: param_str = ', '.join(restify(p) for p in params) - return ':class:`%s`\\ [%s]' % (qualname, param_str) + return reftext + '\\ [%s]' % (param_str) else: - return ':class:`%s`' % qualname + return reftext elif (hasattr(cls, '__origin__') and cls.__origin__ is typing.Union): params = cls.__args__ @@ -231,32 +244,36 @@ def _restify_py36(cls: Optional[Type]) -> str: if len(params) > 1 and params[-1] is NoneType: if len(params) > 2: param_str = ", ".join(restify(p) for p in params[:-1]) - return ':obj:`Optional`\\ [:obj:`Union`\\ [%s]]' % param_str + return (':obj:`~typing.Optional`\\ ' + '[:obj:`~typing.Union`\\ [%s]]' % param_str) else: - return ':obj:`Optional`\\ [%s]' % restify(params[0]) + return ':obj:`~typing.Optional`\\ [%s]' % restify(params[0]) else: param_str = ', '.join(restify(p) for p in params) - return ':obj:`Union`\\ [%s]' % param_str + return ':obj:`~typing.Union`\\ [%s]' % param_str else: return ':obj:`Union`' elif hasattr(cls, '__qualname__'): if cls.__module__ == 'typing': - return ':class:`%s`' % cls.__qualname__ + return ':class:`~%s.%s`' % (cls.__module__, cls.__qualname__) else: return ':class:`%s.%s`' % (cls.__module__, cls.__qualname__) elif hasattr(cls, '_name'): # SpecialForm if cls.__module__ == 'typing': - return ':obj:`%s`' % cls._name + return ':obj:`~%s.%s`' % (cls.__module__, cls._name) else: return ':obj:`%s.%s`' % (cls.__module__, cls._name) elif hasattr(cls, '__name__'): # not a class (ex. TypeVar) - return ':obj:`%s.%s`' % (cls.__module__, cls.__name__) + if cls.__module__ == 'typing': + return ':obj:`~%s.%s`' % (cls.__module__, cls.__name__) + else: + return ':obj:`%s.%s`' % (cls.__module__, cls.__name__) else: # others (ex. Any) if cls.__module__ == 'typing': - return ':obj:`%s`' % qualname + return ':obj:`~%s.%s`' % (cls.__module__, qualname) else: return ':obj:`%s.%s`' % (cls.__module__, qualname) diff --git a/tests/test_ext_autodoc.py b/tests/test_ext_autodoc.py index 8ace97813..ccdee6bb2 100644 --- a/tests/test_ext_autodoc.py +++ b/tests/test_ext_autodoc.py @@ -1893,12 +1893,12 @@ def test_autodoc_GenericAlias(app): ' .. py:attribute:: Class.T', ' :module: target.genericalias', '', - ' alias of :class:`List`\\ [:class:`int`]', + ' alias of :class:`~typing.List`\\ [:class:`int`]', '', '.. py:attribute:: T', ' :module: target.genericalias', '', - ' alias of :class:`List`\\ [:class:`int`]', + ' alias of :class:`~typing.List`\\ [:class:`int`]', ] else: assert list(actual) == [ diff --git a/tests/test_ext_autodoc_autoclass.py b/tests/test_ext_autodoc_autoclass.py index 940263387..d879f8e14 100644 --- a/tests/test_ext_autodoc_autoclass.py +++ b/tests/test_ext_autodoc_autoclass.py @@ -256,7 +256,8 @@ def test_show_inheritance_for_subclass_of_generic_type(app): '.. py:class:: Quux(iterable=(), /)', ' :module: target.classes', '', - ' Bases: :class:`List`\\ [:obj:`Union`\\ [:class:`int`, :class:`float`]]', + ' Bases: :class:`~typing.List`\\ ' + '[:obj:`~typing.Union`\\ [:class:`int`, :class:`float`]]', '', ' A subclass of List[Union[int, float]]', '', diff --git a/tests/test_util_typing.py b/tests/test_util_typing.py index 5a5808ac5..d85eb0849 100644 --- a/tests/test_util_typing.py +++ b/tests/test_util_typing.py @@ -47,42 +47,56 @@ def test_restify(): assert restify(Integral) == ":class:`numbers.Integral`" assert restify(Struct) == ":class:`struct.Struct`" assert restify(TracebackType) == ":class:`types.TracebackType`" - assert restify(Any) == ":obj:`Any`" + assert restify(Any) == ":obj:`~typing.Any`" def test_restify_type_hints_containers(): - assert restify(List) == ":class:`List`" - assert restify(Dict) == ":class:`Dict`" - assert restify(List[int]) == ":class:`List`\\ [:class:`int`]" - assert restify(List[str]) == ":class:`List`\\ [:class:`str`]" - assert restify(Dict[str, float]) == ":class:`Dict`\\ [:class:`str`, :class:`float`]" - assert restify(Tuple[str, str, str]) == ":class:`Tuple`\\ [:class:`str`, :class:`str`, :class:`str`]" - assert restify(Tuple[str, ...]) == ":class:`Tuple`\\ [:class:`str`, ...]" - assert restify(List[Dict[str, Tuple]]) == ":class:`List`\\ [:class:`Dict`\\ [:class:`str`, :class:`Tuple`]]" - assert restify(MyList[Tuple[int, int]]) == ":class:`tests.test_util_typing.MyList`\\ [:class:`Tuple`\\ [:class:`int`, :class:`int`]]" - assert restify(Generator[None, None, None]) == ":class:`Generator`\\ [:obj:`None`, :obj:`None`, :obj:`None`]" + assert restify(List) == ":class:`~typing.List`" + assert restify(Dict) == ":class:`~typing.Dict`" + assert restify(List[int]) == ":class:`~typing.List`\\ [:class:`int`]" + assert restify(List[str]) == ":class:`~typing.List`\\ [:class:`str`]" + assert restify(Dict[str, float]) == (":class:`~typing.Dict`\\ " + "[:class:`str`, :class:`float`]") + assert restify(Tuple[str, str, str]) == (":class:`~typing.Tuple`\\ " + "[:class:`str`, :class:`str`, :class:`str`]") + assert restify(Tuple[str, ...]) == ":class:`~typing.Tuple`\\ [:class:`str`, ...]" + assert restify(List[Dict[str, Tuple]]) == (":class:`~typing.List`\\ " + "[:class:`~typing.Dict`\\ " + "[:class:`str`, :class:`~typing.Tuple`]]") + assert restify(MyList[Tuple[int, int]]) == (":class:`tests.test_util_typing.MyList`\\ " + "[:class:`~typing.Tuple`\\ " + "[:class:`int`, :class:`int`]]") + assert restify(Generator[None, None, None]) == (":class:`~typing.Generator`\\ " + "[:obj:`None`, :obj:`None`, :obj:`None`]") def test_restify_type_hints_Callable(): - assert restify(Callable) == ":class:`Callable`" + assert restify(Callable) == ":class:`~typing.Callable`" if sys.version_info >= (3, 7): - assert restify(Callable[[str], int]) == ":class:`Callable`\\ [[:class:`str`], :class:`int`]" - assert restify(Callable[..., int]) == ":class:`Callable`\\ [[...], :class:`int`]" + assert restify(Callable[[str], int]) == (":class:`~typing.Callable`\\ " + "[[:class:`str`], :class:`int`]") + assert restify(Callable[..., int]) == (":class:`~typing.Callable`\\ " + "[[...], :class:`int`]") else: - assert restify(Callable[[str], int]) == ":class:`Callable`\\ [:class:`str`, :class:`int`]" - assert restify(Callable[..., int]) == ":class:`Callable`\\ [..., :class:`int`]" + assert restify(Callable[[str], int]) == (":class:`~typing.Callable`\\ " + "[:class:`str`, :class:`int`]") + assert restify(Callable[..., int]) == (":class:`~typing.Callable`\\ " + "[..., :class:`int`]") def test_restify_type_hints_Union(): - assert restify(Optional[int]) == ":obj:`Optional`\\ [:class:`int`]" - assert restify(Union[str, None]) == ":obj:`Optional`\\ [:class:`str`]" - assert restify(Union[int, str]) == ":obj:`Union`\\ [:class:`int`, :class:`str`]" + assert restify(Optional[int]) == ":obj:`~typing.Optional`\\ [:class:`int`]" + assert restify(Union[str, None]) == ":obj:`~typing.Optional`\\ [:class:`str`]" + assert restify(Union[int, str]) == ":obj:`~typing.Union`\\ [:class:`int`, :class:`str`]" if sys.version_info >= (3, 7): - assert restify(Union[int, Integral]) == ":obj:`Union`\\ [:class:`int`, :class:`numbers.Integral`]" + assert restify(Union[int, Integral]) == (":obj:`~typing.Union`\\ " + "[:class:`int`, :class:`numbers.Integral`]") assert (restify(Union[MyClass1, MyClass2]) == - ":obj:`Union`\\ [:class:`tests.test_util_typing.MyClass1`, :class:`tests.test_util_typing.`]") + (":obj:`~typing.Union`\\ " + "[:class:`tests.test_util_typing.MyClass1`, " + ":class:`tests.test_util_typing.`]")) else: assert restify(Union[int, Integral]) == ":class:`numbers.Integral`" assert restify(Union[MyClass1, MyClass2]) == ":class:`tests.test_util_typing.MyClass1`" @@ -97,7 +111,7 @@ def test_restify_type_hints_typevars(): assert restify(T) == ":obj:`tests.test_util_typing.T`" assert restify(T_co) == ":obj:`tests.test_util_typing.T_co`" assert restify(T_contra) == ":obj:`tests.test_util_typing.T_contra`" - assert restify(List[T]) == ":class:`List`\\ [:obj:`tests.test_util_typing.T`]" + assert restify(List[T]) == ":class:`~typing.List`\\ [:obj:`tests.test_util_typing.T`]" assert restify(MyInt) == ":class:`MyInt`" @@ -110,7 +124,7 @@ def test_restify_type_hints_alias(): MyStr = str MyTuple = Tuple[str, str] assert restify(MyStr) == ":class:`str`" - assert restify(MyTuple) == ":class:`Tuple`\\ [:class:`str`, :class:`str`]" # type: ignore + assert restify(MyTuple) == ":class:`~typing.Tuple`\\ [:class:`str`, :class:`str`]" @pytest.mark.skipif(sys.version_info < (3, 7), reason='python 3.7+ is required.') From 025ec263f9f03c27f7ef2ade4b5b1d7a8f42fcac Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Sun, 18 Apr 2021 16:39:58 +0200 Subject: [PATCH 29/29] C, C++, fix KeyError due to alias directive --- CHANGES | 3 +++ sphinx/domains/c.py | 3 ++- sphinx/domains/cpp.py | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index bb776bcc8..929638388 100644 --- a/CHANGES +++ b/CHANGES @@ -24,6 +24,9 @@ Features added Bugs fixed ---------- +* C, C++, fix ``KeyError`` when an ``alias`` directive is the first C/C++ + directive in a file with another C/C++ directive later. + Testing -------- diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 1eeaf65cf..b0711f68e 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -3454,7 +3454,8 @@ class AliasNode(nodes.Element): if 'c:parent_symbol' not in env.temp_data: root = env.domaindata['c']['root_symbol'] env.temp_data['c:parent_symbol'] = root - self.parentKey = env.temp_data['c:parent_symbol'].get_lookup_key() + env.ref_context['c:parent_key'] = root.get_lookup_key() + self.parentKey = env.ref_context['c:parent_key'] else: assert parentKey is not None self.parentKey = parentKey diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 4a36f6131..d57c6e257 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -7177,7 +7177,8 @@ class AliasNode(nodes.Element): if 'cpp:parent_symbol' not in env.temp_data: root = env.domaindata['cpp']['root_symbol'] env.temp_data['cpp:parent_symbol'] = root - self.parentKey = env.temp_data['cpp:parent_symbol'].get_lookup_key() + env.ref_context['cpp:parent_key'] = root.get_lookup_key() + self.parentKey = env.ref_context['cpp:parent_key'] else: assert parentKey is not None self.parentKey = parentKey