diff --git a/CHANGES.rst b/CHANGES.rst index e1b247ac3..51bb9b8b2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -73,6 +73,9 @@ Features added * #9732: Add the new ``ref.any`` warnings sub-type to allow suppressing the ambiguous 'any' cross-reference warning. Patch by Simão Afonso and Adam Turner. +* #13272: The Python and JavaScript module directives now support + the ``:no-index-entry:`` option. + Patch by Adam Turner. Bugs fixed ---------- diff --git a/doc/usage/domains/index.rst b/doc/usage/domains/index.rst index eda2da3f6..cc3f27264 100644 --- a/doc/usage/domains/index.rst +++ b/doc/usage/domains/index.rst @@ -48,7 +48,7 @@ the content should be the description. A domain will typically keep an internal index of all entities to aid cross-referencing. -Typically it will also add entries in the shown general index. +Typically, it will also add entries in the shown general index. If you want to suppress the addition of an entry in the shown index, you can give the directive option flag ``:no-index-entry:``. If you want to exclude the object description from the table of contents, you diff --git a/sphinx/domains/javascript.py b/sphinx/domains/javascript.py index cd9e4ea83..de984cf46 100644 --- a/sphinx/domains/javascript.py +++ b/sphinx/domains/javascript.py @@ -155,8 +155,8 @@ class JSObject(ObjectDescription[tuple[str, str]]): def add_target_and_index( self, name_obj: tuple[str, str], sig: str, signode: desc_signature ) -> None: - mod_name = self.env.ref_context.get('js:module') - fullname = (mod_name + '.' if mod_name else '') + name_obj[0] + mod_name = self.env.ref_context.get('js:module', '') + fullname = (f'{mod_name}.' if mod_name else '') + name_obj[0] node_id = make_id(self.env, self.state.document, '', fullname) signode['ids'].append(node_id) self.state.document.note_explicit_target(signode) @@ -165,11 +165,10 @@ class JSObject(ObjectDescription[tuple[str, str]]): domain.note_object(fullname, self.objtype, node_id, location=signode) if 'no-index-entry' not in self.options: - indextext = self.get_index_text(mod_name, name_obj) # type: ignore[arg-type] - if indextext: + if index_text := self.get_index_text(mod_name, name_obj): self.indexnode['entries'].append(( 'single', - indextext, + index_text, node_id, '', None, @@ -333,6 +332,7 @@ class JSModule(SphinxDirective): final_argument_whitespace = False option_spec: ClassVar[OptionSpec] = { 'no-index': directives.flag, + 'no-index-entry': directives.flag, 'no-contents-entry': directives.flag, 'no-typesetting': directives.flag, 'noindex': directives.flag, @@ -365,9 +365,12 @@ class JSModule(SphinxDirective): ) # The node order is: index node first, then target node - indextext = _('%s (module)') % mod_name - inode = addnodes.index(entries=[('single', indextext, node_id, '', None)]) - ret.append(inode) + if 'no-index-entry' not in self.options: + index_text = _('%s (module)') % mod_name + inode = addnodes.index( + entries=[('single', index_text, node_id, '', None)] + ) + ret.append(inode) target = nodes.target('', '', ids=[node_id], ismod=True) self.state.document.note_explicit_target(target) ret.append(target) diff --git a/sphinx/domains/python/__init__.py b/sphinx/domains/python/__init__.py index 2fa152602..6ca92900e 100644 --- a/sphinx/domains/python/__init__.py +++ b/sphinx/domains/python/__init__.py @@ -479,6 +479,7 @@ class PyModule(SphinxDirective): 'platform': lambda x: x, 'synopsis': lambda x: x, 'no-index': directives.flag, + 'no-index-entry': directives.flag, 'no-contents-entry': directives.flag, 'no-typesetting': directives.flag, 'noindex': directives.flag, @@ -520,10 +521,15 @@ class PyModule(SphinxDirective): # the platform and synopsis aren't printed; in fact, they are only # used in the modindex currently - indextext = f'module; {modname}' - inode = addnodes.index(entries=[('pair', indextext, node_id, '', None)]) - # The node order is: index node first, then target node. - ret.extend((inode, target)) + + if 'no-index-entry' not in self.options: + index_text = f'module; {modname}' + inode = addnodes.index( + entries=[('pair', index_text, node_id, '', None)] + ) + # The node order is: index node first, then target node. + ret.append(inode) + ret.append(target) ret.extend(content_nodes) return ret diff --git a/sphinx/domains/python/_object.py b/sphinx/domains/python/_object.py index b72f35a6f..b2e8b5e38 100644 --- a/sphinx/domains/python/_object.py +++ b/sphinx/domains/python/_object.py @@ -409,8 +409,8 @@ class PyObject(ObjectDescription[tuple[str, str]]): def add_target_and_index( self, name_cls: tuple[str, str], sig: str, signode: desc_signature ) -> None: - modname = self.options.get('module', self.env.ref_context.get('py:module')) - fullname = (f'{modname}.' if modname else '') + name_cls[0] + mod_name = self.options.get('module', self.env.ref_context.get('py:module')) + fullname = (f'{mod_name}.' if mod_name else '') + name_cls[0] node_id = make_id(self.env, self.state.document, '', fullname) signode['ids'].append(node_id) self.state.document.note_explicit_target(signode) @@ -425,11 +425,10 @@ class PyObject(ObjectDescription[tuple[str, str]]): ) if 'no-index-entry' not in self.options: - indextext = self.get_index_text(modname, name_cls) - if indextext: + if index_text := self.get_index_text(mod_name, name_cls): self.indexnode['entries'].append(( 'single', - indextext, + index_text, node_id, '', None, diff --git a/sphinx/domains/rst.py b/sphinx/domains/rst.py index 1f168bc90..cd5d8312d 100644 --- a/sphinx/domains/rst.py +++ b/sphinx/domains/rst.py @@ -57,11 +57,10 @@ class ReSTMarkup(ObjectDescription[str]): domain.note_object(self.objtype, name, node_id, location=signode) if 'no-index-entry' not in self.options: - indextext = self.get_index_text(self.objtype, name) - if indextext: + if index_text := self.get_index_text(self.objtype, name): self.indexnode['entries'].append(( 'single', - indextext, + index_text, node_id, '', None, diff --git a/tests/test_domains/test_domain_js.py b/tests/test_domains/test_domain_js.py index fad3907fb..7354a157b 100644 --- a/tests/test_domains/test_domain_js.py +++ b/tests/test_domains/test_domain_js.py @@ -326,6 +326,25 @@ def test_no_index_entry(app): ) assert_node(doctree[2], addnodes.index, entries=[]) + text = '.. js:class:: f\n.. js:class:: g\n :no-index-entry:\n' + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, desc, addnodes.index, desc)) + assert_node( + doctree[0], + addnodes.index, + entries=[('single', 'f() (class)', 'f', '', None)], + ) + assert_node(doctree[2], addnodes.index, entries=[]) + + text = '.. js:module:: f\n.. js:module:: g\n :no-index-entry:\n' + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, nodes.target, nodes.target)) + assert_node( + doctree[0], + addnodes.index, + entries=[('single', 'f (module)', 'module-f', '', None)], + ) + @pytest.mark.sphinx('html', testroot='root') def test_module_content_line_number(app): diff --git a/tests/test_domains/test_domain_py.py b/tests/test_domains/test_domain_py.py index c67a1f09d..1e6d86194 100644 --- a/tests/test_domains/test_domain_py.py +++ b/tests/test_domains/test_domain_py.py @@ -836,6 +836,15 @@ def test_no_index_entry(app): ) assert_node(doctree[2], addnodes.index, entries=[]) + text = '.. py:module:: f\n.. py:module:: g\n :no-index-entry:\n' + doctree = restructuredtext.parse(app, text) + assert_node(doctree, (addnodes.index, nodes.target, nodes.target)) + assert_node( + doctree[0], + addnodes.index, + entries=[('pair', 'module; f', 'module-f', '', None)], + ) + @pytest.mark.sphinx('html', testroot='domain-py-python_use_unqualified_type_names') def test_python_python_use_unqualified_type_names(app):