Source code for sphinx_ros.domain

"""
``sphinx_ros.domain`` module
============================

This module defines the ROS domain. It defines three object types (messages,
services, and actions) and registers the roles and directives in the Sphinx
application.
"""

from six import iteritems
from docutils import nodes
from sphinx.domains import Domain, ObjType
from sphinx.util.nodes import make_refnode
from .xref_role import RosXRefRole
from .directives import RosPackageDirective, RosCurrentPackageDirective, \
    RosMessageDirective, RosActionDirective, RosServiceDirective
from .indices import RosPackageIndex, RosMessageIndex


[docs]class RosDomain(Domain): """ The actual domain class. """ name = 'ros' label = 'ROS' object_types = { 'message': ObjType('message', 'msg', 'obj'), 'service': ObjType('service', 'srv', 'obj'), 'action': ObjType('action', 'action', 'obj') } roles = { 'pkg': RosXRefRole(), 'msg': RosXRefRole(), 'srv': RosXRefRole(), 'act': RosXRefRole() } directives = { 'package': RosPackageDirective, 'currentpackage': RosCurrentPackageDirective, 'message': RosMessageDirective, 'service': RosServiceDirective, 'action': RosActionDirective } initial_data = { 'objects': {}, # fullname -> docname, objtype 'packages': {}, # name -> document name, anchor, priority, deprecated 'messages': {}, # name -> document name, anchor, priority, deprecated 'labels': { 'ros-pkgindex': ('ros-pkgindex', '', 'Package Index'), 'ros-msgindex': ('ros-msgindex', '', 'Message Type Index') }, 'anonlabels': { 'ros-pkgindex': ('ros-pkgindex', ''), 'ros-msgindex': ('ros-msgindex', '') } } indices = [ RosPackageIndex, RosMessageIndex, ] def clear_doc(self, docname): for fullname, (fn, _l) in list(self.data['objects'].items()): if fn == docname: del self.data['objects'][fullname] # name -> document name, anchor, priority, deprecated for pkgname, (fn, _, _, _) in list(self.data['packages'].items()): if fn == docname: del self.data['packages'][pkgname]
[docs] def find_obj(self, env, pkgname, name, type, searchmode=0): """ Find a ROS object for ``name``, perhaps using ``pkgname``. """ if name.endswith('()'): name = name[:-2] if not name: return [] objects = self.data['objects'] matches = [] newname = None if searchmode == 1: if type is None: objtypes = list(self.object_types) else: objtypes = self.objtypes_for_role(type) else: # NOTE: searching for exact match, object type is not considered if name in objects: newname = name elif type == 'pkg': # only exact matches allowed for packages return [] elif pkgname and pkgname + '.' + name in objects: newname = pkgname + '.' + name if newname is not None: matches.append((newname, objects[newname])) return matches
def resolve_xref(self, env, fromdocname, builder, type, target, node, contnode): pkgname = node.get('ros:package') searchmode = node.hasattr('refspecific') and 1 or 0 matches = self.find_obj(env, pkgname, target, type, searchmode) if not matches: return None elif len(matches) > 1: env.warn_node( 'more than one target found for cross-reference ' '%r: %s' % (target, ', '.join(match[0] for match in matches)), node) name, obj = matches[0] if obj[1] == 'package': return self._make_package_refnode(builder, fromdocname, name, contnode) else: return make_refnode(builder, fromdocname, obj[0], name, contnode, name) def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): pkgname = node.get('ros:package') results = [] # Always search in 'refspecific' mode with the :any: role matches = self.find_obj(env, pkgname, target, None, 1) for name, obj in matches: if obj[1] == 'package': results.append(('ros:pkg', self._make_package_refnode(builder, fromdocname, name, contnode))) else: results.append(('ros:' + self.role_for_objtype(obj[1]), make_refnode(builder, fromdocname, obj[0], name, contnode, name))) return results def _make_package_refnode(self, builder, fromdocname, name, contnode): # Get additional info for packages # name -> document name, anchor, priority, deprecated docname, anchor, _, deprecated = self.data['packages'][name] title = name if deprecated: title += ' (deprecated)' return make_refnode(builder, fromdocname, docname, anchor, contnode, title) def get_objects(self): for pkgname, info in iteritems(self.data['packages']): yield (pkgname, pkgname, 'package', info[0], 'package-' + pkgname, 0) for refname, (docname, type) in iteritems(self.data['objects']): if type != 'package': yield (refname, refname, type, docname, refname, 1)
[docs] def add_package(self, name, deprecated): """ Adds a package to the domain data. :param str name: The name of the package :param bool deprecated: Indicates whether the package is deprecated. :return: The unique anchor of the package. :rtype: str """ anchor = 'ros-pkg-{}'.format(name) # name -> document name, anchor, priority, deprecated self.data['packages'][name] = (self.env.docname, anchor, 0, deprecated) # make a duplicate entry in 'objects' to facilitate searching for the # package in RosDomain.find_obj() self.data['objects'][name] = (self.env.docname, 'package') return anchor
def add_message(self, name, deprecated): # name -> document name, anchor, priority, deprecated self.data['messages'][name] = (self.env.docname, name, 0, deprecated) self.data['objects'][name] = (self.env.docname, 'message') return name