Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add content_offset parameter to nested_parse_with_titles #11147

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES
Expand Up @@ -23,6 +23,8 @@ Bugs fixed

* #11079: LaTeX: figures with align attribute may disappear and strangely impact
following lists
* #11147: Fix source file/line number info in object description content and in
other uses of ``nested_parse_with_titles``. Patch by Jeremy Maitin-Shepard.

Testing
--------
Expand Down
2 changes: 1 addition & 1 deletion sphinx/directives/__init__.py
Expand Up @@ -262,7 +262,7 @@ def run(self) -> list[Node]:
# needed for association of version{added,changed} directives
self.env.temp_data['object'] = self.names[0]
self.before_content()
nested_parse_with_titles(self.state, self.content, contentnode)
nested_parse_with_titles(self.state, self.content, contentnode, self.content_offset)
self.transform_content(contentnode)
self.env.app.emit('object-description-transform',
self.domain, self.objtype, contentnode)
Expand Down
9 changes: 4 additions & 5 deletions sphinx/domains/javascript.py
Expand Up @@ -20,7 +20,7 @@
from sphinx.roles import XRefRole
from sphinx.util import logging
from sphinx.util.docfields import Field, GroupedField, TypedField
from sphinx.util.docutils import SphinxDirective, switch_source_input
from sphinx.util.docutils import SphinxDirective
from sphinx.util.nodes import make_id, make_refnode, nested_parse_with_titles
from sphinx.util.typing import OptionSpec

Expand Down Expand Up @@ -297,10 +297,9 @@ def run(self) -> list[Node]:
noindex = 'noindex' in self.options

content_node: Element = nodes.section()
with switch_source_input(self.state, self.content):
# necessary so that the child nodes get the right source/line set
content_node.document = self.state.document
nested_parse_with_titles(self.state, self.content, content_node)
# necessary so that the child nodes get the right source/line set
content_node.document = self.state.document
nested_parse_with_titles(self.state, self.content, content_node, self.content_offset)

ret: list[Node] = []
if not noindex:
Expand Down
9 changes: 4 additions & 5 deletions sphinx/domains/python.py
Expand Up @@ -26,7 +26,7 @@
from sphinx.roles import XRefRole
from sphinx.util import logging
from sphinx.util.docfields import Field, GroupedField, TypedField
from sphinx.util.docutils import SphinxDirective, switch_source_input
from sphinx.util.docutils import SphinxDirective
from sphinx.util.inspect import signature_from_str
from sphinx.util.nodes import (
find_pending_xref_condition,
Expand Down Expand Up @@ -1033,10 +1033,9 @@ def run(self) -> list[Node]:
self.env.ref_context['py:module'] = modname

content_node: Element = nodes.section()
with switch_source_input(self.state, self.content):
# necessary so that the child nodes get the right source/line set
content_node.document = self.state.document
nested_parse_with_titles(self.state, self.content, content_node)
# necessary so that the child nodes get the right source/line set
content_node.document = self.state.document
nested_parse_with_titles(self.state, self.content, content_node, self.content_offset)

ret: list[Node] = []
if not noindex:
Expand Down
3 changes: 1 addition & 2 deletions sphinx/ext/autodoc/__init__.py
Expand Up @@ -313,7 +313,7 @@ class Documenter:
#: order if autodoc_member_order is set to 'groupwise'
member_order = 0
#: true if the generated content may contain titles
titles_allowed = False
titles_allowed = True

option_spec: OptionSpec = {
'noindex': bool_option
Expand Down Expand Up @@ -956,7 +956,6 @@ class ModuleDocumenter(Documenter):
"""
objtype = 'module'
content_indent = ''
titles_allowed = True
_extra_indent = ' '

option_spec: OptionSpec = {
Expand Down
2 changes: 1 addition & 1 deletion sphinx/ext/ifconfig.py
Expand Up @@ -45,7 +45,7 @@ def run(self) -> list[Node]:
node.document = self.state.document
self.set_source_info(node)
node['expr'] = self.arguments[0]
nested_parse_with_titles(self.state, self.content, node)
nested_parse_with_titles(self.state, self.content, node, self.content_offset)
return [node]


Expand Down
5 changes: 3 additions & 2 deletions sphinx/util/nodes.py
Expand Up @@ -311,7 +311,8 @@ def traverse_translatable_index(
yield node, entries


def nested_parse_with_titles(state: Any, content: StringList, node: Node) -> str:
def nested_parse_with_titles(state: Any, content: StringList, node: Node,
content_offset: int = 0) -> str:
"""Version of state.nested_parse() that allows titles and does not require
titles to have the same decoration as the calling document.

Expand All @@ -324,7 +325,7 @@ def nested_parse_with_titles(state: Any, content: StringList, node: Node) -> str
state.memo.title_styles = []
state.memo.section_level = 0
try:
return state.nested_parse(content, 0, node, match_titles=1)
return state.nested_parse(content, content_offset, node, match_titles=1)
finally:
state.memo.title_styles = surrounding_title_styles
state.memo.section_level = surrounding_section_level
Expand Down
14 changes: 14 additions & 0 deletions tests/test_directive_object_description.py
@@ -1,10 +1,12 @@
"""Test object description directives."""

import docutils.utils
import pytest
from docutils import nodes

from sphinx import addnodes
from sphinx.io import create_publisher
from sphinx.testing import restructuredtext
from sphinx.util.docutils import sphinx_domains


Expand Down Expand Up @@ -43,3 +45,15 @@ def test_object_description_sections(app):
assert doctree[1][1][0][0][0] == 'Overview'
assert isinstance(doctree[1][1][0][1], nodes.paragraph)
assert doctree[1][1][0][1][0] == 'Lorem ipsum dolar sit amet'


def test_object_description_content_line_number(app):
text = (".. py:function:: foo(bar)\n" +
"\n" +
" Some link here: :ref:`abc`\n")
doc = restructuredtext.parse(app, text)
xrefs = list(doc.findall(condition=addnodes.pending_xref))
assert len(xrefs) == 1
source, line = docutils.utils.get_source_line(xrefs[0])
assert 'index.rst' in source
assert line == 3
13 changes: 13 additions & 0 deletions tests/test_domain_js.py
Expand Up @@ -2,6 +2,7 @@

from unittest.mock import Mock

import docutils.utils
import pytest
from docutils import nodes

Expand Down Expand Up @@ -229,3 +230,15 @@ def test_noindexentry(app):
assert_node(doctree, (addnodes.index, desc, addnodes.index, desc))
assert_node(doctree[0], addnodes.index, entries=[('single', 'f() (built-in function)', 'f', '', None)])
assert_node(doctree[2], addnodes.index, entries=[])


def test_module_content_line_number(app):
text = (".. js:module:: foo\n" +
"\n" +
" Some link here: :ref:`abc`\n")
doc = restructuredtext.parse(app, text)
xrefs = list(doc.findall(condition=addnodes.pending_xref))
assert len(xrefs) == 1
source, line = docutils.utils.get_source_line(xrefs[0])
assert 'index.rst' in source
assert line == 3
12 changes: 12 additions & 0 deletions tests/test_domain_py.py
Expand Up @@ -1458,3 +1458,15 @@ def test_signature_line_number(app, include_options):
source, line = docutils.utils.get_source_line(xrefs[0])
assert 'index.rst' in source
assert line == 1


def test_module_content_line_number(app):
text = (".. py:module:: foo\n" +
"\n" +
" Some link here: :ref:`abc`\n")
doc = restructuredtext.parse(app, text)
xrefs = list(doc.findall(condition=addnodes.pending_xref))
assert len(xrefs) == 1
source, line = docutils.utils.get_source_line(xrefs[0])
assert 'index.rst' in source
assert line == 3
17 changes: 17 additions & 0 deletions tests/test_ext_ifconfig.py
@@ -1,11 +1,28 @@
"""Test sphinx.ext.ifconfig extension."""

import docutils.utils
import pytest

from sphinx import addnodes
from sphinx.testing import restructuredtext


@pytest.mark.sphinx('text', testroot='ext-ifconfig')
def test_ifconfig(app, status, warning):
app.builder.build_all()
result = (app.outdir / 'index.txt').read_text(encoding='utf8')
assert 'spam' in result
assert 'ham' not in result


def test_ifconfig_content_line_number(app):
app.setup_extension("sphinx.ext.ifconfig")
text = (".. ifconfig:: confval1\n" +
"\n" +
" Some link here: :ref:`abc`\n")
doc = restructuredtext.parse(app, text)
xrefs = list(doc.findall(condition=addnodes.pending_xref))
assert len(xrefs) == 1
source, line = docutils.utils.get_source_line(xrefs[0])
assert 'index.rst' in source
assert line == 3