From 77b3c1504959d9860316a6fd3df81e8f9fa9833a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 23 May 2010 11:59:20 +0200 Subject: [PATCH] Move :ref: labels over to std domain. --- sphinx/domains/__init__.py | 6 ++ sphinx/domains/std.py | 118 +++++++++++++++++++++++++++++++++++-- sphinx/environment.py | 103 +------------------------------- sphinx/roles.py | 4 -- 4 files changed, 121 insertions(+), 110 deletions(-) diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py index 64306b706..699533b1f 100644 --- a/sphinx/domains/__init__.py +++ b/sphinx/domains/__init__.py @@ -199,6 +199,12 @@ class Domain(object): """ pass + def process_doc(self, env, docname, document): + """ + Process a document after it is read by the environment. + """ + pass + def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): """ diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 3d617e1dd..79eed37fa 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -20,7 +20,7 @@ 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.nodes import make_refnode +from sphinx.util.nodes import clean_astext, make_refnode from sphinx.util.compat import Directive @@ -327,6 +327,7 @@ class StandardDomain(Domain): object_types = { 'term': ObjType(l_('glossary term'), 'term', searchprio=-1), 'token': ObjType(l_('grammar token'), 'token', searchprio=-1), + 'label': ObjType(l_('reference label'), 'ref', searchprio=-1), 'envvar': ObjType(l_('environment variable'), 'envvar'), 'cmdoption': ObjType(l_('program option'), 'option'), } @@ -340,15 +341,31 @@ class StandardDomain(Domain): 'productionlist': ProductionList, } roles = { - 'option': OptionXRefRole(innernodeclass=addnodes.literal_emphasis), - 'envvar': EnvVarXRefRole(), - 'token': XRefRole(), - 'term': XRefRole(lowercase=True, innernodeclass=nodes.emphasis), + 'option': OptionXRefRole(innernodeclass=addnodes.literal_emphasis), + 'envvar': EnvVarXRefRole(), + # links to tokens in grammar productions + 'token': XRefRole(), + # links to terms in glossary + 'term': XRefRole(lowercase=True, innernodeclass=nodes.emphasis), + # links to headings or arbitrary labels + 'ref': XRefRole(lowercase=True, innernodeclass=nodes.emphasis), + # links to labels, without a different title + 'keyword': XRefRole(), } initial_data = { 'progoptions': {}, # (program, name) -> docname, labelid 'objects': {}, # (type, name) -> docname, labelid + 'labels': { # labelname -> docname, labelid, sectionname + 'genindex': ('genindex', '', l_('Index')), + 'modindex': ('py-modindex', '', l_('Module Index')), + 'search': ('search', '', l_('Search Page')), + }, + 'anonlabels': { # labelname -> docname, labelid + 'genindex': ('genindex', ''), + 'modindex': ('py-modindex', ''), + 'search': ('search', ''), + }, } def clear_doc(self, docname): @@ -358,10 +375,97 @@ class StandardDomain(Domain): for key, (fn, _) in self.data['objects'].items(): if fn == docname: del self.data['objects'][key] + for key, (fn, _, _) in self.data['labels'].items(): + if fn == docname: + del self.data['labels'][key] + for key, (fn, _) in self.data['anonlabels'].items(): + if fn == docname: + del self.data['anonlabels'][key] + + def process_doc(self, env, docname, document): + labels, anonlabels = self.data['labels'], self.data['anonlabels'] + for name, explicit in document.nametypes.iteritems(): + if not explicit: + continue + labelid = document.nameids[name] + if labelid is None: + continue + node = document.ids[labelid] + if name.isdigit() or node.has_key('refuri') or \ + node.tagname.startswith('desc_'): + # ignore footnote labels, labels automatically generated from a + # link and object descriptions + continue + if name in labels: + env.warn(docname, 'duplicate label %s, ' % name + + 'other instance in ' + env.doc2path(labels[name][0]), + node.line) + anonlabels[name] = docname, labelid + if node.tagname == 'section': + sectname = clean_astext(node[0]) # node[0] == title node + elif node.tagname == 'figure': + for n in node: + if n.tagname == 'caption': + sectname = clean_astext(n) + break + else: + continue + else: + # anonymous-only labels + continue + labels[name] = docname, labelid, sectname def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): - if typ == 'option': + if typ == 'ref': + refdoc = node.get('refdoc', fromdocname) + if node['refexplicit']: + # reference to anonymous label; the reference uses + # the supplied link caption + docname, labelid = self.data['anonlabels'].get(target, ('','')) + sectname = node.astext() + if not docname: + env.warn(refdoc, 'undefined label: %s' % + target, node.line) + else: + # reference to named label; the final node will + # contain the section name after the label + docname, labelid, sectname = self.data['labels'].get(target, + ('','','')) + if not docname: + env.warn(refdoc, + 'undefined label: %s' % target + ' -- if you ' + 'don\'t give a link caption the label must ' + 'precede a section header.', node.line) + if not docname: + return None + newnode = nodes.reference('', '') + 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 + elif typ == 'keyword': + # keywords are oddballs: they are referenced by named labels + docname, labelid, _ = self.data['labels'].get(target, ('','','')) + if not docname: + #env.warn(refdoc, 'unknown keyword: %s' % target) + return None + else: + return make_refnode(builder, fromdocname, docname, + labelid, contnode) + elif typ == 'option': progname = node['refprogram'] docname, labelid = self.data['progoptions'].get((progname, target), ('', '')) @@ -387,3 +491,5 @@ class StandardDomain(Domain): for (type, name), info in self.data['objects'].iteritems(): yield (name, type, info[0], info[1], self.object_types[type].attrs['searchprio']) + for name, info in self.data['labels'].iteritems(): + yield (name, 'label', info[0], info[1], -1) diff --git a/sphinx/environment.py b/sphinx/environment.py index c4649ae83..3d13eb6da 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -304,12 +304,8 @@ class BuildEnvironment: # domain-specific inventories, here to be pickled self.domaindata = {} # domainname -> domain-specific dict - # X-ref target inventory - self.labels = {} # labelname -> docname, labelid, sectionname - self.anonlabels = {} # labelname -> docname, labelid - self.citations = {} # citation name -> docname, labelid - # Other inventories + self.citations = {} # citation name -> docname, labelid self.indexentries = {} # docname -> list of # (type, string, target, aliasname) self.versionchanges = {} # version -> list of (type, docname, @@ -322,16 +318,6 @@ class BuildEnvironment: # temporary data storage while reading a document self.temp_data = {} - # Some magically present labels - def add_magic_label(name, description, target=None): - self.labels[name] = (target or name, '', description) - self.anonlabels[name] = (target or name, '') - add_magic_label('genindex', _('Index')) - # XXX add per domain? - # compatibility alias - add_magic_label('modindex', _('Module Index'), 'py-modindex') - add_magic_label('search', _('Search Page')) - def set_warnfunc(self, func): self._warnfunc = func self.settings['warning_stream'] = WarningStream(func) @@ -366,9 +352,6 @@ class BuildEnvironment: fnset.discard(docname) if not fnset: del self.files_to_rebuild[subfn] - for labelname, (fn, _, _) in self.labels.items(): - if fn == docname: - del self.labels[labelname] for key, (fn, _) in self.citations.items(): if fn == docname: del self.citations[key] @@ -669,10 +652,11 @@ class BuildEnvironment: self.process_metadata(docname, doctree) self.process_refonly_bullet_lists(docname, doctree) self.create_title_from(docname, doctree) - self.note_labels_from(docname, doctree) self.note_indexentries_from(docname, doctree) self.note_citations_from(docname, doctree) self.build_toc_from(docname, doctree) + for domain in self.domains.itervalues(): + domain.process_doc(self, docname, doctree) # allow extension-specific post-processing if app: @@ -949,39 +933,6 @@ class BuildEnvironment: self.titles[docname] = titlenode self.longtitles[docname] = longtitlenode - def note_labels_from(self, docname, document): - for name, explicit in document.nametypes.iteritems(): - if not explicit: - continue - labelid = document.nameids[name] - if labelid is None: - continue - node = document.ids[labelid] - if name.isdigit() or node.has_key('refuri') or \ - node.tagname.startswith('desc_'): - # ignore footnote labels, labels automatically generated from a - # link and object descriptions - continue - if name in self.labels: - self.warn(docname, 'duplicate label %s, ' % name + - 'other instance in ' + - self.doc2path(self.labels[name][0]), - node.line) - self.anonlabels[name] = docname, labelid - if node.tagname == 'section': - sectname = clean_astext(node[0]) # node[0] == title node - elif node.tagname == 'figure': - for n in node: - if n.tagname == 'caption': - sectname = clean_astext(n) - break - else: - continue - else: - # anonymous-only labels - continue - self.labels[name] = docname, labelid, sectname - def note_indexentries_from(self, docname, document): entries = self.indexentries[docname] = [] for node in document.traverse(addnodes.index): @@ -1309,44 +1260,6 @@ class BuildEnvironment: newnode = domain.resolve_xref(self, fromdocname, builder, typ, target, node, contnode) # really hardwired reference types - elif typ == 'ref': - if node['refexplicit']: - # reference to anonymous label; the reference uses - # the supplied link caption - docname, labelid = self.anonlabels.get(target, ('','')) - sectname = node.astext() - if not docname: - self.warn(refdoc, 'undefined label: %s' % - target, node.line) - warned = True - else: - # reference to named label; the final node will - # contain the section name after the label - docname, labelid, sectname = self.labels.get(target, - ('','','')) - if not docname: - self.warn(refdoc, - 'undefined label: %s' % target + ' -- if you ' - 'don\'t give a link caption the label must ' - 'precede a section header.', node.line) - warned = True - if docname: - newnode = nodes.reference('', '') - 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) elif typ == 'doc': # directly reference to document by source name; # can be absolute or relative @@ -1375,16 +1288,6 @@ class BuildEnvironment: else: newnode = make_refnode(builder, fromdocname, docname, labelid, contnode) - elif typ == 'keyword': - # keywords are oddballs: they are referenced by named labels - docname, labelid, _ = self.labels.get(target, ('','','')) - if not docname: - #self.warn(refdoc, 'unknown keyword: %s' % target) - pass - else: - newnode = make_refnode(builder, fromdocname, docname, - labelid, contnode) - # no new node found? try the missing-reference event if newnode is None: newnode = builder.app.emit_firstresult( diff --git a/sphinx/roles.py b/sphinx/roles.py index e93e00d67..489d6ae8b 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -244,12 +244,8 @@ def abbr_role(typ, rawtext, text, lineno, inliner, options={}, content=[]): specific_docroles = { # links to download references 'download': XRefRole(nodeclass=addnodes.download_reference), - # links to headings or arbitrary labels - 'ref': XRefRole(lowercase=True, innernodeclass=nodes.emphasis), # links to documents 'doc': XRefRole(), - # links to labels, without a different title - 'keyword': XRefRole(), 'pep': indexmarkup_role, 'rfc': indexmarkup_role,