Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplifications and fixes #503

Merged
merged 5 commits into from
Apr 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 33 additions & 52 deletions breathe/renderer/sphinxrenderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,53 +30,25 @@ def __exit__(self, et, ev, bt):
self.previous = None


class DoxyCPPClassObject(cpp.CPPClassObject):
__bases = []
class DoxyCPPInterfaceObject(cpp.CPPClassObject):
object_type = 'class'

@property
def display_object_type(self):
# override because we also have the 'interface' type
assert self.objtype in ('class', 'struct', 'interface')
# TODO: remove this if it should be rendered as 'interface' as well
if self.objtype == 'interface':
return 'class'
return self.objtype
# override because we have the 'interface' type
assert self.objtype == 'interface'
return 'class' # TODO: change to 'interface' if that is the desired rendering

def parse_definition(self, parser):
# add the base classes
ast = parser.parse_declaration("class", "class")

bases = []

for base in self.__bases:
namestr = base.content_[0].value

# build a name object
# TODO: work out if we can use base.refid in a pending_xref somewhere
parser = cpp.DefinitionParser(namestr,
location=self.get_source_info(),
config=self.env.config)
name = parser._parse_nested_name()
parser.assert_end()

bases.append(
cpp.ASTBaseClass(name, base.prot, base.virt == 'virtual', False)
)

ast.declaration.bases = bases

return ast

def augment(self, bases):
self.__bases = bases or []
return parser.parse_declaration("class", "class")


class DomainDirectiveFactory(object):
# A mapping from node kinds to cpp domain classes and directive names.
cpp_classes = {
'class': (DoxyCPPClassObject, 'class'),
'struct': (DoxyCPPClassObject, 'class'),
'interface': (DoxyCPPClassObject, 'interface'),
'class': (cpp.CPPClassObject, 'class'),
'struct': (cpp.CPPClassObject, 'struct'),
'interface': (DoxyCPPInterfaceObject, 'interface'),
'function': (cpp.CPPFunctionObject, 'function'),
'friend': (cpp.CPPFunctionObject, 'function'),
'signal': (cpp.CPPFunctionObject, 'function'),
Expand Down Expand Up @@ -148,11 +120,13 @@ def create(domain, args):
cls, name = DomainDirectiveFactory.php_classes.get(
arg_0, (php.PhpClasslike, 'class'))
else:
domain = 'cpp'
cls, name = DomainDirectiveFactory.cpp_classes.get(
args[0], (cpp.CPPMemberObject, 'member'))
# Replace the directive name because domain directives don't know how to handle
# Breathe's "doxygen" directives.
args = [name] + args[1:]
assert ':' not in name
args = [domain + ':' + name] + args[1:]
return cls(*args)


Expand Down Expand Up @@ -356,13 +330,10 @@ def create_template_prefix(self, decl):
nodes = self.render(decl.templateparamlist)
return 'template<' + ''.join(n.astext() for n in nodes) + '>'

def run_domain_directive(self, kind, names, augment=None):
def run_domain_directive(self, kind, names):
domain_directive = DomainDirectiveFactory.create(
self.context.domain, [kind, names] + self.context.directive_args[2:])

if hasattr(domain_directive, 'augment') and augment is not None:
domain_directive.augment(**augment)

# Translate Breathe's no-link option into the standard noindex option.
if 'no-link' in self.context.directive_args[2]:
domain_directive.options['noindex'] = True
Expand Down Expand Up @@ -498,21 +469,33 @@ def render_signature(file_data, doxygen_target, name, kind):
templatePrefix = self.create_template_prefix(file_data.compounddef)
arg = "%s %s" % (templatePrefix, self.get_fully_qualified_name())

# add base classes
if kind in ('class', 'struct'):
augment = dict(bases=file_data.compounddef.basecompoundref)
else:
augment = None
bs = []
for base in file_data.compounddef.basecompoundref:
b = []
if base.prot is not None:
b.append(base.prot)
if base.virt == 'virtual':
b.append("virtual")
b.append(base.content_[0].value)
bs.append(" ".join(b))
if len(bs) != 0:
arg += " : "
arg += ", ".join(bs)

self.context.directive_args[1] = [arg]
nodes = self.run_domain_directive(kind, self.context.directive_args[1], augment=augment)

nodes = self.run_domain_directive(kind, self.context.directive_args[1])
rst_node = nodes[1]

finder = NodeFinder(rst_node.document)
rst_node.walk(finder)

# The cpp domain in Sphinx doesn't support structs at the moment, so change the text
# from "class " to the correct kind which can be "class " or "struct ".
finder.declarator[0] = self.node_factory.desc_annotation(kind + ' ', kind + ' ')
if kind in ('interface', 'namespace'):
# This is not a real C++ declaration type that Sphinx supports,
# so we hax the replacement of it.
finder.declarator[0] = self.node_factory.desc_annotation(kind + ' ', kind + ' ')

rst_node.children[0].insert(0, doxygen_target)
return nodes, finder.content
Expand Down Expand Up @@ -1062,10 +1045,8 @@ def update_define_signature(signature, obj_type):
return self.render_declaration(node, declaration, update_signature=update_define_signature)

def visit_enum(self, node):
# Sphinx requires a name to be a valid identifier, so replace anonymous enum name of the
# form @id generated by Doxygen with "@anon_id".
name = self.get_fully_qualified_name()
declaration = name.replace("@", "@anon_") if node.name.startswith("@") else name
declaration = name

description_nodes = self.description(node)
name = self.node_factory.emphasis("", self.node_factory.Text("Values:"))
Expand Down
1 change: 1 addition & 0 deletions documentation/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@
"c_union":"../../examples/specific/c_union/xml/",
"define":"../../examples/specific/define/xml/",
"multifile":"../../examples/specific/multifilexml/xml/",
"cpp_anon":"../../examples/specific/cpp_anon/xml/",
}

breathe_projects_source = {
Expand Down
8 changes: 8 additions & 0 deletions documentation/source/specific.rst
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,11 @@ Interface Class

.. doxygeninterface:: InterfaceClass
:project: interface

C++ Anonymous Entities
----------------------

.. cpp:namespace:: @ex_specific_cpp_anon

.. doxygenfile:: cpp_anon.h
:project: cpp_anon
1 change: 1 addition & 0 deletions examples/specific/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ projects = nutshell alias rst inline namespacefile array inheritance \
headings links parameters template_class template_class_non_type \
template_function template_type_alias template_specialisation \
enum define interface \
cpp_anon \
c_file c_struct c_enum c_typedef c_macro c_union

special = programlisting decl_impl multifilexml auto class typedef
Expand Down
11 changes: 11 additions & 0 deletions examples/specific/cpp_anon.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
PROJECT_NAME = "C++ Anonymous"
OUTPUT_DIRECTORY = cpp_anon
GENERATE_LATEX = NO
GENERATE_MAN = NO
GENERATE_RTF = NO
CASE_SENSE_NAMES = NO
INPUT = cpp_anon.h
QUIET = YES
JAVADOC_AUTOBRIEF = YES
GENERATE_HTML = NO
GENERATE_XML = YES
13 changes: 13 additions & 0 deletions examples/specific/cpp_anon.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
struct ClassWithAnonEntities {
struct {
int structMember;
};

union {
int unionMember;
};

enum {
Enumerator
};
};