X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/blobdiff_plain/8998b430cbb4382aa4bee4f25dcb58e6bdfd7bbc:/docs/source/_ext/autodoxy/autodoxy/autodoxy.py..bd02d3682100837c80468bb5b24d06cafa64cd30:/docs/source/_ext/autodoxy.py diff --git a/docs/source/_ext/autodoxy/autodoxy/autodoxy.py b/docs/source/_ext/autodoxy.py similarity index 65% rename from docs/source/_ext/autodoxy/autodoxy/autodoxy.py rename to docs/source/_ext/autodoxy.py index 40ca007782..4d36e795d1 100644 --- a/docs/source/_ext/autodoxy/autodoxy/autodoxy.py +++ b/docs/source/_ext/autodoxy.py @@ -1,15 +1,181 @@ +""" +This is autodoxy, a sphinx extension providing autodoc-like directives +that are feed with Doxygen files. + +It is highly inspired from the autodoc_doxygen sphinx extension, but +adapted to my own needs in SimGrid. +https://github.com/rmcgibbo/sphinxcontrib-autodoc_doxygen + +Licence: MIT +Copyright (c) 2015 Robert T. McGibbon +Copyright (c) 2019 Martin Quinson +""" from __future__ import print_function, absolute_import, division +import os.path +import re +import sys + from six import itervalues from lxml import etree as ET from sphinx.ext.autodoc import Documenter, AutoDirective, members_option, ALL from sphinx.errors import ExtensionError from sphinx.util import logging -from . import get_doxygen_root -from .xmlutils import format_xml_paragraph -import sys +########################################################################## +# XML utils +########################################################################## +def format_xml_paragraph(xmlnode): + """Format an Doxygen XML segment (principally a detaileddescription) + as a paragraph for inclusion in the rst document + + Parameters + ---------- + xmlnode + + Returns + ------- + lines + A list of lines. + """ + return [l.rstrip() for l in _DoxygenXmlParagraphFormatter().generic_visit(xmlnode).lines] + + +class _DoxygenXmlParagraphFormatter(object): + # This class follows the model of the stdlib's ast.NodeVisitor for tree traversal + # where you dispatch on the element type to a different method for each node + # during the traverse. + + # It's supposed to handle paragraphs, references, preformatted text (code blocks), and lists. + + def __init__(self): + self.lines = [''] + self.continue_line = False + + def visit(self, node): + method = 'visit_' + node.tag + visitor = getattr(self, method, self.generic_visit) + return visitor(node) + + def generic_visit(self, node): + for child in node.getchildren(): + self.visit(child) + return self + + def visit_ref(self, node): + ref = get_doxygen_root().findall('.//*[@id="%s"]' % node.get('refid')) + if ref: + ref = ref[0] + if ref.tag == 'memberdef': + parent = ref.xpath('./ancestor::compounddef/compoundname')[0].text + name = ref.find('./name').text + real_name = parent + '::' + name + elif ref.tag in ('compounddef', 'enumvalue'): + name_node = ref.find('./name') + real_name = name_node.text if name_node is not None else '' + else: + raise NotImplementedError(ref.tag) + else: + real_name = None + + val = [':cpp:any:`', node.text] + if real_name: + val.extend((' <', real_name, '>`')) + else: + val.append('`') + if node.tail is not None: + val.append(node.tail) + + self.lines[-1] += ''.join(val) + + def visit_para(self, node): + if node.text is not None: + if self.continue_line: + self.lines[-1] += node.text + else: + self.lines.append(node.text) + self.generic_visit(node) + self.lines.append('') + self.continue_line = False + + def visit_verbatim(self, node): + if node.text is not None: + # remove the leading ' *' of any lines + lines = [re.sub('^\s*\*','', l) for l in node.text.split('\n')] + # Merge each paragraph together + text = re.sub("\n\n", "PaRaGrraphSplit", '\n'.join(lines)) + text = re.sub('\n', '', text) + lines = text.split('PaRaGrraphSplit') + + # merge content to the built doc + if self.continue_line: + self.lines[-1] += lines[0] + lines = lines[1:] + for l in lines: + self.lines.append('') + self.lines.append(l) + self.generic_visit(node) + self.lines.append('') + self.continue_line = False + + def visit_parametername(self, node): + if 'direction' in node.attrib: + direction = '[%s] ' % node.get('direction') + else: + direction = '' + + self.lines.append('**%s** -- %s' % ( + node.text, direction)) + self.continue_line = True + + def visit_parameterlist(self, node): + lines = [l for l in type(self)().generic_visit(node).lines if l is not ''] + self.lines.extend([':parameters:', ''] + ['* %s' % l for l in lines] + ['']) + + def visit_simplesect(self, node): + if node.get('kind') == 'return': + self.lines.append(':returns: ') + self.continue_line = True + self.generic_visit(node) + + def visit_listitem(self, node): + self.lines.append(' - ') + self.continue_line = True + self.generic_visit(node) + + def visit_preformatted(self, node): + segment = [node.text if node.text is not None else ''] + for n in node.getchildren(): + segment.append(n.text) + if n.tail is not None: + segment.append(n.tail) + + lines = ''.join(segment).split('\n') + self.lines.extend(('.. code-block:: C++', '')) + self.lines.extend([' ' + l for l in lines]) + + def visit_computeroutput(self, node): + c = node.find('preformatted') + if c is not None: + return self.visit_preformatted(c) + return self.visit_preformatted(node) + + def visit_xrefsect(self, node): + if node.find('xreftitle').text == 'Deprecated': + sublines = type(self)().generic_visit(node).lines + self.lines.extend(['.. admonition:: Deprecated'] + [' ' + s for s in sublines]) + else: + raise ValueError(node) + + def visit_subscript(self, node): + self.lines[-1] += '\ :sub:`%s` %s' % (node.text, node.tail) + + +########################################################################## +# Directives +########################################################################## + class DoxygenDocumenter(Documenter): # Variables to store the names of the object being documented. modname and fullname are redundant, @@ -368,3 +534,57 @@ class DoxygenVariableDocumenter(DoxygenDocumenter): def document_members(self, all_members=False): pass + +########################################################################## +# Setup the extension +########################################################################## +def set_doxygen_xml(app): + """Load all doxygen XML files from the app config variable + `app.config.doxygen_xml` which should be a path to a directory + containing doxygen xml output + """ + err = ExtensionError( + '[autodoxy] No doxygen xml output found in doxygen_xml="%s"' % app.config.doxygen_xml) + + if not os.path.isdir(app.config.doxygen_xml): + raise err + + files = [os.path.join(app.config.doxygen_xml, f) + for f in os.listdir(app.config.doxygen_xml) + if f.lower().endswith('.xml') and not f.startswith('._')] + if len(files) == 0: + raise err + + setup.DOXYGEN_ROOT = ET.ElementTree(ET.Element('root')).getroot() + for file in files: + root = ET.parse(file).getroot() + for node in root: + setup.DOXYGEN_ROOT.append(node) + + +def get_doxygen_root(): + """Get the root element of the doxygen XML document. + """ + if not hasattr(setup, 'DOXYGEN_ROOT'): + setup.DOXYGEN_ROOT = ET.Element("root") # dummy + return setup.DOXYGEN_ROOT + + +def setup(app): + import sphinx.ext.autosummary + + app.connect("builder-inited", set_doxygen_xml) +# app.connect("builder-inited", process_generate_options) + + app.setup_extension('sphinx.ext.autodoc') +# app.setup_extension('sphinx.ext.autosummary') + + app.add_autodocumenter(DoxygenClassDocumenter) + app.add_autodocumenter(DoxygenMethodDocumenter) + app.add_autodocumenter(DoxygenVariableDocumenter) + app.add_config_value("doxygen_xml", "", True) + +# app.add_directive('autodoxysummary', DoxygenAutosummary) +# app.add_directive('autodoxyenum', DoxygenAutoEnum) + + return {'version': sphinx.__display_version__, 'parallel_read_safe': True}