Add the `:no-typesetting:` option for only creating targets (#10478)

Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
This commit is contained in:
latosha-maltba 2023-07-28 18:43:27 +00:00 committed by GitHub
parent 05a14ff007
commit 97d2c5da2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 347 additions and 20 deletions

View File

@ -29,6 +29,9 @@ Features added
* #6319: viewcode: Add :confval:`viewcode_line_numbers` to control * #6319: viewcode: Add :confval:`viewcode_line_numbers` to control
whether line numbers are added to rendered source code. whether line numbers are added to rendered source code.
Patch by Ben Krikler. Patch by Ben Krikler.
* #9662: Add the ``:no-typesetting:`` option to suppress textual output
and only create a linkable anchor.
Patch by Latosha Maltba.
Bugs fixed Bugs fixed
---------- ----------

View File

@ -54,6 +54,9 @@ can give the directive option flag ``:nocontentsentry:``.
If you want to typeset an object description, without even making it available If you want to typeset an object description, without even making it available
for cross-referencing, you can give the directive option flag ``:noindex:`` for cross-referencing, you can give the directive option flag ``:noindex:``
(which implies ``:noindexentry:``). (which implies ``:noindexentry:``).
If you do not want to typeset anything, you can give the directive option flag
``:no-typesetting:``. This can for example be used to create only a target and
index entry for later reference.
Though, note that not every directive in every domain may support these Though, note that not every directive in every domain may support these
options. options.
@ -65,6 +68,10 @@ options.
The directive option ``:nocontentsentry:`` in the Python, C, C++, Javascript, The directive option ``:nocontentsentry:`` in the Python, C, C++, Javascript,
and reStructuredText domains. and reStructuredText domains.
.. versionadded:: 7.2
The directive option ``no-typesetting`` in the Python, C, C++, Javascript,
and reStructuredText domains.
An example using a Python domain directive:: An example using a Python domain directive::
.. py:function:: spam(eggs) .. py:function:: spam(eggs)
@ -91,6 +98,23 @@ you could say ::
As you can see, both directive and role names contain the domain name and the As you can see, both directive and role names contain the domain name and the
directive name. directive name.
The directive option ``:no-typesetting:`` can be used to create a target
(and index entry) which can later be referenced
by the roles provided by the domain.
This is particularly useful for literate programming:
.. code-block:: rst
.. py:function:: spam(eggs)
:no-typesetting:
.. code::
def spam(eggs):
pass
The function :py:func:`spam` does nothing.
.. rubric:: Default Domain .. rubric:: Default Domain
For documentation describing objects from solely one domain, authors will not For documentation describing objects from solely one domain, authors will not

View File

@ -57,6 +57,7 @@ class ObjectDescription(SphinxDirective, Generic[ObjDescT]):
'noindex': directives.flag, 'noindex': directives.flag,
'noindexentry': directives.flag, 'noindexentry': directives.flag,
'nocontentsentry': directives.flag, 'nocontentsentry': directives.flag,
'no-typesetting': directives.flag,
} }
# types of doc fields that this directive handles, see sphinx.util.docfields # types of doc fields that this directive handles, see sphinx.util.docfields
@ -218,6 +219,7 @@ class ObjectDescription(SphinxDirective, Generic[ObjDescT]):
node['noindex'] = noindex = ('noindex' in self.options) node['noindex'] = noindex = ('noindex' in self.options)
node['noindexentry'] = ('noindexentry' in self.options) node['noindexentry'] = ('noindexentry' in self.options)
node['nocontentsentry'] = ('nocontentsentry' in self.options) node['nocontentsentry'] = ('nocontentsentry' in self.options)
node['no-typesetting'] = ('no-typesetting' in self.options)
if self.domain: if self.domain:
node['classes'].append(self.domain) node['classes'].append(self.domain)
node['classes'].append(node['objtype']) node['classes'].append(node['objtype'])
@ -270,6 +272,19 @@ class ObjectDescription(SphinxDirective, Generic[ObjDescT]):
DocFieldTransformer(self).transform_all(contentnode) DocFieldTransformer(self).transform_all(contentnode)
self.env.temp_data['object'] = None self.env.temp_data['object'] = None
self.after_content() self.after_content()
if node['no-typesetting']:
# Attempt to return the index node, and a new target node
# containing all the ids of this node and its children.
# If ``:noindex:`` is set, or there are no ids on the node
# or any of its children, then just return the index node,
# as Docutils expects a target node to have at least one id.
if node_ids := [node_id for el in node.findall(nodes.Element)
for node_id in el.get('ids', ())]:
target_node = nodes.target(ids=node_ids)
self.set_source_info(target_node)
return [self.indexnode, target_node]
return [self.indexnode]
return [self.indexnode, node] return [self.indexnode, node]

View File

@ -3166,6 +3166,7 @@ class CObject(ObjectDescription[ASTDeclaration]):
option_spec: OptionSpec = { option_spec: OptionSpec = {
'noindexentry': directives.flag, 'noindexentry': directives.flag,
'nocontentsentry': directives.flag, 'nocontentsentry': directives.flag,
'no-typesetting': directives.flag,
'single-line-parameter-list': directives.flag, 'single-line-parameter-list': directives.flag,
} }

View File

@ -7214,6 +7214,7 @@ class CPPObject(ObjectDescription[ASTDeclaration]):
option_spec: OptionSpec = { option_spec: OptionSpec = {
'noindexentry': directives.flag, 'noindexentry': directives.flag,
'nocontentsentry': directives.flag, 'nocontentsentry': directives.flag,
'no-typesetting': directives.flag,
'tparam-line-spec': directives.flag, 'tparam-line-spec': directives.flag,
'single-line-parameter-list': directives.flag, 'single-line-parameter-list': directives.flag,
} }

View File

@ -46,6 +46,7 @@ class JSObject(ObjectDescription[tuple[str, str]]):
'noindex': directives.flag, 'noindex': directives.flag,
'noindexentry': directives.flag, 'noindexentry': directives.flag,
'nocontentsentry': directives.flag, 'nocontentsentry': directives.flag,
'no-typesetting': directives.flag,
'single-line-parameter-list': directives.flag, 'single-line-parameter-list': directives.flag,
} }
@ -293,6 +294,7 @@ class JSModule(SphinxDirective):
option_spec: OptionSpec = { option_spec: OptionSpec = {
'noindex': directives.flag, 'noindex': directives.flag,
'nocontentsentry': directives.flag, 'nocontentsentry': directives.flag,
'no-typesetting': directives.flag,
} }
def run(self) -> list[Node]: def run(self) -> list[Node]:
@ -316,12 +318,13 @@ class JSModule(SphinxDirective):
domain.note_object(mod_name, 'module', node_id, domain.note_object(mod_name, 'module', node_id,
location=(self.env.docname, self.lineno)) location=(self.env.docname, self.lineno))
target = nodes.target('', '', ids=[node_id], ismod=True) # The node order is: index node first, then target node
self.state.document.note_explicit_target(target)
ret.append(target)
indextext = _('%s (module)') % mod_name indextext = _('%s (module)') % mod_name
inode = addnodes.index(entries=[('single', indextext, node_id, '', None)]) inode = addnodes.index(entries=[('single', indextext, node_id, '', None)])
ret.append(inode) ret.append(inode)
target = nodes.target('', '', ids=[node_id], ismod=True)
self.state.document.note_explicit_target(target)
ret.append(target)
ret.extend(content_node.children) ret.extend(content_node.children)
return ret return ret

View File

@ -662,6 +662,7 @@ class PyObject(ObjectDescription[tuple[str, str]]):
'noindex': directives.flag, 'noindex': directives.flag,
'noindexentry': directives.flag, 'noindexentry': directives.flag,
'nocontentsentry': directives.flag, 'nocontentsentry': directives.flag,
'no-typesetting': directives.flag,
'single-line-parameter-list': directives.flag, 'single-line-parameter-list': directives.flag,
'single-line-type-parameter-list': directives.flag, 'single-line-type-parameter-list': directives.flag,
'module': directives.unchanged, 'module': directives.unchanged,
@ -1262,6 +1263,7 @@ class PyModule(SphinxDirective):
'synopsis': lambda x: x, 'synopsis': lambda x: x,
'noindex': directives.flag, 'noindex': directives.flag,
'nocontentsentry': directives.flag, 'nocontentsentry': directives.flag,
'no-typesetting': directives.flag,
'deprecated': directives.flag, 'deprecated': directives.flag,
} }
@ -1294,10 +1296,11 @@ class PyModule(SphinxDirective):
# the platform and synopsis aren't printed; in fact, they are only # the platform and synopsis aren't printed; in fact, they are only
# used in the modindex currently # used in the modindex currently
ret.append(target)
indextext = f'module; {modname}' indextext = f'module; {modname}'
inode = addnodes.index(entries=[('pair', indextext, node_id, '', None)]) inode = addnodes.index(entries=[('pair', indextext, node_id, '', None)])
# The node order is: index node first, then target node.
ret.append(inode) ret.append(inode)
ret.append(target)
ret.extend(content_node.children) ret.extend(content_node.children)
return ret return ret

View File

@ -37,6 +37,7 @@ class ReSTMarkup(ObjectDescription[str]):
'noindex': directives.flag, 'noindex': directives.flag,
'noindexentry': directives.flag, 'noindexentry': directives.flag,
'nocontentsentry': directives.flag, 'nocontentsentry': directives.flag,
'no-typesetting': directives.flag,
} }
def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None: def add_target_and_index(self, name: str, sig: str, signode: desc_signature) -> None:

View File

@ -414,6 +414,77 @@ class GlossarySorter(SphinxTransform):
) )
class ReorderConsecutiveTargetAndIndexNodes(SphinxTransform):
"""Index nodes interspersed between target nodes prevent other
Transformations from combining those target nodes,
e.g. ``PropagateTargets``. This transformation reorders them:
Given the following ``document`` as input::
<document>
<target ids="id1" ...>
<index entries="...1...">
<target ids="id2" ...>
<target ids="id3" ...>
<index entries="...2...">
<target ids="id4" ...>
The transformed result will be::
<document>
<index entries="...1...">
<index entries="...2...">
<target ids="id1" ...>
<target ids="id2" ...>
<target ids="id3" ...>
<target ids="id4" ...>
"""
# This transform MUST run before ``PropagateTargets``.
default_priority = 220
def apply(self, **kwargs: Any) -> None:
for target in self.document.findall(nodes.target):
_reorder_index_target_nodes(target)
def _reorder_index_target_nodes(start_node: nodes.target) -> None:
"""Sort target and index nodes.
Find all consecutive target and index nodes starting from ``start_node``,
and move all index nodes to before the first target node.
"""
nodes_to_reorder: list[nodes.target | addnodes.index] = []
# Note that we cannot use 'condition' to filter,
# as we want *consecutive* target & index nodes.
node: nodes.Node
for node in start_node.findall(descend=False, siblings=True):
if isinstance(node, (nodes.target, addnodes.index)):
nodes_to_reorder.append(node)
continue
break # must be a consecutive run of target or index nodes
if len(nodes_to_reorder) < 2:
return # Nothing to reorder
parent = nodes_to_reorder[0].parent
if parent == nodes_to_reorder[-1].parent:
first_idx = parent.index(nodes_to_reorder[0])
last_idx = parent.index(nodes_to_reorder[-1])
if first_idx + len(nodes_to_reorder) - 1 == last_idx:
parent[first_idx:last_idx + 1] = sorted(nodes_to_reorder, key=_sort_key)
def _sort_key(node: nodes.Node) -> int:
# Must be a stable sort.
if isinstance(node, addnodes.index):
return 0
if isinstance(node, nodes.target):
return 1
raise ValueError(f'_sort_key called with unexpected node type {type(node)!r}')
def setup(app: Sphinx) -> dict[str, Any]: def setup(app: Sphinx) -> dict[str, Any]:
app.add_transform(ApplySourceWorkaround) app.add_transform(ApplySourceWorkaround)
app.add_transform(ExtraTranslatableNodes) app.add_transform(ExtraTranslatableNodes)
@ -430,6 +501,7 @@ def setup(app: Sphinx) -> dict[str, Any]:
app.add_transform(DoctreeReadEvent) app.add_transform(DoctreeReadEvent)
app.add_transform(ManpageLink) app.add_transform(ManpageLink)
app.add_transform(GlossarySorter) app.add_transform(GlossarySorter)
app.add_transform(ReorderConsecutiveTargetAndIndexNodes)
return { return {
'version': 'builtin', 'version': 'builtin',

View File

@ -0,0 +1,108 @@
"""Tests the directives"""
import pytest
from docutils import nodes
from sphinx import addnodes
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
DOMAINS = [
# directive, noindex, noindexentry, signature of f, signature of g, index entry of g
('c:function', False, True, 'void f()', 'void g()', ('single', 'g (C function)', 'c.g', '', None)),
('cpp:function', False, True, 'void f()', 'void g()', ('single', 'g (C++ function)', '_CPPv41gv', '', None)),
('js:function', True, True, 'f()', 'g()', ('single', 'g() (built-in function)', 'g', '', None)),
('py:function', True, True, 'f()', 'g()', ('pair', 'built-in function; g()', 'g', '', None)),
('rst:directive', True, False, 'f', 'g', ('single', 'g (directive)', 'directive-g', '', None)),
('cmdoption', True, False, 'f', 'g', ('pair', 'command line option; g', 'cmdoption-arg-g', '', None)),
('envvar', True, False, 'f', 'g', ('single', 'environment variable; g', 'envvar-g', '', None)),
]
@pytest.mark.parametrize(('directive', 'noindex', 'noindexentry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
def test_object_description_no_typesetting(app, directive, noindex, noindexentry, sig_f, sig_g, index_g):
text = (f'.. {directive}:: {sig_f}\n'
f' :no-typesetting:\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, nodes.target))
@pytest.mark.parametrize(('directive', 'noindex', 'noindexentry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
def test_object_description_no_typesetting_twice(app, directive, noindex, noindexentry, sig_f, sig_g, index_g):
text = (f'.. {directive}:: {sig_f}\n'
f' :no-typesetting:\n'
f'.. {directive}:: {sig_g}\n'
f' :no-typesetting:\n')
doctree = restructuredtext.parse(app, text)
# Note that all index nodes come before the target nodes
assert_node(doctree, (addnodes.index, addnodes.index, nodes.target, nodes.target))
@pytest.mark.parametrize(('directive', 'noindex', 'noindexentry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
def test_object_description_no_typesetting_noindex_orig(app, directive, noindex, noindexentry, sig_f, sig_g, index_g):
if not noindex:
pytest.skip(f'{directive} does not support :noindex: option')
text = (f'.. {directive}:: {sig_f}\n'
f' :noindex:\n'
f'.. {directive}:: {sig_g}\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, addnodes.desc, addnodes.index, addnodes.desc))
assert_node(doctree[2], addnodes.index, entries=[index_g])
@pytest.mark.parametrize(('directive', 'noindex', 'noindexentry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
def test_object_description_no_typesetting_noindex(app, directive, noindex, noindexentry, sig_f, sig_g, index_g):
if not noindex:
pytest.skip(f'{directive} does not support :noindex: option')
text = (f'.. {directive}:: {sig_f}\n'
f' :noindex:\n'
f' :no-typesetting:\n'
f'.. {directive}:: {sig_g}\n'
f' :no-typesetting:\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, addnodes.index, nodes.target))
assert_node(doctree[0], addnodes.index, entries=[])
assert_node(doctree[1], addnodes.index, entries=[index_g])
@pytest.mark.parametrize(('directive', 'noindex', 'noindexentry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
def test_object_description_no_typesetting_noindexentry(app, directive, noindex, noindexentry, sig_f, sig_g, index_g):
if not noindexentry:
pytest.skip(f'{directive} does not support :noindexentry: option')
text = (f'.. {directive}:: {sig_f}\n'
f' :noindexentry:\n'
f' :no-typesetting:\n'
f'.. {directive}:: {sig_g}\n'
f' :no-typesetting:\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index, addnodes.index, nodes.target, nodes.target))
assert_node(doctree[0], addnodes.index, entries=[])
assert_node(doctree[1], addnodes.index, entries=[index_g])
@pytest.mark.parametrize(('directive', 'noindex', 'noindexentry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
def test_object_description_no_typesetting_code(app, directive, noindex, noindexentry, sig_f, sig_g, index_g):
text = (f'.. {directive}:: {sig_f}\n'
f' :no-typesetting:\n'
f'.. {directive}:: {sig_g}\n'
f' :no-typesetting:\n'
f'.. code::\n'
f'\n'
f' code\n')
doctree = restructuredtext.parse(app, text)
# Note that all index nodes come before the targets
assert_node(doctree, (addnodes.index, addnodes.index, nodes.target, nodes.target, nodes.literal_block))
@pytest.mark.parametrize(('directive', 'noindex', 'noindexentry', 'sig_f', 'sig_g', 'index_g'), DOMAINS)
def test_object_description_no_typesetting_heading(app, directive, noindex, noindexentry, sig_f, sig_g, index_g):
text = (f'.. {directive}:: {sig_f}\n'
f' :no-typesetting:\n'
f'.. {directive}:: {sig_g}\n'
f' :no-typesetting:\n'
f'\n'
f'Heading\n'
f'=======\n')
doctree = restructuredtext.parse(app, text)
# Note that all index nodes come before the targets and the heading is floated before those.
assert_node(doctree, (nodes.title, addnodes.index, addnodes.index, nodes.target, nodes.target))

View File

@ -177,11 +177,11 @@ def test_get_full_qualified_name():
def test_js_module(app): def test_js_module(app):
text = ".. js:module:: sphinx" text = ".. js:module:: sphinx"
doctree = restructuredtext.parse(app, text) doctree = restructuredtext.parse(app, text)
assert_node(doctree, (nodes.target, assert_node(doctree, (addnodes.index,
addnodes.index)) nodes.target))
assert_node(doctree[0], nodes.target, ids=["module-sphinx"]) assert_node(doctree[0], addnodes.index,
assert_node(doctree[1], addnodes.index,
entries=[("single", "sphinx (module)", "module-sphinx", "", None)]) entries=[("single", "sphinx (module)", "module-sphinx", "", None)])
assert_node(doctree[1], nodes.target, ids=["module-sphinx"])
def test_js_function(app): def test_js_function(app):

View File

@ -660,9 +660,9 @@ def test_pydata(app):
" :type: int\n") " :type: int\n")
domain = app.env.get_domain('py') domain = app.env.get_domain('py')
doctree = restructuredtext.parse(app, text) doctree = restructuredtext.parse(app, text)
assert_node(doctree, (nodes.target, assert_node(doctree, (addnodes.index,
addnodes.index,
addnodes.index, addnodes.index,
nodes.target,
[desc, ([desc_signature, ([desc_addname, "example."], [desc, ([desc_signature, ([desc_addname, "example."],
[desc_name, "var"], [desc_name, "var"],
[desc_annotation, ([desc_sig_punctuation, ':'], [desc_annotation, ([desc_sig_punctuation, ':'],
@ -685,9 +685,9 @@ def test_pyfunction(app):
[desc, ([desc_signature, ([desc_name, "func1"], [desc, ([desc_signature, ([desc_name, "func1"],
[desc_parameterlist, ()])], [desc_parameterlist, ()])],
[desc_content, ()])], [desc_content, ()])],
addnodes.index,
addnodes.index,
nodes.target, nodes.target,
addnodes.index,
addnodes.index,
[desc, ([desc_signature, ([desc_annotation, ([desc_sig_keyword, 'async'], [desc, ([desc_signature, ([desc_annotation, ([desc_sig_keyword, 'async'],
desc_sig_space)], desc_sig_space)],
[desc_addname, "example."], [desc_addname, "example."],
@ -696,9 +696,9 @@ def test_pyfunction(app):
[desc_content, ()])])) [desc_content, ()])]))
assert_node(doctree[0], addnodes.index, assert_node(doctree[0], addnodes.index,
entries=[('pair', 'built-in function; func1()', 'func1', '', None)]) entries=[('pair', 'built-in function; func1()', 'func1', '', None)])
assert_node(doctree[3], addnodes.index, assert_node(doctree[2], addnodes.index,
entries=[('pair', 'module; example', 'module-example', '', None)]) entries=[('pair', 'module; example', 'module-example', '', None)])
assert_node(doctree[4], addnodes.index, assert_node(doctree[3], addnodes.index,
entries=[('single', 'func2() (in module example)', 'example.func2', '', None)]) entries=[('single', 'func2() (in module example)', 'example.func2', '', None)])
assert 'func1' in domain.objects assert 'func1' in domain.objects
@ -1043,9 +1043,9 @@ def test_info_field_list(app):
doctree = restructuredtext.parse(app, text) doctree = restructuredtext.parse(app, text)
print(doctree) print(doctree)
assert_node(doctree, (nodes.target, assert_node(doctree, (addnodes.index,
addnodes.index,
addnodes.index, addnodes.index,
nodes.target,
[desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)],
[desc_addname, "example."], [desc_addname, "example."],
[desc_name, "Class"])], [desc_name, "Class"])],
@ -1134,9 +1134,9 @@ def test_info_field_list_piped_type(app):
doctree = restructuredtext.parse(app, text) doctree = restructuredtext.parse(app, text)
assert_node(doctree, assert_node(doctree,
(nodes.target, (addnodes.index,
addnodes.index,
addnodes.index, addnodes.index,
nodes.target,
[desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)],
[desc_addname, "example."], [desc_addname, "example."],
[desc_name, "Class"])], [desc_name, "Class"])],
@ -1168,9 +1168,9 @@ def test_info_field_list_Literal(app):
doctree = restructuredtext.parse(app, text) doctree = restructuredtext.parse(app, text)
assert_node(doctree, assert_node(doctree,
(nodes.target, (addnodes.index,
addnodes.index,
addnodes.index, addnodes.index,
nodes.target,
[desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)], [desc, ([desc_signature, ([desc_annotation, ("class", desc_sig_space)],
[desc_addname, "example."], [desc_addname, "example."],
[desc_name, "Class"])], [desc_name, "Class"])],

View File

@ -0,0 +1,96 @@
"""Tests the transformations"""
from docutils import nodes
from sphinx import addnodes
from sphinx.testing import restructuredtext
from sphinx.testing.util import assert_node
def test_transforms_reorder_consecutive_target_and_index_nodes_preserve_order(app):
text = ('.. index:: abc\n'
'.. index:: def\n'
'.. index:: ghi\n'
'.. index:: jkl\n'
'\n'
'text\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index,
addnodes.index,
addnodes.index,
addnodes.index,
nodes.target,
nodes.target,
nodes.target,
nodes.target,
nodes.paragraph))
assert_node(doctree[0], addnodes.index, entries=[('single', 'abc', 'index-0', '', None)])
assert_node(doctree[1], addnodes.index, entries=[('single', 'def', 'index-1', '', None)])
assert_node(doctree[2], addnodes.index, entries=[('single', 'ghi', 'index-2', '', None)])
assert_node(doctree[3], addnodes.index, entries=[('single', 'jkl', 'index-3', '', None)])
assert_node(doctree[4], nodes.target, refid='index-0')
assert_node(doctree[5], nodes.target, refid='index-1')
assert_node(doctree[6], nodes.target, refid='index-2')
assert_node(doctree[7], nodes.target, refid='index-3')
# assert_node(doctree[8], nodes.paragraph)
def test_transforms_reorder_consecutive_target_and_index_nodes_no_merge_across_other_nodes(app):
text = ('.. index:: abc\n'
'.. index:: def\n'
'\n'
'text\n'
'\n'
'.. index:: ghi\n'
'.. index:: jkl\n'
'\n'
'text\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (addnodes.index,
addnodes.index,
nodes.target,
nodes.target,
nodes.paragraph,
addnodes.index,
addnodes.index,
nodes.target,
nodes.target,
nodes.paragraph))
assert_node(doctree[0], addnodes.index, entries=[('single', 'abc', 'index-0', '', None)])
assert_node(doctree[1], addnodes.index, entries=[('single', 'def', 'index-1', '', None)])
assert_node(doctree[2], nodes.target, refid='index-0')
assert_node(doctree[3], nodes.target, refid='index-1')
# assert_node(doctree[4], nodes.paragraph)
assert_node(doctree[5], addnodes.index, entries=[('single', 'ghi', 'index-2', '', None)])
assert_node(doctree[6], addnodes.index, entries=[('single', 'jkl', 'index-3', '', None)])
assert_node(doctree[7], nodes.target, refid='index-2')
assert_node(doctree[8], nodes.target, refid='index-3')
# assert_node(doctree[9], nodes.paragraph)
def test_transforms_reorder_consecutive_target_and_index_nodes_merge_with_labels(app):
text = ('.. _abc:\n'
'.. index:: def\n'
'.. _ghi:\n'
'.. index:: jkl\n'
'.. _mno:\n'
'\n'
'Heading\n'
'=======\n')
doctree = restructuredtext.parse(app, text)
assert_node(doctree, (nodes.title,
addnodes.index,
addnodes.index,
nodes.target,
nodes.target,
nodes.target,
nodes.target,
nodes.target))
# assert_node(doctree[8], nodes.title)
assert_node(doctree[1], addnodes.index, entries=[('single', 'def', 'index-0', '', None)])
assert_node(doctree[2], addnodes.index, entries=[('single', 'jkl', 'index-1', '', None)])
assert_node(doctree[3], nodes.target, refid='abc')
assert_node(doctree[4], nodes.target, refid='index-0')
assert_node(doctree[5], nodes.target, refid='ghi')
assert_node(doctree[6], nodes.target, refid='index-1')
assert_node(doctree[7], nodes.target, refid='mno')