Files
obitools3/doc/breathe/directives.py
2015-05-11 17:18:23 +02:00

1051 lines
35 KiB
Python

from .finder.core import FinderFactory
from .parser import DoxygenParserFactory, CacheFactory
from .renderer.rst.doxygen import DoxygenToRstRendererFactoryCreatorConstructor, \
RstContentCreator, RenderContext
from .renderer.rst.doxygen.filter import FilterFactory, GlobFactory
from .renderer.rst.doxygen.target import TargetHandlerFactory
from .renderer.rst.doxygen.mask import MaskFactory, NullMaskFactory, NoParameterNamesMask
from .finder.doxygen.core import DoxygenItemFinderFactoryCreator
from .directive.base import BaseDirective, create_warning
from .directive.index import DoxygenIndexDirective, AutoDoxygenIndexDirective
from .directive.file import DoxygenFileDirective, AutoDoxygenFileDirective
from .process import AutoDoxygenProcessHandle
from .exception import BreatheError
from .project import ProjectInfoFactory, ProjectError
from docutils.parsers.rst.directives import unchanged_required, unchanged, flag
from docutils.statemachine import ViewList
from sphinx.domains import cpp, c, python
from sphinx.writers.text import TextWriter
from sphinx.builders.text import TextBuilder
import docutils.nodes
import sphinx.addnodes
import sphinx.ext.mathbase
import os
import fnmatch
import re
import textwrap
import collections
import subprocess
# Somewhat outrageously, reach in and fix a Sphinx regex
cpp._identifier_re = re.compile(r'(~?\b[a-zA-Z_][a-zA-Z0-9_]*)\b')
class NoMatchingFunctionError(BreatheError):
pass
class UnableToResolveFunctionError(BreatheError):
def __init__(self, signatures):
self.signatures = signatures
class NodeNotFoundError(BreatheError):
pass
class FakeDestination(object):
def write(self, output):
return output
class TextRenderer(object):
def __init__(self, app):
self.app = app
def render(self, nodes, document):
new_document = document.copy()
new_document.children = nodes
writer = TextWriter(TextBuilder(self.app))
output = writer.write(new_document, FakeDestination())
return output.strip()
# Directives
# ----------
class DoxygenFunctionDirective(BaseDirective):
required_arguments = 1
option_spec = {
"path": unchanged_required,
"project": unchanged_required,
"outline": flag,
"no-link": flag,
}
has_content = False
final_argument_whitespace = True
def __init__(self, node_factory, text_renderer, *args, **kwargs):
BaseDirective.__init__(self, *args, **kwargs)
self.node_factory = node_factory
self.text_renderer = text_renderer
def run(self):
# Separate possible arguments (delimited by a "(") from the namespace::name
match = re.match(r"([^(]*)(.*)", self.arguments[0])
namespaced_function, args = match.group(1), match.group(2)
# Split the namespace and the function name
try:
(namespace, function_name) = namespaced_function.rsplit("::", 1)
except ValueError:
(namespace, function_name) = "", namespaced_function
try:
project_info = self.project_info_factory.create_project_info(self.options)
except ProjectError as e:
warning = create_warning(None, self.state, self.lineno)
return warning.warn('doxygenfunction: %s' % e)
try:
finder = self.finder_factory.create_finder(project_info)
except MTimerError as e:
warning = create_warning(None, self.state, self.lineno)
return warning.warn('doxygenfunction: %s' % e)
# Extract arguments from the function name.
args = self.parse_args(args)
finder_filter = self.filter_factory.create_member_finder_filter(
namespace, function_name, 'function')
matches = []
finder.filter_(finder_filter, matches)
# Create it ahead of time as it is cheap and it is ugly to declare it for both exception
# clauses below
warning = create_warning(
project_info,
self.state,
self.lineno,
namespace='%s::' % namespace if namespace else '',
function=function_name,
args=', '.join(args)
)
try:
node_stack = self.resolve_function(matches, args, project_info)
except NoMatchingFunctionError:
return warning.warn('doxygenfunction: Cannot find function "{namespace}{function}" '
'{tail}')
except UnableToResolveFunctionError as error:
message = 'doxygenfunction: Unable to resolve multiple matches for function ' \
'"{namespace}{function}" with arguments ({args}) {tail}.\n' \
'Potential matches:\n'
# We want to create a raw_text string for the console output and a set of docutils nodes
# for rendering into the final output. We handle the final output as a literal string
# with a txt based list of the options.
raw_text = message
literal_text = ''
# TODO: We're cheating here with the set() as signatures has repeating entries for some
# reason (failures in the matcher_stack code) so we consolidate them by shoving them in
# a set to remove duplicates. Should be fixed!
for i, entry in enumerate(set(error.signatures)):
if i:
literal_text += '\n'
# Replace new lines with a new line & enough spacing to reach the appropriate
# alignment for our simple plain text list
literal_text += '- %s' % entry.replace('\n', '\n ')
raw_text += ' - %s\n' % entry.replace('\n', '\n ')
block = self.node_factory.literal_block('', '', self.node_factory.Text(literal_text))
formatted_message = warning.format(message)
warning_nodes = [
self.node_factory.paragraph(
"", "",
self.node_factory.Text(formatted_message)
),
block
]
result = warning.warn(raw_text, warning_nodes)
return result
target_handler = self.target_handler_factory.create_target_handler(
self.options, project_info, self.state.document
)
filter_ = self.filter_factory.create_outline_filter(self.options)
return self.render(node_stack, project_info, self.options, filter_, target_handler,
NullMaskFactory())
def parse_args(self, function_description):
# Strip off trailing qualifiers
pattern = re.compile(r'''(?<= \)) \s*
(?: const)? \s*
(?: volatile)? \s*
(?: = \s* 0)? \s* $ ''',
re.VERBOSE)
function_description = re.sub(pattern,
'',
function_description)
paren_index = function_description.find('(')
if paren_index == -1:
return []
# If it is empty parenthesis, then return empty list as we want empty parenthesis coming
# from the xml file to match the user's function when the user doesn't provide parenthesis
# ie. when there are no args anyway
elif function_description == '()':
return []
else:
# Parse the function name string, eg. f(int, float) to
# extract the types so we can use them for matching
args = []
num_open_brackets = -1
start = paren_index + 1
for i in range(paren_index, len(function_description)):
c = function_description[i]
if c == '(' or c == '<':
num_open_brackets += 1
elif c == ')' or c == '>':
num_open_brackets -= 1
elif c == ',' and num_open_brackets == 0:
args.append(function_description[start:i].strip())
start = i + 1
args.append(function_description[start:-1].strip())
return args
def resolve_function(self, matches, args, project_info):
if not matches:
raise NoMatchingFunctionError()
if len(matches) == 1:
return matches[0]
node_stack = None
signatures = []
# Iterate over the potential matches
for entry in matches:
text_options = {'no-link': u'', 'outline': u''}
# Render the matches to docutils nodes
target_handler = self.target_handler_factory.create_target_handler(
{'no-link': u''}, project_info, self.state.document
)
filter_ = self.filter_factory.create_outline_filter(text_options)
mask_factory = MaskFactory({'param': NoParameterNamesMask})
nodes = self.render(entry, project_info, text_options, filter_, target_handler,
mask_factory)
# Render the nodes to text
signature = self.text_renderer.render(nodes, self.state.document)
signatures.append(signature)
match = re.match(r"([^(]*)(.*)", signature)
match_args = match.group(2)
# Parse the text to find the arguments
match_args = self.parse_args(match_args)
# Match them against the arg spec
if args == match_args:
node_stack = entry
break
if not node_stack:
raise UnableToResolveFunctionError(signatures)
return node_stack
class DoxygenClassLikeDirective(BaseDirective):
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = True
option_spec = {
"path": unchanged_required,
"project": unchanged_required,
"members": unchanged,
"protected-members": flag,
"private-members": flag,
"undoc-members": flag,
"show": unchanged_required,
"outline": flag,
"no-link": flag,
}
has_content = False
def run(self):
name = self.arguments[0]
try:
project_info = self.project_info_factory.create_project_info(self.options)
except ProjectError as e:
warning = create_warning(None, self.state, self.lineno, kind=self.kind)
return warning.warn('doxygen{kind}: %s' % e)
try:
finder = self.finder_factory.create_finder(project_info)
except MTimerError as e:
warning = create_warning(None, self.state, self.lineno, kind=self.kind)
return warning.warn('doxygen{kind}: %s' % e)
finder_filter = self.filter_factory.create_compound_finder_filter(name, self.kind)
matches = []
finder.filter_(finder_filter, matches)
if len(matches) == 0:
warning = create_warning(project_info, self.state, self.lineno, name=name,
kind=self.kind)
return warning.warn('doxygen{kind}: Cannot find class "{name}" {tail}')
target_handler = self.target_handler_factory.create_target_handler(
self.options, project_info, self.state.document
)
filter_ = self.filter_factory.create_class_filter(name, self.options)
mask_factory = NullMaskFactory()
return self.render(matches[0], project_info, self.options, filter_, target_handler,
mask_factory)
class DoxygenClassDirective(DoxygenClassLikeDirective):
kind = "class"
class DoxygenStructDirective(DoxygenClassLikeDirective):
kind = "struct"
class DoxygenContentBlockDirective(BaseDirective):
"""Base class for namespace and group directives which have very similar behaviours"""
required_arguments = 1
optional_arguments = 1
option_spec = {
"path": unchanged_required,
"project": unchanged_required,
"content-only": flag,
"outline": flag,
"members": flag,
"protected-members": flag,
"private-members": flag,
"undoc-members": flag,
"no-link": flag
}
has_content = False
def run(self):
name = self.arguments[0]
try:
project_info = self.project_info_factory.create_project_info(self.options)
except ProjectError as e:
warning = create_warning(None, self.state, self.lineno, kind=self.kind)
return warning.warn('doxygen{kind}: %s' % e)
try:
finder = self.finder_factory.create_finder(project_info)
except MTimerError as e:
warning = create_warning(None, self.state, self.lineno, kind=self.kind)
return warning.warn('doxygen{kind}: %s' % e)
finder_filter = self.filter_factory.create_finder_filter(self.kind, name)
matches = []
finder.filter_(finder_filter, matches)
# It shouldn't be possible to have too many matches as namespaces & groups in their nature
# are merged together if there are multiple declarations, so we only check for no matches
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}')
if 'content-only' in self.options:
# Unpack the single entry in the matches list
(node_stack,) = matches
filter_ = self.filter_factory.create_content_filter(self.kind, self.options)
# Having found the compound node for the namespace or group in the index we want to grab
# the contents of it which match the filter
contents_finder = self.finder_factory.create_finder_from_root(node_stack[0],
project_info)
contents = []
contents_finder.filter_(filter_, contents)
# Replaces matches with our new starting points
matches = contents
target_handler = self.target_handler_factory.create_target_handler(
self.options, project_info, self.state.document
)
filter_ = self.filter_factory.create_render_filter(self.kind, self.options)
renderer_factory_creator = self.renderer_factory_creator_constructor.create_factory_creator(
project_info,
self.state.document,
self.options,
target_handler
)
node_list = []
for node_stack in matches:
renderer_factory = renderer_factory_creator.create_factory(
node_stack,
self.state,
self.state.document,
filter_,
target_handler,
)
mask_factory = NullMaskFactory()
context = RenderContext(node_stack, mask_factory, self.directive_args)
object_renderer = renderer_factory.create_renderer(context)
node_list.extend(object_renderer.render())
return node_list
class DoxygenNamespaceDirective(DoxygenContentBlockDirective):
kind = "namespace"
class DoxygenGroupDirective(DoxygenContentBlockDirective):
kind = "group"
# 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
# abstracted in a far nicer way to avoid repeating so much code
#
# Now we've removed the definition_list wrap so we really need to refactor this!
class DoxygenBaseItemDirective(BaseDirective):
required_arguments = 1
optional_arguments = 1
option_spec = {
"path": unchanged_required,
"project": unchanged_required,
"outline": flag,
"no-link": flag,
}
has_content = False
def create_finder_filter(self, namespace, name):
"""Creates a filter to find the node corresponding to this item."""
return self.filter_factory.create_member_finder_filter(
namespace, name, self.kind)
def run(self):
try:
namespace, name = self.arguments[0].rsplit("::", 1)
except ValueError:
namespace, name = "", self.arguments[0]
try:
project_info = self.project_info_factory.create_project_info(self.options)
except ProjectError as e:
warning = create_warning(None, self.state, self.lineno, kind=self.kind)
return warning.warn('doxygen{kind}: %s' % e)
try:
finder = self.finder_factory.create_finder(project_info)
except MTimerError as e:
warning = create_warning(None, self.state, self.lineno, kind=self.kind)
return warning.warn('doxygen{kind}: %s' % e)
finder_filter = self.create_finder_filter(namespace, name)
matches = []
finder.filter_(finder_filter, matches)
if len(matches) == 0:
display_name = "%s::%s" % (namespace, name) if namespace else name
warning = create_warning(project_info, self.state, self.lineno, kind=self.kind,
display_name=display_name)
return warning.warn('doxygen{kind}: Cannot find {kind} "{display_name}" {tail}')
target_handler = self.target_handler_factory.create_target_handler(
self.options, project_info, self.state.document
)
filter_ = self.filter_factory.create_outline_filter(self.options)
node_stack = matches[0]
mask_factory = NullMaskFactory()
return self.render(node_stack, project_info, self.options, filter_, target_handler,
mask_factory)
class DoxygenVariableDirective(DoxygenBaseItemDirective):
kind = "variable"
def render(self, node_stack, project_info, options, filter_, target_handler, mask_factory):
# Remove 'extern' keyword as Sphinx doesn't support it.
definition = node_stack[0].definition
extern = 'extern '
if definition.startswith(extern):
definition = definition[len(extern):]
self.directive_args[1] = [definition]
return DoxygenBaseItemDirective.render(self, node_stack, project_info, options, filter_,
target_handler, mask_factory)
class DoxygenDefineDirective(DoxygenBaseItemDirective):
kind = "define"
class DoxygenEnumDirective(DoxygenBaseItemDirective):
kind = "enum"
class DoxygenEnumValueDirective(DoxygenBaseItemDirective):
kind = "enumvalue"
def create_finder_filter(self, namespace, name):
return self.filter_factory.create_enumvalue_finder_filter(name)
class DoxygenTypedefDirective(DoxygenBaseItemDirective):
kind = "typedef"
class DoxygenUnionDirective(DoxygenBaseItemDirective):
kind = "union"
def create_finder_filter(self, namespace, name):
# Unions are stored in the xml file with their fully namespaced name
# We're using C++ namespaces here, it might be best to make this file
# type dependent
#
xml_name = "%s::%s" % (namespace, name) if namespace else name
return self.filter_factory.create_compound_finder_filter(xml_name, 'union')
# Setup Administration
# --------------------
class DirectiveContainer(object):
def __init__(self, directive, *args):
self.directive = directive
self.args = args
# Required for sphinx to inspect
self.required_arguments = directive.required_arguments
self.optional_arguments = directive.optional_arguments
self.option_spec = directive.option_spec
self.has_content = directive.has_content
self.final_argument_whitespace = directive.final_argument_whitespace
def __call__(self, *args):
call_args = []
call_args.extend(self.args)
call_args.extend(args)
return self.directive(*call_args)
class DoxygenDirectiveFactory(object):
directives = {
"doxygenindex": DoxygenIndexDirective,
"autodoxygenindex": AutoDoxygenIndexDirective,
"doxygenfunction": DoxygenFunctionDirective,
"doxygenstruct": DoxygenStructDirective,
"doxygenclass": DoxygenClassDirective,
"doxygenvariable": DoxygenVariableDirective,
"doxygendefine": DoxygenDefineDirective,
"doxygenenum": DoxygenEnumDirective,
"doxygenenumvalue": DoxygenEnumValueDirective,
"doxygentypedef": DoxygenTypedefDirective,
"doxygenunion": DoxygenUnionDirective,
"doxygennamespace": DoxygenNamespaceDirective,
"doxygengroup": DoxygenGroupDirective,
"doxygenfile": DoxygenFileDirective,
"autodoxygenfile": AutoDoxygenFileDirective,
}
def __init__(self, node_factory, text_renderer, root_data_object,
renderer_factory_creator_constructor, finder_factory,
project_info_factory, filter_factory, target_handler_factory, parser_factory):
self.node_factory = node_factory
self.text_renderer = text_renderer
self.root_data_object = root_data_object
self.renderer_factory_creator_constructor = renderer_factory_creator_constructor
self.finder_factory = finder_factory
self.project_info_factory = project_info_factory
self.filter_factory = filter_factory
self.target_handler_factory = target_handler_factory
self.parser_factory = parser_factory
# TODO: This methods should be scrapped as they are only called in one place. We should just
# inline the code at the call site
def create_index_directive_container(self):
return self.create_directive_container("doxygenindex")
def create_function_directive_container(self):
# Pass text_renderer to the function directive
return DirectiveContainer(
self.directives["doxygenfunction"],
self.node_factory,
self.text_renderer,
self.root_data_object,
self.renderer_factory_creator_constructor,
self.finder_factory,
self.project_info_factory,
self.filter_factory,
self.target_handler_factory,
self.parser_factory
)
def create_struct_directive_container(self):
return self.create_directive_container("doxygenstruct")
def create_enum_directive_container(self):
return self.create_directive_container("doxygenenum")
def create_enumvalue_directive_container(self):
return self.create_directive_container("doxygenenumvalue")
def create_typedef_directive_container(self):
return self.create_directive_container("doxygentypedef")
def create_union_directive_container(self):
return self.create_directive_container("doxygenunion")
def create_class_directive_container(self):
return self.create_directive_container("doxygenclass")
def create_file_directive_container(self):
return self.create_directive_container("doxygenfile")
def create_namespace_directive_container(self):
return self.create_directive_container("doxygennamespace")
def create_group_directive_container(self):
return self.create_directive_container("doxygengroup")
def create_variable_directive_container(self):
return self.create_directive_container("doxygenvariable")
def create_define_directive_container(self):
return self.create_directive_container("doxygendefine")
def create_auto_index_directive_container(self):
return self.create_directive_container("autodoxygenindex")
def create_auto_file_directive_container(self):
return self.create_directive_container("autodoxygenfile")
def create_directive_container(self, type_):
return DirectiveContainer(
self.directives[type_],
self.root_data_object,
self.renderer_factory_creator_constructor,
self.finder_factory,
self.project_info_factory,
self.filter_factory,
self.target_handler_factory,
self.parser_factory
)
def get_config_values(self, app):
# All DirectiveContainers maintain references to this project info factory
# so we can update this to update them
self.project_info_factory.update(
app.config.breathe_projects,
app.config.breathe_default_project,
app.config.breathe_domain_by_extension,
app.config.breathe_domain_by_file_pattern,
app.config.breathe_projects_source,
app.config.breathe_build_directory
)
class NodeFactory(object):
def __init__(self, *args):
self.sources = args
def __getattr__(self, node_name):
for source in self.sources:
try:
return getattr(source, node_name)
except AttributeError:
pass
raise NodeNotFoundError(node_name)
class RootDataObject(object):
node_type = "root"
class PathHandler(object):
def __init__(self, config_directory, sep, basename, join):
self.config_directory = config_directory
self.sep = sep
self.basename = basename
self.join = join
def includes_directory(self, file_path):
# Check for backslash or forward slash as we don't know what platform we're on and sometimes
# the doxygen paths will have forward slash even on Windows.
return bool(file_path.count('\\')) or bool(file_path.count('/'))
def resolve_path(self, directory, filename):
"""Returns a full path to the filename in the given directory assuming that if the directory
path is relative, then it is relative to the conf.py directory.
"""
# os.path.join does the appropriate handling if _project_path is an absolute path
return self.join(self.config_directory, directory, filename)
def write_file(directory, filename, content):
# Check the directory exists
if not os.path.exists(directory):
os.makedirs(directory)
# Write the file with the provided contents
with open(os.path.join(directory, filename), "w") as f:
f.write(content)
class MTimerError(Exception):
pass
class MTimer(object):
def __init__(self, getmtime):
self.getmtime = getmtime
def get_mtime(self, filename):
try:
return self.getmtime(filename)
except OSError:
raise MTimerError('Cannot find file: %s' % os.path.realpath(filename))
class FileStateCache(object):
"""
Stores the modified time of the various doxygen xml files against the
reStructuredText file that they are referenced from so that we know which
reStructuredText files to rebuild if the doxygen xml is modified.
We store the information in the environment object so that it is pickled
down and stored between builds as Sphinx is designed to do.
"""
def __init__(self, mtimer, app):
self.app = app
self.mtimer = mtimer
def update(self, source_file):
if not hasattr(self.app.env, "breathe_file_state"):
self.app.env.breathe_file_state = {}
new_mtime = self.mtimer.get_mtime(source_file)
mtime, docnames = self.app.env.breathe_file_state.setdefault(
source_file, (new_mtime, set())
)
docnames.add(self.app.env.docname)
self.app.env.breathe_file_state[source_file] = (new_mtime, docnames)
def get_outdated(self, app, env, added, changed, removed):
if not hasattr(self.app.env, "breathe_file_state"):
return []
stale = []
for filename, info in self.app.env.breathe_file_state.iteritems():
old_mtime, docnames = info
if self.mtimer.get_mtime(filename) > old_mtime:
stale.extend(docnames)
return list(set(stale).difference(removed))
def purge_doc(self, app, env, docname):
if not hasattr(self.app.env, "breathe_file_state"):
return
toremove = []
for filename, info in self.app.env.breathe_file_state.iteritems():
_, docnames = info
docnames.discard(docname)
if not docnames:
toremove.append(filename)
for filename in toremove:
del self.app.env.breathe_file_state[filename]
class DomainDirectiveFactory(object):
# A mapping from node kinds to cpp domain classes and directive names.
cpp_classes = {
'class': (cpp.CPPClassObject, 'class'),
'struct': (cpp.CPPClassObject, 'class'),
'function': (cpp.CPPFunctionObject, 'function'),
'friend': (cpp.CPPFunctionObject, 'function'),
'slot': (cpp.CPPFunctionObject, 'function'),
'enum': (cpp.CPPTypeObject, 'type'),
'typedef': (cpp.CPPTypeObject, 'type'),
'union': (cpp.CPPTypeObject, 'type'),
'namespace': (cpp.CPPTypeObject, 'type'),
# Use CPPClassObject for enum values as the cpp domain doesn't have a directive for
# enum values and CPPMemberObject requires a type.
'enumvalue': (cpp.CPPClassObject, 'member'),
'define': (c.CObject, 'macro')
}
python_classes = {
'function': (python.PyModulelevel, 'function'),
'variable': (python.PyClassmember, 'attribute')
}
@staticmethod
def fix_python_signature(sig):
def_ = 'def '
if sig.startswith(def_):
sig = sig[len(def_):]
# Doxygen uses an invalid separator ('::') in Python signatures. Replace them with '.'.
return sig.replace('::', '.')
@staticmethod
def create(domain, args):
if domain == 'c':
return c.CObject(*args)
if domain == 'py':
cls, name = DomainDirectiveFactory.python_classes.get(
args[0], (python.PyClasslike, 'class'))
args[1] = [DomainDirectiveFactory.fix_python_signature(n) for n in args[1]]
else:
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:]
return cls(*args)
# Setup
# -----
def setup(app):
cache_factory = CacheFactory()
cache = cache_factory.create_cache()
path_handler = PathHandler(app.confdir, os.sep, os.path.basename, os.path.join)
mtimer = MTimer(os.path.getmtime)
file_state_cache = FileStateCache(mtimer, app)
parser_factory = DoxygenParserFactory(cache, path_handler, file_state_cache)
glob_factory = GlobFactory(fnmatch.fnmatch)
filter_factory = FilterFactory(glob_factory, path_handler)
item_finder_factory_creator = DoxygenItemFinderFactoryCreator(parser_factory, filter_factory)
index_parser = parser_factory.create_index_parser()
finder_factory = FinderFactory(index_parser, item_finder_factory_creator)
# Create a math_nodes object with a displaymath member for the displaymath
# node so that we can treat it in the same way as the nodes & addnodes
# modules in the NodeFactory
math_nodes = collections.namedtuple("MathNodes", ["displaymath"])
math_nodes.displaymath = sphinx.ext.mathbase.displaymath
node_factory = NodeFactory(docutils.nodes, sphinx.addnodes, math_nodes)
rst_content_creator = RstContentCreator(ViewList, textwrap.dedent)
renderer_factory_creator_constructor = DoxygenToRstRendererFactoryCreatorConstructor(
node_factory,
parser_factory,
DomainDirectiveFactory,
rst_content_creator
)
# Assume general build directory is the doctree directory without the last component. We strip
# off any trailing slashes so that dirname correctly drops the last part. This can be overriden
# with the breathe_build_directory config variable
build_dir = os.path.dirname(app.doctreedir.rstrip(os.sep))
project_info_factory = ProjectInfoFactory(app.srcdir, build_dir, app.confdir, fnmatch.fnmatch)
target_handler_factory = TargetHandlerFactory(node_factory)
root_data_object = RootDataObject()
text_renderer = TextRenderer(app)
directive_factory = DoxygenDirectiveFactory(
node_factory,
text_renderer,
root_data_object,
renderer_factory_creator_constructor,
finder_factory,
project_info_factory,
filter_factory,
target_handler_factory,
parser_factory
)
DoxygenFunctionDirective.app = app
app.add_directive(
"doxygenindex",
directive_factory.create_index_directive_container(),
)
app.add_directive(
"doxygenfunction",
directive_factory.create_function_directive_container(),
)
app.add_directive(
"doxygenstruct",
directive_factory.create_struct_directive_container(),
)
app.add_directive(
"doxygenenum",
directive_factory.create_enum_directive_container(),
)
app.add_directive(
"doxygenenumvalue",
directive_factory.create_enumvalue_directive_container(),
)
app.add_directive(
"doxygentypedef",
directive_factory.create_typedef_directive_container(),
)
app.add_directive(
"doxygenunion",
directive_factory.create_union_directive_container(),
)
app.add_directive(
"doxygenclass",
directive_factory.create_class_directive_container(),
)
app.add_directive(
"doxygenfile",
directive_factory.create_file_directive_container(),
)
app.add_directive(
"doxygennamespace",
directive_factory.create_namespace_directive_container(),
)
app.add_directive(
"doxygengroup",
directive_factory.create_group_directive_container(),
)
app.add_directive(
"doxygenvariable",
directive_factory.create_variable_directive_container(),
)
app.add_directive(
"doxygendefine",
directive_factory.create_define_directive_container(),
)
app.add_directive(
"autodoxygenindex",
directive_factory.create_auto_index_directive_container(),
)
app.add_directive(
"autodoxygenfile",
directive_factory.create_auto_file_directive_container(),
)
app.add_config_value("breathe_projects", {}, True)
app.add_config_value("breathe_default_project", "", True)
# Provide reasonable defaults for domain_by_extension mapping. Can be overridden by users.
app.add_config_value("breathe_domain_by_extension", {'py': 'py'}, True)
app.add_config_value("breathe_domain_by_file_pattern", {}, True)
app.add_config_value("breathe_projects_source", {}, True)
app.add_config_value("breathe_build_directory", '', True)
app.add_config_value("breathe_default_members", (), True)
app.add_config_value("breathe_implementation_filename_extensions", ['.c', '.cc', '.cpp'], True)
breathe_css = "breathe.css"
if (os.path.exists(os.path.join(app.confdir, "_static", breathe_css))):
app.add_stylesheet(breathe_css)
doxygen_handle = AutoDoxygenProcessHandle(
path_handler,
subprocess.check_call,
write_file,
project_info_factory
)
app.connect("builder-inited", doxygen_handle.generate_xml)
app.connect("builder-inited", directive_factory.get_config_values)
app.connect("builder-inited", filter_factory.get_config_values)
app.connect("env-get-outdated", file_state_cache.get_outdated)
app.connect("env-purge-doc", file_state_cache.purge_doc)