summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortk0miya <i.tkomiya@gmail.com>2014-10-06 23:57:16 +0900
committertk0miya <i.tkomiya@gmail.com>2014-10-06 23:57:16 +0900
commit6e2e4d12b96413b460ec01dc9c5d90519ce8d079 (patch)
tree3c8238f06b7c2e33fcdb37663779d45bbc92d98e
parent72d21037e99936a76d51aab4b6b7b88ba3144f20 (diff)
parent541467233bd282b400d139b150da111e09880264 (diff)
downloadsphinx-6e2e4d12b96413b460ec01dc9c5d90519ce8d079.tar.gz
Merge with default
-rw-r--r--sphinx/addnodes.py4
-rw-r--r--sphinx/domains/std.py75
-rw-r--r--sphinx/environment.py19
-rw-r--r--sphinx/util/__init__.py17
-rw-r--r--sphinx/writers/html.py6
-rw-r--r--sphinx/writers/latex.py26
-rw-r--r--sphinx/writers/manpage.py5
-rw-r--r--sphinx/writers/texinfo.py5
-rw-r--r--sphinx/writers/text.py5
-rw-r--r--tests/root/conf.py1
-rw-r--r--tests/root/markup.txt3
-rw-r--r--tests/roots/test-numfig/baz.rst6
-rw-r--r--tests/roots/test-numfig/index.rst14
-rw-r--r--tests/test_build_html.py33
-rw-r--r--tests/test_build_latex.py16
15 files changed, 197 insertions, 38 deletions
diff --git a/sphinx/addnodes.py b/sphinx/addnodes.py
index 9d8c4690..362abd5a 100644
--- a/sphinx/addnodes.py
+++ b/sphinx/addnodes.py
@@ -184,6 +184,10 @@ class pending_xref(nodes.Inline, nodes.Element):
"""
+class number_reference(nodes.reference):
+ """Node for number references, similar to pending_xref."""
+
+
class download_reference(nodes.reference):
"""Node for download references, similar to pending_xref."""
diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py
index 0338a6b5..03228da2 100644
--- a/sphinx/domains/std.py
+++ b/sphinx/domains/std.py
@@ -22,7 +22,7 @@ from sphinx.roles import XRefRole
from sphinx.locale import l_, _
from sphinx.domains import Domain, ObjType
from sphinx.directives import ObjectDescription
-from sphinx.util import ws_re
+from sphinx.util import ws_re, get_figtype
from sphinx.util.nodes import clean_astext, make_refnode
from sphinx.util.compat import Directive
@@ -466,6 +466,9 @@ class StandardDomain(Domain):
# links to headings or arbitrary labels
'ref': XRefRole(lowercase=True, innernodeclass=nodes.emphasis,
warn_dangling=True),
+ # links to labels of numbered figures, tables and code-blocks
+ 'numref': XRefRole(lowercase=True,
+ warn_dangling=True),
# links to labels, without a different title
'keyword': XRefRole(warn_dangling=True),
}
@@ -489,6 +492,7 @@ class StandardDomain(Domain):
'term': 'term not in glossary: %(target)s',
'ref': 'undefined label: %(target)s (if the link has no caption '
'the label must precede a section header)',
+ 'numref': 'undefined label: %(target)s',
'keyword': 'unknown keyword: %(target)s',
}
@@ -574,6 +578,28 @@ class StandardDomain(Domain):
continue
labels[name] = docname, labelid, sectname
+ def build_reference_node(self, fromdocname, builder,
+ docname, labelid, sectname,
+ **options):
+ nodeclass = options.pop('nodeclass', nodes.reference)
+ newnode = nodeclass('', '', internal=True, **options)
+ innernode = nodes.emphasis(sectname, sectname)
+ if docname == fromdocname:
+ newnode['refid'] = labelid
+ else:
+ # set more info in contnode; in case the
+ # get_relative_uri call raises NoUri,
+ # the builder will then have to resolve these
+ contnode = addnodes.pending_xref('')
+ contnode['refdocname'] = docname
+ contnode['refsectname'] = sectname
+ newnode['refuri'] = builder.get_relative_uri(
+ fromdocname, docname)
+ if labelid:
+ newnode['refuri'] += '#' + labelid
+ newnode.append(innernode)
+ return newnode
+
def resolve_xref(self, env, fromdocname, builder,
typ, target, node, contnode):
if typ == 'ref':
@@ -589,23 +615,38 @@ class StandardDomain(Domain):
('', '', ''))
if not docname:
return None
- newnode = nodes.reference('', '', internal=True)
- innernode = nodes.emphasis(sectname, sectname)
- if docname == fromdocname:
- newnode['refid'] = labelid
+
+ return self.build_reference_node(fromdocname, builder,
+ docname, labelid, sectname)
+ elif typ == 'numref':
+ docname, labelid = self.data['anonlabels'].get(target, ('', ''))
+ if not docname:
+ return None
+
+ if env.config.numfig is False:
+ env.warn(fromdocname, 'numfig is disabled. :numref: is ignored.')
+ return contnode
+
+ try:
+ target = env.get_doctree(docname).ids[labelid]
+ figtype = get_figtype(target)
+ figure_id = target['ids'][0]
+ fignumber = env.toc_fignumbers[docname][figtype][figure_id]
+ except (KeyError, IndexError):
+ return None
+
+ title = contnode.astext()
+ if labelid == title:
+ prefix = env.config.numfig_prefix.get(figtype, '')
+ title = prefix.replace('%s', '#')
+ newtitle = prefix % '.'.join(map(str, fignumber))
else:
- # set more info in contnode; in case the
- # get_relative_uri call raises NoUri,
- # the builder will then have to resolve these
- contnode = addnodes.pending_xref('')
- contnode['refdocname'] = docname
- contnode['refsectname'] = sectname
- newnode['refuri'] = builder.get_relative_uri(
- fromdocname, docname)
- if labelid:
- newnode['refuri'] += '#' + labelid
- newnode.append(innernode)
- return newnode
+ newtitle = title.replace('#', '.'.join(map(str, fignumber)))
+
+ return self.build_reference_node(fromdocname, builder,
+ docname, labelid, newtitle,
+ nodeclass=addnodes.number_reference,
+ title=title)
elif typ == 'keyword':
# keywords are oddballs: they are referenced by named labels
docname, labelid, _ = self.data['labels'].get(target, ('', '', ''))
diff --git a/sphinx/environment.py b/sphinx/environment.py
index 9111e34f..38cf2d8c 100644
--- a/sphinx/environment.py
+++ b/sphinx/environment.py
@@ -37,7 +37,7 @@ from docutils.frontend import OptionParser
from sphinx import addnodes
from sphinx.util import url_re, get_matching_docs, docname_join, split_into, \
- FilenameUniqDict
+ FilenameUniqDict, get_figtype
from sphinx.util.nodes import clean_astext, make_refnode, WarningStream
from sphinx.util.osutil import SEP, find_catalog_files, getcwd, fs_encoding
from sphinx.util.console import bold, purple
@@ -1706,9 +1706,6 @@ class BuildEnvironment:
self.toc_fignumbers = {}
fignum_counter = {}
- def has_child(node, cls):
- return any(isinstance(child, cls) for child in node)
-
def get_section_number(docname, section):
anchorname = '#' + section['ids'][0]
secnumbers = self.toc_secnumbers.get(docname, {})
@@ -1750,16 +1747,10 @@ class BuildEnvironment:
continue
- if isinstance(subnode, nodes.figure):
- figure_id = subnode['ids'][0]
- register_fignumber(docname, secnum, 'figure', figure_id)
- elif isinstance(subnode, nodes.table):
- table_id = subnode['ids'][0]
- register_fignumber(docname, secnum, 'table', table_id)
- elif isinstance(subnode, nodes.container):
- if has_child(subnode, nodes.literal_block):
- code_block_id = subnode['ids'][0]
- register_fignumber(docname, secnum, 'code-block', code_block_id)
+ figtype = get_figtype(subnode)
+ if figtype and subnode['ids']:
+ register_fignumber(docname, secnum,
+ figtype, subnode['ids'][0])
_walk_doctree(docname, subnode, secnum)
diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py
index e7277520..024e25b1 100644
--- a/sphinx/util/__init__.py
+++ b/sphinx/util/__init__.py
@@ -475,3 +475,20 @@ class PeekableIterator(object):
item = next(self)
self.push(item)
return item
+
+
+def get_figtype(node):
+ """Return figtype for given node."""
+ def has_child(node, cls):
+ return any(isinstance(child, cls) for child in node)
+
+ from docutils import nodes
+ if isinstance(node, nodes.figure):
+ return 'figure'
+ elif isinstance(node, nodes.table):
+ return 'table'
+ elif isinstance(node, nodes.container):
+ if has_child(node, nodes.literal_block):
+ return 'code-block'
+
+ return None
diff --git a/sphinx/writers/html.py b/sphinx/writers/html.py
index 1d2cff05..1c7bed99 100644
--- a/sphinx/writers/html.py
+++ b/sphinx/writers/html.py
@@ -207,6 +207,12 @@ class HTMLTranslator(BaseTranslator):
self.body.append(('%s' + self.secnumber_suffix) %
'.'.join(map(str, node['secnumber'])))
+ def visit_number_reference(self, node):
+ self.visit_reference(node)
+
+ def depart_number_reference(self, node):
+ self.depart_reference(node)
+
# overwritten -- we don't want source comments to show up in the HTML
def visit_comment(self, node):
raise nodes.SkipNode
diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py
index ea79761f..dd85a806 100644
--- a/sphinx/writers/latex.py
+++ b/sphinx/writers/latex.py
@@ -682,6 +682,9 @@ class LaTeXTranslator(nodes.NodeVisitor):
if not self.table.longtable and self.table.caption is not None:
self.body.append(u'\n\n\\begin{threeparttable}\n'
u'\\capstart\\caption{%s}\n' % self.table.caption)
+ for id in self.next_table_ids:
+ self.body.append(self.hypertarget(id, anchor=False))
+ self.next_table_ids.clear()
if self.table.longtable:
self.body.append('\n\\begin{longtable}')
endmacro = '\\end{longtable}\n\n'
@@ -709,11 +712,11 @@ class LaTeXTranslator(nodes.NodeVisitor):
else:
self.body.append('{|' + ('L|' * self.table.colcount) + '}\n')
if self.table.longtable and self.table.caption is not None:
- self.body.append(u'\\caption{%s} \\\\\n' % self.table.caption)
- if self.table.caption is not None:
+ self.body.append(u'\\caption{%s}' % self.table.caption)
for id in self.next_table_ids:
self.body.append(self.hypertarget(id, anchor=False))
self.next_table_ids.clear()
+ self.body.append(u'\\\\\n')
if self.table.longtable:
self.body.append('\\hline\n')
self.body.extend(self.tableheaders)
@@ -1114,7 +1117,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
return
elif isinstance(next, nodes.table):
# same for tables, but only if they have a caption
- for n in node:
+ for n in next:
if isinstance(n, nodes.title):
if node.get('refid'):
self.next_table_ids.add(node['refid'])
@@ -1244,6 +1247,18 @@ class LaTeXTranslator(nodes.NodeVisitor):
def depart_reference(self, node):
self.body.append(self.context.pop())
+ def visit_number_reference(self, node):
+ if node.get('refid'):
+ id = self.curfilestack[-1] + ':' + node['refid']
+ else:
+ id = node.get('refuri', '')[1:].replace('#', ':')
+
+ ref = '\\ref{%s}' % self.idescape(id)
+ title = node.get('title', '#')
+ self.body.append(title.replace('#', ref))
+
+ raise nodes.SkipNode
+
def visit_download_reference(self, node):
pass
def depart_download_reference(self, node):
@@ -1511,11 +1526,12 @@ class LaTeXTranslator(nodes.NodeVisitor):
for id in self.next_literal_ids:
ids += self.hypertarget(id, anchor=False)
self.next_literal_ids.clear()
- self.body.append('\n\\begin{literal-block}' + ids)
+ self.body.append('\n\\begin{literal-block}\n')
+ self.context.append(ids + '\n\\end{literal-block}\n')
def depart_container(self, node):
if node.get('literal_block'):
- self.body.append('\\end{literal-block}\n')
+ self.body.append(self.context.pop())
def visit_decoration(self, node):
pass
diff --git a/sphinx/writers/manpage.py b/sphinx/writers/manpage.py
index 8d49f807..bc842057 100644
--- a/sphinx/writers/manpage.py
+++ b/sphinx/writers/manpage.py
@@ -246,6 +246,11 @@ class ManualPageTranslator(BaseTranslator):
'>'])
raise nodes.SkipNode
+ def visit_number_reference(self, node):
+ text = nodes.Text(node.get('title', '#'))
+ self.visit_Text(text)
+ raise nodes.SkipNode
+
def visit_centered(self, node):
self.ensure_eol()
self.body.append('.sp\n.ce\n')
diff --git a/sphinx/writers/texinfo.py b/sphinx/writers/texinfo.py
index 61d2e62f..93d4d6aa 100644
--- a/sphinx/writers/texinfo.py
+++ b/sphinx/writers/texinfo.py
@@ -722,6 +722,11 @@ class TexinfoTranslator(nodes.NodeVisitor):
def depart_reference(self, node):
pass
+ def visit_number_reference(self, node):
+ text = nodes.Text(node.get('title', '#'))
+ self.visit_Text(text)
+ raise nodes.SkipNode
+
def visit_title_reference(self, node):
text = node.astext()
self.body.append('@cite{%s}' % self.escape_arg(text))
diff --git a/sphinx/writers/text.py b/sphinx/writers/text.py
index 5b32b05a..efd32df3 100644
--- a/sphinx/writers/text.py
+++ b/sphinx/writers/text.py
@@ -748,6 +748,11 @@ class TextTranslator(nodes.NodeVisitor):
def depart_reference(self, node):
pass
+ def visit_number_reference(self, node):
+ text = nodes.Text(node.get('title', '#'))
+ self.visit_Text(text)
+ raise nodes.SkipNode
+
def visit_download_reference(self, node):
pass
def depart_download_reference(self, node):
diff --git a/tests/root/conf.py b/tests/root/conf.py
index d12e8167..5186f371 100644
--- a/tests/root/conf.py
+++ b/tests/root/conf.py
@@ -24,6 +24,7 @@ exclude_patterns = ['_build', '**/excluded.*']
keep_warnings = True
pygments_style = 'sphinx'
show_authors = True
+numfig = True
rst_epilog = '.. |subst| replace:: global substitution'
diff --git a/tests/root/markup.txt b/tests/root/markup.txt
index 6ed396ac..1ad7a13b 100644
--- a/tests/root/markup.txt
+++ b/tests/root/markup.txt
@@ -143,6 +143,9 @@ Adding \n to test unescaping.
* :ref:`my-figure`
* :ref:`my-table`
* :ref:`my-code-block`
+* :numref:`my-figure`
+* :numref:`my-table`
+* :numref:`my-code-block`
* :doc:`subdir/includes`
* ``:download:`` is tested in includes.txt
* :option:`Python -c option <python -c>`
diff --git a/tests/roots/test-numfig/baz.rst b/tests/roots/test-numfig/baz.rst
index 9e2ccfeb..da9e0fe0 100644
--- a/tests/roots/test-numfig/baz.rst
+++ b/tests/roots/test-numfig/baz.rst
@@ -1,15 +1,21 @@
Baz A
-----
+.. _fig22:
+
.. figure:: rimg.png
should be Fig.2.2
+.. _table22:
+
.. csv-table:: should be Table 2.2
:header-rows: 0
hello,world
+.. _code22:
+
.. code-block:: python
:caption: should be List 2.2
diff --git a/tests/roots/test-numfig/index.rst b/tests/roots/test-numfig/index.rst
index 564018e3..7bdf135c 100644
--- a/tests/roots/test-numfig/index.rst
+++ b/tests/roots/test-numfig/index.rst
@@ -7,6 +7,8 @@ test-tocdepth
foo
bar
+.. _fig1:
+
.. figure:: rimg.png
should be Fig.1
@@ -15,6 +17,8 @@ test-tocdepth
should be Fig.2
+.. _table1:
+
.. csv-table:: should be Table 1
:header-rows: 0
@@ -25,6 +29,8 @@ test-tocdepth
hello,world
+.. _code1:
+
.. code-block:: python
:caption: should be List 1
@@ -34,3 +40,11 @@ test-tocdepth
:caption: should be List 2
print('hello world')
+
+
+* Fig.1 is :numref:`fig1`
+* Fig.2.2 is :numref:`Figure# <fig22>`
+* Table.1 is :numref:`table1`
+* Table.2.2 is :numref:`Table:# <table22>`
+* List.1 is :numref:`code1`
+* List.2.2 is :numref:`Code-# <code22>`
diff --git a/tests/test_build_html.py b/tests/test_build_html.py
index f5c9a38e..baf3b6a6 100644
--- a/tests/test_build_html.py
+++ b/tests/test_build_html.py
@@ -463,8 +463,7 @@ def test_tocdepth_singlehtml(app, status, warning):
@gen_with_app(buildername='html', testroot='numfig')
-def test_numfig(app, status, warning):
- # issue #1251
+def test_numfig_disabled(app, status, warning):
app.builder.build_all()
expects = {
@@ -474,6 +473,12 @@ def test_numfig(app, status, warning):
(".//table/caption/span[@class='caption-number']", None, True),
(".//div[@class='code-block-caption']/"
"span[@class='caption-number']", None, True),
+ (".//li/code/span", '^fig1$', True),
+ (".//li/code/span", '^Figure#$', True),
+ (".//li/code/span", '^table1$', True),
+ (".//li/code/span", '^Table:#$', True),
+ (".//li/code/span", '^code1$', True),
+ (".//li/code/span", '^Code-#$', True),
],
'foo.html': [
(".//div[@class='figure']/p[@class='caption']/"
@@ -534,6 +539,12 @@ def test_numfig_without_numbered_toctree(app, status, warning):
"span[@class='caption-number']", '^Listing 9 $', True),
(".//div[@class='code-block-caption']/"
"span[@class='caption-number']", '^Listing 10 $', True),
+ (".//li/a/em", '^Fig. 9$', True),
+ (".//li/a/em", '^Figure6$', True),
+ (".//li/a/em", '^Table 9$', True),
+ (".//li/a/em", '^Table:6$', True),
+ (".//li/a/em", '^Listing 9$', True),
+ (".//li/a/em", '^Code-6$', True),
],
'foo.html': [
(".//div[@class='figure']/p[@class='caption']/"
@@ -623,6 +634,12 @@ def test_numfig_with_numbered_toctree(app, status, warning):
"span[@class='caption-number']", '^Listing 1 $', True),
(".//div[@class='code-block-caption']/"
"span[@class='caption-number']", '^Listing 2 $', True),
+ (".//li/a/em", '^Fig. 1$', True),
+ (".//li/a/em", '^Figure2.2$', True),
+ (".//li/a/em", '^Table 1$', True),
+ (".//li/a/em", '^Table:2.2$', True),
+ (".//li/a/em", '^Listing 1$', True),
+ (".//li/a/em", '^Code-2.2$', True),
],
'foo.html': [
(".//div[@class='figure']/p[@class='caption']/"
@@ -715,6 +732,12 @@ def test_numfig_with_prefix(app, status, warning):
"span[@class='caption-number']", '^Code-1 $', True),
(".//div[@class='code-block-caption']/"
"span[@class='caption-number']", '^Code-2 $', True),
+ (".//li/a/em", '^Figure:1$', True),
+ (".//li/a/em", '^Figure2.2$', True),
+ (".//li/a/em", '^Tab_1$', True),
+ (".//li/a/em", '^Table:2.2$', True),
+ (".//li/a/em", '^Code-1$', True),
+ (".//li/a/em", '^Code-2.2$', True),
],
'foo.html': [
(".//div[@class='figure']/p[@class='caption']/"
@@ -804,6 +827,12 @@ def test_numfig_with_secnum_depth(app, status, warning):
"span[@class='caption-number']", '^Listing 1 $', True),
(".//div[@class='code-block-caption']/"
"span[@class='caption-number']", '^Listing 2 $', True),
+ (".//li/a/em", '^Fig. 1$', True),
+ (".//li/a/em", '^Figure2.1.2$', True),
+ (".//li/a/em", '^Table 1$', True),
+ (".//li/a/em", '^Table:2.1.2$', True),
+ (".//li/a/em", '^Listing 1$', True),
+ (".//li/a/em", '^Code-2.1.2$', True),
],
'foo.html': [
(".//div[@class='figure']/p[@class='caption']/"
diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py
index 374b54a2..3b6dd3e2 100644
--- a/tests/test_build_latex.py
+++ b/tests/test_build_latex.py
@@ -93,6 +93,22 @@ def test_latex(app, status, warning):
os.chdir(cwd)
+@with_app(buildername='latex', testroot='numfig',
+ confoverrides={'numfig': True})
+def test_numref(app, status, warning):
+ app.builder.build_all()
+ result = (app.outdir / 'Python.tex').text(encoding='utf8')
+ print(result)
+ print(status.getvalue())
+ print(warning.getvalue())
+ assert '\\ref{index:fig1}' in result
+ assert '\\ref{baz:fig22}' in result
+ assert '\\ref{index:table1}' in result
+ assert '\\ref{baz:table22}' in result
+ assert '\\ref{index:code1}' in result
+ assert '\\ref{baz:code22}' in result
+
+
@with_app(buildername='latex')
def test_latex_add_latex_package(app, status, warning):
app.add_latex_package('foo')