-from __future__ import print_function, absolute_import, division
-
-import re
-import operator
-from functools import reduce
-from itertools import count, groupby
-
-from docutils import nodes
-from docutils.statemachine import ViewList
-from sphinx import addnodes
-from sphinx.ext.autosummary import Autosummary, autosummary_table
-
-from autodoxy import get_doxygen_root
-from autodoxy.autodoxy import DoxygenMethodDocumenter, DoxygenClassDocumenter
-from autodoxy.xmlutils import format_xml_paragraph
-
-
-def import_by_name(name, env=None, prefixes=None, i=0):
- """Get xml documentation for a class/method with a given name.
- If there are multiple classes or methods with that name, you
- can use the `i` kwarg to pick which one.
- """
- if prefixes is None:
- prefixes = [None]
-
- if env is not None:
- parent = env.ref_context.get('cpp:parent_symbol')
- parent_symbols = []
- while parent is not None and parent.identifier is not None:
- parent_symbols.insert(0, str(parent.identifier))
- parent = parent.parent
- prefixes.append('::'.join(parent_symbols))
-
- tried = []
- for prefix in prefixes:
- try:
- if prefix:
- prefixed_name = '::'.join([prefix, name])
- else:
- prefixed_name = name
- return _import_by_name(prefixed_name, i=i)
- except ImportError:
- tried.append(prefixed_name)
- raise ImportError('no module named %s' % ' or '.join(tried))
-
-
-def _import_by_name(name, i=0):
- root = get_doxygen_root()
- name = name.replace('.', '::')
-
- if '::' in name:
- xpath_query = (
- './/compoundname[text()="%s"]/../'
- 'sectiondef[@kind="public-func"]/memberdef[@kind="function"]/'
- 'name[text()="%s"]/..') % tuple(name.rsplit('::', 1))
- m = root.xpath(xpath_query)
- if len(m) > 0:
- obj = m[i]
- full_name = '.'.join(name.rsplit('::', 1))
- return full_name, obj, full_name, ''
-
- xpath_query = (
- './/compoundname[text()="%s"]/../'
- 'sectiondef[@kind="public-type"]/memberdef[@kind="enum"]/'
- 'name[text()="%s"]/..') % tuple(name.rsplit('::', 1))
- m = root.xpath(xpath_query)
- if len(m) > 0:
- obj = m[i]
- full_name = '.'.join(name.rsplit('::', 1))
- return full_name, obj, full_name, ''
-
- xpath_query = ('.//compoundname[text()="%s"]/..' % name)
- m = root.xpath(xpath_query)
- if len(m) > 0:
- obj = m[i]
- return (name, obj, name, '')
-
- raise ImportError()
-
-
-def get_documenter(obj, full_name):
- if obj.tag == 'memberdef' and obj.get('kind') == 'function':
- return DoxygenMethodDocumenter
- elif obj.tag == 'compounddef':
- return DoxygenClassDocumenter
-
- raise NotImplementedError(obj.tag)
-
-
-class DoxygenAutosummary(Autosummary):
- def get_items(self, names):
- """Try to import the given names, and return a list of
- ``[(name, signature, summary_string, real_name), ...]``.
- """
- env = self.state.document.settings.env
- items = []
-
- names_and_counts = reduce(operator.add,
- [tuple(zip(g, count())) for _, g in groupby(names)]) # type: List[(Str, Int)]
-
- for name, i in names_and_counts:
- display_name = name
- if name.startswith('~'):
- name = name[1:]
- display_name = name.split('::')[-1]
-
- try:
- real_name, obj, parent, modname = import_by_name(name, env=env, i=i)
- except ImportError:
- self.warn('failed to import %s' % name)
- items.append((name, '', '', name))
- continue
-
- self.result = ViewList() # initialize for each documenter
- documenter = get_documenter(obj, parent)(self, real_name, id=obj.get('id'))
- if not documenter.parse_name():
- self.warn('failed to parse name %s' % real_name)
- items.append((display_name, '', '', real_name))
- continue
- if not documenter.import_object():
- self.warn('failed to import object %s' % real_name)
- items.append((display_name, '', '', real_name))
- continue
- if documenter.options.members and not documenter.check_module():
- continue
- # -- Grab the signature
- sig = documenter.format_signature()
-
- # -- Grab the summary
- documenter.add_content(None)
- doc = list(documenter.process_doc([self.result.data]))
-
- while doc and not doc[0].strip():
- doc.pop(0)
-
- # If there's a blank line, then we can assume the first sentence /
- # paragraph has ended, so anything after shouldn't be part of the
- # summary
- for i, piece in enumerate(doc):
- if not piece.strip():
- doc = doc[:i]
- break
-
- # Try to find the "first sentence", which may span multiple lines
- m = re.search(r"^([A-Z].*?\.)(?:\s|$)", " ".join(doc).strip())
- if m:
- summary = m.group(1).strip()
- elif doc:
- summary = doc[0].strip()
- else:
- summary = ''
-
- items.append((display_name, sig, summary, real_name))
-
- return items
-
- def get_tablespec(self):
- table_spec = addnodes.tabular_col_spec()
- table_spec['spec'] = 'll'
-
- table = autosummary_table('')
- real_table = nodes.table('', classes=['longtable'])
- table.append(real_table)
- group = nodes.tgroup('', cols=2)
- real_table.append(group)
- group.append(nodes.colspec('', colwidth=10))
- group.append(nodes.colspec('', colwidth=90))
- body = nodes.tbody('')
- group.append(body)
-
- def append_row(*column_texts):
- row = nodes.row('')
- for text in column_texts:
- node = nodes.paragraph('')
- vl = ViewList()
- vl.append(text, '<autosummary>')
- self.state.nested_parse(vl, 0, node)
- try:
- if isinstance(node[0], nodes.paragraph):
- node = node[0]
- except IndexError:
- pass
- row.append(nodes.entry('', node))
- body.append(row)
- return table, table_spec, append_row
-
- def get_table(self, items):
- """Generate a proper list of table nodes for autosummary:: directive.
-
- *items* is a list produced by :meth:`get_items`.
- """
- table, table_spec, append_row = self.get_tablespec()
- for name, sig, summary, real_name in items:
- qualifier = 'cpp:any'
- # required for cpp autolink
- full_name = real_name.replace('.', '::')
- col1 = ':%s:`%s <%s>`' % (qualifier, name, full_name)
- col2 = summary
- append_row(col1, col2)
-
- self.result.append(' .. rubric: sdsf', 0)
- return [table_spec, table]
-
-
-class DoxygenAutoEnum(DoxygenAutosummary):
-
- def get_items(self, names):
- env = self.state.document.settings.env
- self.name = names[0]
-
- real_name, obj, parent, modname = import_by_name(self.name, env=env)
- names = [n.text for n in obj.findall('./enumvalue/name')]
- descriptions = [format_xml_paragraph(d) for d in obj.findall('./enumvalue/detaileddescription')]
- return zip(names, descriptions)
-
- def get_table(self, items):
- table, table_spec, append_row = self.get_tablespec()
- for name, description in items:
- col1 = ':strong:`' + name + '`'
- while description and not description[0].strip():
- description.pop(0)
- col2 = ' '.join(description)
- append_row(col1, col2)
- return [nodes.rubric('', 'Enum: %s' % self.name), table]