1from breathe.directives import BaseDirective 2from breathe.file_state_cache import MTimeError 3from breathe.project import ProjectError 4from breathe.renderer import RenderContext 5from breathe.renderer.mask import NullMaskFactory 6from breathe.renderer.sphinxrenderer import SphinxRenderer 7from breathe.renderer.target import create_target_handler 8 9from docutils.nodes import Node 10from docutils.parsers.rst.directives import unchanged_required, flag # type: ignore 11 12from typing import Any, List, Optional, Type # noqa 13 14 15class _DoxygenContentBlockDirective(BaseDirective): 16 """Base class for namespace and group directives which have very similar behaviours""" 17 18 required_arguments = 1 19 optional_arguments = 1 20 option_spec = { 21 "path": unchanged_required, 22 "project": unchanged_required, 23 "content-only": flag, 24 "outline": flag, 25 "members": flag, 26 "protected-members": flag, 27 "private-members": flag, 28 "undoc-members": flag, 29 "no-link": flag 30 } 31 has_content = False 32 33 def run(self) -> List[Node]: 34 name = self.arguments[0] 35 36 try: 37 project_info = self.project_info_factory.create_project_info(self.options) 38 except ProjectError as e: 39 warning = self.create_warning(None, kind=self.kind) 40 return warning.warn('doxygen{kind}: %s' % e) 41 42 try: 43 finder = self.finder_factory.create_finder(project_info) 44 except MTimeError as e: 45 warning = self.create_warning(None, kind=self.kind) 46 return warning.warn('doxygen{kind}: %s' % e) 47 48 finder_filter = self.filter_factory.create_finder_filter(self.kind, name) 49 50 # TODO: find a more specific type for the Doxygen nodes 51 matches = [] # type: List[Any] 52 finder.filter_(finder_filter, matches) 53 54 # It shouldn't be possible to have too many matches as namespaces & groups in their nature 55 # are merged together if there are multiple declarations, so we only check for no matches 56 if not matches: 57 warning = self.create_warning(project_info, name=name, kind=self.kind) 58 return warning.warn('doxygen{kind}: Cannot find {kind} "{name}" {tail}') 59 60 if 'content-only' in self.options and self.kind != "page": 61 # Unpack the single entry in the matches list 62 (node_stack,) = matches 63 64 filter_ = self.filter_factory.create_content_filter(self.kind, self.options) 65 # Having found the compound node for the namespace or group in the index we want to grab 66 # the contents of it which match the filter 67 contents_finder = self.finder_factory.create_finder_from_root(node_stack[0], 68 project_info) 69 # TODO: find a more specific type for the Doxygen nodes 70 contents = [] # type: List[Any] 71 contents_finder.filter_(filter_, contents) 72 73 # Replaces matches with our new starting points 74 matches = contents 75 76 target_handler = create_target_handler(self.options, project_info, self.state.document) 77 filter_ = self.filter_factory.create_render_filter(self.kind, self.options) 78 79 node_list = [] 80 for node_stack in matches: 81 object_renderer = SphinxRenderer( 82 self.parser_factory.app, 83 project_info, 84 node_stack, 85 self.state, 86 self.state.document, 87 target_handler, 88 self.parser_factory.create_compound_parser(project_info), 89 filter_ 90 ) 91 92 mask_factory = NullMaskFactory() 93 context = RenderContext(node_stack, mask_factory, self.directive_args) 94 node_list.extend(object_renderer.render(context.node_stack[0], context)) 95 96 return node_list 97 98 99class DoxygenNamespaceDirective(_DoxygenContentBlockDirective): 100 kind = "namespace" 101 102 103class DoxygenGroupDirective(_DoxygenContentBlockDirective): 104 kind = "group" 105 option_spec = { 106 **_DoxygenContentBlockDirective.option_spec, 107 "inner": flag, 108 } 109 110 111class DoxygenPageDirective(_DoxygenContentBlockDirective): 112 kind = "page" 113 option_spec = { 114 "path": unchanged_required, 115 "project": unchanged_required, 116 "content-only": flag, 117 } 118