summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakeshi KOMIYA <i.tkomiya@gmail.com>2017-03-06 13:06:30 +0900
committerTakeshi KOMIYA <i.tkomiya@gmail.com>2017-03-10 10:49:08 +0900
commiteb40a36aa45549a6759297c6d0181c55ea76b063 (patch)
tree1e86cbbae638db90a153120060bf4abd7c895b35
parent7117206b2af64ba2278d4bab9f874dcf020e2301 (diff)
downloadsphinx-git-eb40a36aa45549a6759297c6d0181c55ea76b063.tar.gz
Add ReferenceResolver as a post-transform
-rw-r--r--sphinx/application.py1
-rw-r--r--sphinx/environment/__init__.py43
-rw-r--r--sphinx/transforms/__init__.py25
-rw-r--r--sphinx/transforms/post_transforms.py74
4 files changed, 110 insertions, 33 deletions
diff --git a/sphinx/application.py b/sphinx/application.py
index 97339c62b..c09b738bf 100644
--- a/sphinx/application.py
+++ b/sphinx/application.py
@@ -105,6 +105,7 @@ builtin_extensions = (
'sphinx.directives.other',
'sphinx.directives.patches',
'sphinx.roles',
+ 'sphinx.transforms.post_transforms',
# collectors should be loaded by specific order
'sphinx.environment.collectors.dependencies',
'sphinx.environment.collectors.asset',
diff --git a/sphinx/environment/__init__.py b/sphinx/environment/__init__.py
index c46ec7db2..8b08e4915 100644
--- a/sphinx/environment/__init__.py
+++ b/sphinx/environment/__init__.py
@@ -44,6 +44,7 @@ from sphinx.util.matching import compile_matchers
from sphinx.util.parallel import ParallelTasks, parallel_available, make_chunks
from sphinx.util.websupport import is_commentable
from sphinx.errors import SphinxError, ExtensionError
+from sphinx.transforms import SphinxTransformer
from sphinx.versioning import add_uids, merge_doctrees
from sphinx.deprecation import RemovedInSphinx20Warning
from sphinx.environment.adapters.indexentries import IndexEntries
@@ -920,38 +921,16 @@ class BuildEnvironment(object):
def resolve_references(self, doctree, fromdocname, builder):
# type: (nodes.Node, unicode, Builder) -> None
- for node in doctree.traverse(addnodes.pending_xref):
- contnode = node[0].deepcopy()
- newnode = None
-
- typ = node['reftype']
- target = node['reftarget']
- refdoc = node.get('refdoc', fromdocname)
- domain = None
-
- try:
- if 'refdomain' in node and node['refdomain']:
- # let the domain try to resolve the reference
- try:
- domain = self.domains[node['refdomain']]
- except KeyError:
- raise NoUri
- newnode = domain.resolve_xref(self, refdoc, builder,
- typ, target, node, contnode)
- # really hardwired reference types
- elif typ == 'any':
- newnode = self._resolve_any_reference(builder, refdoc, node, contnode)
- # no new node found? try the missing-reference event
- if newnode is None:
- newnode = builder.app.emit_firstresult(
- 'missing-reference', self, node, contnode)
- # still not found? warn if node wishes to be warned about or
- # we are in nit-picky mode
- if newnode is None:
- self._warn_missing_reference(refdoc, typ, target, node, domain)
- except NoUri:
- newnode = contnode
- node.replace_self(newnode or contnode)
+ # apply all post-transforms
+ try:
+ # set env.docname during applying post-transforms
+ self.temp_data['docname'] = fromdocname
+
+ transformer = SphinxTransformer(doctree)
+ transformer.add_transforms(self.app.post_transforms)
+ transformer.apply_transforms()
+ finally:
+ self.temp_data.clear()
# remove only-nodes that do not belong to our builder
process_only_nodes(doctree, builder.tags)
diff --git a/sphinx/transforms/__init__.py b/sphinx/transforms/__init__.py
index f6fc0f18f..7c35e78a1 100644
--- a/sphinx/transforms/__init__.py
+++ b/sphinx/transforms/__init__.py
@@ -10,8 +10,9 @@
"""
from docutils import nodes
-from docutils.transforms import Transform
+from docutils.transforms import Transform, Transformer
from docutils.transforms.parts import ContentsFilter
+from docutils.utils import new_document
from sphinx import addnodes
from sphinx.locale import _
@@ -69,6 +70,28 @@ class SphinxTransform(Transform):
return self.document.settings.env.config
+class SphinxTransformer(Transformer):
+ """
+ A transformer for Sphinx.
+ """
+
+ document = None # type: nodes.Node
+
+ def apply_transforms(self):
+ # type: () -> None
+ if isinstance(self.document, nodes.document):
+ Transformer.apply_transforms(self)
+ else:
+ # wrap the target node by document node during transforming
+ try:
+ document = new_document('')
+ document += self.document
+ self.document = document
+ Transformer.apply_transforms(self)
+ finally:
+ self.document = self.document[0]
+
+
class DefaultSubstitutions(SphinxTransform):
"""
Replace some substitutions if they aren't defined in the document.
diff --git a/sphinx/transforms/post_transforms.py b/sphinx/transforms/post_transforms.py
new file mode 100644
index 000000000..9177b0308
--- /dev/null
+++ b/sphinx/transforms/post_transforms.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.transforms.post_transforms
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Docutils transforms used by Sphinx.
+
+ :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+from sphinx import addnodes
+from sphinx.environment import NoUri
+from sphinx.transforms import SphinxTransform
+
+if False:
+ # For type annotation
+ from typing import Any, Dict # NOQA
+ from sphinx.application import Sphinx # NOQA
+
+
+class ReferencesResolver(SphinxTransform):
+ """
+ Resolves cross-references on doctrees.
+ """
+
+ default_priority = 10
+
+ def apply(self):
+ # type: () -> None
+ for node in self.document.traverse(addnodes.pending_xref):
+ contnode = node[0].deepcopy()
+ newnode = None
+
+ typ = node['reftype']
+ target = node['reftarget']
+ refdoc = node.get('refdoc', self.env.docname)
+ domain = None
+
+ try:
+ if 'refdomain' in node and node['refdomain']:
+ # let the domain try to resolve the reference
+ try:
+ domain = self.env.domains[node['refdomain']]
+ except KeyError:
+ raise NoUri
+ newnode = domain.resolve_xref(self.env, refdoc, self.app.builder,
+ typ, target, node, contnode)
+ # really hardwired reference types
+ elif typ == 'any':
+ newnode = self.env._resolve_any_reference(self.app.builder, refdoc,
+ node, contnode)
+ # no new node found? try the missing-reference event
+ if newnode is None:
+ newnode = self.app.emit_firstresult('missing-reference', self.env,
+ node, contnode)
+ # still not found? warn if node wishes to be warned about or
+ # we are in nit-picky mode
+ if newnode is None:
+ self.env._warn_missing_reference(refdoc, typ, target, node, domain)
+ except NoUri:
+ newnode = contnode
+ node.replace_self(newnode or contnode)
+
+
+def setup(app):
+ # type: (Sphinx) -> Dict[unicode, Any]
+ app.add_post_transform(ReferencesResolver)
+
+ return {
+ 'version': 'builtin',
+ 'parallel_read_safe': True,
+ 'parallel_write_safe': True,
+ }