Skip to content

Commit

Permalink
Merge pull request #2857 from tk0miya/std_domain_handles_citations
Browse files Browse the repository at this point in the history
std domain handles citations
  • Loading branch information
tk0miya committed Aug 20, 2016
2 parents 372e83f + d5a770b commit 3639231
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 44 deletions.
45 changes: 45 additions & 0 deletions sphinx/domains/std.py
Expand Up @@ -466,6 +466,7 @@ class StandardDomain(Domain):
initial_data = {
'progoptions': {}, # (program, name) -> docname, labelid
'objects': {}, # (type, name) -> docname, labelid
'citations': {}, # name -> docname, labelid
'labels': { # labelname -> docname, labelid, sectionname
'genindex': ('genindex', '', l_('Index')),
'modindex': ('py-modindex', '', l_('Module Index')),
Expand All @@ -485,6 +486,7 @@ class StandardDomain(Domain):
'numref': 'undefined label: %(target)s',
'keyword': 'unknown keyword: %(target)s',
'option': 'unknown option: %(target)s',
'citation': 'citation not found: %(target)s',
}

enumerable_nodes = { # node_class -> (figtype, title_getter)
Expand All @@ -500,6 +502,9 @@ def clear_doc(self, docname):
for key, (fn, _l) in list(self.data['objects'].items()):
if fn == docname:
del self.data['objects'][key]
for key, (fn, _l) in list(self.data['citations'].items()):
if fn == docname:
del self.data['citations'][key]
for key, (fn, _l, _l) in list(self.data['labels'].items()):
if fn == docname:
del self.data['labels'][key]
Expand All @@ -515,6 +520,9 @@ def merge_domaindata(self, docnames, otherdata):
for key, data in otherdata['objects'].items():
if data[0] in docnames:
self.data['objects'][key] = data
for key, data in otherdata['citations'].items():
if data[0] in docnames:
self.data['citations'][key] = data
for key, data in otherdata['labels'].items():
if data[0] in docnames:
self.data['labels'][key] = data
Expand All @@ -523,6 +531,19 @@ def merge_domaindata(self, docnames, otherdata):
self.data['anonlabels'][key] = data

def process_doc(self, env, docname, document):
self.note_citations(env, docname, document)
self.note_labels(env, docname, document)

def note_citations(self, env, docname, document):
for node in document.traverse(nodes.citation):
label = node[0].astext()
if label in self.data['citations']:
path = env.doc2path(self.data['citations'][0])
env.warn_node('duplicate citation %s, other instance in %s' %
(label, path), node)
self.data['citations'][label] = (docname, node['ids'][0])

def note_labels(self, env, docname, document):
labels, anonlabels = self.data['labels'], self.data['anonlabels']
for name, explicit in iteritems(document.nametypes):
if not explicit:
Expand Down Expand Up @@ -593,6 +614,8 @@ def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode):
resolver = self._resolve_keyword_xref
elif typ == 'option':
resolver = self._resolve_option_xref
elif typ == 'citation':
resolver = self._resolve_citation_xref
else:
resolver = self._resolve_obj_xref

Expand Down Expand Up @@ -683,6 +706,28 @@ def _resolve_option_xref(self, env, fromdocname, builder, typ, target, node, con
return make_refnode(builder, fromdocname, docname,
labelid, contnode)

def _resolve_citation_xref(self, env, fromdocname, builder, typ, target, node, contnode):
from sphinx.environment import NoUri

docname, labelid = self.data['citations'].get(target, ('', ''))
if not docname:
if 'ids' in node:
# remove ids attribute that annotated at
# transforms.CitationReference.apply.
del node['ids'][:]
return None

try:
return make_refnode(builder, fromdocname, docname,
labelid, contnode)
except NoUri:
# remove the ids we added in the CitationReferences
# transform since they can't be transfered to
# the contnode (if it's a Text node)
if not isinstance(contnode, nodes.Element):
del node['ids'][:]
raise

def _resolve_obj_xref(self, env, fromdocname, builder, typ, target, node, contnode):
objtypes = self.objtypes_for_role(typ) or []
for objtype in objtypes:
Expand Down
45 changes: 2 additions & 43 deletions sphinx/environment.py
Expand Up @@ -38,7 +38,7 @@
from sphinx.io import SphinxStandaloneReader, SphinxDummyWriter, SphinxFileInput
from sphinx.util import url_re, get_matching_docs, docname_join, split_into, \
FilenameUniqDict, split_index_msg
from sphinx.util.nodes import clean_astext, make_refnode, WarningStream, is_translatable
from sphinx.util.nodes import clean_astext, WarningStream, is_translatable
from sphinx.util.osutil import SEP, getcwd, fs_encoding, ensuredir
from sphinx.util.images import guess_mimetype
from sphinx.util.i18n import find_catalog_files, get_image_filename_for_language, \
Expand Down Expand Up @@ -76,7 +76,7 @@ class ElementLookupError(Exception):
# or changed to properly invalidate pickle files.
#
# NOTE: increase base version by 2 to have distinct numbers for Py2 and 3
ENV_VERSION = 49 + (sys.version_info[0] - 2)
ENV_VERSION = 50 + (sys.version_info[0] - 2)


dummy_reporter = Reporter('', 4, 4)
Expand Down Expand Up @@ -199,7 +199,6 @@ def __init__(self, srcdir, doctreedir, config):
self.domaindata = {} # domainname -> domain-specific dict

# Other inventories
self.citations = {} # citation name -> docname, labelid
self.indexentries = {} # docname -> list of
# (type, string, target, aliasname)
self.versionchanges = {} # version -> list of (type, docname,
Expand Down Expand Up @@ -276,9 +275,6 @@ def clear_doc(self, docname):
fnset.discard(docname)
if not fnset:
del self.files_to_rebuild[subfn]
for key, (fn, _ignore) in list(self.citations.items()):
if fn == docname:
del self.citations[key]
for version, changes in self.versionchanges.items():
new = [change for change in changes if change[1] != docname]
changes[:] = new
Expand Down Expand Up @@ -318,10 +314,6 @@ def merge_info_from(self, docnames, other, app):

for subfn, fnset in other.files_to_rebuild.items():
self.files_to_rebuild.setdefault(subfn, set()).update(fnset & docnames)
for key, data in other.citations.items():
# XXX duplicates?
if data[0] in docnames:
self.citations[key] = data
for version, changes in other.versionchanges.items():
self.versionchanges.setdefault(version, []).extend(
change for change in changes if change[1] in docnames)
Expand Down Expand Up @@ -745,7 +737,6 @@ def read_doc(self, docname, app=None):
self.process_refonly_bullet_lists(docname, doctree)
self.create_title_from(docname, doctree)
self.note_indexentries_from(docname, doctree)
self.note_citations_from(docname, doctree)
self.build_toc_from(docname, doctree)
for domain in itervalues(self.domains):
domain.process_doc(self, docname, doctree)
Expand Down Expand Up @@ -1100,15 +1091,6 @@ def note_indexentries_from(self, docname, document):
else:
entries.append(entry + (None,))

def note_citations_from(self, docname, document):
for node in document.traverse(nodes.citation):
label = node[0].astext()
if label in self.citations:
self.warn_node('duplicate citation %s, ' % label +
'other instance in %s' % self.doc2path(
self.citations[label][0]), node)
self.citations[label] = (docname, node['ids'][0])

def note_toctree(self, docname, toctreenode):
"""Note a TOC tree directive in a document and gather information about
file relations from it.
Expand Down Expand Up @@ -1527,8 +1509,6 @@ def resolve_references(self, doctree, fromdocname, builder):
newnode = self._resolve_any_reference(builder, refdoc, node, contnode)
elif typ == 'doc':
newnode = self._resolve_doc_reference(builder, refdoc, node, contnode)
elif typ == 'citation':
newnode = self._resolve_citation(builder, refdoc, node, contnode)
# no new node found? try the missing-reference event
if newnode is None:
newnode = builder.app.emit_firstresult(
Expand Down Expand Up @@ -1565,8 +1545,6 @@ def _warn_missing_reference(self, refdoc, typ, target, node, domain):
msg = domain.dangling_warnings[typ]
elif typ == 'doc':
msg = 'unknown document: %(target)s'
elif typ == 'citation':
msg = 'citation not found: %(target)s'
elif node.get('refdomain', 'std') not in ('', 'std'):
msg = '%s:%s reference target not found: %%(target)s' % \
(node['refdomain'], typ)
Expand All @@ -1591,25 +1569,6 @@ def _resolve_doc_reference(self, builder, refdoc, node, contnode):
newnode.append(innernode)
return newnode

def _resolve_citation(self, builder, fromdocname, node, contnode):
docname, labelid = self.citations.get(node['reftarget'], ('', ''))
if docname:
try:
newnode = make_refnode(builder, fromdocname,
docname, labelid, contnode)
return newnode
except NoUri:
# remove the ids we added in the CitationReferences
# transform since they can't be transfered to
# the contnode (if it's a Text node)
if not isinstance(contnode, nodes.Element):
del node['ids'][:]
raise
elif 'ids' in node:
# remove ids attribute that annotated at
# transforms.CitationReference.apply.
del node['ids'][:]

def _resolve_any_reference(self, builder, refdoc, node, contnode):
"""Resolve reference generated by the "any" role."""
target = node['reftarget']
Expand Down
2 changes: 1 addition & 1 deletion sphinx/transforms.py
Expand Up @@ -140,7 +140,7 @@ class CitationReferences(Transform):
def apply(self):
for citnode in self.document.traverse(nodes.citation_reference):
cittext = citnode.astext()
refnode = addnodes.pending_xref(cittext, reftype='citation',
refnode = addnodes.pending_xref(cittext, refdomain='std', reftype='citation',
reftarget=cittext, refwarn=True,
ids=citnode["ids"])
refnode.source = citnode.source or citnode.parent.source
Expand Down
2 changes: 2 additions & 0 deletions tests/test_domain_std.py
Expand Up @@ -26,6 +26,7 @@ def test_process_doc_handle_figure_caption():
nameids={'testname': 'testid'},
ids={'testid': figure_node},
)
document.traverse.return_value = []

domain = StandardDomain(env)
if 'testname' in domain.data['labels']:
Expand All @@ -47,6 +48,7 @@ def test_process_doc_handle_table_title():
nameids={'testname': 'testid'},
ids={'testid': table_node},
)
document.traverse.return_value = []

domain = StandardDomain(env)
if 'testname' in domain.data['labels']:
Expand Down

0 comments on commit 3639231

Please sign in to comment.