including the ``m/`` directory into one of your :py:`PLUGIN_PATHS` and add
:py:`m.dox` package to your plugins in ``pelicanconf.py``. The plugin uses
Doxygen tag files to get a list of linkable symbols and you need to provide
-list of 3-tuples containing tag file path, URL prefix and list of implicitly
-prepended namespaces in :py:`M_DOX_TAGFILES` configuration to make the plugin
-work. Example configuration:
+list of tuples containing tag file path, URL prefix and an optional list of
+implicitly prepended namespaces in :py:`M_DOX_TAGFILES` configuration to make
+the plugin work. Example configuration:
.. code:: python
PLUGINS += ['m.dox']
M_DOX_TAGFILES = [
+ ('doxygen/stl.tag', 'https://en.cppreference.com/w/'),
('doxygen/corrade.tag', 'https://doc.magnum.graphics/corrade/', ['Corrade::']),
('doxygen/magnum.tag', 'https://doc.magnum.graphics/magnum/', ['Magnum::'])]
- :dox:`Link to an anchor <Interconnect::Emitter#pub-methods>`
- Flat link: :dox-flat:`plugin-management`
+It's also possible to add custom CSS classes via a fourth tuple item. For
+example, to make the links consistent with the Doxygen theme (where
+documentation links are not underscored, internal doxygen links are bold and
+external not), you could do this:
+
+.. code:: python
+
+ PLUGINS += ['m.dox']
+ M_DOX_TAGFILES = [
+ ('doxygen/stl.tag', 'https://en.cppreference.com/w/', [],
+ ['m-flat']),
+ ('doxygen/your-lib.tag', 'https://doc.your-lib.com/', ['YourLib::'],
+ ['m-flat', 'm-text', 'm-strong'])]
+
.. note-success::
If you haven't noticed yet, m.css also provides a
symbol_mapping = {}
symbol_prefixes = ['']
- for tagfile, path, prefixes in tagfiles:
- tagfile_basenames += [(os.path.splitext(os.path.basename(tagfile))[0], path)]
+ for f in tagfiles:
+ tagfile, path = f[:2]
+ prefixes = f[2] if len(f) > 2 else []
+ css_classes = f[3] if len(f) > 3 else []
+
+ tagfile_basenames += [(os.path.splitext(os.path.basename(tagfile))[0], path, css_classes)]
symbol_prefixes += prefixes
tree = ET.parse(tagfile)
# Linking to pages
if child.attrib['kind'] == 'page':
link = path + child.find('filename').text + '.html'
- symbol_mapping[child.find('name').text] = (child.find('title').text, link)
+ symbol_mapping[child.find('name').text] = (child.find('title').text, link, css_classes)
# Linking to files
if child.attrib['kind'] == 'file':
link = path + child.find('filename').text + ".html"
- symbol_mapping[child.find('path').text + child.find('name').text] = (None, link)
+ symbol_mapping[child.find('path').text + child.find('name').text] = (None, link, css_classes)
for member in child.findall('member'):
if not 'kind' in member.attrib: continue
# Preprocessor defines and macros
if member.attrib['kind'] == 'define':
- symbol_mapping[member.find('name').text + ('()' if member.find('arglist').text else '')] = (None, link + '#' + member.find('anchor').text)
+ symbol_mapping[member.find('name').text + ('()' if member.find('arglist').text else '')] = (None, link + '#' + member.find('anchor').text, css_classes)
# Linking to namespaces, structs and classes
if child.attrib['kind'] in ['class', 'struct', 'namespace']:
name = child.find('name').text
link = path + child.findtext('filename') # <filename> can be empty (cppreference tag file)
- symbol_mapping[name] = (None, link)
+ symbol_mapping[name] = (None, link, css_classes)
for member in child.findall('member'):
if not 'kind' in member.attrib: continue
# Typedefs, constants
if member.attrib['kind'] == 'typedef' or member.attrib['kind'] == 'enumvalue':
- symbol_mapping[name + '::' + member.find('name').text] = (None, link + '#' + member.find('anchor').text)
+ symbol_mapping[name + '::' + member.find('name').text] = (None, link + '#' + member.find('anchor').text, css_classes)
# Functions
if member.attrib['kind'] == 'function':
# <filename> can be empty (cppreference tag file)
- symbol_mapping[name + '::' + member.find('name').text + "()"] = (None, link + '#' + member.findtext('anchor'))
+ symbol_mapping[name + '::' + member.find('name').text + "()"] = (None, link + '#' + member.findtext('anchor'), css_classes)
# Enums with values
if member.attrib['kind'] == 'enumeration':
enumeration = name + '::' + member.find('name').text
- symbol_mapping[enumeration] = (None, link + '#' + member.find('anchor').text)
+ symbol_mapping[enumeration] = (None, link + '#' + member.find('anchor').text, css_classes)
for value in member.findall('enumvalue'):
- symbol_mapping[enumeration + '::' + value.text] = (None, link + '#' + value.attrib['anchor'])
+ symbol_mapping[enumeration + '::' + value.text] = (None, link + '#' + value.attrib['anchor'], css_classes)
# Sections
for section in child.findall('docanchor'):
- symbol_mapping[section.text] = (section.attrib.get('title', ''), link + '#' + section.text)
+ symbol_mapping[section.text] = (section.attrib.get('title', ''), link + '#' + section.text, css_classes)
def dox(name, rawtext, text, lineno, inliner: Inliner, options={}, content=[]):
title, target, hash = parse_link(text)
- set_classes(options)
+ # Otherwise adding classes to the options behaves globally (uh?)
+ _options = dict(options)
+ set_classes(_options)
+ # Avoid assert on adding to undefined member later
+ if 'classes' not in _options: _options['classes'] = []
# Try linking to the whole docs first
- for basename, url in tagfile_basenames:
+ for basename, url, css_classes in tagfile_basenames:
if basename == target:
if not title:
# TODO: extract title from index page in the tagfile
logger.warning("Link to main page `{}` requires a title".format(target))
title = target
- node = nodes.reference(rawtext, title, refuri=url + hash, **options)
+ _options['classes'] += css_classes
+ node = nodes.reference(rawtext, title, refuri=url + hash, **_options)
return [node], []
for prefix in symbol_prefixes:
if prefix + target in symbol_mapping:
- link_title, url = symbol_mapping[prefix + target]
+ link_title, url, css_classes = symbol_mapping[prefix + target]
if title:
use_title = title
elif link_title:
logger.warning("Doxygen anchor `{}` has no title, using its ID as link title".format(target))
use_title = target
- node = nodes.reference(rawtext, use_title, refuri=url + hash, **options)
+
+ _options['classes'] += css_classes
+ node = nodes.reference(rawtext, use_title, refuri=url + hash, **_options)
return [node], []
# TODO: print file and line
#prb = inliner.problematic(rawtext, rawtext, msg)
if title:
logger.warning("Doxygen symbol `{}` not found, rendering just link title".format(target))
- node = nodes.inline(rawtext, title, **options)
+ node = nodes.inline(rawtext, title, **_options)
else:
logger.warning("Doxygen symbol `{}` not found, rendering as monospace".format(target))
- node = nodes.literal(rawtext, target, **options)
+ node = nodes.literal(rawtext, target, **_options)
return [node], []
def register():
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>m.dox | A Pelican Blog</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+ <link rel="stylesheet" href="static/m-dark.css" />
+ <link rel="canonical" href="page.html" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+ <div class="m-container">
+ <div class="m-row">
+ <a href="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</a>
+ </div>
+ </div>
+</nav></header>
+<main>
+<article>
+ <div class="m-container m-container-inflatable">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <h1>m.dox</h1>
+<!-- content -->
+<ul>
+<li>Function link: <a class="m-flat m-text m-strong" href="https://doc.magnum.graphics/corrade/namespaceCorrade_1_1Utility_1_1Directory.html#ad80859f373fbf1ed39b11eb27649c34b">Utility::Directory::mkpath()</a></li>
+<li>Class link: <a class="m-flat m-text m-strong" href="https://doc.magnum.graphics/corrade/classCorrade_1_1Interconnect_1_1Emitter.html">Interconnect::Emitter</a></li>
+<li>Page link: <a class="m-flat m-text m-strong" href="https://doc.magnum.graphics/corrade/building-corrade.html">Downloading and building</a></li>
+<li><a class="m-flat m-text m-strong" href="https://doc.magnum.graphics/corrade/testsuite.html">Custom link title</a></li>
+<li><a class="m-flat m-text m-strong" href="https://doc.magnum.graphics/corrade/corrade-cmake.html">Page link with custom title</a></li>
+<li><a class="m-flat m-text m-strong" href="https://doc.magnum.graphics/corrade/">Link to index page</a></li>
+<li><a class="m-flat m-text m-strong" href="https://doc.magnum.graphics/corrade/classCorrade_1_1TestSuite_1_1Tester.html#TestSuite-Tester-command-line">Link to class documentation section</a></li>
+<li><a class="m-flat m-text m-strong" href="https://doc.magnum.graphics/corrade/#search">Link to index page with hash after</a></li>
+<li><a class="m-flat m-text m-strong" href="https://doc.magnum.graphics/corrade/corrade-cmake.html#search">Link to page with hash after</a></li>
+<li><a class="m-flat m-text m-strong" href="https://doc.magnum.graphics/corrade/namespaceCorrade_1_1Utility_1_1Directory.html?q=hello#search">Link to class with query and hash after</a></li>
+<li>Flat link: <a class="m-flat m-text m-strong" href="https://doc.magnum.graphics/corrade/plugin-management.html">Plugin management tutorial</a></li>
+</ul>
+<p>These should produce warnings:</p>
+<ul>
+<li>Link to nonexistent name will be rendered as code: <code>nonExistent()</code></li>
+<li><span>Link to nonexistent name with custom title will be just text</span></li>
+<li>Link to a section that doesn't have a title will keep the ID (this <em>may</em>
+break on tagfile update, watch out): <a class="m-flat m-text m-strong" href="https://doc.magnum.graphics/corrade/corrade-cmake.html#corrade-cmake-add-test">corrade-cmake-add-test</a></li>
+<li>Link to index page without title will have the tag file basename:
+<a class="m-flat m-text m-strong" href="https://doc.magnum.graphics/corrade/">corrade</a></li>
+</ul>
+<!-- /content -->
+ </div>
+ </div>
+ </div>
+</article>
+</main>
+</body>
+</html>