147 lines
4.5 KiB
Python
147 lines
4.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
doxylink
|
|
~~~~~~~~
|
|
|
|
Sphinx extension to link to external Doxygen API documentation.
|
|
|
|
It works much like the extlinks extension but it does some more processing to link C++ symbols against their Doxygen HTML documentation.
|
|
|
|
.. confval:: doxylink
|
|
|
|
The environment is set up with a dictionary mapping the interpereted text role
|
|
to a tuple of tag file and prefix:
|
|
|
|
.. code-block:: python
|
|
|
|
doxylink = {
|
|
'polyvox' : ('/home/matt/PolyVox.tag', '/home/matt/PolyVox/html/'),
|
|
'qtogre' : ('/home/matt/QtOgre.tag', '/home/matt/QtOgre/html/'),
|
|
}
|
|
|
|
This allows one to do:
|
|
|
|
.. code-block:: rst
|
|
|
|
:polyvox:`Array <PolyVox::Array>`.
|
|
:polyvox:`PolyVox::Volume`
|
|
:qtogre:`QtOgre::Log`
|
|
:polyvox:`tidyUpMemeory(int) <tidyUpMemory>`
|
|
:polyvox:`PolyVox::Array::operator[]`
|
|
|
|
:requires: Python 2.5
|
|
|
|
.. todo::
|
|
|
|
Make the extension atuomatically re-run if the tag file is altered on disc
|
|
|
|
Find a way to parse the tag file to a DOM tree *once* and then just reference it from then on.
|
|
|
|
:copyright: Copyright 2010 by Matt Williams
|
|
:license: BSD, see LICENSE for details.
|
|
"""
|
|
|
|
from docutils import nodes, utils
|
|
|
|
from sphinx.util.nodes import split_explicit_title
|
|
|
|
import xml.etree.ElementTree as ET
|
|
|
|
def find_url(doc, symbol):
|
|
"""
|
|
Return the URL for a given symbol.
|
|
|
|
This is where the magic happens.
|
|
This function could be a lot more clever. At present it required the passed symbol to be almost exactly the same as the entries in the Doxygen tag file.
|
|
|
|
.. todo::
|
|
|
|
Maybe print a list of all possible matches as a warning (but still only return the first)
|
|
|
|
:Parameters:
|
|
doc : xml.etree.ElementTree
|
|
The XML DOM object
|
|
symbol : string
|
|
The symbol to lookup in the file. E.g. something like 'PolyVox::Array' or 'tidyUpMemory'
|
|
|
|
:return: String representing the filename part of the URL
|
|
"""
|
|
|
|
#First check for an exact match with a top-level object (namespaces, objects etc.)
|
|
|
|
#env = inliner.document.settings.env
|
|
|
|
matches = []
|
|
for compound in doc.findall('.//compound'):
|
|
if compound.find('name').text == symbol:
|
|
matches += [compound.find('filename').text]
|
|
|
|
if len(matches) > 1:
|
|
pass
|
|
#env.warn(env.docname, 'There were multiple matches for `%s`: %s' % (symbol, matches))
|
|
if len(matches) == 1:
|
|
return matches[0]
|
|
|
|
|
|
#Strip off first namespace bit of the compound name so that 'ArraySizes' can match 'PolyVox::ArraySizes'
|
|
for compound in doc.findall('.//compound'):
|
|
symbol_list = compound.find('name').text.split('::', 1)
|
|
if len(symbol_list) == 2:
|
|
reducedsymbol = symbol_list[1]
|
|
if reducedsymbol == symbol:
|
|
return compound.find('filename').text
|
|
|
|
#Now split the symbol by '::'. Find an exact match for the first part and then a member match for the second
|
|
#So PolyVox::Array::operator[] becomes like {namespace: "PolyVox::Array", endsymbol: "operator[]"}
|
|
symbol_list = symbol.rsplit('::', 1)
|
|
if len(symbol_list) == 2:
|
|
namespace = symbol_list[0]
|
|
endsymbol = symbol_list[1]
|
|
for compound in doc.findall('.//compound'):
|
|
if compound.find('name').text == namespace:
|
|
for member in compound.findall('member'):
|
|
# #If this compound object contains the matching member then return it
|
|
if member.find('name').text == endsymbol:
|
|
return member.find('anchorfile').text + '#' + member.find('anchor').text
|
|
|
|
#Then we'll look at unqualified members
|
|
for member in doc.findall('.//member'):
|
|
if member.find('name').text == symbol:
|
|
return member.find('anchorfile').text + '#' + member.find('anchor').text
|
|
|
|
return None
|
|
|
|
def create_role(app, tag_filename, rootdir):
|
|
def find_doxygen_link(name, rawtext, text, lineno, inliner, options={}, content=[]):
|
|
text = utils.unescape(text)
|
|
# from :name:`title <part>`
|
|
has_explicit_title, title, part = split_explicit_title(text)
|
|
|
|
try:
|
|
tag_file = ET.parse(tag_filename)
|
|
except (IOError):
|
|
env = inliner.document.settings.env
|
|
env.warn(env.docname, 'Could not open %s' % tag_filename)
|
|
else:
|
|
url = find_url(tag_file, part)
|
|
if url:
|
|
full_url = rootdir + url
|
|
pnode = nodes.reference(title, title, internal=False, refuri=full_url)
|
|
return [pnode], []
|
|
#By here, no match was found
|
|
env = app.env
|
|
env.warn(env.docname, 'Could not find match for `%s` in `%s` tag file' % (part, tag_filename), lineno)
|
|
|
|
pnode = nodes.inline(rawsource=title, text=title)
|
|
return [pnode], []
|
|
|
|
return find_doxygen_link
|
|
|
|
def setup_doxylink_roles(app):
|
|
for name, [tag_filename, rootdir] in app.config.doxylink.iteritems():
|
|
app.add_role(name, create_role(app, tag_filename, rootdir))
|
|
|
|
def setup(app):
|
|
app.add_config_value('doxylink', {}, 'env')
|
|
app.connect('builder-inited', setup_doxylink_roles)
|