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