chiark / gitweb /
m.dox: support adding custom classes to the links.
authorVladimír Vondruš <mosra@centrum.cz>
Sun, 4 Nov 2018 11:31:22 +0000 (12:31 +0100)
committerVladimír Vondruš <mosra@centrum.cz>
Sun, 4 Nov 2018 12:00:03 +0000 (13:00 +0100)
doc/plugins/links.rst
pelican-plugins/m/dox.py
pelican-plugins/m/test/dox/page_css_classes.html [new file with mode: 0644]
pelican-plugins/m/test/test_dox.py

index 3f124408ffcf38332e72fa193046d654109a4f8e..77de9354bdc2960dc3cb2189c573e651169283ef 100644 (file)
@@ -236,14 +236,15 @@ Download the `m/dox.py <{filename}/plugins.rst>`_ file, put it
 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::'])]
 
@@ -293,6 +294,20 @@ and adding the :rst:`:class:` option.
     -   :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
index 00427d5425195a1ba95b0a53acd2b3740fa942ee..7d74208d289492812240f2f5aa986276e659a556 100644 (file)
@@ -61,8 +61,12 @@ def init(pelicanobj):
     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)
@@ -72,68 +76,73 @@ def init(pelicanobj):
                 # 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:
@@ -143,7 +152,9 @@ def dox(name, rawtext, text, lineno, inliner: Inliner, options={}, content=[]):
                     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
@@ -152,10 +163,10 @@ def dox(name, rawtext, text, lineno, inliner: Inliner, options={}, content=[]):
     #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():
diff --git a/pelican-plugins/m/test/dox/page_css_classes.html b/pelican-plugins/m/test/dox/page_css_classes.html
new file mode 100644 (file)
index 0000000..f6aa3de
--- /dev/null
@@ -0,0 +1,55 @@
+<!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>
index b6c7d56ef83e2027fdbc6239280611c8727169f0..5820abaa250b08ba14db2a80d96737678f638672 100644 (file)
@@ -36,3 +36,12 @@ class Dox(PluginTestCase):
         })
 
         self.assertEqual(*self.actual_expected_contents('page.html'))
+
+    def test_css_classes(self):
+        self.run_pelican({
+            'PLUGINS': ['m.htmlsanity', 'm.dox'],
+            'M_DOX_TAGFILES': [
+                ('../doc/doxygen/corrade.tag', 'https://doc.magnum.graphics/corrade/', ['Corrade::'], ['m-flat', 'm-text', 'm-strong'])]
+        })
+
+        self.assertEqual(*self.actual_expected_contents('page.html', 'page_css_classes.html'))