12c593315Sopenharmony_ci# -*- coding: utf-8 -*-
22c593315Sopenharmony_ci"""
32c593315Sopenharmony_ci    sphinx.domains.ruby
42c593315Sopenharmony_ci    ~~~~~~~~~~~~~~~~~~~
52c593315Sopenharmony_ci
62c593315Sopenharmony_ci    The Ruby domain.
72c593315Sopenharmony_ci
82c593315Sopenharmony_ci    :copyright: Copyright 2010 by SHIBUKAWA Yoshiki
92c593315Sopenharmony_ci    :license: BSD, see LICENSE for details.
102c593315Sopenharmony_ci"""
112c593315Sopenharmony_ci
122c593315Sopenharmony_ciimport re
132c593315Sopenharmony_ci
142c593315Sopenharmony_cifrom docutils import nodes
152c593315Sopenharmony_cifrom docutils.parsers.rst import directives
162c593315Sopenharmony_cifrom docutils.parsers.rst import Directive
172c593315Sopenharmony_ci
182c593315Sopenharmony_cifrom sphinx import addnodes
192c593315Sopenharmony_cifrom sphinx import version_info
202c593315Sopenharmony_cifrom sphinx.roles import XRefRole
212c593315Sopenharmony_cifrom sphinx.locale import _
222c593315Sopenharmony_cifrom sphinx.domains import Domain, ObjType, Index
232c593315Sopenharmony_cifrom sphinx.directives import ObjectDescription
242c593315Sopenharmony_cifrom sphinx.util.nodes import make_refnode
252c593315Sopenharmony_cifrom sphinx.util.docfields import Field, GroupedField, TypedField
262c593315Sopenharmony_ci
272c593315Sopenharmony_ci# REs for Ruby signatures
282c593315Sopenharmony_cirb_sig_re = re.compile(
292c593315Sopenharmony_ci    r'''^ ([\w.]*\.)?            # class name(s)
302c593315Sopenharmony_ci          (\$?\w+\??!?)  \s*     # thing name
312c593315Sopenharmony_ci          (?: \((.*)\)           # optional: arguments
322c593315Sopenharmony_ci           (?:\s* -> \s* (.*))?  #           return annotation
332c593315Sopenharmony_ci          )? $                   # and nothing more
342c593315Sopenharmony_ci          ''', re.VERBOSE)
352c593315Sopenharmony_ci
362c593315Sopenharmony_cirb_paramlist_re = re.compile(r'([\[\],])')  # split at '[', ']' and ','
372c593315Sopenharmony_ci
382c593315Sopenharmony_ciseparators = {
392c593315Sopenharmony_ci  'method':'#', 'attr_reader':'#', 'attr_writer':'#', 'attr_accessor':'#',
402c593315Sopenharmony_ci  'function':'.', 'classmethod':'.', 'class':'::', 'module':'::',
412c593315Sopenharmony_ci  'global':'', 'const':'::'}
422c593315Sopenharmony_ci
432c593315Sopenharmony_cirb_separator = re.compile(r"(?:\w+)?(?:::)?(?:\.)?(?:#)?")
442c593315Sopenharmony_ci
452c593315Sopenharmony_ci
462c593315Sopenharmony_cidef _iteritems(d):
472c593315Sopenharmony_ci
482c593315Sopenharmony_ci    for k in d:
492c593315Sopenharmony_ci        yield k, d[k]
502c593315Sopenharmony_ci
512c593315Sopenharmony_ci
522c593315Sopenharmony_cidef ruby_rsplit(fullname):
532c593315Sopenharmony_ci    items = [item for item in rb_separator.findall(fullname)]
542c593315Sopenharmony_ci    return ''.join(items[:-2]), items[-1]
552c593315Sopenharmony_ci
562c593315Sopenharmony_ci
572c593315Sopenharmony_ciclass RubyObject(ObjectDescription):
582c593315Sopenharmony_ci    """
592c593315Sopenharmony_ci    Description of a general Ruby object.
602c593315Sopenharmony_ci    """
612c593315Sopenharmony_ci    option_spec = {
622c593315Sopenharmony_ci        'noindex': directives.flag,
632c593315Sopenharmony_ci        'module': directives.unchanged,
642c593315Sopenharmony_ci    }
652c593315Sopenharmony_ci
662c593315Sopenharmony_ci    doc_field_types = [
672c593315Sopenharmony_ci        TypedField('parameter', label=_('Parameters'),
682c593315Sopenharmony_ci                   names=('param', 'parameter', 'arg', 'argument'),
692c593315Sopenharmony_ci                   typerolename='obj', typenames=('paramtype', 'type')),
702c593315Sopenharmony_ci        TypedField('variable', label=_('Variables'), rolename='obj',
712c593315Sopenharmony_ci                   names=('var', 'ivar', 'cvar'),
722c593315Sopenharmony_ci                   typerolename='obj', typenames=('vartype',)),
732c593315Sopenharmony_ci        GroupedField('exceptions', label=_('Raises'), rolename='exc',
742c593315Sopenharmony_ci                     names=('raises', 'raise', 'exception', 'except'),
752c593315Sopenharmony_ci                     can_collapse=True),
762c593315Sopenharmony_ci        Field('returnvalue', label=_('Returns'), has_arg=False,
772c593315Sopenharmony_ci              names=('returns', 'return')),
782c593315Sopenharmony_ci        Field('returntype', label=_('Return type'), has_arg=False,
792c593315Sopenharmony_ci              names=('rtype',)),
802c593315Sopenharmony_ci    ]
812c593315Sopenharmony_ci
822c593315Sopenharmony_ci    def get_signature_prefix(self, sig):
832c593315Sopenharmony_ci        """
842c593315Sopenharmony_ci        May return a prefix to put before the object name in the signature.
852c593315Sopenharmony_ci        """
862c593315Sopenharmony_ci        return ''
872c593315Sopenharmony_ci
882c593315Sopenharmony_ci    def needs_arglist(self):
892c593315Sopenharmony_ci        """
902c593315Sopenharmony_ci        May return true if an empty argument list is to be generated even if
912c593315Sopenharmony_ci        the document contains none.
922c593315Sopenharmony_ci        """
932c593315Sopenharmony_ci        return False
942c593315Sopenharmony_ci
952c593315Sopenharmony_ci    def handle_signature(self, sig, signode):
962c593315Sopenharmony_ci        """
972c593315Sopenharmony_ci        Transform a Ruby signature into RST nodes.
982c593315Sopenharmony_ci        Returns (fully qualified name of the thing, classname if any).
992c593315Sopenharmony_ci
1002c593315Sopenharmony_ci        If inside a class, the current class name is handled intelligently:
1012c593315Sopenharmony_ci        * it is stripped from the displayed name if present
1022c593315Sopenharmony_ci        * it is added to the full name (return value) if not present
1032c593315Sopenharmony_ci        """
1042c593315Sopenharmony_ci        m = rb_sig_re.match(sig)
1052c593315Sopenharmony_ci        if m is None:
1062c593315Sopenharmony_ci            raise ValueError
1072c593315Sopenharmony_ci        name_prefix, name, arglist, retann = m.groups()
1082c593315Sopenharmony_ci        if not name_prefix:
1092c593315Sopenharmony_ci            name_prefix = ""
1102c593315Sopenharmony_ci        # determine module and class name (if applicable), as well as full name
1112c593315Sopenharmony_ci        modname = self.options.get(
1122c593315Sopenharmony_ci            'module', self.env.temp_data.get('rb:module'))
1132c593315Sopenharmony_ci        classname = self.env.temp_data.get('rb:class')
1142c593315Sopenharmony_ci        if self.objtype == 'global':
1152c593315Sopenharmony_ci            add_module = False
1162c593315Sopenharmony_ci            modname = None
1172c593315Sopenharmony_ci            classname = None
1182c593315Sopenharmony_ci            fullname = name
1192c593315Sopenharmony_ci        elif classname:
1202c593315Sopenharmony_ci            add_module = False
1212c593315Sopenharmony_ci            if name_prefix and name_prefix.startswith(classname):
1222c593315Sopenharmony_ci                fullname = name_prefix + name
1232c593315Sopenharmony_ci                # class name is given again in the signature
1242c593315Sopenharmony_ci                name_prefix = name_prefix[len(classname):].lstrip('.')
1252c593315Sopenharmony_ci            else:
1262c593315Sopenharmony_ci                separator = separators[self.objtype]
1272c593315Sopenharmony_ci                fullname = classname + separator + name_prefix + name
1282c593315Sopenharmony_ci        else:
1292c593315Sopenharmony_ci            add_module = True
1302c593315Sopenharmony_ci            if name_prefix:
1312c593315Sopenharmony_ci                classname = name_prefix.rstrip('.')
1322c593315Sopenharmony_ci                fullname = name_prefix + name
1332c593315Sopenharmony_ci            else:
1342c593315Sopenharmony_ci                classname = ''
1352c593315Sopenharmony_ci                fullname = name
1362c593315Sopenharmony_ci
1372c593315Sopenharmony_ci        signode['module'] = modname
1382c593315Sopenharmony_ci        signode['class'] = self.class_name = classname
1392c593315Sopenharmony_ci        signode['fullname'] = fullname
1402c593315Sopenharmony_ci
1412c593315Sopenharmony_ci        sig_prefix = self.get_signature_prefix(sig)
1422c593315Sopenharmony_ci        if sig_prefix:
1432c593315Sopenharmony_ci            signode += addnodes.desc_annotation(sig_prefix, sig_prefix)
1442c593315Sopenharmony_ci
1452c593315Sopenharmony_ci        if name_prefix:
1462c593315Sopenharmony_ci            signode += addnodes.desc_addname(name_prefix, name_prefix)
1472c593315Sopenharmony_ci        # exceptions are a special case, since they are documented in the
1482c593315Sopenharmony_ci        # 'exceptions' module.
1492c593315Sopenharmony_ci        elif add_module and self.env.config.add_module_names:
1502c593315Sopenharmony_ci            if self.objtype == 'global':
1512c593315Sopenharmony_ci                nodetext = ''
1522c593315Sopenharmony_ci                signode += addnodes.desc_addname(nodetext, nodetext)
1532c593315Sopenharmony_ci            else:
1542c593315Sopenharmony_ci                modname = self.options.get(
1552c593315Sopenharmony_ci                    'module', self.env.temp_data.get('rb:module'))
1562c593315Sopenharmony_ci                if modname and modname != 'exceptions':
1572c593315Sopenharmony_ci                    nodetext = modname + separators[self.objtype]
1582c593315Sopenharmony_ci                    signode += addnodes.desc_addname(nodetext, nodetext)
1592c593315Sopenharmony_ci
1602c593315Sopenharmony_ci        signode += addnodes.desc_name(name, name)
1612c593315Sopenharmony_ci        if not arglist:
1622c593315Sopenharmony_ci            if self.needs_arglist():
1632c593315Sopenharmony_ci                # for callables, add an empty parameter list
1642c593315Sopenharmony_ci                signode += addnodes.desc_parameterlist()
1652c593315Sopenharmony_ci            if retann:
1662c593315Sopenharmony_ci                signode += addnodes.desc_returns(retann, retann)
1672c593315Sopenharmony_ci            return fullname, name_prefix
1682c593315Sopenharmony_ci        signode += addnodes.desc_parameterlist()
1692c593315Sopenharmony_ci
1702c593315Sopenharmony_ci        stack = [signode[-1]]
1712c593315Sopenharmony_ci        for token in rb_paramlist_re.split(arglist):
1722c593315Sopenharmony_ci            if token == '[':
1732c593315Sopenharmony_ci                opt = addnodes.desc_optional()
1742c593315Sopenharmony_ci                stack[-1] += opt
1752c593315Sopenharmony_ci                stack.append(opt)
1762c593315Sopenharmony_ci            elif token == ']':
1772c593315Sopenharmony_ci                try:
1782c593315Sopenharmony_ci                    stack.pop()
1792c593315Sopenharmony_ci                except IndexError:
1802c593315Sopenharmony_ci                    raise ValueError
1812c593315Sopenharmony_ci            elif not token or token == ',' or token.isspace():
1822c593315Sopenharmony_ci                pass
1832c593315Sopenharmony_ci            else:
1842c593315Sopenharmony_ci                token = token.strip()
1852c593315Sopenharmony_ci                stack[-1] += addnodes.desc_parameter(token, token)
1862c593315Sopenharmony_ci        if len(stack) != 1:
1872c593315Sopenharmony_ci            raise ValueError
1882c593315Sopenharmony_ci        if retann:
1892c593315Sopenharmony_ci            signode += addnodes.desc_returns(retann, retann)
1902c593315Sopenharmony_ci        return fullname, name_prefix
1912c593315Sopenharmony_ci
1922c593315Sopenharmony_ci    def get_index_text(self, modname, name):
1932c593315Sopenharmony_ci        """
1942c593315Sopenharmony_ci        Return the text for the index entry of the object.
1952c593315Sopenharmony_ci        """
1962c593315Sopenharmony_ci        raise NotImplementedError('must be implemented in subclasses')
1972c593315Sopenharmony_ci
1982c593315Sopenharmony_ci    def _is_class_member(self):
1992c593315Sopenharmony_ci        return self.objtype.endswith('method') or self.objtype.startswith('attr')
2002c593315Sopenharmony_ci
2012c593315Sopenharmony_ci    def add_target_and_index(self, name_cls, sig, signode):
2022c593315Sopenharmony_ci        if self.objtype == 'global':
2032c593315Sopenharmony_ci            modname = ''
2042c593315Sopenharmony_ci        else:
2052c593315Sopenharmony_ci            modname = self.options.get(
2062c593315Sopenharmony_ci                'module', self.env.temp_data.get('rb:module'))
2072c593315Sopenharmony_ci        separator = separators[self.objtype]
2082c593315Sopenharmony_ci        if self._is_class_member():
2092c593315Sopenharmony_ci            if signode['class']:
2102c593315Sopenharmony_ci                prefix = modname and modname + '::' or ''
2112c593315Sopenharmony_ci            else:
2122c593315Sopenharmony_ci                prefix = modname and modname + separator or ''
2132c593315Sopenharmony_ci        else:
2142c593315Sopenharmony_ci            prefix = modname and modname + separator or ''
2152c593315Sopenharmony_ci        fullname = prefix + name_cls[0]
2162c593315Sopenharmony_ci        # note target
2172c593315Sopenharmony_ci        if fullname not in self.state.document.ids:
2182c593315Sopenharmony_ci            signode['names'].append(fullname)
2192c593315Sopenharmony_ci            signode['ids'].append(fullname)
2202c593315Sopenharmony_ci            signode['first'] = (not self.names)
2212c593315Sopenharmony_ci            self.state.document.note_explicit_target(signode)
2222c593315Sopenharmony_ci            objects = self.env.domaindata['rb']['objects']
2232c593315Sopenharmony_ci            if fullname in objects:
2242c593315Sopenharmony_ci                self.env.warn(
2252c593315Sopenharmony_ci                    self.env.docname,
2262c593315Sopenharmony_ci                    'duplicate object description of %s, ' % fullname +
2272c593315Sopenharmony_ci                    'other instance in ' +
2282c593315Sopenharmony_ci                    self.env.doc2path(objects[fullname][0]),
2292c593315Sopenharmony_ci                    self.lineno)
2302c593315Sopenharmony_ci            objects[fullname] = (self.env.docname, self.objtype)
2312c593315Sopenharmony_ci
2322c593315Sopenharmony_ci        indextext = self.get_index_text(modname, name_cls)
2332c593315Sopenharmony_ci        if indextext:
2342c593315Sopenharmony_ci            self.indexnode['entries'].append(
2352c593315Sopenharmony_ci                _make_index('single', indextext, fullname, fullname))
2362c593315Sopenharmony_ci
2372c593315Sopenharmony_ci    def before_content(self):
2382c593315Sopenharmony_ci        # needed for automatic qualification of members (reset in subclasses)
2392c593315Sopenharmony_ci        self.clsname_set = False
2402c593315Sopenharmony_ci
2412c593315Sopenharmony_ci    def after_content(self):
2422c593315Sopenharmony_ci        if self.clsname_set:
2432c593315Sopenharmony_ci            self.env.temp_data['rb:class'] = None
2442c593315Sopenharmony_ci
2452c593315Sopenharmony_ci
2462c593315Sopenharmony_ciclass RubyModulelevel(RubyObject):
2472c593315Sopenharmony_ci    """
2482c593315Sopenharmony_ci    Description of an object on module level (functions, data).
2492c593315Sopenharmony_ci    """
2502c593315Sopenharmony_ci
2512c593315Sopenharmony_ci    def needs_arglist(self):
2522c593315Sopenharmony_ci        return self.objtype == 'function'
2532c593315Sopenharmony_ci
2542c593315Sopenharmony_ci    def get_index_text(self, modname, name_cls):
2552c593315Sopenharmony_ci        if self.objtype == 'function':
2562c593315Sopenharmony_ci            if not modname:
2572c593315Sopenharmony_ci                return _('%s() (global function)') % name_cls[0]
2582c593315Sopenharmony_ci            return _('%s() (module function in %s)') % (name_cls[0], modname)
2592c593315Sopenharmony_ci        else:
2602c593315Sopenharmony_ci            return ''
2612c593315Sopenharmony_ci
2622c593315Sopenharmony_ci
2632c593315Sopenharmony_ciclass RubyGloballevel(RubyObject):
2642c593315Sopenharmony_ci    """
2652c593315Sopenharmony_ci    Description of an object on module level (functions, data).
2662c593315Sopenharmony_ci    """
2672c593315Sopenharmony_ci
2682c593315Sopenharmony_ci    def get_index_text(self, modname, name_cls):
2692c593315Sopenharmony_ci        if self.objtype == 'global':
2702c593315Sopenharmony_ci            return _('%s (global variable)') % name_cls[0]
2712c593315Sopenharmony_ci        else:
2722c593315Sopenharmony_ci            return ''
2732c593315Sopenharmony_ci
2742c593315Sopenharmony_ci
2752c593315Sopenharmony_ciclass RubyEverywhere(RubyObject):
2762c593315Sopenharmony_ci    """
2772c593315Sopenharmony_ci    Description of a class member (methods, attributes).
2782c593315Sopenharmony_ci    """
2792c593315Sopenharmony_ci
2802c593315Sopenharmony_ci    def needs_arglist(self):
2812c593315Sopenharmony_ci        return self.objtype == 'method'
2822c593315Sopenharmony_ci
2832c593315Sopenharmony_ci    def get_index_text(self, modname, name_cls):
2842c593315Sopenharmony_ci        name, cls = name_cls
2852c593315Sopenharmony_ci        add_modules = self.env.config.add_module_names
2862c593315Sopenharmony_ci        if self.objtype == 'method':
2872c593315Sopenharmony_ci            try:
2882c593315Sopenharmony_ci                clsname, methname = ruby_rsplit(name)
2892c593315Sopenharmony_ci            except ValueError:
2902c593315Sopenharmony_ci                if modname:
2912c593315Sopenharmony_ci                    return _('%s() (in module %s)') % (name, modname)
2922c593315Sopenharmony_ci                else:
2932c593315Sopenharmony_ci                    return '%s()' % name
2942c593315Sopenharmony_ci            if modname and add_modules:
2952c593315Sopenharmony_ci                return _('%s() (%s::%s method)') % (methname, modname,
2962c593315Sopenharmony_ci                                                          clsname)
2972c593315Sopenharmony_ci            else:
2982c593315Sopenharmony_ci                return _('%s() (%s method)') % (methname, clsname)
2992c593315Sopenharmony_ci        else:
3002c593315Sopenharmony_ci            return ''
3012c593315Sopenharmony_ci
3022c593315Sopenharmony_ci
3032c593315Sopenharmony_ciclass RubyClasslike(RubyObject):
3042c593315Sopenharmony_ci    """
3052c593315Sopenharmony_ci    Description of a class-like object (classes, exceptions).
3062c593315Sopenharmony_ci    """
3072c593315Sopenharmony_ci
3082c593315Sopenharmony_ci    def get_signature_prefix(self, sig):
3092c593315Sopenharmony_ci        return self.objtype + ' '
3102c593315Sopenharmony_ci
3112c593315Sopenharmony_ci    def get_index_text(self, modname, name_cls):
3122c593315Sopenharmony_ci        if self.objtype == 'class':
3132c593315Sopenharmony_ci            if not modname:
3142c593315Sopenharmony_ci                return _('%s (class)') % name_cls[0]
3152c593315Sopenharmony_ci            return _('%s (class in %s)') % (name_cls[0], modname)
3162c593315Sopenharmony_ci        elif self.objtype == 'exception':
3172c593315Sopenharmony_ci            return name_cls[0]
3182c593315Sopenharmony_ci        else:
3192c593315Sopenharmony_ci            return ''
3202c593315Sopenharmony_ci
3212c593315Sopenharmony_ci    def before_content(self):
3222c593315Sopenharmony_ci        RubyObject.before_content(self)
3232c593315Sopenharmony_ci        if self.names:
3242c593315Sopenharmony_ci            self.env.temp_data['rb:class'] = self.names[0][0]
3252c593315Sopenharmony_ci            self.clsname_set = True
3262c593315Sopenharmony_ci
3272c593315Sopenharmony_ci
3282c593315Sopenharmony_ciclass RubyClassmember(RubyObject):
3292c593315Sopenharmony_ci    """
3302c593315Sopenharmony_ci    Description of a class member (methods, attributes).
3312c593315Sopenharmony_ci    """
3322c593315Sopenharmony_ci
3332c593315Sopenharmony_ci    def needs_arglist(self):
3342c593315Sopenharmony_ci        return self.objtype.endswith('method')
3352c593315Sopenharmony_ci
3362c593315Sopenharmony_ci    def get_signature_prefix(self, sig):
3372c593315Sopenharmony_ci        if self.objtype == 'classmethod':
3382c593315Sopenharmony_ci            return "classmethod %s." % self.class_name
3392c593315Sopenharmony_ci        elif self.objtype == 'attr_reader':
3402c593315Sopenharmony_ci            return "attribute [R] "
3412c593315Sopenharmony_ci        elif self.objtype == 'attr_writer':
3422c593315Sopenharmony_ci            return "attribute [W] "
3432c593315Sopenharmony_ci        elif self.objtype == 'attr_accessor':
3442c593315Sopenharmony_ci            return "attribute [R/W] "
3452c593315Sopenharmony_ci        return ''
3462c593315Sopenharmony_ci
3472c593315Sopenharmony_ci    def get_index_text(self, modname, name_cls):
3482c593315Sopenharmony_ci        name, cls = name_cls
3492c593315Sopenharmony_ci        add_modules = self.env.config.add_module_names
3502c593315Sopenharmony_ci        if self.objtype == 'classmethod':
3512c593315Sopenharmony_ci            try:
3522c593315Sopenharmony_ci                clsname, methname = ruby_rsplit(name)
3532c593315Sopenharmony_ci            except ValueError:
3542c593315Sopenharmony_ci                return '%s()' % name
3552c593315Sopenharmony_ci            if modname:
3562c593315Sopenharmony_ci                return _('%s() (%s.%s class method)') % (methname, modname,
3572c593315Sopenharmony_ci                                                         clsname)
3582c593315Sopenharmony_ci            else:
3592c593315Sopenharmony_ci                return _('%s() (%s class method)') % (methname, clsname)
3602c593315Sopenharmony_ci        elif self.objtype.startswith('attr'):
3612c593315Sopenharmony_ci            try:
3622c593315Sopenharmony_ci                clsname, attrname = ruby_rsplit(name)
3632c593315Sopenharmony_ci            except ValueError:
3642c593315Sopenharmony_ci                return name
3652c593315Sopenharmony_ci            if modname and add_modules:
3662c593315Sopenharmony_ci                return _('%s (%s.%s attribute)') % (attrname, modname, clsname)
3672c593315Sopenharmony_ci            else:
3682c593315Sopenharmony_ci                return _('%s (%s attribute)') % (attrname, clsname)
3692c593315Sopenharmony_ci        else:
3702c593315Sopenharmony_ci            return ''
3712c593315Sopenharmony_ci
3722c593315Sopenharmony_ci    def before_content(self):
3732c593315Sopenharmony_ci        RubyObject.before_content(self)
3742c593315Sopenharmony_ci        lastname = self.names and self.names[-1][1]
3752c593315Sopenharmony_ci        if lastname and not self.env.temp_data.get('rb:class'):
3762c593315Sopenharmony_ci            self.env.temp_data['rb:class'] = lastname.strip('.')
3772c593315Sopenharmony_ci            self.clsname_set = True
3782c593315Sopenharmony_ci
3792c593315Sopenharmony_ci
3802c593315Sopenharmony_ciclass RubyModule(Directive):
3812c593315Sopenharmony_ci    """
3822c593315Sopenharmony_ci    Directive to mark description of a new module.
3832c593315Sopenharmony_ci    """
3842c593315Sopenharmony_ci
3852c593315Sopenharmony_ci    has_content = False
3862c593315Sopenharmony_ci    required_arguments = 1
3872c593315Sopenharmony_ci    optional_arguments = 0
3882c593315Sopenharmony_ci    final_argument_whitespace = False
3892c593315Sopenharmony_ci    option_spec = {
3902c593315Sopenharmony_ci        'platform': lambda x: x,
3912c593315Sopenharmony_ci        'synopsis': lambda x: x,
3922c593315Sopenharmony_ci        'noindex': directives.flag,
3932c593315Sopenharmony_ci        'deprecated': directives.flag,
3942c593315Sopenharmony_ci    }
3952c593315Sopenharmony_ci
3962c593315Sopenharmony_ci    def run(self):
3972c593315Sopenharmony_ci        env = self.state.document.settings.env
3982c593315Sopenharmony_ci        modname = self.arguments[0].strip()
3992c593315Sopenharmony_ci        noindex = 'noindex' in self.options
4002c593315Sopenharmony_ci        env.temp_data['rb:module'] = modname
4012c593315Sopenharmony_ci        env.domaindata['rb']['modules'][modname] = \
4022c593315Sopenharmony_ci            (env.docname, self.options.get('synopsis', ''),
4032c593315Sopenharmony_ci             self.options.get('platform', ''), 'deprecated' in self.options)
4042c593315Sopenharmony_ci        targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True)
4052c593315Sopenharmony_ci        self.state.document.note_explicit_target(targetnode)
4062c593315Sopenharmony_ci        ret = [targetnode]
4072c593315Sopenharmony_ci        # XXX this behavior of the module directive is a mess...
4082c593315Sopenharmony_ci        if 'platform' in self.options:
4092c593315Sopenharmony_ci            platform = self.options['platform']
4102c593315Sopenharmony_ci            node = nodes.paragraph()
4112c593315Sopenharmony_ci            node += nodes.emphasis('', _('Platforms: '))
4122c593315Sopenharmony_ci            node += nodes.Text(platform, platform)
4132c593315Sopenharmony_ci            ret.append(node)
4142c593315Sopenharmony_ci        # the synopsis isn't printed; in fact, it is only used in the
4152c593315Sopenharmony_ci        # modindex currently
4162c593315Sopenharmony_ci        if not noindex:
4172c593315Sopenharmony_ci            indextext = _('%s (module)') % modname
4182c593315Sopenharmony_ci            inode = addnodes.index(entries=[_make_index(
4192c593315Sopenharmony_ci                'single', indextext, 'module-' + modname, modname)])
4202c593315Sopenharmony_ci            ret.append(inode)
4212c593315Sopenharmony_ci        return ret
4222c593315Sopenharmony_ci
4232c593315Sopenharmony_cidef _make_index(entrytype, entryname, target, ignored, key=None):
4242c593315Sopenharmony_ci    # Sphinx 1.4 introduced backward incompatible changes, it now
4252c593315Sopenharmony_ci    # requires 5 tuples.  Last one is categorization key.  See
4262c593315Sopenharmony_ci    # http://www.sphinx-doc.org/en/stable/extdev/nodes.html#sphinx.addnodes.index
4272c593315Sopenharmony_ci    if version_info >= (1, 4, 0, '', 0):
4282c593315Sopenharmony_ci        return (entrytype, entryname, target, ignored, key)
4292c593315Sopenharmony_ci    else:
4302c593315Sopenharmony_ci        return (entrytype, entryname, target, ignored)
4312c593315Sopenharmony_ci
4322c593315Sopenharmony_ciclass RubyCurrentModule(Directive):
4332c593315Sopenharmony_ci    """
4342c593315Sopenharmony_ci    This directive is just to tell Sphinx that we're documenting
4352c593315Sopenharmony_ci    stuff in module foo, but links to module foo won't lead here.
4362c593315Sopenharmony_ci    """
4372c593315Sopenharmony_ci
4382c593315Sopenharmony_ci    has_content = False
4392c593315Sopenharmony_ci    required_arguments = 1
4402c593315Sopenharmony_ci    optional_arguments = 0
4412c593315Sopenharmony_ci    final_argument_whitespace = False
4422c593315Sopenharmony_ci    option_spec = {}
4432c593315Sopenharmony_ci
4442c593315Sopenharmony_ci    def run(self):
4452c593315Sopenharmony_ci        env = self.state.document.settings.env
4462c593315Sopenharmony_ci        modname = self.arguments[0].strip()
4472c593315Sopenharmony_ci        if modname == 'None':
4482c593315Sopenharmony_ci            env.temp_data['rb:module'] = None
4492c593315Sopenharmony_ci        else:
4502c593315Sopenharmony_ci            env.temp_data['rb:module'] = modname
4512c593315Sopenharmony_ci        return []
4522c593315Sopenharmony_ci
4532c593315Sopenharmony_ci
4542c593315Sopenharmony_ciclass RubyXRefRole(XRefRole):
4552c593315Sopenharmony_ci    def process_link(self, env, refnode, has_explicit_title, title, target):
4562c593315Sopenharmony_ci        if not has_explicit_title:
4572c593315Sopenharmony_ci            title = title.lstrip('.')   # only has a meaning for the target
4582c593315Sopenharmony_ci            title = title.lstrip('#')
4592c593315Sopenharmony_ci            if title.startswith("::"):
4602c593315Sopenharmony_ci                title = title[2:]
4612c593315Sopenharmony_ci            target = target.lstrip('~') # only has a meaning for the title
4622c593315Sopenharmony_ci            # if the first character is a tilde, don't display the module/class
4632c593315Sopenharmony_ci            # parts of the contents
4642c593315Sopenharmony_ci            if title[0:1] == '~':
4652c593315Sopenharmony_ci                m = re.search(r"(?:\.)?(?:#)?(?:::)?(.*)\Z", title)
4662c593315Sopenharmony_ci                if m:
4672c593315Sopenharmony_ci                    title = m.group(1)
4682c593315Sopenharmony_ci        if not title.startswith("$"):
4692c593315Sopenharmony_ci            refnode['rb:module'] = env.temp_data.get('rb:module')
4702c593315Sopenharmony_ci            refnode['rb:class'] = env.temp_data.get('rb:class')
4712c593315Sopenharmony_ci        # if the first character is a dot, search more specific namespaces first
4722c593315Sopenharmony_ci        # else search builtins first
4732c593315Sopenharmony_ci        if target[0:1] == '.':
4742c593315Sopenharmony_ci            target = target[1:]
4752c593315Sopenharmony_ci            refnode['refspecific'] = True
4762c593315Sopenharmony_ci        return title, target
4772c593315Sopenharmony_ci
4782c593315Sopenharmony_ci
4792c593315Sopenharmony_ciclass RubyModuleIndex(Index):
4802c593315Sopenharmony_ci    """
4812c593315Sopenharmony_ci    Index subclass to provide the Ruby module index.
4822c593315Sopenharmony_ci    """
4832c593315Sopenharmony_ci
4842c593315Sopenharmony_ci    name = 'modindex'
4852c593315Sopenharmony_ci    localname = _('Ruby Module Index')
4862c593315Sopenharmony_ci    shortname = _('modules')
4872c593315Sopenharmony_ci
4882c593315Sopenharmony_ci    def generate(self, docnames=None):
4892c593315Sopenharmony_ci        content = {}
4902c593315Sopenharmony_ci        # list of prefixes to ignore
4912c593315Sopenharmony_ci        ignores = self.domain.env.config['modindex_common_prefix']
4922c593315Sopenharmony_ci        ignores = sorted(ignores, key=len, reverse=True)
4932c593315Sopenharmony_ci        # list of all modules, sorted by module name
4942c593315Sopenharmony_ci        modules = sorted(_iteritems(self.domain.data['modules']),
4952c593315Sopenharmony_ci                         key=lambda x: x[0].lower())
4962c593315Sopenharmony_ci        # sort out collapsible modules
4972c593315Sopenharmony_ci        prev_modname = ''
4982c593315Sopenharmony_ci        num_toplevels = 0
4992c593315Sopenharmony_ci        for modname, (docname, synopsis, platforms, deprecated) in modules:
5002c593315Sopenharmony_ci            if docnames and docname not in docnames:
5012c593315Sopenharmony_ci                continue
5022c593315Sopenharmony_ci
5032c593315Sopenharmony_ci            for ignore in ignores:
5042c593315Sopenharmony_ci                if modname.startswith(ignore):
5052c593315Sopenharmony_ci                    modname = modname[len(ignore):]
5062c593315Sopenharmony_ci                    stripped = ignore
5072c593315Sopenharmony_ci                    break
5082c593315Sopenharmony_ci            else:
5092c593315Sopenharmony_ci                stripped = ''
5102c593315Sopenharmony_ci
5112c593315Sopenharmony_ci            # we stripped the whole module name?
5122c593315Sopenharmony_ci            if not modname:
5132c593315Sopenharmony_ci                modname, stripped = stripped, ''
5142c593315Sopenharmony_ci
5152c593315Sopenharmony_ci            entries = content.setdefault(modname[0].lower(), [])
5162c593315Sopenharmony_ci
5172c593315Sopenharmony_ci            package = modname.split('::')[0]
5182c593315Sopenharmony_ci            if package != modname:
5192c593315Sopenharmony_ci                # it's a submodule
5202c593315Sopenharmony_ci                if prev_modname == package:
5212c593315Sopenharmony_ci                    # first submodule - make parent a group head
5222c593315Sopenharmony_ci                    entries[-1][1] = 1
5232c593315Sopenharmony_ci                elif not prev_modname.startswith(package):
5242c593315Sopenharmony_ci                    # submodule without parent in list, add dummy entry
5252c593315Sopenharmony_ci                    entries.append([stripped + package, 1, '', '', '', '', ''])
5262c593315Sopenharmony_ci                subtype = 2
5272c593315Sopenharmony_ci            else:
5282c593315Sopenharmony_ci                num_toplevels += 1
5292c593315Sopenharmony_ci                subtype = 0
5302c593315Sopenharmony_ci
5312c593315Sopenharmony_ci            qualifier = deprecated and _('Deprecated') or ''
5322c593315Sopenharmony_ci            entries.append([stripped + modname, subtype, docname,
5332c593315Sopenharmony_ci                            'module-' + stripped + modname, platforms,
5342c593315Sopenharmony_ci                            qualifier, synopsis])
5352c593315Sopenharmony_ci            prev_modname = modname
5362c593315Sopenharmony_ci
5372c593315Sopenharmony_ci        # apply heuristics when to collapse modindex at page load:
5382c593315Sopenharmony_ci        # only collapse if number of toplevel modules is larger than
5392c593315Sopenharmony_ci        # number of submodules
5402c593315Sopenharmony_ci        collapse = len(modules) - num_toplevels < num_toplevels
5412c593315Sopenharmony_ci
5422c593315Sopenharmony_ci        # sort by first letter
5432c593315Sopenharmony_ci        content = sorted(_iteritems(content))
5442c593315Sopenharmony_ci
5452c593315Sopenharmony_ci        return content, collapse
5462c593315Sopenharmony_ci
5472c593315Sopenharmony_ci
5482c593315Sopenharmony_ciclass RubyDomain(Domain):
5492c593315Sopenharmony_ci    """Ruby language domain."""
5502c593315Sopenharmony_ci    name = 'rb'
5512c593315Sopenharmony_ci    label = 'Ruby'
5522c593315Sopenharmony_ci    object_types = {
5532c593315Sopenharmony_ci        'function':        ObjType(_('function'),         'func', 'obj'),
5542c593315Sopenharmony_ci        'global':          ObjType(_('global variable'),  'global', 'obj'),
5552c593315Sopenharmony_ci        'method':          ObjType(_('method'),           'meth', 'obj'),
5562c593315Sopenharmony_ci        'class':           ObjType(_('class'),            'class', 'obj'),
5572c593315Sopenharmony_ci        'exception':       ObjType(_('exception'),        'exc', 'obj'),
5582c593315Sopenharmony_ci        'classmethod':     ObjType(_('class method'),     'meth', 'obj'),
5592c593315Sopenharmony_ci        'attr_reader':     ObjType(_('attribute'),        'attr', 'obj'),
5602c593315Sopenharmony_ci        'attr_writer':     ObjType(_('attribute'),        'attr', 'obj'),
5612c593315Sopenharmony_ci        'attr_accessor':   ObjType(_('attribute'),        'attr', 'obj'),
5622c593315Sopenharmony_ci        'const':           ObjType(_('const'),            'const', 'obj'),
5632c593315Sopenharmony_ci        'module':          ObjType(_('module'),           'mod', 'obj'),
5642c593315Sopenharmony_ci    }
5652c593315Sopenharmony_ci
5662c593315Sopenharmony_ci    directives = {
5672c593315Sopenharmony_ci        'function':        RubyModulelevel,
5682c593315Sopenharmony_ci        'global':          RubyGloballevel,
5692c593315Sopenharmony_ci        'method':          RubyEverywhere,
5702c593315Sopenharmony_ci        'const':           RubyEverywhere,
5712c593315Sopenharmony_ci        'class':           RubyClasslike,
5722c593315Sopenharmony_ci        'exception':       RubyClasslike,
5732c593315Sopenharmony_ci        'classmethod':     RubyClassmember,
5742c593315Sopenharmony_ci        'attr_reader':     RubyClassmember,
5752c593315Sopenharmony_ci        'attr_writer':     RubyClassmember,
5762c593315Sopenharmony_ci        'attr_accessor':   RubyClassmember,
5772c593315Sopenharmony_ci        'module':          RubyModule,
5782c593315Sopenharmony_ci        'currentmodule':   RubyCurrentModule,
5792c593315Sopenharmony_ci    }
5802c593315Sopenharmony_ci
5812c593315Sopenharmony_ci    roles = {
5822c593315Sopenharmony_ci        'func':  RubyXRefRole(fix_parens=False),
5832c593315Sopenharmony_ci        'global':RubyXRefRole(),
5842c593315Sopenharmony_ci        'class': RubyXRefRole(),
5852c593315Sopenharmony_ci        'exc':   RubyXRefRole(),
5862c593315Sopenharmony_ci        'meth':  RubyXRefRole(fix_parens=False),
5872c593315Sopenharmony_ci        'attr':  RubyXRefRole(),
5882c593315Sopenharmony_ci        'const': RubyXRefRole(),
5892c593315Sopenharmony_ci        'mod':   RubyXRefRole(),
5902c593315Sopenharmony_ci        'obj':   RubyXRefRole(),
5912c593315Sopenharmony_ci    }
5922c593315Sopenharmony_ci    initial_data = {
5932c593315Sopenharmony_ci        'objects': {},  # fullname -> docname, objtype
5942c593315Sopenharmony_ci        'modules': {},  # modname -> docname, synopsis, platform, deprecated
5952c593315Sopenharmony_ci    }
5962c593315Sopenharmony_ci    indices = [
5972c593315Sopenharmony_ci        RubyModuleIndex,
5982c593315Sopenharmony_ci    ]
5992c593315Sopenharmony_ci
6002c593315Sopenharmony_ci    def clear_doc(self, docname):
6012c593315Sopenharmony_ci        for fullname, (fn, _) in list(self.data['objects'].items()):
6022c593315Sopenharmony_ci            if fn == docname:
6032c593315Sopenharmony_ci                del self.data['objects'][fullname]
6042c593315Sopenharmony_ci        for modname, (fn, _, _, _) in list(self.data['modules'].items()):
6052c593315Sopenharmony_ci            if fn == docname:
6062c593315Sopenharmony_ci                del self.data['modules'][modname]
6072c593315Sopenharmony_ci
6082c593315Sopenharmony_ci    def find_obj(self, env, modname, classname, name, type, searchorder=0):
6092c593315Sopenharmony_ci        """
6102c593315Sopenharmony_ci        Find a Ruby object for "name", perhaps using the given module and/or
6112c593315Sopenharmony_ci        classname.
6122c593315Sopenharmony_ci        """
6132c593315Sopenharmony_ci        # skip parens
6142c593315Sopenharmony_ci        if name[-2:] == '()':
6152c593315Sopenharmony_ci            name = name[:-2]
6162c593315Sopenharmony_ci
6172c593315Sopenharmony_ci        if not name:
6182c593315Sopenharmony_ci            return None, None
6192c593315Sopenharmony_ci
6202c593315Sopenharmony_ci        objects = self.data['objects']
6212c593315Sopenharmony_ci
6222c593315Sopenharmony_ci        newname = None
6232c593315Sopenharmony_ci        if searchorder == 1:
6242c593315Sopenharmony_ci            if modname and classname and \
6252c593315Sopenharmony_ci                     modname + '::' + classname + '#' + name in objects:
6262c593315Sopenharmony_ci                newname = modname + '::' + classname + '#' + name
6272c593315Sopenharmony_ci            elif modname and classname and \
6282c593315Sopenharmony_ci                     modname + '::' + classname + '.' + name in objects:
6292c593315Sopenharmony_ci                newname = modname + '::' + classname + '.' + name
6302c593315Sopenharmony_ci            elif modname and modname + '::' + name in objects:
6312c593315Sopenharmony_ci                newname = modname + '::' + name
6322c593315Sopenharmony_ci            elif modname and modname + '#' + name in objects:
6332c593315Sopenharmony_ci                newname = modname + '#' + name
6342c593315Sopenharmony_ci            elif modname and modname + '.' + name in objects:
6352c593315Sopenharmony_ci                newname = modname + '.' + name
6362c593315Sopenharmony_ci            elif classname and classname + '.' + name in objects:
6372c593315Sopenharmony_ci                newname = classname + '.' + name
6382c593315Sopenharmony_ci            elif classname and classname + '#' + name in objects:
6392c593315Sopenharmony_ci                newname = classname + '#' + name
6402c593315Sopenharmony_ci            elif name in objects:
6412c593315Sopenharmony_ci                newname = name
6422c593315Sopenharmony_ci        else:
6432c593315Sopenharmony_ci            if name in objects:
6442c593315Sopenharmony_ci                newname = name
6452c593315Sopenharmony_ci            elif classname and classname + '.' + name in objects:
6462c593315Sopenharmony_ci                newname = classname + '.' + name
6472c593315Sopenharmony_ci            elif classname and classname + '#' + name in objects:
6482c593315Sopenharmony_ci                newname = classname + '#' + name
6492c593315Sopenharmony_ci            elif modname and modname + '::' + name in objects:
6502c593315Sopenharmony_ci                newname = modname + '::' + name
6512c593315Sopenharmony_ci            elif modname and modname + '#' + name in objects:
6522c593315Sopenharmony_ci                newname = modname + '#' + name
6532c593315Sopenharmony_ci            elif modname and modname + '.' + name in objects:
6542c593315Sopenharmony_ci                newname = modname + '.' + name
6552c593315Sopenharmony_ci            elif modname and classname and \
6562c593315Sopenharmony_ci                     modname + '::' + classname + '#' + name in objects:
6572c593315Sopenharmony_ci                newname = modname + '::' + classname + '#' + name
6582c593315Sopenharmony_ci            elif modname and classname and \
6592c593315Sopenharmony_ci                     modname + '::' + classname + '.' + name in objects:
6602c593315Sopenharmony_ci                newname = modname + '::' + classname + '.' + name
6612c593315Sopenharmony_ci            # special case: object methods
6622c593315Sopenharmony_ci            elif type in ('func', 'meth') and '.' not in name and \
6632c593315Sopenharmony_ci                 'object.' + name in objects:
6642c593315Sopenharmony_ci                newname = 'object.' + name
6652c593315Sopenharmony_ci        if newname is None:
6662c593315Sopenharmony_ci            return None, None
6672c593315Sopenharmony_ci        return newname, objects[newname]
6682c593315Sopenharmony_ci
6692c593315Sopenharmony_ci    def resolve_xref(self, env, fromdocname, builder,
6702c593315Sopenharmony_ci                     typ, target, node, contnode):
6712c593315Sopenharmony_ci        if (typ == 'mod' or
6722c593315Sopenharmony_ci            typ == 'obj' and target in self.data['modules']):
6732c593315Sopenharmony_ci            docname, synopsis, platform, deprecated = \
6742c593315Sopenharmony_ci                self.data['modules'].get(target, ('','','', ''))
6752c593315Sopenharmony_ci            if not docname:
6762c593315Sopenharmony_ci                return None
6772c593315Sopenharmony_ci            else:
6782c593315Sopenharmony_ci                title = '%s%s%s' % ((platform and '(%s) ' % platform),
6792c593315Sopenharmony_ci                                    synopsis,
6802c593315Sopenharmony_ci                                    (deprecated and ' (deprecated)' or ''))
6812c593315Sopenharmony_ci                return make_refnode(builder, fromdocname, docname,
6822c593315Sopenharmony_ci                                    'module-' + target, contnode, title)
6832c593315Sopenharmony_ci        else:
6842c593315Sopenharmony_ci            modname = node.get('rb:module')
6852c593315Sopenharmony_ci            clsname = node.get('rb:class')
6862c593315Sopenharmony_ci            searchorder = node.hasattr('refspecific') and 1 or 0
6872c593315Sopenharmony_ci            name, obj = self.find_obj(env, modname, clsname,
6882c593315Sopenharmony_ci                                      target, typ, searchorder)
6892c593315Sopenharmony_ci            if not obj:
6902c593315Sopenharmony_ci                return None
6912c593315Sopenharmony_ci            else:
6922c593315Sopenharmony_ci                return make_refnode(builder, fromdocname, obj[0], name,
6932c593315Sopenharmony_ci                                    contnode, name)
6942c593315Sopenharmony_ci
6952c593315Sopenharmony_ci    def get_objects(self):
6962c593315Sopenharmony_ci        for modname, info in _iteritems(self.data['modules']):
6972c593315Sopenharmony_ci            yield (modname, modname, 'module', info[0], 'module-' + modname, 0)
6982c593315Sopenharmony_ci        for refname, (docname, type) in _iteritems(self.data['objects']):
6992c593315Sopenharmony_ci            yield (refname, refname, type, docname, refname, 1)
7002c593315Sopenharmony_ci
7012c593315Sopenharmony_ci
7022c593315Sopenharmony_cidef setup(app):
7032c593315Sopenharmony_ci    app.add_domain(RubyDomain)
704