Let’s get started!
What even is a role?
First, we need the background. There’s this thing called interpreted text.
It’s a reserved bit of functionality for specially marked text.
Folks coming to reStructuredText from Markdown mostly know it as the weird
reason they have to use double backticks for
Intepreted text has all sorts of fancy potential. I mainly know it for the fact that rst links use it. Unless told otherwise, Docutils treats interpreted text as a citation.
It assumes any interpreted text is
:title-reference: — that is, it references
the title of a book, movie, song, or other publication. The
element is a perfectly reasonable choice for that.
But what if you aren’t specifically talking about a title? Roles provide an explicit label for your interpreted text.
:term: in rst? Nothing. I made it up. Seems like a good role for
when I introduce a new name and I want it to stand out.
I need to define the role to use it. Otherwise?
So up at the top of my document use the role directive to create
and register it with the parser.
Now that Docutils knows about the role, it can turn it into HTML.
It still doesn’t have any inherent meaning, but I can put some style rules on
it so that anything I label with the
:term: role shows up a little
Inline roles in your document
If I want the term to stand out a little more, I can adjust my role definition.
Now it inherits from the
:strong: role, keeping the
"term" CSS class.
You can inherit from any role. That makes it a nice way to create aliases or slight variations to existing roles.
But I want to get fancy. Let’s look at defining reStructuredText roles in Python.
Defining roles in your code
Defining a role has two main steps. Okay, three. Because first we need to import some libraries.
Now we create a function that knows what to do when given a role and some preprocessed parameters.
That’s quite a function signature to take in without context, so here’s a
breakdown of what got sent when Docutils saw my first
||the role name|
||all text input including role and markup|
||the interpreted text content|
||the interpreted text starts on this line|
||the object that called this function|
||a dictionary of customization options|
||a list of strings containing text content|
I won’t pretend I know how to use all these yet. That’s okay.
only cares about three:
options— just in case
I chose to mirror the inline directive I made earlier, creating a
with a class of
role_term expects a tuple with two node lists: one for
content, and another holding any error nodes I may need to create. In this case
the content list has my term node and the error list is empty.
With our role implementation defined, we register it and the name associated with it.
I don’t need my inline
role directive anymore, so I remove it. Registering
role_term makes it available to every document processed by this particular
Okay, now I basically know how to implement a reStructuredText role. Let’s keep going.
I link to tags on this site frequently. Since I’m the main audience for this site, it’s mostly to give me a shortcut to related content. But hey it may help you find related content to if you happen to click through.
Couple of problems with those tag links, though. First off, they look exactly like every other link in my published HTML. It would be nice for them to stand out a bit when I’m reading. Second, they look like every other link in my post source. It would be nice for them to stand out a bit when I’m writing.
So let’s make a
:tag: reference role.
It looks similar to
:term:, except because I’m referencing something I use a
reference node and give it a link to that tag’s page as
p-category class is a #microformats thing for #indieweb. I also decided to prefix my tag text with the traditional octothorpe used
to mark tags out in the wild.
Oh yes that is much nicer to read than a standard reStructuredText link.
p-category class, along with an unsurprising
reference — since
it’s a clear way to indicate the reference node I used — and a slightly
external class. Pretty sure that means “external to the document.”
Something I need rather often is a way to indicate keyboard input. Control c, stuff like that.
Well that was easy. A bit verbose, but okay. That’s not the real problem though.
There’s a perfectly good
This blog is HTML, right? Can’t I just use the
kbd element in my role?
Yes, but kind of no. It’s considered poor form to put raw HTML in your output
nodes. Docutils writes all sorts of content, and a
<kbd> would be pretty
ungainly sitting in a PDF. Ideally you’d take care of writing HTML in an HTML
Writer. Unfortunately, I have no idea how to work an HTML Writer yet.
But we can output raw HTML in a role implementation. It would be frowned on slightly less if we flagged it as a raw role.
Yeah, that works. It’s not too bad to look at while writing.
And there we go. An honest to goodness
<kbd> element. And
:raw-kbd: will be
easier to search for if and when I get around to custom HTML Writers.
Figuring out a role for keyboard input was the reason I started writing this
post — though my favorite new role is
:tag:. Anyways, I think this is a
good spot to stop writing and start editing.
Wrap it up
…pardon me while I copy those role functions back into my Neovim plugin…
Well that was fun. I wanted a role for keyboard input, and I got it. Plus, my
tags are a little easier to find in the page. And I have a
for when I’m feeling pedagogical.
Roles are just a first step in customizing Docutils output. No idea when I’ll get to the rest. You can learn more for yourself with Docutils and heavily customized publishing environments like Sphinx.
Me, I’m just having a grand time embedding this whole authoring flow in the middle of my #hugo site. May want to think about a new theme though if I’m going to continue with Hugo. Perhaps borrow from Alexander Carlton’s Hugo B-side.