Skip to content

Commit

Permalink
extension: add support for source code links
Browse files Browse the repository at this point in the history
Sphinx has sphinx.ext.viewcode and sphinx.ext.linkcode extensions to
fully incorporate source code into the documentation and to link to
externally hosted source code, respectively. They are not trivial to
integrate with Hawkmoth, however.

Add a dedicated version of linkcode that can be configured with a simple
template URI.
  • Loading branch information
jnikula committed Oct 25, 2023
1 parent 6523562 commit bc87830
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 0 deletions.
21 changes: 21 additions & 0 deletions doc/extension.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,27 @@ See also additional configuration options in the :ref:`built-in extensions
You can also pass in the compiler to use, for example
``get_include_args('gcc')``.

.. py:data:: hawkmoth_source_uri
:type: str

A template URI to source code. If set, add links to externally hosted source
code for each documented symbol, similar to the :external+sphinx:doc:`Sphinx
linkcode extension <usage/extensions/linkcode>`. Defaults to ``None``.

The template URI will be formatted using
:external+python:py:meth:`str.format`, with the following replacement fields:

``{source}``
Path to source file relative to :py:data:`hawkmoth_root`.
``{line}``
Line number in source file.

Example:

.. code-block:: python
hawkmoth_source_uri = 'https://example.org/src/{source}#L{line}'
.. py:data:: cautodoc_root
:type: str

Expand Down
46 changes: 46 additions & 0 deletions src/hawkmoth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from docutils import nodes
from docutils.parsers.rst import directives
from docutils.statemachine import ViewList
from sphinx import addnodes
from sphinx.util.nodes import nested_parse_with_titles
from sphinx.util.docutils import switch_source_input, SphinxDirective
from sphinx.util import logging
Expand Down Expand Up @@ -303,6 +304,47 @@ class CppAutoClassDirective(_AutoCompoundDirective):
_domain = 'cpp'
_docstring_types = [docstring.ClassDocstring]

def _uri_format(env, signode):
"""Generate a source URI for signode"""
uri_template = env.config.hawkmoth_source_uri

if signode.source is None or signode.line is None:
return None

source = os.path.relpath(signode.source, start=env.config.hawkmoth_root)

# Note: magic +1 to take into account we've added signode ourselves and its
# not present in source
uri = uri_template.format(source=source, line=signode.line + 1)

return uri

def _doctree_read(app, doctree):
env = app.builder.env

# Bail out early if not configured
uri_template = env.config.hawkmoth_source_uri
if uri_template is None:
return

for objnode in list(doctree.findall(addnodes.desc)):
if objnode.get('domain') not in ['c', 'cpp']:
continue

for signode in objnode:
if not isinstance(signode, addnodes.desc_signature):
continue

uri = _uri_format(env, signode)
if not uri:
continue

# Similar to sphinx.ext.linkcode
inline = nodes.inline('', '[source]', classes=['viewcode-link'])
onlynode = addnodes.only(expr='html')
onlynode += nodes.reference('', '', inline, internal=False, refuri=uri)
signode += onlynode

def _deprecate(conf, old, new, default=None):
if conf[old]:
logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -357,5 +399,9 @@ def setup(app):
# Setup transformations for compatibility.
app.setup_extension('hawkmoth.ext.transformations')

# Source code link
app.add_config_value('hawkmoth_source_uri', None, 'env', [str])
app.connect('doctree-read', _doctree_read)

return dict(version=__version__,
parallel_read_safe=True, parallel_write_safe=True)

0 comments on commit bc87830

Please sign in to comment.