Reputation: 2543
I'm trying to extend Sphinx to have a new inline role called newcontrib
. What it should do is take its content and (1) add some extra text to it, (2) make the text bold in the rendered output, and (3) make the text a link, whose target is the original content of the role. Example:
:newcontrib:`Jane Doe`
Should end up being parsed as if it were
`**new contributor Jane Doe** <Jane Doe_>`_
The target is defined in another file (names.inc
) and looks like:
.. _Jane Doe: https://jane-does-website.com
My attempt so far (based loosely on this example) is this:
from docutils.nodes import reference, strong, target
from sphinx.addnodes import pending_xref
def newcontrib_role(name, rawtext, text, lineno, inliner, options={},
content=[]):
"""Create a role to highlight new contributors in changelog entries."""
newcontrib = f'new contributor {text}'
targ = f' <{text}_>'
rawtext = f'`{newcontrib}{targ}`_'
options.update(name=newcontrib, refname=text.lower())
strong_node = strong(rawtext, newcontrib)
node = reference('', '', strong_node, **options)
return [node], []
def setup(app):
app.add_role('newcontrib', newcontrib_role)
return
I've also tried using pending_xref()
instead of reference()
, and tried explicitly making a target
node and adding it as a sibling. Results vary depending on exactly what I try; sometimes I get an error during build (missing attribute refname, refdomain, reftype...), other times I get a successful build but the link goes to url-of-current-page#jane-doe
. I also briefly tried subclassing XRefRole
instead of defining the role function... open to any approach.
Upvotes: 2
Views: 440
Reputation: 2543
What ended up working was this:
def newcontrib_role(name, rawtext, text, lineno, inliner, options={},
content=[]):
"""Create a role to highlight new contributors in changelog entries."""
newcontrib = f'new contributor {text}'
alias_text = f' <{text}_>'
rawtext = f'`{newcontrib}{alias_text}`_'
refname = text.lower()
strong_node = strong(rawtext, newcontrib)
target_node = target(alias_text, refname=refname, names=[newcontrib])
target_node.indirect_reference_name = text
options.update(name=newcontrib, refname=refname)
ref_node = reference('', '', strong_node, **options)
ref_node[0].rawsource = rawtext
inliner.document.note_indirect_target(target_node)
inliner.document.note_refname(ref_node)
return [ref_node, target_node], []
(plus the setup(app)
function as in the question)
in particular, the key steps I hadn't tried before were inliner.document.note_indirect_target
and inliner.document.note_refname
. I got the idea from looking at how docutils parses links.
Upvotes: 2