From 98db4067536c460d738f361f4b00ac211e2708aa Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Fri, 14 Jun 2019 09:16:49 -0700 Subject: [PATCH 1/5] Add Ruby domain source for future modification (note that this is slightly modified from the original to add license details to the file itself). --- ACKNOWLEDGEMENTS | 27 + documentation/sphinx/extensions/rubydomain.py | 715 ++++++++++++++++++ 2 files changed, 742 insertions(+) create mode 100755 documentation/sphinx/extensions/rubydomain.py diff --git a/ACKNOWLEDGEMENTS b/ACKNOWLEDGEMENTS index dee3ad8d23..f3bca3a3f8 100644 --- a/ACKNOWLEDGEMENTS +++ b/ACKNOWLEDGEMENTS @@ -457,3 +457,30 @@ John W. Wilkinson (JSON Spirit) CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +SHIBUKAWA Yoshiki (sphinxcontrib-rubydomain) + Copyright (c) 2010 by SHIBUKAWA Yoshiki. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/documentation/sphinx/extensions/rubydomain.py b/documentation/sphinx/extensions/rubydomain.py new file mode 100755 index 0000000000..bfd02a3dbb --- /dev/null +++ b/documentation/sphinx/extensions/rubydomain.py @@ -0,0 +1,715 @@ +# -*- coding: utf-8 -*- +""" + sphinx.domains.ruby + ~~~~~~~~~~~~~~~~~~~ + + The Ruby domain. + + :copyright: Copyright 2010 by SHIBUKAWA Yoshiki + :license: BSD + + Copyright (c) 2010 by SHIBUKAWA Yoshiki. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" + +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, GroupedField, TypedField + + +# REs for Ruby signatures +rb_sig_re = re.compile( + r'''^ ([\w.]*\.)? # class name(s) + (\$?\w+\??!?) \s* # thing name + (?: \((.*)\) # optional: arguments + (?:\s* -> \s* (.*))? # return annotation + )? $ # and nothing more + ''', re.VERBOSE) + +rb_paramlist_re = re.compile(r'([\[\],])') # split at '[', ']' and ',' + +separators = { + 'method':'#', 'attr_reader':'#', 'attr_writer':'#', 'attr_accessor':'#', + 'function':'.', 'classmethod':'.', 'class':'::', 'module':'::', + 'global':'', 'const':'::'} + +rb_separator = re.compile(r"(?:\w+)?(?:::)?(?:\.)?(?:#)?") + + +def ruby_rsplit(fullname): + items = [item for item in rb_separator.findall(fullname)] + return ''.join(items[:-2]), items[-1] + + +class RubyObject(ObjectDescription): + """ + Description of a general Ruby object. + """ + option_spec = { + 'noindex': directives.flag, + 'module': directives.unchanged, + } + + doc_field_types = [ + TypedField('parameter', label=l_('Parameters'), + names=('param', 'parameter', 'arg', 'argument'), + typerolename='obj', typenames=('paramtype', 'type')), + TypedField('variable', label=l_('Variables'), rolename='obj', + names=('var', 'ivar', 'cvar'), + typerolename='obj', typenames=('vartype',)), + GroupedField('exceptions', label=l_('Raises'), rolename='exc', + names=('raises', 'raise', 'exception', 'except'), + 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 Ruby signature into RST nodes. + Returns (fully qualified name of the thing, classname 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 = rb_sig_re.match(sig) + if m is None: + raise ValueError + name_prefix, name, arglist, retann = m.groups() + if not name_prefix: + name_prefix = "" + # determine module and class name (if applicable), as well as full name + modname = self.options.get( + 'module', self.env.temp_data.get('rb:module')) + classname = self.env.temp_data.get('rb:class') + if self.objtype == 'global': + add_module = False + modname = None + classname = None + fullname = name + elif classname: + add_module = False + if name_prefix and name_prefix.startswith(classname): + fullname = name_prefix + name + # class name is given again in the signature + name_prefix = name_prefix[len(classname):].lstrip('.') + else: + separator = separators[self.objtype] + fullname = classname + separator + name_prefix + name + else: + add_module = True + if name_prefix: + classname = name_prefix.rstrip('.') + fullname = name_prefix + name + else: + classname = '' + fullname = name + + signode['module'] = modname + signode['class'] = self.class_name = classname + 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' module. + elif add_module and self.env.config.add_module_names: + if self.objtype == 'global': + nodetext = '' + signode += addnodes.desc_addname(nodetext, nodetext) + else: + modname = self.options.get( + 'module', self.env.temp_data.get('rb:module')) + if modname and modname != 'exceptions': + nodetext = modname + separators[self.objtype] + 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 + signode += addnodes.desc_parameterlist() + + stack = [signode[-1]] + for token in rb_paramlist_re.split(arglist): + if token == '[': + opt = addnodes.desc_optional() + stack[-1] += opt + stack.append(opt) + elif token == ']': + try: + stack.pop() + except IndexError: + raise ValueError + elif not token or token == ',' or token.isspace(): + pass + else: + token = token.strip() + stack[-1] += addnodes.desc_parameter(token, token) + if len(stack) != 1: + raise ValueError + 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 _is_class_member(self): + return self.objtype.endswith('method') or self.objtype.startswith('attr') + + def add_target_and_index(self, name_cls, sig, signode): + if self.objtype == 'global': + modname = '' + else: + modname = self.options.get( + 'module', self.env.temp_data.get('rb:module')) + separator = separators[self.objtype] + if self._is_class_member(): + if signode['class']: + prefix = modname and modname + '::' or '' + else: + prefix = modname and modname + separator or '' + else: + prefix = modname and modname + separator or '' + fullname = prefix + 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['rb']['objects'] + if fullname in objects: + self.env.warn( + self.env.docname, + 'duplicate object description of %s, ' % fullname + + 'other instance in ' + + self.env.doc2path(objects[fullname][0]), + 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, fullname)) + + def before_content(self): + # needed for automatic qualification of members (reset in subclasses) + self.clsname_set = False + + def after_content(self): + if self.clsname_set: + self.env.temp_data['rb:class'] = None + + +class RubyModulelevel(RubyObject): + """ + Description of an object on module level (functions, data). + """ + + def needs_arglist(self): + return self.objtype == 'function' + + def get_index_text(self, modname, name_cls): + if self.objtype == 'function': + if not modname: + return _('%s() (global function)') % name_cls[0] + return _('%s() (module function in %s)') % (name_cls[0], modname) + else: + return '' + + +class RubyGloballevel(RubyObject): + """ + Description of an object on module level (functions, data). + """ + + def get_index_text(self, modname, name_cls): + if self.objtype == 'global': + return _('%s (global variable)') % name_cls[0] + else: + return '' + + +class RubyEverywhere(RubyObject): + """ + Description of a class member (methods, attributes). + """ + + def needs_arglist(self): + return self.objtype == 'method' + + def get_index_text(self, modname, name_cls): + name, cls = name_cls + add_modules = self.env.config.add_module_names + if self.objtype == 'method': + try: + clsname, methname = ruby_rsplit(name) + except ValueError: + if modname: + return _('%s() (in module %s)') % (name, modname) + else: + return '%s()' % name + if modname and add_modules: + return _('%s() (%s::%s method)') % (methname, modname, + clsname) + else: + return _('%s() (%s method)') % (methname, clsname) + else: + return '' + + +class RubyClasslike(RubyObject): + """ + Description of a class-like object (classes, exceptions). + """ + + def get_signature_prefix(self, sig): + return self.objtype + ' ' + + def get_index_text(self, modname, name_cls): + if self.objtype == 'class': + if not modname: + return _('%s (class)') % name_cls[0] + return _('%s (class in %s)') % (name_cls[0], modname) + elif self.objtype == 'exception': + return name_cls[0] + else: + return '' + + def before_content(self): + RubyObject.before_content(self) + if self.names: + self.env.temp_data['rb:class'] = self.names[0][0] + self.clsname_set = True + + +class RubyClassmember(RubyObject): + """ + Description of a class member (methods, attributes). + """ + + def needs_arglist(self): + return self.objtype.endswith('method') + + def get_signature_prefix(self, sig): + if self.objtype == 'classmethod': + return "classmethod %s." % self.class_name + elif self.objtype == 'attr_reader': + return "attribute [R] " + elif self.objtype == 'attr_writer': + return "attribute [W] " + elif self.objtype == 'attr_accessor': + return "attribute [R/W] " + return '' + + def get_index_text(self, modname, name_cls): + name, cls = name_cls + add_modules = self.env.config.add_module_names + if self.objtype == 'classmethod': + try: + clsname, methname = ruby_rsplit(name) + except ValueError: + return '%s()' % name + if modname: + return _('%s() (%s.%s class method)') % (methname, modname, + clsname) + else: + return _('%s() (%s class method)') % (methname, clsname) + elif self.objtype.startswith('attr'): + try: + clsname, attrname = ruby_rsplit(name) + except ValueError: + return name + if modname and add_modules: + return _('%s (%s.%s attribute)') % (attrname, modname, clsname) + else: + return _('%s (%s attribute)') % (attrname, clsname) + else: + return '' + + def before_content(self): + RubyObject.before_content(self) + lastname = self.names and self.names[-1][1] + if lastname and not self.env.temp_data.get('rb:class'): + self.env.temp_data['rb:class'] = lastname.strip('.') + self.clsname_set = True + + +class RubyModule(Directive): + """ + Directive to mark description of a new module. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + 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 + modname = self.arguments[0].strip() + noindex = 'noindex' in self.options + env.temp_data['rb:module'] = modname + env.domaindata['rb']['modules'][modname] = \ + (env.docname, self.options.get('synopsis', ''), + self.options.get('platform', ''), 'deprecated' in self.options) + targetnode = nodes.target('', '', ids=['module-' + modname], ismod=True) + self.state.document.note_explicit_target(targetnode) + ret = [targetnode] + # XXX this behavior of the module directive is a mess... + if 'platform' in self.options: + platform = self.options['platform'] + node = nodes.paragraph() + node += nodes.emphasis('', _('Platforms: ')) + node += nodes.Text(platform, platform) + ret.append(node) + # the synopsis isn't printed; in fact, it is only used in the + # modindex currently + if not noindex: + indextext = _('%s (module)') % modname + inode = addnodes.index(entries=[('single', indextext, + 'module-' + modname, modname)]) + ret.append(inode) + return ret + + +class RubyCurrentModule(Directive): + """ + This directive is just to tell Sphinx that we're documenting + stuff in module foo, but links to module foo won't lead here. + """ + + has_content = False + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = False + option_spec = {} + + def run(self): + env = self.state.document.settings.env + modname = self.arguments[0].strip() + if modname == 'None': + env.temp_data['rb:module'] = None + else: + env.temp_data['rb:module'] = modname + return [] + + +class RubyXRefRole(XRefRole): + def process_link(self, env, refnode, has_explicit_title, title, target): + if not has_explicit_title: + title = title.lstrip('.') # only has a meaning for the target + title = title.lstrip('#') + if title.startswith("::"): + title = title[2:] + target = target.lstrip('~') # only has a meaning for the title + # if the first character is a tilde, don't display the module/class + # parts of the contents + if title[0:1] == '~': + m = re.search(r"(?:\.)?(?:#)?(?:::)?(.*)\Z", title) + if m: + title = m.group(1) + if not title.startswith("$"): + refnode['rb:module'] = env.temp_data.get('rb:module') + refnode['rb:class'] = env.temp_data.get('rb:class') + # 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 RubyModuleIndex(Index): + """ + Index subclass to provide the Ruby module index. + """ + + name = 'modindex' + localname = l_('Ruby Module Index') + shortname = l_('modules') + + def generate(self, docnames=None): + content = {} + # list of prefixes to ignore + ignores = self.domain.env.config['modindex_common_prefix'] + ignores = sorted(ignores, key=len, reverse=True) + # list of all modules, sorted by module name + modules = sorted(self.domain.data['modules'].iteritems(), + key=lambda x: x[0].lower()) + # sort out collapsable modules + prev_modname = '' + num_toplevels = 0 + for modname, (docname, synopsis, platforms, deprecated) in modules: + if docnames and docname not in docnames: + continue + + for ignore in ignores: + if modname.startswith(ignore): + modname = modname[len(ignore):] + stripped = ignore + break + else: + stripped = '' + + # we stripped the whole module name? + if not modname: + modname, stripped = stripped, '' + + entries = content.setdefault(modname[0].lower(), []) + + package = modname.split('::')[0] + if package != modname: + # it's a submodule + if prev_modname == package: + # first submodule - make parent a group head + entries[-1][1] = 1 + elif not prev_modname.startswith(package): + # submodule without parent in list, add dummy entry + entries.append([stripped + package, 1, '', '', '', '', '']) + subtype = 2 + else: + num_toplevels += 1 + subtype = 0 + + qualifier = deprecated and _('Deprecated') or '' + entries.append([stripped + modname, subtype, docname, + 'module-' + stripped + modname, platforms, + qualifier, synopsis]) + prev_modname = modname + + # apply heuristics when to collapse modindex at page load: + # only collapse if number of toplevel modules is larger than + # number of submodules + collapse = len(modules) - num_toplevels < num_toplevels + + # sort by first letter + content = sorted(content.iteritems()) + + return content, collapse + + +class RubyDomain(Domain): + """Ruby language domain.""" + name = 'rb' + label = 'Ruby' + object_types = { + 'function': ObjType(l_('function'), 'func', 'obj'), + 'global': ObjType(l_('global variable'), 'global', 'obj'), + 'method': ObjType(l_('method'), 'meth', 'obj'), + 'class': ObjType(l_('class'), 'class', 'obj'), + 'exception': ObjType(l_('exception'), 'exc', 'obj'), + 'classmethod': ObjType(l_('class method'), 'meth', 'obj'), + 'attr_reader': ObjType(l_('attribute'), 'attr', 'obj'), + 'attr_writer': ObjType(l_('attribute'), 'attr', 'obj'), + 'attr_accessor': ObjType(l_('attribute'), 'attr', 'obj'), + 'const': ObjType(l_('const'), 'const', 'obj'), + 'module': ObjType(l_('module'), 'mod', 'obj'), + } + + directives = { + 'function': RubyModulelevel, + 'global': RubyGloballevel, + 'method': RubyEverywhere, + 'const': RubyEverywhere, + 'class': RubyClasslike, + 'exception': RubyClasslike, + 'classmethod': RubyClassmember, + 'attr_reader': RubyClassmember, + 'attr_writer': RubyClassmember, + 'attr_accessor': RubyClassmember, + 'module': RubyModule, + 'currentmodule': RubyCurrentModule, + } + + roles = { + 'func': RubyXRefRole(fix_parens=False), + 'global':RubyXRefRole(), + 'class': RubyXRefRole(), + 'exc': RubyXRefRole(), + 'meth': RubyXRefRole(fix_parens=False), + 'attr': RubyXRefRole(), + 'const': RubyXRefRole(), + 'mod': RubyXRefRole(), + 'obj': RubyXRefRole(), + } + initial_data = { + 'objects': {}, # fullname -> docname, objtype + 'modules': {}, # modname -> docname, synopsis, platform, deprecated + } + indices = [ + RubyModuleIndex, + ] + + 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['modules'].items(): + if fn == docname: + del self.data['modules'][modname] + + def find_obj(self, env, modname, classname, name, type, searchorder=0): + """ + Find a Ruby object for "name", perhaps using the given module and/or + classname. + """ + # skip parens + if name[-2:] == '()': + name = name[:-2] + + if not name: + return None, None + + objects = self.data['objects'] + + newname = None + if searchorder == 1: + if modname and classname and \ + modname + '::' + classname + '#' + name in objects: + newname = modname + '::' + classname + '#' + name + elif modname and classname and \ + modname + '::' + classname + '.' + name in objects: + newname = modname + '::' + classname + '.' + name + elif modname and modname + '::' + name in objects: + newname = modname + '::' + name + elif modname and modname + '#' + name in objects: + newname = modname + '#' + name + elif modname and modname + '.' + name in objects: + newname = modname + '.' + name + elif classname and classname + '.' + name in objects: + newname = classname + '.' + name + elif classname and classname + '#' + name in objects: + newname = classname + '#' + name + elif name in objects: + newname = name + else: + if name in objects: + newname = name + elif classname and classname + '.' + name in objects: + newname = classname + '.' + name + elif classname and classname + '#' + name in objects: + newname = classname + '#' + name + elif modname and modname + '::' + name in objects: + newname = modname + '::' + name + elif modname and modname + '#' + name in objects: + newname = modname + '#' + name + elif modname and modname + '.' + name in objects: + newname = modname + '.' + name + elif modname and classname and \ + modname + '::' + classname + '#' + name in objects: + newname = modname + '::' + classname + '#' + name + elif modname and classname and \ + modname + '::' + classname + '.' + name in objects: + newname = modname + '::' + classname + '.' + name + # special case: object methods + elif type in ('func', 'meth') and '.' not in name and \ + 'object.' + name in objects: + newname = 'object.' + name + if newname is None: + return None, None + return newname, objects[newname] + + def resolve_xref(self, env, fromdocname, builder, + typ, target, node, contnode): + if (typ == 'mod' or + typ == 'obj' and target in self.data['modules']): + docname, synopsis, platform, deprecated = \ + self.data['modules'].get(target, ('','','', '')) + if not docname: + return None + else: + title = '%s%s%s' % ((platform and '(%s) ' % platform), + synopsis, + (deprecated and ' (deprecated)' or '')) + return make_refnode(builder, fromdocname, docname, + 'module-' + target, contnode, title) + else: + modname = node.get('rb:module') + clsname = node.get('rb:class') + searchorder = node.hasattr('refspecific') and 1 or 0 + name, obj = self.find_obj(env, modname, clsname, + target, typ, searchorder) + if not obj: + return None + else: + return make_refnode(builder, fromdocname, obj[0], name, + contnode, name) + + def get_objects(self): + for modname, info in self.data['modules'].iteritems(): + yield (modname, modname, 'module', info[0], 'module-' + modname, 0) + for refname, (docname, type) in self.data['objects'].iteritems(): + yield (refname, refname, type, docname, refname, 1) + + +def setup(app): + app.add_domain(RubyDomain) From 29d78649c9332e2b0da96ac6074ef8732c74e11b Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Fri, 14 Jun 2019 09:18:51 -0700 Subject: [PATCH 2/5] Sphinx now uses 5-column indexes, so update usages to add the fifth column. --- documentation/sphinx/extensions/rubydomain.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/sphinx/extensions/rubydomain.py b/documentation/sphinx/extensions/rubydomain.py index bfd02a3dbb..13b364f8a3 100755 --- a/documentation/sphinx/extensions/rubydomain.py +++ b/documentation/sphinx/extensions/rubydomain.py @@ -252,7 +252,7 @@ class RubyObject(ObjectDescription): indextext = self.get_index_text(modname, name_cls) if indextext: self.indexnode['entries'].append(('single', indextext, - fullname, fullname)) + fullname, fullname, None)) def before_content(self): # needed for automatic qualification of members (reset in subclasses) @@ -436,7 +436,7 @@ class RubyModule(Directive): if not noindex: indextext = _('%s (module)') % modname inode = addnodes.index(entries=[('single', indextext, - 'module-' + modname, modname)]) + 'module-' + modname, modname, None)]) ret.append(inode) return ret From 5ad23ffd5993541d74813c1db562ab104a7da36a Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Fri, 14 Jun 2019 09:19:22 -0700 Subject: [PATCH 3/5] sphinx.util.compat.Directive is deprecated in favor of docutils.parsers.rst.Directive --- documentation/sphinx/extensions/rubydomain.py | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/documentation/sphinx/extensions/rubydomain.py b/documentation/sphinx/extensions/rubydomain.py index 13b364f8a3..540f8487d3 100755 --- a/documentation/sphinx/extensions/rubydomain.py +++ b/documentation/sphinx/extensions/rubydomain.py @@ -9,36 +9,36 @@ :license: BSD Copyright (c) 2010 by SHIBUKAWA Yoshiki. - All rights reserved. +All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ import re from docutils import nodes -from docutils.parsers.rst import directives +from docutils.parsers.rst import directives, Directive from sphinx import addnodes from sphinx.roles import XRefRole @@ -46,7 +46,6 @@ 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, GroupedField, TypedField From 58e6f0d753584d0f3482104603284f912af64886 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Fri, 14 Jun 2019 09:22:55 -0700 Subject: [PATCH 4/5] Switch to using the local copy of the rubydomain extension --- documentation/sphinx/conf.py | 2 +- documentation/sphinx/requirements.txt | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/documentation/sphinx/conf.py b/documentation/sphinx/conf.py index 58af651492..5ec9238930 100644 --- a/documentation/sphinx/conf.py +++ b/documentation/sphinx/conf.py @@ -32,7 +32,7 @@ extensions = [ 'sphinx.ext.ifconfig', 'brokenrole', 'relativelink', - 'sphinxcontrib.rubydomain' + 'rubydomain', ] # Add any paths that contain templates here, relative to this directory. diff --git a/documentation/sphinx/requirements.txt b/documentation/sphinx/requirements.txt index 8cf8ae7f95..0cfa0c9f71 100644 --- a/documentation/sphinx/requirements.txt +++ b/documentation/sphinx/requirements.txt @@ -1,5 +1,4 @@ --index-url https://pypi.python.org/simple sphinx==1.5.6 sphinx-bootstrap-theme==0.4.8 -pygments-style-solarized -sphinxcontrib-rubydomain==0.1dev-20100804 \ No newline at end of file +pygments-style-solarized \ No newline at end of file From 9619013abcddea8dbb059ffc6ed879525d92a988 Mon Sep 17 00:00:00 2001 From: "A.J. Beamon" Date: Fri, 14 Jun 2019 09:45:18 -0700 Subject: [PATCH 5/5] Convert warnings to errors in Sphinx documentation builds --- documentation/CMakeLists.txt | 2 +- documentation/sphinx/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/CMakeLists.txt b/documentation/CMakeLists.txt index 20262c29ae..ba4b299433 100644 --- a/documentation/CMakeLists.txt +++ b/documentation/CMakeLists.txt @@ -43,7 +43,7 @@ function(add_documentation_target) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target}_done COMMAND ${CMAKE_COMMAND} -E make_directory ${out_dir} && - ${python_command} ${SPHINX_COMMAND} -b ${target} + ${python_command} ${SPHINX_COMMAND} -W -b ${target} -d ${doctree} -c ${sphinx_dir} ${sphinx_dir}/source ${CMAKE_CURRENT_BINARY_DIR}/${target} && diff --git a/documentation/sphinx/Makefile b/documentation/sphinx/Makefile index 3aab160be2..ea672b8aa4 100644 --- a/documentation/sphinx/Makefile +++ b/documentation/sphinx/Makefile @@ -87,7 +87,7 @@ cleanvirtualenv: rm -rf $(VENVDIR) html: buildsphinx cleanhtml - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + $(SPHINXBUILD) -W -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."