diff --git a/breathe/directives.py b/breathe/directives.py index 40b0dac9..bb56e9a7 100644 --- a/breathe/directives.py +++ b/breathe/directives.py @@ -358,7 +358,7 @@ def run(self) -> List[Node]: if not matches: warning = create_warning(project_info, self.state, self.lineno, name=name, kind=self.kind) - return warning.warn('doxygen{kind}: Cannot find namespace "{name}" {tail}') + return warning.warn('doxygen{kind}: Cannot find {kind} "{name}" {tail}') if 'content-only' in self.options: # Unpack the single entry in the matches list @@ -412,6 +412,14 @@ class DoxygenGroupDirective(_DoxygenContentBlockDirective): } +class DoxygenPageDirective(_DoxygenContentBlockDirective): + kind = "page" + option_spec = { + "path": unchanged_required, + "project": unchanged_required, + } + + # TODO: is this comment still relevant? # This class was the same as the DoxygenBaseDirective above, except that it # wraps the output in a definition_list before passing it back. This should be @@ -557,6 +565,7 @@ def setup(app: Sphinx) -> None: "doxygengroup": DoxygenGroupDirective, "doxygenfile": DoxygenFileDirective, "autodoxygenfile": AutoDoxygenFileDirective, + "doxygenpage": DoxygenPageDirective, } # note: the parser factory contains a cache of the parsed XML diff --git a/breathe/parser/compound.py b/breathe/parser/compound.py index 16750187..a1f0c5cf 100644 --- a/breathe/parser/compound.py +++ b/breathe/parser/compound.py @@ -642,6 +642,12 @@ class docVarListEntryTypeSub(supermod.docVarListEntryType): def __init__(self, term=None): supermod.docVarListEntryType.__init__(self, term) + def buildChildren(self, child_, nodeName_): + if child_.nodeType == Node.ELEMENT_NODE and nodeName_ == 'term': + obj_ = supermod.docTitleType.factory() + obj_.build(child_) + self.set_term(obj_) + supermod.docVarListEntryType.subclass = docVarListEntryTypeSub # end class docVarListEntryTypeSub @@ -858,6 +864,33 @@ def __init__(self, id=None, xreftitle=None, xrefdescription=None): # end class docXRefSectTypeSub +class docVariableListTypeSub(supermod.docVariableListType): + + node_type = "docvariablelist" + + def __init__(self, valueOf_=''): + supermod.docVariableListType.__init__(self, valueOf_) + + self.varlistentries = [] + self.listitems = [] + + def buildChildren(self, child_, nodeName_): + supermod.docVariableListType.buildChildren(self, child_, nodeName_) + + if child_.nodeType == Node.ELEMENT_NODE and nodeName_ == "varlistentry": + obj_ = supermod.docVarListEntryType.factory() + obj_.build(child_) + self.varlistentries.append(obj_) + elif child_.nodeType == Node.ELEMENT_NODE and nodeName_ == "listitem": + obj_ = supermod.docListItemType.factory() + obj_.build(child_) + self.listitems.append(obj_) + + +supermod.docVariableListType.subclass = docVariableListTypeSub +# end class docVariableListTypeSub + + class docCopyTypeSub(supermod.docCopyType): node_type = "doccopy" @@ -1009,6 +1042,10 @@ def buildChildren(self, child_, nodeName_): obj_ = supermod.docXRefSectType.factory() obj_.build(child_) self.content.append(obj_) + elif child_.nodeType == Node.ELEMENT_NODE and nodeName_ == "variablelist": + obj_ = supermod.docVariableListType.factory() + obj_.build(child_) + self.content.append(obj_) supermod.docParaType.subclass = docParaTypeSub @@ -1052,6 +1089,19 @@ def __init__(self, valueOf_='', mixedclass_=None, content_=None): supermod.docTitleType.__init__(self, valueOf_, mixedclass_, content_) self.type_ = None + def buildChildren(self, child_, nodeName_): + supermod.docTitleType.buildChildren(self, child_, nodeName_) + + if child_.nodeType == Node.ELEMENT_NODE and nodeName_ == "ref": + obj_ = supermod.docRefTextType.factory() + obj_.build(child_) + self.content_.append(obj_) + self.valueOf_ += obj_.valueOf_ + elif child_.nodeType == Node.ELEMENT_NODE and nodeName_ == "anchor": + obj_ = supermod.docAnchorType.factory() + obj_.build(child_) + self.content_.append(obj_) + supermod.docTitleType.subclass = docTitleTypeSub # end class docTitleTypeSub diff --git a/breathe/renderer/filter.py b/breathe/renderer/filter.py index 0ba742f9..d9ef3ff9 100644 --- a/breathe/renderer/filter.py +++ b/breathe/renderer/filter.py @@ -577,7 +577,7 @@ def __init__(self, app: Sphinx) -> None: def create_render_filter(self, kind: str, options: Dict[str, Any]) -> Filter: """Render filter for group & namespace blocks""" - if kind not in ['group', 'namespace']: + if kind not in ['group', 'page', 'namespace']: raise UnrecognisedKindError(kind) # Generate new dictionary from defaults @@ -933,7 +933,7 @@ def create_content_filter(self, kind: str, options: Dict[str, Any]) -> Filter: As a finder/content filter we only need to match exactly what we're interested in. """ - if kind not in ['group', 'namespace']: + if kind not in ['group', 'page', 'namespace']: raise UnrecognisedKindError(kind) node = Node() @@ -1063,6 +1063,12 @@ def create_finder_filter(self, kind: str, name: str) -> Filter: InFilter(KindAccessor(Node()), ["group"]), InFilter(NameAccessor(Node()), [name]) ) + elif kind == 'page': + filter_ = AndFilter( + InFilter(NodeTypeAccessor(Node()), ["compound"]), + InFilter(KindAccessor(Node()), ["page"]), + InFilter(NameAccessor(Node()), [name]) + ) else: # Assume kind == 'namespace' filter_ = AndFilter( diff --git a/breathe/renderer/sphinxrenderer.py b/breathe/renderer/sphinxrenderer.py index 9f313d3f..7c7b0f7c 100644 --- a/breathe/renderer/sphinxrenderer.py +++ b/breathe/renderer/sphinxrenderer.py @@ -1543,7 +1543,15 @@ def visit_docxrefsect(self, node) -> List[Node]: signode = addnodes.desc_signature() title = node.xreftitle[0] + ':' titlenode = nodes.emphasis(text=title) - signode += titlenode + ref = addnodes.pending_xref( + "", + reftype="ref", + refdomain="std", + refexplicit=True, + reftarget=node.id, + refdoc=self.app.env.docname, + *[titlenode]) + signode += ref nodelist = self.render(node.xrefdescription) contentnode = addnodes.desc_content() @@ -1556,6 +1564,27 @@ def visit_docxrefsect(self, node) -> List[Node]: return [descnode] + def visit_docvariablelist(self, node) -> List[Node]: + output = [] + for varlistentry, listitem in zip(node.varlistentries, node.listitems): + descnode = addnodes.desc() + descnode['objtype'] = 'varentry' + signode = addnodes.desc_signature() + signode += self.render_optional(varlistentry) + descnode += signode + contentnode = addnodes.desc_content() + contentnode += self.render_iterable(listitem.para) + descnode += contentnode + output.append(descnode) + return output + + def visit_docvarlistentry(self, node) -> List[Node]: + content = node.term.content_ + return self.render_iterable(content) + + def visit_docanchor(self, node) -> List[None]: + return self.create_doxygen_target(node) + def visit_mixedcontainer(self, node) -> List[Node]: return self.render_optional(node.getValue()) @@ -1932,6 +1961,9 @@ def dispatch_memberdef(self, node) -> List[Node]: "docparamname": visit_docparamname, "templateparamlist": visit_templateparamlist, "docxrefsect": visit_docxrefsect, + "docvariablelist": visit_docvariablelist, + "docvarlistentry": visit_docvarlistentry, + "docanchor": visit_docanchor, } def render(self, node, context: Optional[RenderContext] = None) -> List[Node]: diff --git a/documentation/source/directives.rst b/documentation/source/directives.rst index dcf24025..de83fb47 100644 --- a/documentation/source/directives.rst +++ b/documentation/source/directives.rst @@ -18,6 +18,7 @@ Directives & Config Variables file group autofile + page .. contents:: Table of Contents @@ -193,7 +194,7 @@ doxygengroup This directive generates the appropriate output for the contents of a doxygen group. A doxygen group can be declared with specific doxygen markup in the -source comments as covered in the `doxygen documentation`_. +source comments as covered in the `doxygen grouping documentation`_. It takes the standard ``project``, ``path``, ``outline`` and ``no-link`` options and additionally the ``content-only``, ``members``, ``protected-members``, @@ -216,7 +217,7 @@ and additionally the ``content-only``, ``members``, ``protected-members``, Checkout the :ref:`doxygengroup documentation ` for more details and to see it in action. -.. _doxygen documentation: http://www.stack.nl/~dimitri/doxygen/manual/grouping.html +.. _doxygen grouping documentation: https://www.doxygen.nl/manual/grouping.html .. _doxygenindex: @@ -387,6 +388,28 @@ It behaves the same as the doxygenstruct directive. Checkout the :ref:`example ` to see it in action. +doxygenpage +~~~~~~~~~~~ + +This directive generates the appropriate output for the contents of a doxygen +page. A doxygen page is created for each "key" of every \\xrefitem command used +for markup in the source comments. For more information check the +`doxygen xrefitem documentation`_. + +It takes the standard ``project`` and ``path`` options. + +:: + + .. doxygenpage:: + :project: ... + :path: ... + +Checkout the :ref:`doxygenpage documentation ` for more details +and to see it in action. + +.. _doxygen xrefitem documentation: https://www.doxygen.nl/manual/commands.html#cmdxrefitem + + Config Values ------------- diff --git a/documentation/source/group.rst b/documentation/source/group.rst index bf8b7202..6e006827 100644 --- a/documentation/source/group.rst +++ b/documentation/source/group.rst @@ -6,7 +6,7 @@ doxygengroup Directive This directive generates the appropriate output for the contents of a doxygen group. A doxygen group can be declared with specific doxygen markup in the -source comments as cover in the `doxygen documentation`_. +source comments as cover in the `doxygen grouping documentation`_. It takes the standard ``project``, ``path``, ``outline`` and ``no-link`` options and additionally the ``content-only``, ``members``, ``protected-members``, @@ -47,7 +47,7 @@ variable to set it in the ``conf.py``. defining them inside the scope of another group, or by using the Doxygen \ingroup command, are also parsed and loaded. -.. _doxygen documentation: http://www.stack.nl/~dimitri/doxygen/manual/grouping.html +.. _doxygen grouping documentation: https://www.doxygen.nl/manual/grouping.html .. contents:: diff --git a/documentation/source/page.rst b/documentation/source/page.rst new file mode 100644 index 00000000..66a3cafb --- /dev/null +++ b/documentation/source/page.rst @@ -0,0 +1,60 @@ + +.. _page-example: + +doxygenpage Directive +===================== + +This directive generates the appropriate output for the contents of a doxygen +page. A doxygen page is created for each "key" of every \\xrefitem command used +for markup in the source comments. For more information check the +`doxygen documentation`_. + +It takes the standard ``project`` and ``path`` options. + +:: + + .. doxygenpage:: + :project: ... + :path: ... + +.. _doxygen documentation: https://www.doxygen.nl/manual/commands.html#cmdxrefitem + +.. contents:: + + +Basic Example +------------- + +.. cpp:namespace:: @ex_page_basic + +The plain ``doxygenpage`` directive will output the page name and description +and any variable entries which were defined to be part of this page (with an +\xrefitem usage). + +.. code-block:: rst + + .. doxygenpage:: xrefsample + :project: xrefsect + +It produces this output: + +.. doxygenpage:: xrefsample + :project: xrefsect + + +Failing Example +--------------- + +.. cpp:namespace:: @ex_page_failing + +This intentionally fails: + +.. code-block:: rst + + .. doxygengroup:: madeuppage + :project: xrefsect + +It produces the following warning message: + +.. warning:: Cannot find file "madeuppage" in doxygen xml output for project + "xrefsect" from directory: ../../examples/specific/xrefsect/xml/