diff options
author | tk0miya <i.tkomiya@gmail.com> | 2014-10-06 23:57:16 +0900 |
---|---|---|
committer | tk0miya <i.tkomiya@gmail.com> | 2014-10-06 23:57:16 +0900 |
commit | 6e2e4d12b96413b460ec01dc9c5d90519ce8d079 (patch) | |
tree | 3c8238f06b7c2e33fcdb37663779d45bbc92d98e | |
parent | 72d21037e99936a76d51aab4b6b7b88ba3144f20 (diff) | |
parent | 541467233bd282b400d139b150da111e09880264 (diff) | |
download | sphinx-6e2e4d12b96413b460ec01dc9c5d90519ce8d079.tar.gz |
Merge with default
-rw-r--r-- | sphinx/addnodes.py | 4 | ||||
-rw-r--r-- | sphinx/domains/std.py | 75 | ||||
-rw-r--r-- | sphinx/environment.py | 19 | ||||
-rw-r--r-- | sphinx/util/__init__.py | 17 | ||||
-rw-r--r-- | sphinx/writers/html.py | 6 | ||||
-rw-r--r-- | sphinx/writers/latex.py | 26 | ||||
-rw-r--r-- | sphinx/writers/manpage.py | 5 | ||||
-rw-r--r-- | sphinx/writers/texinfo.py | 5 | ||||
-rw-r--r-- | sphinx/writers/text.py | 5 | ||||
-rw-r--r-- | tests/root/conf.py | 1 | ||||
-rw-r--r-- | tests/root/markup.txt | 3 | ||||
-rw-r--r-- | tests/roots/test-numfig/baz.rst | 6 | ||||
-rw-r--r-- | tests/roots/test-numfig/index.rst | 14 | ||||
-rw-r--r-- | tests/test_build_html.py | 33 | ||||
-rw-r--r-- | tests/test_build_latex.py | 16 |
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') |