Commits

Georg Brandl committed f4a3d92

Move xref resolution to domain class.

  • Participants
  • Parent commits 23384ad

Comments (0)

Files changed (2)

File sphinx/domains.py

     ~~~~~~~~~~~~~~
 
     Support for domains, which are groupings of description directives
-    describing e.g. constructs of one programming language.
+    and roles describing e.g. constructs of one programming language.
 
     :copyright: Copyright 2007-2009 by the Sphinx team, see AUTHORS.
     :license: BSD, see LICENSE for details.
     roles = {}
     label = ''
 
-    def __init__(self, app):
-        self.app = app
+    def __init__(self):
         self._role_cache = {}
         self._directive_cache = {}
 
+    def __getstate__(self):
+        # can't pickle the adapter caches
+        state = self.__dict__.copy()
+        state['_role_cache'] = {}
+        state['_directive_cache'] = {}
+        return state
+
     def role(self, name):
+        """
+        Return a role adapter function that always gives the registered
+        role its full name ('domain:name') as the first argument.
+        """
         if name in self._role_cache:
             return self._role_cache[name]
         if name not in self.roles:
         return role_adapter
         
     def directive(self, name):
+        """
+        Return a directive adapter class that always gives the registered
+        directive its full name ('domain:name') as ``self.name``.
+        """
         if name in self._directive_cache:
             return self._directive_cache[name]
         if name not in self.directives:
         self._directive_cache[name] = DirectiveAdapter
         return DirectiveAdapter
 
+    def resolve_xref(self, typ, target, node, contnode):
+        pass
+
 
 # REs for Python signatures
 py_sig_re = re.compile(
         'obj': PyXRefRole(),
     }
 
+    def find_desc(self, env, modname, classname, name, type, searchorder=0):
+        """Find a description node matching "name", perhaps using
+           the given module and/or classname."""
+        # skip parens
+        if name[-2:] == '()':
+            name = name[:-2]
+
+        if not name:
+            return None, None
+
+        # don't add module and class names for C things
+        if type[0] == 'c' and type not in ('class', 'const'):
+            # skip trailing star and whitespace
+            name = name.rstrip(' *')
+            if name in env.descrefs and env.descrefs[name][1][0] == 'c':
+                return name, env.descrefs[name]
+            return None, None
+
+        newname = None
+        if searchorder == 1:
+            if modname and classname and \
+                   modname + '.' + classname + '.' + name in env.descrefs:
+                newname = modname + '.' + classname + '.' + name
+            elif modname and modname + '.' + name in env.descrefs:
+                newname = modname + '.' + name
+            elif name in env.descrefs:
+                newname = name
+        else:
+            if name in env.descrefs:
+                newname = name
+            elif modname and modname + '.' + name in env.descrefs:
+                newname = modname + '.' + name
+            elif modname and classname and \
+                     modname + '.' + classname + '.' + name in env.descrefs:
+                newname = modname + '.' + classname + '.' + name
+            # special case: builtin exceptions have module "exceptions" set
+            elif type == 'exc' and '.' not in name and \
+                 'exceptions.' + name in env.descrefs:
+                newname = 'exceptions.' + name
+            # special case: object methods
+            elif type in ('func', 'meth') and '.' not in name and \
+                 'object.' + name in env.descrefs:
+                newname = 'object.' + name
+        if newname is None:
+            return None, None
+        return newname, env.descrefs[newname]
+
+    def resolve_xref(self, env, fromdocname, builder,
+                     typ, target, node, contnode):
+
+        if typ == 'mod' or \
+           typ == 'obj' and target in env.modules:
+            docname, synopsis, platform, deprecated = \
+                env.modules.get(target, ('','','', ''))
+            if not docname:
+                newnode = builder.app.emit_firstresult(
+                    'missing-reference', env, node, contnode)
+                if not newnode:
+                    newnode = contnode
+            elif docname == fromdocname:
+                # don't link to self
+                newnode = contnode
+            else:
+                newnode = nodes.reference('', '')
+                newnode['refuri'] = builder.get_relative_uri(
+                    fromdocname, docname) + '#module-' + target
+                newnode['reftitle'] = '%s%s%s' % (
+                    (platform and '(%s) ' % platform),
+                    synopsis, (deprecated and ' (deprecated)' or ''))
+                newnode.append(contnode)
+        elif typ in env.descroles:
+            # "descrefs"
+            modname = node['modname']
+            clsname = node['classname']
+            searchorder = node.hasattr('refspecific') and 1 or 0
+            name, desc = self.find_desc(env, modname, clsname,
+                                        target, typ, searchorder)
+            if not desc:
+                newnode = builder.app.emit_firstresult(
+                    'missing-reference', env, node, contnode)
+                if not newnode:
+                    newnode = contnode
+            else:
+                newnode = nodes.reference('', '')
+                if desc[0] == fromdocname:
+                    newnode['refid'] = name
+                else:
+                    newnode['refuri'] = (
+                        builder.get_relative_uri(fromdocname, desc[0])
+                        + '#' + name)
+                newnode['reftitle'] = name
+                newnode.append(contnode)
+
+
 
 # RE to split at word boundaries
 wsplit_re = re.compile(r'(\W+)')

File sphinx/environment.py

                            'cdata', 'ctype', 'cmacro'))
 
     def resolve_references(self, doctree, fromdocname, builder):
+        # XXX remove this
         reftarget_roles = set(('token', 'term', 'citation'))
         # add all custom xref types too
         reftarget_roles.update(i[0] for i in additional_xref_types.values())
             target = node['reftarget']
 
             try:
-                if typ == 'ref':
+                if node.has_key('refdomain'):
+                    # let the domain resolve the reference
+                    try:
+                        domain = builder.app.domains[node['refdomain']]
+                    except KeyError:
+                        raise NoUri
+                    newnode = domain.resolve_xref(self, fromdocname, builder,
+                                                  typ, target, node, contnode)
+                elif typ == 'ref':
                     if node['refcaption']:
                         # reference to anonymous label; the reference uses
                         # the supplied link caption
                             newnode['refuri'] = builder.get_relative_uri(
                                 fromdocname, docname, typ) + '#' + labelid
                         newnode.append(contnode)
-                elif typ == 'mod' or \
-                         typ == 'obj' and target in self.modules:
-                    docname, synopsis, platform, deprecated = \
-                        self.modules.get(target, ('','','', ''))
-                    if not docname:
-                        newnode = builder.app.emit_firstresult(
-                            'missing-reference', self, node, contnode)
-                        if not newnode:
-                            newnode = contnode
-                    elif docname == fromdocname:
-                        # don't link to self
-                        newnode = contnode
-                    else:
-                        newnode = nodes.reference('', '')
-                        newnode['refuri'] = builder.get_relative_uri(
-                            fromdocname, docname) + '#module-' + target
-                        newnode['reftitle'] = '%s%s%s' % (
-                            (platform and '(%s) ' % platform),
-                            synopsis, (deprecated and ' (deprecated)' or ''))
-                        newnode.append(contnode)
-                elif typ in self.descroles:
-                    # "descrefs"
-                    modname = node['modname']
-                    clsname = node['classname']
-                    searchorder = node.hasattr('refspecific') and 1 or 0
-                    name, desc = self.find_desc(modname, clsname,
-                                                target, typ, searchorder)
-                    if not desc:
-                        newnode = builder.app.emit_firstresult(
-                            'missing-reference', self, node, contnode)
-                        if not newnode:
-                            newnode = contnode
-                    else:
-                        newnode = nodes.reference('', '')
-                        if desc[0] == fromdocname:
-                            newnode['refid'] = name
-                        else:
-                            newnode['refuri'] = (
-                                builder.get_relative_uri(fromdocname, desc[0])
-                                + '#' + name)
-                        newnode['reftitle'] = name
-                        newnode.append(contnode)
                 else:
                     raise RuntimeError('unknown xfileref node encountered: %s'
                                        % node)
                     # the master file is not included anywhere ;)
                     continue
                 self.warn(docname, 'document isn\'t included in any toctree')
-
-    # --------- QUERYING -------------------------------------------------------
-
-    def find_desc(self, modname, classname, name, type, searchorder=0):
-        """Find a description node matching "name", perhaps using
-           the given module and/or classname."""
-        # skip parens
-        if name[-2:] == '()':
-            name = name[:-2]
-
-        if not name:
-            return None, None
-
-        # don't add module and class names for C things
-        if type[0] == 'c' and type not in ('class', 'const'):
-            # skip trailing star and whitespace
-            name = name.rstrip(' *')
-            if name in self.descrefs and self.descrefs[name][1][0] == 'c':
-                return name, self.descrefs[name]
-            return None, None
-
-        newname = None
-        if searchorder == 1:
-            if modname and classname and \
-                   modname + '.' + classname + '.' + name in self.descrefs:
-                newname = modname + '.' + classname + '.' + name
-            elif modname and modname + '.' + name in self.descrefs:
-                newname = modname + '.' + name
-            elif name in self.descrefs:
-                newname = name
-        else:
-            if name in self.descrefs:
-                newname = name
-            elif modname and modname + '.' + name in self.descrefs:
-                newname = modname + '.' + name
-            elif modname and classname and \
-                     modname + '.' + classname + '.' + name in self.descrefs:
-                newname = modname + '.' + classname + '.' + name
-            # special case: builtin exceptions have module "exceptions" set
-            elif type == 'exc' and '.' not in name and \
-                 'exceptions.' + name in self.descrefs:
-                newname = 'exceptions.' + name
-            # special case: object methods
-            elif type in ('func', 'meth') and '.' not in name and \
-                 'object.' + name in self.descrefs:
-                newname = 'object.' + name
-        if newname is None:
-            return None, None
-        return newname, self.descrefs[newname]