Fix #326: numref role can also refer sections

This commit is contained in:
Takeshi KOMIYA 2016-06-21 12:39:37 +09:00
parent 98886d3e07
commit 1aafc7e2e5
11 changed files with 122 additions and 38 deletions

View File

@ -118,6 +118,7 @@ Features added
* #2926: EPUB3 builder supports vertical mode (``epub3_writing_mode`` option) * #2926: EPUB3 builder supports vertical mode (``epub3_writing_mode`` option)
* #2695: ``build_sphinx`` subcommand for setuptools handles exceptions as same * #2695: ``build_sphinx`` subcommand for setuptools handles exceptions as same
as ``sphinx-build`` does as ``sphinx-build`` does
* #326: `numref` role can also refer sections
Bugs fixed Bugs fixed
---------- ----------

View File

@ -294,13 +294,16 @@ General configuration
.. confval:: numfig_format .. confval:: numfig_format
A dictionary mapping ``'figure'``, ``'table'`` and ``'code-block'`` to A dictionary mapping ``'figure'``, ``'table'``, ``'code-block'`` and
strings that are used for format of figure numbers. Default is to use ``'section'`` to strings that are used for format of figure numbers. Default
``'Fig. %s'`` for ``'figure'``, ``'Table %s'`` for ``'table'`` and is to use ``'Fig. %s'`` for ``'figure'``, ``'Table %s'`` for ``'table'``,
``'Listing %s'`` for ``'code-block'``. ``'Listing %s'`` for ``'code-block'`` and ``'Section'`` for ``'section'``.
.. versionadded:: 1.3 .. versionadded:: 1.3
.. versionchanged:: 1.5
Support format of section
.. confval:: numfig_secnum_depth .. confval:: numfig_secnum_depth
The scope of figure numbers, that is, the numfig feature numbers figures The scope of figure numbers, that is, the numfig feature numbers figures

View File

@ -212,11 +212,14 @@ Cross-referencing figures by figure number
.. versionadded:: 1.3 .. versionadded:: 1.3
.. versionchanged:: 1.5
`numref` role can also refer sections
.. rst:role:: numref .. rst:role:: numref
Link to the specified figures, tables and code-blocks; the standard reST Link to the specified figures, tables, code-blocks and sections; the standard
labels are used. When you use this role, it will insert a reference to the reST labels are used. When you use this role, it will insert a reference to
figure with link text by its figure number like "Fig. 1.1". the figure with link text by its figure number like "Fig. 1.1".
If an explicit link text is given (like usual: ``:numref:`Image of Sphinx (Fig. If an explicit link text is given (like usual: ``:numref:`Image of Sphinx (Fig.
%s) <my-figure>```), the link caption will be the title of the reference. %s) <my-figure>```), the link caption will be the title of the reference.

View File

@ -104,7 +104,8 @@ class Config(object):
nitpick_ignore = ([], None), nitpick_ignore = ([], None),
numfig = (False, 'env'), numfig = (False, 'env'),
numfig_secnum_depth = (1, 'env'), numfig_secnum_depth = (1, 'env'),
numfig_format = ({'figure': l_('Fig. %s'), numfig_format = ({'section': l_('Section %s'),
'figure': l_('Fig. %s'),
'table': l_('Table %s'), 'table': l_('Table %s'),
'code-block': l_('Listing %s')}, 'code-block': l_('Listing %s')},
'env'), 'env'),

View File

@ -653,11 +653,10 @@ class StandardDomain(Domain):
return None return None
try: try:
figure_id = target_node['ids'][0] fignumber = self.get_fignumber(env, builder, figtype, docname, target_node)
fignumber = env.toc_fignumbers[docname][figtype][figure_id] if fignumber is None:
except (KeyError, IndexError): return contnode
# target_node is found, but fignumber is not assigned. except ValueError:
# Maybe it is defined in orphaned document.
env.warn_node("no number is assigned for %s: %s" % (figtype, labelid), node) env.warn_node("no number is assigned for %s: %s" % (figtype, labelid), node)
return contnode return contnode
@ -670,7 +669,7 @@ class StandardDomain(Domain):
newtitle = title % '.'.join(map(str, fignumber)) newtitle = title % '.'.join(map(str, fignumber))
except TypeError: except TypeError:
env.warn_node('invalid numfig_format: %s' % title, node) env.warn_node('invalid numfig_format: %s' % title, node)
return None return contnode
return self.build_reference_node(fromdocname, builder, return self.build_reference_node(fromdocname, builder,
docname, labelid, newtitle, 'numref', docname, labelid, newtitle, 'numref',
@ -804,7 +803,9 @@ class StandardDomain(Domain):
def has_child(node, cls): def has_child(node, cls):
return any(isinstance(child, cls) for child in node) return any(isinstance(child, cls) for child in node)
if isinstance(node, nodes.container): if isinstance(node, nodes.section):
return 'section'
elif isinstance(node, nodes.container):
if node.get('literal_block') and has_child(node, nodes.literal_block): if node.get('literal_block') and has_child(node, nodes.literal_block):
return 'code-block' return 'code-block'
else: else:
@ -813,6 +814,28 @@ class StandardDomain(Domain):
figtype, _ = self.enumerable_nodes.get(node.__class__, (None, None)) figtype, _ = self.enumerable_nodes.get(node.__class__, (None, None))
return figtype return figtype
def get_fignumber(self, env, builder, figtype, docname, target_node):
if figtype == 'section':
if builder.name == 'latex':
return tuple()
elif docname not in env.toc_secnumbers:
raise ValueError # no number assigned
else:
anchorname = '#' + target_node['ids'][0]
if anchorname not in env.toc_secnumbers[docname]:
# try first heading which has no anchor
return env.toc_secnumbers[docname].get('')
else:
return env.toc_secnumbers[docname].get(anchorname)
else:
try:
figure_id = target_node['ids'][0]
return env.toc_fignumbers[docname][figtype][figure_id]
except (KeyError, IndexError):
# target_node is found, but fignumber is not assigned.
# Maybe it is defined in orphaned document.
raise ValueError
def setup(app): def setup(app):
app.add_domain(StandardDomain) app.add_domain(StandardDomain)

View File

@ -1,7 +1,11 @@
.. _bar:
=== ===
Bar Bar
=== ===
.. _bar_a:
Bar A Bar A
===== =====
@ -37,9 +41,13 @@ Bar A
print('hello world') print('hello world')
.. _bar_b:
Bar B Bar B
===== =====
.. _bar_b1:
Bar B1 Bar B1
------ ------

View File

@ -1,3 +1,5 @@
.. _baz_a:
Baz A Baz A
----- -----

View File

@ -1,3 +1,5 @@
.. _foo:
=== ===
Foo Foo
=== ===
@ -16,6 +18,8 @@ Foo
print('hello world') print('hello world')
.. _foo_a:
Foo A Foo A
===== =====
@ -47,12 +51,18 @@ Foo A
print('hello world') print('hello world')
.. _foo_a1:
Foo A1 Foo A1
------ ------
.. _foo_b:
Foo B Foo B
===== =====
.. _foo_b1:
Foo B1 Foo B1
------ ------

View File

@ -1,3 +1,5 @@
.. _index:
test-tocdepth test-tocdepth
============= =============
@ -48,5 +50,8 @@ test-tocdepth
* Table.2.2 is :numref:`Table:%s <table22>` * Table.2.2 is :numref:`Table:%s <table22>`
* List.1 is :numref:`CODE_1` * List.1 is :numref:`CODE_1`
* List.2.2 is :numref:`Code-%s <CODE22>` * List.2.2 is :numref:`Code-%s <CODE22>`
* Section.1 is :numref:`foo`
* Section.2.1 is :numref:`bar_a`
* Unnumbered section is :numref:`index`
* Invalid numfig_format 01: :numref:`invalid <fig1>` * Invalid numfig_format 01: :numref:`invalid <fig1>`
* Invalid numfig_format 02: :numref:`Fig %s %s <fig1>` * Invalid numfig_format 02: :numref:`Fig %s %s <fig1>`

View File

@ -503,10 +503,11 @@ def test_tocdepth_singlehtml(app, status, warning):
def test_numfig_disabled(app, status, warning): def test_numfig_disabled(app, status, warning):
app.builder.build_all() app.builder.build_all()
assert ('index.rst:45: WARNING: numfig is disabled. :numref: is ignored.' warnings = warning.getvalue()
in warning.getvalue()) assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' in warnings
assert 'index.rst:51: WARNING: invalid numfig_format: invalid' not in warning.getvalue() assert 'index.rst:55: WARNING: no number is assigned for section: index' not in warnings
assert 'index.rst:52: WARNING: invalid numfig_format: Fig %s %s' not in warning.getvalue() assert 'index.rst:56: WARNING: invalid numfig_format: invalid' not in warnings
assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' not in warnings
expects = { expects = {
'index.html': [ 'index.html': [
@ -521,6 +522,8 @@ def test_numfig_disabled(app, status, warning):
(".//li/code/span", '^Table:%s$', True), (".//li/code/span", '^Table:%s$', True),
(".//li/code/span", '^CODE_1$', True), (".//li/code/span", '^CODE_1$', True),
(".//li/code/span", '^Code-%s$', True), (".//li/code/span", '^Code-%s$', True),
(".//li/code/span", '^foo$', True),
(".//li/code/span", '^bar_a$', True),
], ],
'foo.html': [ 'foo.html': [
(".//div[@class='figure']/p[@class='caption']/" (".//div[@class='figure']/p[@class='caption']/"
@ -562,10 +565,11 @@ def test_numfig_without_numbered_toctree(app, status, warning):
(app.srcdir / 'index.rst').write_text(index, encoding='utf-8') (app.srcdir / 'index.rst').write_text(index, encoding='utf-8')
app.builder.build_all() app.builder.build_all()
assert ('index.rst:45: WARNING: numfig is disabled. :numref: is ignored.' warnings = warning.getvalue()
not in warning.getvalue()) assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings
assert 'index.rst:51: WARNING: invalid numfig_format: invalid' in warning.getvalue() assert 'index.rst:55: WARNING: no number is assigned for section: index' in warnings
assert 'index.rst:52: WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue() assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings
assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings
expects = { expects = {
'index.html': [ 'index.html': [
@ -587,6 +591,8 @@ def test_numfig_without_numbered_toctree(app, status, warning):
(".//li/a/span", '^Table:6$', True), (".//li/a/span", '^Table:6$', True),
(".//li/a/span", '^Listing 9$', True), (".//li/a/span", '^Listing 9$', True),
(".//li/a/span", '^Code-6$', True), (".//li/a/span", '^Code-6$', True),
(".//li/code/span", '^foo$', True),
(".//li/code/span", '^bar_a$', True),
], ],
'foo.html': [ 'foo.html': [
(".//div[@class='figure']/p[@class='caption']/" (".//div[@class='figure']/p[@class='caption']/"
@ -657,10 +663,11 @@ def test_numfig_without_numbered_toctree(app, status, warning):
def test_numfig_with_numbered_toctree(app, status, warning): def test_numfig_with_numbered_toctree(app, status, warning):
app.builder.build_all() app.builder.build_all()
assert ('index.rst:45: WARNING: numfig is disabled. :numref: is ignored.' warnings = warning.getvalue()
not in warning.getvalue()) assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings
assert 'index.rst:51: WARNING: invalid numfig_format: invalid' in warning.getvalue() assert 'index.rst:55: WARNING: no number is assigned for section: index' in warnings
assert 'index.rst:52: WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue() assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings
assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings
expects = { expects = {
'index.html': [ 'index.html': [
@ -682,6 +689,8 @@ def test_numfig_with_numbered_toctree(app, status, warning):
(".//li/a/span", '^Table:2.2$', True), (".//li/a/span", '^Table:2.2$', True),
(".//li/a/span", '^Listing 1$', True), (".//li/a/span", '^Listing 1$', True),
(".//li/a/span", '^Code-2.2$', True), (".//li/a/span", '^Code-2.2$', True),
(".//li/a/span", '^Section.1$', True),
(".//li/a/span", '^Section.2.1$', True),
], ],
'foo.html': [ 'foo.html': [
(".//div[@class='figure']/p[@class='caption']/" (".//div[@class='figure']/p[@class='caption']/"
@ -751,14 +760,16 @@ def test_numfig_with_numbered_toctree(app, status, warning):
confoverrides={'numfig': True, confoverrides={'numfig': True,
'numfig_format': {'figure': 'Figure:%s', 'numfig_format': {'figure': 'Figure:%s',
'table': 'Tab_%s', 'table': 'Tab_%s',
'code-block': 'Code-%s'}}) 'code-block': 'Code-%s',
'section': 'SECTION-%s'}})
def test_numfig_with_prefix(app, status, warning): def test_numfig_with_prefix(app, status, warning):
app.builder.build_all() app.builder.build_all()
assert ('index.rst:45: WARNING: numfig is disabled. :numref: is ignored.' warnings = warning.getvalue()
not in warning.getvalue()) assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings
assert 'index.rst:51: WARNING: invalid numfig_format: invalid' in warning.getvalue() assert 'index.rst:55: WARNING: no number is assigned for section: index' in warnings
assert 'index.rst:52: WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue() assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings
assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings
expects = { expects = {
'index.html': [ 'index.html': [
@ -780,6 +791,8 @@ def test_numfig_with_prefix(app, status, warning):
(".//li/a/span", '^Table:2.2$', True), (".//li/a/span", '^Table:2.2$', True),
(".//li/a/span", '^Code-1$', True), (".//li/a/span", '^Code-1$', True),
(".//li/a/span", '^Code-2.2$', True), (".//li/a/span", '^Code-2.2$', True),
(".//li/a/span", '^SECTION-1$', True),
(".//li/a/span", '^SECTION-2.1$', True),
], ],
'foo.html': [ 'foo.html': [
(".//div[@class='figure']/p[@class='caption']/" (".//div[@class='figure']/p[@class='caption']/"
@ -850,10 +863,11 @@ def test_numfig_with_prefix(app, status, warning):
def test_numfig_with_secnum_depth(app, status, warning): def test_numfig_with_secnum_depth(app, status, warning):
app.builder.build_all() app.builder.build_all()
assert ('index.rst:45: WARNING: numfig is disabled. :numref: is ignored.' warnings = warning.getvalue()
not in warning.getvalue()) assert 'index.rst:47: WARNING: numfig is disabled. :numref: is ignored.' not in warnings
assert 'index.rst:51: WARNING: invalid numfig_format: invalid' in warning.getvalue() assert 'index.rst:55: WARNING: no number is assigned for section: index' in warnings
assert 'index.rst:52: WARNING: invalid numfig_format: Fig %s %s' in warning.getvalue() assert 'index.rst:56: WARNING: invalid numfig_format: invalid' in warnings
assert 'index.rst:57: WARNING: invalid numfig_format: Fig %s %s' in warnings
expects = { expects = {
'index.html': [ 'index.html': [
@ -875,6 +889,8 @@ def test_numfig_with_secnum_depth(app, status, warning):
(".//li/a/span", '^Table:2.1.2$', True), (".//li/a/span", '^Table:2.1.2$', True),
(".//li/a/span", '^Listing 1$', True), (".//li/a/span", '^Listing 1$', True),
(".//li/a/span", '^Code-2.1.2$', True), (".//li/a/span", '^Code-2.1.2$', True),
(".//li/a/span", '^Section.1$', True),
(".//li/a/span", '^Section.2.1$', True),
], ],
'foo.html': [ 'foo.html': [
(".//div[@class='figure']/p[@class='caption']/" (".//div[@class='figure']/p[@class='caption']/"
@ -965,6 +981,8 @@ def test_numfig_with_singlehtml(app, status, warning):
(".//li/a/span", '^Table:2.2$', True), (".//li/a/span", '^Table:2.2$', True),
(".//li/a/span", '^Listing 1$', True), (".//li/a/span", '^Listing 1$', True),
(".//li/a/span", '^Code-2.2$', True), (".//li/a/span", '^Code-2.2$', True),
(".//li/a/span", '^Section.1$', True),
(".//li/a/span", '^Section.2.1$', True),
(".//div[@class='figure']/p[@class='caption']/" (".//div[@class='figure']/p[@class='caption']/"
"span[@class='caption-number']", '^Fig. 1.1 $', True), "span[@class='caption-number']", '^Fig. 1.1 $', True),
(".//div[@class='figure']/p[@class='caption']/" (".//div[@class='figure']/p[@class='caption']/"

View File

@ -151,13 +151,16 @@ def test_numref(app, status, warning):
assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result
assert '\\hyperref[index:code-1]{Listing \\ref{index:code-1}}' in result assert '\\hyperref[index:code-1]{Listing \\ref{index:code-1}}' in result
assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result
assert '\\hyperref[foo:foo]{Section \\ref{foo:foo}}' in result
assert '\\hyperref[bar:bar-a]{Section \\ref{bar:bar-a}}' in result
@with_app(buildername='latex', testroot='numfig', @with_app(buildername='latex', testroot='numfig',
confoverrides={'numfig': True, confoverrides={'numfig': True,
'numfig_format': {'figure': 'Figure:%s', 'numfig_format': {'figure': 'Figure:%s',
'table': 'Tab_%s', 'table': 'Tab_%s',
'code-block': 'Code-%s'}}) 'code-block': 'Code-%s',
'section': 'SECTION-%s'}})
def test_numref_with_prefix1(app, status, warning): def test_numref_with_prefix1(app, status, warning):
app.builder.build_all() app.builder.build_all()
result = (app.outdir / 'Python.tex').text(encoding='utf8') result = (app.outdir / 'Python.tex').text(encoding='utf8')
@ -179,13 +182,16 @@ def test_numref_with_prefix1(app, status, warning):
assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result
assert '\\hyperref[index:code-1]{Code-\\ref{index:code-1}}' in result assert '\\hyperref[index:code-1]{Code-\\ref{index:code-1}}' in result
assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result
assert '\\hyperref[foo:foo]{SECTION-\\ref{foo:foo}}' in result
assert '\\hyperref[bar:bar-a]{SECTION-\\ref{bar:bar-a}}' in result
@with_app(buildername='latex', testroot='numfig', @with_app(buildername='latex', testroot='numfig',
confoverrides={'numfig': True, confoverrides={'numfig': True,
'numfig_format': {'figure': 'Figure:%s.', 'numfig_format': {'figure': 'Figure:%s.',
'table': 'Tab_%s:', 'table': 'Tab_%s:',
'code-block': 'Code-%s | '}}) 'code-block': 'Code-%s | ',
'section': 'SECTION_%s_'}})
def test_numref_with_prefix2(app, status, warning): def test_numref_with_prefix2(app, status, warning):
app.builder.build_all() app.builder.build_all()
result = (app.outdir / 'Python.tex').text(encoding='utf8') result = (app.outdir / 'Python.tex').text(encoding='utf8')
@ -203,6 +209,8 @@ def test_numref_with_prefix2(app, status, warning):
assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result
assert '\\hyperref[index:code-1]{Code-\\ref{index:code-1} \\textbar{} }' in result assert '\\hyperref[index:code-1]{Code-\\ref{index:code-1} \\textbar{} }' in result
assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result
assert '\\hyperref[foo:foo]{SECTION\\_\\ref{foo:foo}\\_}' in result
assert '\\hyperref[bar:bar-a]{SECTION\\_\\ref{bar:bar-a}\\_}' in result
@with_app(buildername='latex', testroot='numfig', @with_app(buildername='latex', testroot='numfig',
@ -222,6 +230,8 @@ def test_numref_with_language_ja(app, status, warning):
assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result assert '\\hyperref[baz:table22]{Table:\\ref{baz:table22}}' in result
assert '\\hyperref[index:code-1]{LIST \\ref{index:code-1}}' in result assert '\\hyperref[index:code-1]{LIST \\ref{index:code-1}}' in result
assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result assert '\\hyperref[baz:code22]{Code-\\ref{baz:code22}}' in result
assert '\\hyperref[foo:foo]{Section \\ref{foo:foo}}' in result
assert '\\hyperref[bar:bar-a]{Section \\ref{bar:bar-a}}' in result
@with_app(buildername='latex') @with_app(buildername='latex')