Commits

Takayuki Shimizukawa committed ddd650a

fix package layout

Comments (0)

Files changed (3)

     packages=find_packages(),
     include_package_data=True,
     install_requires=requires,
-    #namespace_packages=['sphinxcontrib'],
+    namespace_packages=['sphinxcontrib'],
 )

sphinxcontrib/op.py

-# -*- coding: utf-8 -*-
-"""
-    sphinx.domains.operation
-    ~~~~~~~~~~~~~~~~~~~~~~~~
-
-    The Operation domain.
-
-    :copyright: Copyright 2012 by togakushi
-    :license: BSD, see LICENSE for details.
-"""
-
-import re
-
-from docutils import nodes
-from docutils.parsers.rst import directives
-
-from sphinx import addnodes
-from sphinx.roles import XRefRole
-from sphinx.locale import l_, _
-from sphinx.domains import Domain, ObjType, Index
-from sphinx.directives import ObjectDescription
-from sphinx.util.nodes import make_refnode
-from sphinx.util.compat import Directive
-from sphinx.util.docfields import Field, TypedField
-
-
-# REs for Operation signatures
-op_sig_re = re.compile(
-    r'''^ ([\w.]*\.)?            # class name(s)
-          (\w+)  \s*             # thing name
-          (?: \((.*)\)           # optional: arguments
-          )? $                   # and nothing more
-          ''', re.VERBOSE)
-
-
-class OperationObject(ObjectDescription):
-    """
-    Description of a general Operation object.
-    """
-    option_spec = {
-        'noindex': directives.flag,
-        'command': directives.unchanged,
-        'setting': directives.unchanged,
-        'howto': directives.unchanged,
-    }
-
-    doc_field_types = [
-        TypedField('parameter', label=l_('Parameters'),
-                   names=('param', 'parameter', 'arg', 'argument',
-                          'keyword', 'kwarg', 'kwparam'),
-                   typerolename='obj', typenames=('paramtype', 'type'),
-                   can_collapse=True),
-        TypedField('variable', label=l_('Variables'), rolename='obj',
-                   names=('var', 'ivar', 'cvar'),
-                   typerolename='obj', typenames=('vartype',),
-                   can_collapse=True),
-        Field('returnvalue', label=l_('Returns'), has_arg=False,
-              names=('returns', 'return')),
-        Field('returntype', label=l_('Return type'), has_arg=False,
-              names=('rtype',)),
-    ]
-
-    def get_signature_prefix(self, sig):
-        """May return a prefix to put before the object name in the
-        signature.
-        """
-        return ''
-
-    def needs_arglist(self):
-        """May return true if an empty argument list is to be generated even if
-        the document contains none.
-        """
-        return False
-
-    def handle_signature(self, sig, signode):
-        """Transform a Operation signature into RST nodes.
-
-        Return (fully qualified name of the thing if any).
-
-        If inside a class, the current class name is handled intelligently:
-        * it is stripped from the displayed name if present
-        * it is added to the full name (return value) if not present
-        """
-        m = op_sig_re.match(sig)
-        if m is None:
-            raise ValueError
-        name_prefix, name, arglist, retann = m.groups()
-
-        # determine command and class name (if applicable), as well as full name
-        modname = self.options.get('command', self.env.temp_data.get('op:command'))
-        setname = self.options.get('setting', self.env.temp_data.get('op:setting'))
-        howname = self.options.get('howto', self.env.temp_data.get('op:howto'))
-
-        add_command = True
-        if name_prefix:
-            fullname = name_prefix + name
-        else:
-            fullname = name
-
-        signode['command'] = modname
-        signode['setting'] = setname
-        signode['howto'] = setname
-        signode['fullname'] = fullname
-
-        sig_prefix = self.get_signature_prefix(sig)
-        if sig_prefix:
-            signode += addnodes.desc_annotation(sig_prefix, sig_prefix)
-
-        if name_prefix:
-            signode += addnodes.desc_addname(name_prefix, name_prefix)
-        # exceptions are a special case, since they are documented in the
-        # 'exceptions' command.
-        elif add_command and self.env.config.add_command_names:
-            modname = self.options.get('command', self.env.temp_data.get('op:command'))
-            setname = self.options.get('setting', self.env.temp_data.get('op:setting'))
-            howname = self.options.get('howto', self.env.temp_data.get('op:howto'))
-            nodetext = modname + '.'
-            signode += addnodes.desc_addname(nodetext, nodetext)
-
-        signode += addnodes.desc_name(name, name)
-        if not arglist:
-            if self.needs_arglist():
-                # for callables, add an empty parameter list
-                signode += addnodes.desc_parameterlist()
-            if retann:
-                signode += addnodes.desc_returns(retann, retann)
-            return fullname, name_prefix
-
-        if retann:
-            signode += addnodes.desc_returns(retann, retann)
-        return fullname, name_prefix
-
-    def get_index_text(self, modname, name):
-        """Return the text for the index entry of the object."""
-        raise NotImplementedError('must be implemented in subclasses')
-
-    def add_target_and_index(self, name_cls, sig, signode):
-        modname = self.options.get('command', self.env.temp_data.get('op:command'))
-        setname = self.options.get('setting', self.env.temp_data.get('op:setting'))
-        howname = self.options.get('howto', self.env.temp_data.get('op:howto'))
-        fullname = (modname and modname + '.' or '') + name_cls[0]
-        # note target
-        if fullname not in self.state.document.ids:
-            signode['names'].append(fullname)
-            signode['ids'].append(fullname)
-            signode['first'] = (not self.names)
-            self.state.document.note_explicit_target(signode)
-            objects = self.env.domaindata['op']['objects']
-            if fullname in objects:
-                self.state_machine.reporter.warning(
-                    'duplicate object description of %s, ' % fullname +
-                    'other instance in ' +
-                    self.env.doc2path(objects[fullname][0]) +
-                    ', use :noindex: for one of them',
-                    line=self.lineno)
-            objects[fullname] = (self.env.docname, self.objtype)
-
-        indextext = self.get_index_text(modname, name_cls)
-        if indextext:
-            self.indexnode['entries'].append(('single', indextext,
-                                              fullname, ''))
-
-
-class OperationXRefRole(XRefRole):
-    def process_link(self, env, refnode, has_explicit_title, title, target):
-        refnode['op:command'] = env.temp_data.get('op:command')
-        refnode['op:setting'] = env.temp_data.get('op:setting')
-        refnode['op:howto'] = env.temp_data.get('op:howto')
-        if not has_explicit_title:
-            title = title.lstrip('.')   # only has a meaning for the target
-            target = target.lstrip('~') # only has a meaning for the title
-            # if the first character is a tilde, don't display the command/class
-            # parts of the contents
-            if title[0:1] == '~':
-                title = title[1:]
-                dot = title.rfind('.')
-                if dot != -1:
-                    title = title[dot+1:]
-        # if the first character is a dot, search more specific namespaces first
-        # else search builtins first
-        if target[0:1] == '.':
-            target = target[1:]
-            refnode['refspecific'] = True
-        return title, target
-
-
-class OperationCommand(Directive):
-    """
-    Directive to mark description of a new command.
-    """
-
-    has_content = False
-    required_arguments = 1
-    optional_arguments = 0
-    final_argument_whitespace = True
-    option_spec = {
-        'platform': lambda x: x,
-        'synopsis': lambda x: x,
-        'noindex': directives.flag,
-        'deprecated': directives.flag,
-    }
-
-    def run(self):
-        env = self.state.document.settings.env
-        comname = self.arguments[0].strip()
-        noindex = 'noindex' in self.options
-        env.temp_data['op:command'] = comname
-        ret = []
-        if not noindex:
-            env.domaindata['op']['commands'][comname] = \
-                (env.docname, self.options.get('synopsis', ''),
-                 self.options.get('platform', ''), 'deprecated' in self.options)
-            # make a duplicate entry in 'objects' to facilitate searching for
-            # the command in OperationDomain.find_obj()
-            env.domaindata['op']['objects'][comname] = (env.docname, 'command')
-            targetnode = nodes.target('', '', ids=['command-' + comname],
-                                      ismod=True)
-            self.state.document.note_explicit_target(targetnode)
-            # the platform and synopsis aren't printed; in fact, they are only
-            # used in the modindex currently
-            ret.append(targetnode)
-            indextext = _('%s (command)') % comname
-            inode = addnodes.index(entries=[('single', indextext,
-                                             'command-' + comname, '')])
-            ret.append(inode)
-        return ret
-
-
-class OperationCommandIndex(Index):
-    """
-    Index subclass to provide the Operation command index.
-    """
-
-    name = 'commandindex'
-    localname = l_('Command Index')
-    shortname = l_('Command')
-
-    def generate(self, docnames=None):
-        content = {}
-        # list of all commands, sorted by command name
-        commands = sorted(self.domain.data['commands'].iteritems(),
-                         key=lambda x: x[0].lower())
-        # sort out collapsable commands
-        prev_comname = ''
-        num_toplevels = 0
-        for comname, (docname, synopsis, platforms, deprecated) in commands:
-            entries = content.setdefault(comname[0].lower(), [])
-
-            package = comname.split('.')[0]
-            if package != comname:
-                # it's a subentries
-                if prev_comname == package:
-                    # first subentries - make parent a group head
-                    if entries:
-                        entries[-1][1] = 1
-                elif not prev_comname.startswith(package):
-                    # subentries without parent in list, add dummy entry
-                    entries.append([package, 1, '', '', '', '', ''])
-                subtype = 2
-                entriename = comname.split(package + '.', 1)[1]
-            else:
-                num_toplevels += 1
-                subtype = 0
-                entriename = comname
-
-            qualifier = deprecated and _('Deprecated') or ''
-            entries.append([entriename, subtype, docname,
-                            'command-' + comname, platforms, qualifier, synopsis])
-            prev_comname = comname
-
-        # apply heuristics when to collapse modindex at page load:
-        # only collapse if number of toplevel commands is larger than
-        # number of submodules
-        collapse = len(commands) - num_toplevels < num_toplevels
-
-        # sort by first letter
-        content = sorted(content.iteritems())
-
-        return content, collapse
-
-
-class OperationSetting(Directive):
-    """
-    Directive to mark description of a new setting.
-    """
-
-    has_content = False
-    required_arguments = 1
-    optional_arguments = 0
-    final_argument_whitespace = True
-    option_spec = {
-        'platform': lambda x: x,
-        'synopsis': lambda x: x,
-        'noindex': directives.flag,
-        'deprecated': directives.flag,
-    }
-
-    def run(self):
-        env = self.state.document.settings.env
-        setname = self.arguments[0].strip()
-        noindex = 'noindex' in self.options
-        env.temp_data['op:setting'] = setname
-        ret = []
-        if not noindex:
-            env.domaindata['op']['settings'][setname] = \
-                (env.docname, self.options.get('synopsis', ''),
-                 self.options.get('platform', ''), 'deprecated' in self.options)
-            # make a duplicate entry in 'objects' to facilitate searching for
-            # the command in OperationDomain.find_obj()
-            env.domaindata['op']['objects'][setname] = (env.docname, 'setting')
-            targetnode = nodes.target('', '', ids=['setting-' + setname],
-                                      ismod=True)
-            self.state.document.note_explicit_target(targetnode)
-            # the platform and synopsis aren't printed; in fact, they are only
-            # used in the modindex currently
-            ret.append(targetnode)
-            indextext = _('%s (setting)') % setname
-            inode = addnodes.index(entries=[('single', indextext,
-                                             'setting-' + setname, '')])
-            ret.append(inode)
-        return ret
-
-
-class OperationSettingIndex(Index):
-    """
-    Index subclass to provide the Operation Seting index.
-    """
-
-    name = 'settingindex'
-    localname = l_('Setting Index')
-    shortname = l_('Setting')
-
-    def generate(self, docnames=None):
-        content = {}
-        # list of all setting, sorted by setting name
-        settings = sorted(self.domain.data['settings'].iteritems(),
-                         key=lambda x: x[0].lower())
-        # sort out collapsable setting
-        prev_setname = ''
-        num_toplevels = 0
-        for setname, (docname, synopsis, platforms, deprecated) in settings:
-            entries = content.setdefault(setname[0].lower(), [])
-
-            package = setname.split('.')[0]
-            if package != setname:
-                # it's a subentries
-                if prev_setname == package:
-                    # first subentries - make parent a group head
-                    if entries:
-                        entries[-1][1] = 1
-                elif not prev_setname.startswith(package):
-                    # subentries without parent in list, add dummy entry
-                    entries.append([package, 1, '', '', '', '', ''])
-                subtype = 2
-                entriename = setname.split(package + '.', 1)[1]
-            else:
-                num_toplevels += 1
-                subtype = 0
-                entriename = setname
-
-            qualifier = deprecated and _('Deprecated') or ''
-            entries.append([entriename, subtype, docname,
-                            'setting-' + setname, platforms, qualifier, synopsis])
-            prev_setname = setname
-
-        # apply heuristics when to collapse modindex at page load:
-        # only collapse if number of toplevel setting is larger than
-        # number of subsetting
-        collapse = len(settings) - num_toplevels < num_toplevels
-
-        # sort by first letter
-        content = sorted(content.iteritems())
-
-        return content, collapse
-
-
-class OperationHowto(Directive):
-    """
-    Directive to mark description of a new howto.
-    """
-
-    has_content = False
-    required_arguments = 1
-    optional_arguments = 0
-    final_argument_whitespace = True
-    option_spec = {
-        'platform': lambda x: x,
-        'synopsis': lambda x: x,
-        'noindex': directives.flag,
-        'deprecated': directives.flag,
-    }
-
-    def run(self):
-        env = self.state.document.settings.env
-        howname = self.arguments[0].strip()
-        noindex = 'noindex' in self.options
-        env.temp_data['op:howto'] = howname
-        ret = []
-        if not noindex:
-            env.domaindata['op']['howtos'][howname] = \
-                (env.docname, self.options.get('synopsis', ''),
-                 self.options.get('platform', ''), 'deprecated' in self.options)
-            # make a duplicate entry in 'objects' to facilitate searching for
-            # the howto in OperationDomain.find_obj()
-            env.domaindata['op']['objects'][howname] = (env.docname, 'howto')
-            targetnode = nodes.target('', '', ids=['howto-' + howname], ismod=True)
-            self.state.document.note_explicit_target(targetnode)
-            # the platform and synopsis aren't printed; in fact, they are only
-            # used in the modindex currently
-            ret.append(targetnode)
-            indextext = _('%s (howto)') % howname
-            inode = addnodes.index(entries=[('single', indextext, 'howto-' + howname, '')])
-            ret.append(inode)
-        return ret
-
-
-class OperationHowtoIndex(Index):
-    """
-    Index subclass to provide the Operation howto index.
-    """
-
-    name = 'howtoindex'
-    localname = l_('Howto Index')
-    shortname = l_('Howto')
-
-    def generate(self, docnames=None):
-        content = {}
-        # list of all howtos, sorted by howto name
-        howtos = sorted(self.domain.data['howtos'].iteritems(),
-                         key=lambda x: x[0].lower())
-        # sort out collapsable howtos
-        prev_howname = ''
-        num_toplevels = 0
-        for howname, (docname, synopsis, platforms, deprecated) in howtos:
-            entries = content.setdefault(howname[0].lower(), [])
-
-            package = howname.split('.')[0]
-            if package != howname:
-                # it's a subentries
-                if prev_howname == package:
-                    # first subentries - make parent a group head
-                    if entries:
-                        entries[-1][1] = 1
-                elif not prev_howname.startswith(package):
-                    # subentries without parent in list, add dummy entry
-                    entries.append([package, 1, '', '', '', '', ''])
-                subtype = 2
-                entriename = howname.split(package + '.', 1)[1]
-            else:
-                num_toplevels += 1
-                subtype = 0
-                entriename = howname
-
-            qualifier = deprecated and _('Deprecated') or ''
-            entries.append([entriename, subtype, docname,
-                            'howto-' + howname, platforms, qualifier, synopsis])
-            prev_howname = howname
-
-        # apply heuristics when to collapse modindex at page load:
-        # only collapse if number of toplevel howtos is larger than
-        # number of submodules
-        collapse = len(howtos) - num_toplevels < num_toplevels
-
-        # sort by first letter
-        content = sorted(content.iteritems())
-
-        return content, collapse
-
-
-class OperationDomain(Domain):
-    """Operation domain."""
-    name = 'op'
-    label = 'Operation'
-    object_types = {
-        'command':       ObjType(l_('command'),        'command'),
-        'setting':       ObjType(l_('setting'),        'setting'),
-        'howto':         ObjType(l_('howto'),           'howto'),
-    }
-
-    directives = {
-        'command':         OperationCommand,
-        'setting':         OperationSetting,
-        'howto':           OperationHowto,
-    }
-    roles = {
-        'command':   OperationXRefRole(),
-        'setting':   OperationXRefRole(),
-        'howto':   OperationXRefRole(),
-    }
-    initial_data = {
-        'objects': {},  # fullname -> docname, objtype
-        'commands': {}, # modname -> modname, synopsis, platform, deprecated
-        'settings': {}, # setname -> setname, synopsis, platform, deprecated
-        'howtos': {},   # howname -> howname, synopsis, platform, deprecated
-    }
-    indices = [
-        OperationCommandIndex,
-        OperationSettingIndex,
-        OperationHowtoIndex,
-    ]
-
-    def clear_doc(self, docname):
-        for fullname, (fn, _) in self.data['objects'].items():
-            if fn == docname:
-                del self.data['objects'][fullname]
-        for modname, (fn, _, _, _) in self.data['commands'].items():
-            if fn == docname:
-                del self.data['commands'][modname]
-        for setname, (fn, _, _, _) in self.data['settings'].items():
-            if fn == docname:
-                del self.data['settings'][setname]
-        for howname, (fn, _, _, _) in self.data['howtos'].items():
-            if fn == docname:
-                del self.data['howtos'][howname]
-
-    def find_obj(self, env, modname, setname, howname, name, type, searchmode=0):
-        """Find a Operation object for "name", perhaps using the given command
-           Returns a list of (name, object entry) tuples.
-        """
-
-        if not name:
-            return []
-
-        objects = self.data['objects']
-        matches = []
-
-        newname = None
-        if searchmode == 1:
-            objtypes = self.objtypes_for_role(type)
-            if objtypes is not None:
-                if not newname:
-                    if modname and modname + '.' + name in objects and \
-                       objects[modname + '.' + name][1] in objtypes:
-                        newname = modname + '.' + name
-                    elif name in objects and objects[name][1] in objtypes:
-                        newname = name
-                    else:
-                        # "fuzzy" searching mode
-                        searchname = '.' + name
-                        matches = [(oname, objects[oname]) for oname in objects
-                                   if oname.endswith(searchname)
-                                   and objects[oname][1] in objtypes]
-        else:
-            # NOTE: searching for exact match, object type is not considered
-            if name in objects:
-                newname = name
-            elif modname and modname + '.' + name in objects:
-                newname = modname + '.' + name
-        if newname is not None:
-            matches.append((newname, objects[newname]))
-        return matches
-
-    def resolve_xref(self, env, fromdocname, builder,
-                     type, target, node, contnode):
-        modname = node.get('op:command')
-        setname = node.get('op:setting')
-        howname = node.get('op:howto')
-        searchmode = node.hasattr('refspecific') and 1 or 0
-        matches = self.find_obj(env, modname, setname, howname, 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] == 'command':
-            # get additional info for commands
-            docname, synopsis, platform, deprecated = self.data['commands'][name]
-            assert docname == obj[0]
-            title = name
-            if synopsis:
-                title += ': ' + synopsis
-            if deprecated:
-                title += _(' (deprecated)')
-            if platform:
-                title += ' (' + platform + ')'
-            return make_refnode(builder, fromdocname, docname, 'command-' + name, contnode, title)
-        elif obj[1] == 'setting':
-            # get additional info for settings
-            docname, synopsis, platform, deprecated = self.data['settings'][name]
-            assert docname == obj[0]
-            title = name
-            if synopsis:
-                title += ': ' + synopsis
-            if deprecated:
-                title += _(' (deprecated)')
-            if platform:
-                title += ' (' + platform + ')'
-            return make_refnode(builder, fromdocname, docname, 'setting-' + name, contnode, title)
-        elif obj[1] == 'howto':
-            # get additional info for howtos
-            docname, synopsis, platform, deprecated = self.data['howtos'][name]
-            assert docname == obj[0]
-            title = name
-            if synopsis:
-                title += ': ' + synopsis
-            if deprecated:
-                title += _(' (deprecated)')
-            if platform:
-                title += ' (' + platform + ')'
-            return make_refnode(builder, fromdocname, docname, 'howto-' + name, contnode, title)
-        else:
-            return make_refnode(builder, fromdocname, obj[0], name,
-                                contnode, name)
-                                
-    def get_objects(self):
-        for modname, info in self.data['commands'].iteritems():
-            yield (modname, modname, 'command', info[0], 'command-' + modname, 0)
-        for setname, info in self.data['settings'].iteritems():
-            yield (setname, setname, 'setting', info[0], 'setting-' + setname, 0)
-        for howname, info in self.data['howtos'].iteritems():
-            yield (howname, howname, 'howto', info[0], 'howto-' + howname, 0)
-        for refname, (docname, type) in self.data['objects'].iteritems():
-            yield (refname, refname, type, docname, refname, 1)
-
-def setup(app):
-    app.add_domain(OperationDomain)

sphinxcontrib/operationdomain/op.py

+# -*- coding: utf-8 -*-
+"""
+    sphinx.domains.operation
+    ~~~~~~~~~~~~~~~~~~~~~~~~
+
+    The Operation domain.
+
+    :copyright: Copyright 2012 by togakushi
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from docutils import nodes
+from docutils.parsers.rst import directives
+
+from sphinx import addnodes
+from sphinx.roles import XRefRole
+from sphinx.locale import l_, _
+from sphinx.domains import Domain, ObjType, Index
+from sphinx.directives import ObjectDescription
+from sphinx.util.nodes import make_refnode
+from sphinx.util.compat import Directive
+from sphinx.util.docfields import Field, TypedField
+
+
+# REs for Operation signatures
+op_sig_re = re.compile(
+    r'''^ ([\w.]*\.)?            # class name(s)
+          (\w+)  \s*             # thing name
+          (?: \((.*)\)           # optional: arguments
+          )? $                   # and nothing more
+          ''', re.VERBOSE)
+
+
+class OperationObject(ObjectDescription):
+    """
+    Description of a general Operation object.
+    """
+    option_spec = {
+        'noindex': directives.flag,
+        'command': directives.unchanged,
+        'setting': directives.unchanged,
+        'howto': directives.unchanged,
+    }
+
+    doc_field_types = [
+        TypedField('parameter', label=l_('Parameters'),
+                   names=('param', 'parameter', 'arg', 'argument',
+                          'keyword', 'kwarg', 'kwparam'),
+                   typerolename='obj', typenames=('paramtype', 'type'),
+                   can_collapse=True),
+        TypedField('variable', label=l_('Variables'), rolename='obj',
+                   names=('var', 'ivar', 'cvar'),
+                   typerolename='obj', typenames=('vartype',),
+                   can_collapse=True),
+        Field('returnvalue', label=l_('Returns'), has_arg=False,
+              names=('returns', 'return')),
+        Field('returntype', label=l_('Return type'), has_arg=False,
+              names=('rtype',)),
+    ]
+
+    def get_signature_prefix(self, sig):
+        """May return a prefix to put before the object name in the
+        signature.
+        """
+        return ''
+
+    def needs_arglist(self):
+        """May return true if an empty argument list is to be generated even if
+        the document contains none.
+        """
+        return False
+
+    def handle_signature(self, sig, signode):
+        """Transform a Operation signature into RST nodes.
+
+        Return (fully qualified name of the thing if any).
+
+        If inside a class, the current class name is handled intelligently:
+        * it is stripped from the displayed name if present
+        * it is added to the full name (return value) if not present
+        """
+        m = op_sig_re.match(sig)
+        if m is None:
+            raise ValueError
+        name_prefix, name, arglist, retann = m.groups()
+
+        # determine command and class name (if applicable), as well as full name
+        modname = self.options.get('command', self.env.temp_data.get('op:command'))
+        setname = self.options.get('setting', self.env.temp_data.get('op:setting'))
+        howname = self.options.get('howto', self.env.temp_data.get('op:howto'))
+
+        add_command = True
+        if name_prefix:
+            fullname = name_prefix + name
+        else:
+            fullname = name
+
+        signode['command'] = modname
+        signode['setting'] = setname
+        signode['howto'] = setname
+        signode['fullname'] = fullname
+
+        sig_prefix = self.get_signature_prefix(sig)
+        if sig_prefix:
+            signode += addnodes.desc_annotation(sig_prefix, sig_prefix)
+
+        if name_prefix:
+            signode += addnodes.desc_addname(name_prefix, name_prefix)
+        # exceptions are a special case, since they are documented in the
+        # 'exceptions' command.
+        elif add_command and self.env.config.add_command_names:
+            modname = self.options.get('command', self.env.temp_data.get('op:command'))
+            setname = self.options.get('setting', self.env.temp_data.get('op:setting'))
+            howname = self.options.get('howto', self.env.temp_data.get('op:howto'))
+            nodetext = modname + '.'
+            signode += addnodes.desc_addname(nodetext, nodetext)
+
+        signode += addnodes.desc_name(name, name)
+        if not arglist:
+            if self.needs_arglist():
+                # for callables, add an empty parameter list
+                signode += addnodes.desc_parameterlist()
+            if retann:
+                signode += addnodes.desc_returns(retann, retann)
+            return fullname, name_prefix
+
+        if retann:
+            signode += addnodes.desc_returns(retann, retann)
+        return fullname, name_prefix
+
+    def get_index_text(self, modname, name):
+        """Return the text for the index entry of the object."""
+        raise NotImplementedError('must be implemented in subclasses')
+
+    def add_target_and_index(self, name_cls, sig, signode):
+        modname = self.options.get('command', self.env.temp_data.get('op:command'))
+        setname = self.options.get('setting', self.env.temp_data.get('op:setting'))
+        howname = self.options.get('howto', self.env.temp_data.get('op:howto'))
+        fullname = (modname and modname + '.' or '') + name_cls[0]
+        # note target
+        if fullname not in self.state.document.ids:
+            signode['names'].append(fullname)
+            signode['ids'].append(fullname)
+            signode['first'] = (not self.names)
+            self.state.document.note_explicit_target(signode)
+            objects = self.env.domaindata['op']['objects']
+            if fullname in objects:
+                self.state_machine.reporter.warning(
+                    'duplicate object description of %s, ' % fullname +
+                    'other instance in ' +
+                    self.env.doc2path(objects[fullname][0]) +
+                    ', use :noindex: for one of them',
+                    line=self.lineno)
+            objects[fullname] = (self.env.docname, self.objtype)
+
+        indextext = self.get_index_text(modname, name_cls)
+        if indextext:
+            self.indexnode['entries'].append(('single', indextext,
+                                              fullname, ''))
+
+
+class OperationXRefRole(XRefRole):
+    def process_link(self, env, refnode, has_explicit_title, title, target):
+        refnode['op:command'] = env.temp_data.get('op:command')
+        refnode['op:setting'] = env.temp_data.get('op:setting')
+        refnode['op:howto'] = env.temp_data.get('op:howto')
+        if not has_explicit_title:
+            title = title.lstrip('.')   # only has a meaning for the target
+            target = target.lstrip('~') # only has a meaning for the title
+            # if the first character is a tilde, don't display the command/class
+            # parts of the contents
+            if title[0:1] == '~':
+                title = title[1:]
+                dot = title.rfind('.')
+                if dot != -1:
+                    title = title[dot+1:]
+        # if the first character is a dot, search more specific namespaces first
+        # else search builtins first
+        if target[0:1] == '.':
+            target = target[1:]
+            refnode['refspecific'] = True
+        return title, target
+
+
+class OperationCommand(Directive):
+    """
+    Directive to mark description of a new command.
+    """
+
+    has_content = False
+    required_arguments = 1
+    optional_arguments = 0
+    final_argument_whitespace = True
+    option_spec = {
+        'platform': lambda x: x,
+        'synopsis': lambda x: x,
+        'noindex': directives.flag,
+        'deprecated': directives.flag,
+    }
+
+    def run(self):
+        env = self.state.document.settings.env
+        comname = self.arguments[0].strip()
+        noindex = 'noindex' in self.options
+        env.temp_data['op:command'] = comname
+        ret = []
+        if not noindex:
+            env.domaindata['op']['commands'][comname] = \
+                (env.docname, self.options.get('synopsis', ''),
+                 self.options.get('platform', ''), 'deprecated' in self.options)
+            # make a duplicate entry in 'objects' to facilitate searching for
+            # the command in OperationDomain.find_obj()
+            env.domaindata['op']['objects'][comname] = (env.docname, 'command')
+            targetnode = nodes.target('', '', ids=['command-' + comname],
+                                      ismod=True)
+            self.state.document.note_explicit_target(targetnode)
+            # the platform and synopsis aren't printed; in fact, they are only
+            # used in the modindex currently
+            ret.append(targetnode)
+            indextext = _('%s (command)') % comname
+            inode = addnodes.index(entries=[('single', indextext,
+                                             'command-' + comname, '')])
+            ret.append(inode)
+        return ret
+
+
+class OperationCommandIndex(Index):
+    """
+    Index subclass to provide the Operation command index.
+    """
+
+    name = 'commandindex'
+    localname = l_('Command Index')
+    shortname = l_('Command')
+
+    def generate(self, docnames=None):
+        content = {}
+        # list of all commands, sorted by command name
+        commands = sorted(self.domain.data['commands'].iteritems(),
+                         key=lambda x: x[0].lower())
+        # sort out collapsable commands
+        prev_comname = ''
+        num_toplevels = 0
+        for comname, (docname, synopsis, platforms, deprecated) in commands:
+            entries = content.setdefault(comname[0].lower(), [])
+
+            package = comname.split('.')[0]
+            if package != comname:
+                # it's a subentries
+                if prev_comname == package:
+                    # first subentries - make parent a group head
+                    if entries:
+                        entries[-1][1] = 1
+                elif not prev_comname.startswith(package):
+                    # subentries without parent in list, add dummy entry
+                    entries.append([package, 1, '', '', '', '', ''])
+                subtype = 2
+                entriename = comname.split(package + '.', 1)[1]
+            else:
+                num_toplevels += 1
+                subtype = 0
+                entriename = comname
+
+            qualifier = deprecated and _('Deprecated') or ''
+            entries.append([entriename, subtype, docname,
+                            'command-' + comname, platforms, qualifier, synopsis])
+            prev_comname = comname
+
+        # apply heuristics when to collapse modindex at page load:
+        # only collapse if number of toplevel commands is larger than
+        # number of submodules
+        collapse = len(commands) - num_toplevels < num_toplevels
+
+        # sort by first letter
+        content = sorted(content.iteritems())
+
+        return content, collapse
+
+
+class OperationSetting(Directive):
+    """
+    Directive to mark description of a new setting.
+    """
+
+    has_content = False
+    required_arguments = 1
+    optional_arguments = 0
+    final_argument_whitespace = True
+    option_spec = {
+        'platform': lambda x: x,
+        'synopsis': lambda x: x,
+        'noindex': directives.flag,
+        'deprecated': directives.flag,
+    }
+
+    def run(self):
+        env = self.state.document.settings.env
+        setname = self.arguments[0].strip()
+        noindex = 'noindex' in self.options
+        env.temp_data['op:setting'] = setname
+        ret = []
+        if not noindex:
+            env.domaindata['op']['settings'][setname] = \
+                (env.docname, self.options.get('synopsis', ''),
+                 self.options.get('platform', ''), 'deprecated' in self.options)
+            # make a duplicate entry in 'objects' to facilitate searching for
+            # the command in OperationDomain.find_obj()
+            env.domaindata['op']['objects'][setname] = (env.docname, 'setting')
+            targetnode = nodes.target('', '', ids=['setting-' + setname],
+                                      ismod=True)
+            self.state.document.note_explicit_target(targetnode)
+            # the platform and synopsis aren't printed; in fact, they are only
+            # used in the modindex currently
+            ret.append(targetnode)
+            indextext = _('%s (setting)') % setname
+            inode = addnodes.index(entries=[('single', indextext,
+                                             'setting-' + setname, '')])
+            ret.append(inode)
+        return ret
+
+
+class OperationSettingIndex(Index):
+    """
+    Index subclass to provide the Operation Seting index.
+    """
+
+    name = 'settingindex'
+    localname = l_('Setting Index')
+    shortname = l_('Setting')
+
+    def generate(self, docnames=None):
+        content = {}
+        # list of all setting, sorted by setting name
+        settings = sorted(self.domain.data['settings'].iteritems(),
+                         key=lambda x: x[0].lower())
+        # sort out collapsable setting
+        prev_setname = ''
+        num_toplevels = 0
+        for setname, (docname, synopsis, platforms, deprecated) in settings:
+            entries = content.setdefault(setname[0].lower(), [])
+
+            package = setname.split('.')[0]
+            if package != setname:
+                # it's a subentries
+                if prev_setname == package:
+                    # first subentries - make parent a group head
+                    if entries:
+                        entries[-1][1] = 1
+                elif not prev_setname.startswith(package):
+                    # subentries without parent in list, add dummy entry
+                    entries.append([package, 1, '', '', '', '', ''])
+                subtype = 2
+                entriename = setname.split(package + '.', 1)[1]
+            else:
+                num_toplevels += 1
+                subtype = 0
+                entriename = setname
+
+            qualifier = deprecated and _('Deprecated') or ''
+            entries.append([entriename, subtype, docname,
+                            'setting-' + setname, platforms, qualifier, synopsis])
+            prev_setname = setname
+
+        # apply heuristics when to collapse modindex at page load:
+        # only collapse if number of toplevel setting is larger than
+        # number of subsetting
+        collapse = len(settings) - num_toplevels < num_toplevels
+
+        # sort by first letter
+        content = sorted(content.iteritems())
+
+        return content, collapse
+
+
+class OperationHowto(Directive):
+    """
+    Directive to mark description of a new howto.
+    """
+
+    has_content = False
+    required_arguments = 1
+    optional_arguments = 0
+    final_argument_whitespace = True
+    option_spec = {
+        'platform': lambda x: x,
+        'synopsis': lambda x: x,
+        'noindex': directives.flag,
+        'deprecated': directives.flag,
+    }
+
+    def run(self):
+        env = self.state.document.settings.env
+        howname = self.arguments[0].strip()
+        noindex = 'noindex' in self.options
+        env.temp_data['op:howto'] = howname
+        ret = []
+        if not noindex:
+            env.domaindata['op']['howtos'][howname] = \
+                (env.docname, self.options.get('synopsis', ''),
+                 self.options.get('platform', ''), 'deprecated' in self.options)
+            # make a duplicate entry in 'objects' to facilitate searching for
+            # the howto in OperationDomain.find_obj()
+            env.domaindata['op']['objects'][howname] = (env.docname, 'howto')
+            targetnode = nodes.target('', '', ids=['howto-' + howname], ismod=True)
+            self.state.document.note_explicit_target(targetnode)
+            # the platform and synopsis aren't printed; in fact, they are only
+            # used in the modindex currently
+            ret.append(targetnode)
+            indextext = _('%s (howto)') % howname
+            inode = addnodes.index(entries=[('single', indextext, 'howto-' + howname, '')])
+            ret.append(inode)
+        return ret
+
+
+class OperationHowtoIndex(Index):
+    """
+    Index subclass to provide the Operation howto index.
+    """
+
+    name = 'howtoindex'
+    localname = l_('Howto Index')
+    shortname = l_('Howto')
+
+    def generate(self, docnames=None):
+        content = {}
+        # list of all howtos, sorted by howto name
+        howtos = sorted(self.domain.data['howtos'].iteritems(),
+                         key=lambda x: x[0].lower())
+        # sort out collapsable howtos
+        prev_howname = ''
+        num_toplevels = 0
+        for howname, (docname, synopsis, platforms, deprecated) in howtos:
+            entries = content.setdefault(howname[0].lower(), [])
+
+            package = howname.split('.')[0]
+            if package != howname:
+                # it's a subentries
+                if prev_howname == package:
+                    # first subentries - make parent a group head
+                    if entries:
+                        entries[-1][1] = 1
+                elif not prev_howname.startswith(package):
+                    # subentries without parent in list, add dummy entry
+                    entries.append([package, 1, '', '', '', '', ''])
+                subtype = 2
+                entriename = howname.split(package + '.', 1)[1]
+            else:
+                num_toplevels += 1
+                subtype = 0
+                entriename = howname
+
+            qualifier = deprecated and _('Deprecated') or ''
+            entries.append([entriename, subtype, docname,
+                            'howto-' + howname, platforms, qualifier, synopsis])
+            prev_howname = howname
+
+        # apply heuristics when to collapse modindex at page load:
+        # only collapse if number of toplevel howtos is larger than
+        # number of submodules
+        collapse = len(howtos) - num_toplevels < num_toplevels
+
+        # sort by first letter
+        content = sorted(content.iteritems())
+
+        return content, collapse
+
+
+class OperationDomain(Domain):
+    """Operation domain."""
+    name = 'op'
+    label = 'Operation'
+    object_types = {
+        'command':       ObjType(l_('command'),        'command'),
+        'setting':       ObjType(l_('setting'),        'setting'),
+        'howto':         ObjType(l_('howto'),           'howto'),
+    }
+
+    directives = {
+        'command':         OperationCommand,
+        'setting':         OperationSetting,
+        'howto':           OperationHowto,
+    }
+    roles = {
+        'command':   OperationXRefRole(),
+        'setting':   OperationXRefRole(),
+        'howto':   OperationXRefRole(),
+    }
+    initial_data = {
+        'objects': {},  # fullname -> docname, objtype
+        'commands': {}, # modname -> modname, synopsis, platform, deprecated
+        'settings': {}, # setname -> setname, synopsis, platform, deprecated
+        'howtos': {},   # howname -> howname, synopsis, platform, deprecated
+    }
+    indices = [
+        OperationCommandIndex,
+        OperationSettingIndex,
+        OperationHowtoIndex,
+    ]
+
+    def clear_doc(self, docname):
+        for fullname, (fn, _) in self.data['objects'].items():
+            if fn == docname:
+                del self.data['objects'][fullname]
+        for modname, (fn, _, _, _) in self.data['commands'].items():
+            if fn == docname:
+                del self.data['commands'][modname]
+        for setname, (fn, _, _, _) in self.data['settings'].items():
+            if fn == docname:
+                del self.data['settings'][setname]
+        for howname, (fn, _, _, _) in self.data['howtos'].items():
+            if fn == docname:
+                del self.data['howtos'][howname]
+
+    def find_obj(self, env, modname, setname, howname, name, type, searchmode=0):
+        """Find a Operation object for "name", perhaps using the given command
+           Returns a list of (name, object entry) tuples.
+        """
+
+        if not name:
+            return []
+
+        objects = self.data['objects']
+        matches = []
+
+        newname = None
+        if searchmode == 1:
+            objtypes = self.objtypes_for_role(type)
+            if objtypes is not None:
+                if not newname:
+                    if modname and modname + '.' + name in objects and \
+                       objects[modname + '.' + name][1] in objtypes:
+                        newname = modname + '.' + name
+                    elif name in objects and objects[name][1] in objtypes:
+                        newname = name
+                    else:
+                        # "fuzzy" searching mode
+                        searchname = '.' + name
+                        matches = [(oname, objects[oname]) for oname in objects
+                                   if oname.endswith(searchname)
+                                   and objects[oname][1] in objtypes]
+        else:
+            # NOTE: searching for exact match, object type is not considered
+            if name in objects:
+                newname = name
+            elif modname and modname + '.' + name in objects:
+                newname = modname + '.' + name
+        if newname is not None:
+            matches.append((newname, objects[newname]))
+        return matches
+
+    def resolve_xref(self, env, fromdocname, builder,
+                     type, target, node, contnode):
+        modname = node.get('op:command')
+        setname = node.get('op:setting')
+        howname = node.get('op:howto')
+        searchmode = node.hasattr('refspecific') and 1 or 0
+        matches = self.find_obj(env, modname, setname, howname, 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] == 'command':
+            # get additional info for commands
+            docname, synopsis, platform, deprecated = self.data['commands'][name]
+            assert docname == obj[0]
+            title = name
+            if synopsis:
+                title += ': ' + synopsis
+            if deprecated:
+                title += _(' (deprecated)')
+            if platform:
+                title += ' (' + platform + ')'
+            return make_refnode(builder, fromdocname, docname, 'command-' + name, contnode, title)
+        elif obj[1] == 'setting':
+            # get additional info for settings
+            docname, synopsis, platform, deprecated = self.data['settings'][name]
+            assert docname == obj[0]
+            title = name
+            if synopsis:
+                title += ': ' + synopsis
+            if deprecated:
+                title += _(' (deprecated)')
+            if platform:
+                title += ' (' + platform + ')'
+            return make_refnode(builder, fromdocname, docname, 'setting-' + name, contnode, title)
+        elif obj[1] == 'howto':
+            # get additional info for howtos
+            docname, synopsis, platform, deprecated = self.data['howtos'][name]
+            assert docname == obj[0]
+            title = name
+            if synopsis:
+                title += ': ' + synopsis
+            if deprecated:
+                title += _(' (deprecated)')
+            if platform:
+                title += ' (' + platform + ')'
+            return make_refnode(builder, fromdocname, docname, 'howto-' + name, contnode, title)
+        else:
+            return make_refnode(builder, fromdocname, obj[0], name,
+                                contnode, name)
+                                
+    def get_objects(self):
+        for modname, info in self.data['commands'].iteritems():
+            yield (modname, modname, 'command', info[0], 'command-' + modname, 0)
+        for setname, info in self.data['settings'].iteritems():
+            yield (setname, setname, 'setting', info[0], 'setting-' + setname, 0)
+        for howname, info in self.data['howtos'].iteritems():
+            yield (howname, howname, 'howto', info[0], 'howto-' + howname, 0)
+        for refname, (docname, type) in self.data['objects'].iteritems():
+            yield (refname, refname, type, docname, refname, 1)
+
+def setup(app):
+    app.add_domain(OperationDomain)