Merge pull request #3372 from tk0miya/move_docref_to_stddomain

Refactor: Move doc reference to under standard domain
This commit is contained in:
Takeshi KOMIYA 2017-02-02 00:01:53 +09:00 committed by GitHub
commit ecdcb4c123
6 changed files with 38 additions and 35 deletions

View File

@ -23,7 +23,7 @@ from sphinx.roles import XRefRole
from sphinx.locale import l_, _ from sphinx.locale import l_, _
from sphinx.domains import Domain, ObjType from sphinx.domains import Domain, ObjType
from sphinx.directives import ObjectDescription from sphinx.directives import ObjectDescription
from sphinx.util import ws_re, logging from sphinx.util import ws_re, logging, docname_join
from sphinx.util.nodes import clean_astext, make_refnode from sphinx.util.nodes import clean_astext, make_refnode
if False: if False:
@ -465,6 +465,7 @@ class StandardDomain(Domain):
searchprio=-1), searchprio=-1),
'envvar': ObjType(l_('environment variable'), 'envvar'), 'envvar': ObjType(l_('environment variable'), 'envvar'),
'cmdoption': ObjType(l_('program option'), 'option'), 'cmdoption': ObjType(l_('program option'), 'option'),
'doc': ObjType(l_('document'), 'doc', searchprio=-1)
} # type: Dict[unicode, ObjType] } # type: Dict[unicode, ObjType]
directives = { directives = {
@ -491,6 +492,8 @@ class StandardDomain(Domain):
warn_dangling=True), warn_dangling=True),
# links to labels, without a different title # links to labels, without a different title
'keyword': XRefRole(warn_dangling=True), 'keyword': XRefRole(warn_dangling=True),
# links to documents
'doc': XRefRole(warn_dangling=True, innernodeclass=nodes.inline),
} # type: Dict[unicode, Union[RoleFunction, XRefRole]] } # type: Dict[unicode, Union[RoleFunction, XRefRole]]
initial_data = { initial_data = {
@ -515,6 +518,7 @@ class StandardDomain(Domain):
'the label must precede a section header)', 'the label must precede a section header)',
'numref': 'undefined label: %(target)s', 'numref': 'undefined label: %(target)s',
'keyword': 'unknown keyword: %(target)s', 'keyword': 'unknown keyword: %(target)s',
'doc': 'unknown document: %(target)s',
'option': 'unknown option: %(target)s', 'option': 'unknown option: %(target)s',
'citation': 'citation not found: %(target)s', 'citation': 'citation not found: %(target)s',
} }
@ -650,6 +654,8 @@ class StandardDomain(Domain):
resolver = self._resolve_numref_xref resolver = self._resolve_numref_xref
elif typ == 'keyword': elif typ == 'keyword':
resolver = self._resolve_keyword_xref resolver = self._resolve_keyword_xref
elif typ == 'doc':
resolver = self._resolve_doc_xref
elif typ == 'option': elif typ == 'option':
resolver = self._resolve_option_xref resolver = self._resolve_option_xref
elif typ == 'citation': elif typ == 'citation':
@ -747,6 +753,22 @@ class StandardDomain(Domain):
return make_refnode(builder, fromdocname, docname, return make_refnode(builder, fromdocname, docname,
labelid, contnode) labelid, contnode)
def _resolve_doc_xref(self, env, fromdocname, builder, typ, target, node, contnode):
# type: (BuildEnvironment, unicode, Builder, unicode, unicode, nodes.Node, nodes.Node) -> nodes.Node # NOQA
# directly reference to document by source name; can be absolute or relative
refdoc = node.get('refdoc', fromdocname)
docname = docname_join(refdoc, node['reftarget'])
if docname not in env.all_docs:
return None
else:
if node['refexplicit']:
# reference with explicit title
caption = node.astext()
else:
caption = clean_astext(env.titles[docname])
innernode = nodes.inline(caption, caption, classes=['doc'])
return make_refnode(builder, fromdocname, docname, None, innernode)
def _resolve_option_xref(self, env, fromdocname, builder, typ, target, node, contnode): def _resolve_option_xref(self, env, fromdocname, builder, typ, target, node, contnode):
# type: (BuildEnvironment, unicode, Builder, unicode, unicode, nodes.Node, nodes.Node) -> nodes.Node # NOQA # type: (BuildEnvironment, unicode, Builder, unicode, unicode, nodes.Node, nodes.Node) -> nodes.Node # NOQA
progname = node.get('std:program') progname = node.get('std:program')

View File

@ -34,9 +34,8 @@ from docutils.frontend import OptionParser
from sphinx import addnodes from sphinx import addnodes
from sphinx.io import SphinxStandaloneReader, SphinxDummyWriter, SphinxFileInput from sphinx.io import SphinxStandaloneReader, SphinxDummyWriter, SphinxFileInput
from sphinx.util import logging from sphinx.util import logging
from sphinx.util import get_matching_docs, docname_join, FilenameUniqDict, status_iterator from sphinx.util import get_matching_docs, FilenameUniqDict, status_iterator
from sphinx.util.nodes import clean_astext, WarningStream, is_translatable, \ from sphinx.util.nodes import WarningStream, is_translatable, process_only_nodes
process_only_nodes
from sphinx.util.osutil import SEP, ensuredir from sphinx.util.osutil import SEP, ensuredir
from sphinx.util.i18n import find_catalog_files from sphinx.util.i18n import find_catalog_files
from sphinx.util.console import bold # type: ignore from sphinx.util.console import bold # type: ignore
@ -923,8 +922,6 @@ class BuildEnvironment(object):
# really hardwired reference types # really hardwired reference types
elif typ == 'any': elif typ == 'any':
newnode = self._resolve_any_reference(builder, refdoc, node, contnode) newnode = self._resolve_any_reference(builder, refdoc, node, contnode)
elif typ == 'doc':
newnode = self._resolve_doc_reference(builder, refdoc, node, contnode)
# no new node found? try the missing-reference event # no new node found? try the missing-reference event
if newnode is None: if newnode is None:
newnode = builder.app.emit_firstresult( newnode = builder.app.emit_firstresult(
@ -960,8 +957,6 @@ class BuildEnvironment(object):
return return
if domain and typ in domain.dangling_warnings: if domain and typ in domain.dangling_warnings:
msg = domain.dangling_warnings[typ] msg = domain.dangling_warnings[typ]
elif typ == 'doc':
msg = 'unknown document: %(target)s'
elif node.get('refdomain', 'std') not in ('', 'std'): elif node.get('refdomain', 'std') not in ('', 'std'):
msg = '%s:%s reference target not found: %%(target)s' % \ msg = '%s:%s reference target not found: %%(target)s' % \
(node['refdomain'], typ) (node['refdomain'], typ)
@ -970,31 +965,14 @@ class BuildEnvironment(object):
logger.warning(msg % {'target': target}, logger.warning(msg % {'target': target},
location=node, type='ref', subtype=typ) location=node, type='ref', subtype=typ)
def _resolve_doc_reference(self, builder, refdoc, node, contnode):
# type: (Builder, unicode, nodes.Node, nodes.Node) -> nodes.Node
# directly reference to document by source name;
# can be absolute or relative
docname = docname_join(refdoc, node['reftarget'])
if docname in self.all_docs:
if node['refexplicit']:
# reference with explicit title
caption = node.astext()
else:
caption = clean_astext(self.titles[docname])
innernode = nodes.inline(caption, caption)
innernode['classes'].append('doc')
newnode = nodes.reference('', '', internal=True)
newnode['refuri'] = builder.get_relative_uri(refdoc, docname)
newnode.append(innernode)
return newnode
def _resolve_any_reference(self, builder, refdoc, node, contnode): def _resolve_any_reference(self, builder, refdoc, node, contnode):
# type: (Builder, unicode, nodes.Node, nodes.Node) -> nodes.Node # type: (Builder, unicode, nodes.Node, nodes.Node) -> nodes.Node
"""Resolve reference generated by the "any" role.""" """Resolve reference generated by the "any" role."""
target = node['reftarget'] target = node['reftarget']
results = [] # type: List[Tuple[unicode, nodes.Node]] results = [] # type: List[Tuple[unicode, nodes.Node]]
# first, try resolving as :doc: # first, try resolving as :doc:
doc_ref = self._resolve_doc_reference(builder, refdoc, node, contnode) doc_ref = self.domains['std'].resolve_xref(self, refdoc, builder, 'doc',
target, node, contnode)
if doc_ref: if doc_ref:
results.append(('doc', doc_ref)) results.append(('doc', doc_ref))
# next, do the standard domain (makes this a priority) # next, do the standard domain (makes this a priority)

View File

@ -338,9 +338,6 @@ def missing_reference(app, env, node, contnode):
for domain in env.domains.values() for domain in env.domains.values()
for objtype in domain.object_types] for objtype in domain.object_types]
domain = None domain = None
elif node['reftype'] == 'doc':
domain = 'std' # special case
objtypes = ['std:doc']
else: else:
domain = node.get('refdomain') domain = node.get('refdomain')
if not domain: if not domain:

View File

@ -323,8 +323,6 @@ def index_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):
specific_docroles = { specific_docroles = {
# links to download references # links to download references
'download': XRefRole(nodeclass=addnodes.download_reference), 'download': XRefRole(nodeclass=addnodes.download_reference),
# links to documents
'doc': XRefRole(warn_dangling=True, innernodeclass=nodes.inline),
# links to anything # links to anything
'any': AnyXRefRole(warn_dangling=True), 'any': AnyXRefRole(warn_dangling=True),

View File

@ -325,11 +325,14 @@ def make_refnode(builder, fromdocname, todocname, targetid, child, title=None):
# type: (Builder, unicode, unicode, unicode, nodes.Node, unicode) -> nodes.reference # type: (Builder, unicode, unicode, unicode, nodes.Node, unicode) -> nodes.reference
"""Shortcut to create a reference node.""" """Shortcut to create a reference node."""
node = nodes.reference('', '', internal=True) node = nodes.reference('', '', internal=True)
if fromdocname == todocname: if fromdocname == todocname and targetid:
node['refid'] = targetid node['refid'] = targetid
else: else:
node['refuri'] = (builder.get_relative_uri(fromdocname, todocname) + if targetid:
'#' + targetid) node['refuri'] = (builder.get_relative_uri(fromdocname, todocname) +
'#' + targetid)
else:
node['refuri'] = builder.get_relative_uri(fromdocname, todocname)
if title: if title:
node['reftitle'] = title node['reftitle'] = title
node.append(child) node.append(child)

View File

@ -43,6 +43,7 @@ module2 py:module 0 foo.html#module-$ -
module1.func py:function 1 sub/foo.html#$ - module1.func py:function 1 sub/foo.html#$ -
CFunc c:function 2 cfunc.html#CFunc - CFunc c:function 2 cfunc.html#CFunc -
a term std:term -1 glossary.html#term-a-term - a term std:term -1 glossary.html#term-a-term -
docname std:doc -1 docname.html -
a term including:colon std:term -1 glossary.html#term-a-term-including-colon - a term including:colon std:term -1 glossary.html#term-a-term-including-colon -
'''.encode('utf-8')) '''.encode('utf-8'))
@ -212,6 +213,10 @@ def test_missing_reference(tempdir, app, status, warning):
rn = reference_check('py', 'mod', 'py3krelparent:module1', 'foo', refdoc='sub/dir/test') rn = reference_check('py', 'mod', 'py3krelparent:module1', 'foo', refdoc='sub/dir/test')
assert rn['refuri'] == '../../../../py3k/foo.html#module-module1' assert rn['refuri'] == '../../../../py3k/foo.html#module-module1'
# check refs of standard domain
rn = reference_check('std', 'doc', 'docname', 'docname')
assert rn['refuri'] == 'https://docs.python.org/docname.html'
def test_load_mappings_warnings(tempdir, app, status, warning): def test_load_mappings_warnings(tempdir, app, status, warning):
""" """