One interesting feature of Pelican, the static site generator I use for this
blog, is the internal link expansion syntax with {}
. It is documented
here. Some examples are {filename}
, {static}
and {author}
. The
purpose of the syntax is to have shorter and easier aliases to link to internal
content in the blog. For example, {filename}
can be used to link to other
files, like posts.
The idea is good, but to my needs it fell short. What I really need internal link expansions for in my blog are images, posts and code.
The idea for images is really simple. I write my posts in rst, and this is how an image is included in this format:
.. image:: path/to/image.jpg
The way I structure the files in the blog (which you can see in its
repository), is that inside the content
folder where all content is, the
sources for the posts can be found inside articles/<year>/
, and the images
can be found at images/<post_id>/
, where <post_id>
is a string that
identifies the post in which the image appears (it is the trans_id
property
of the post, derived by its filename and used to associate translations of the
same post, but I'm calling it post_id
here since it makes more sense in this
context).
From this structure, if I use a relative path from a post to one of its images,
it'd have to be something like ../../images/<post_id>/image_name.jpg
. The
{static}
expansion can be used here to simplify it a bit:
{static}/images/<post_id>/image_name.jpg
. We can do better than this though
🙂.
It'd be way better if this path could be really shortened. All images are inside
the images/
folder, so that should be implied. Heck, while we're at it,
might as well make the <post_id>/
part be derived from the current post's
id. That would make it perfect, since the only information left is the image
name, which is the unique thing about the link.
The first step in implementing this custom logic was adding the linker plugin
to pelican. It allows you to implement your own {}
link expansions through
python classes.
And the second step was implementing a {image}
expansion through this
commit. This is the commit where I updated all image links to use
{image}
. Feel the joy! It's so much neater 🙂.
Linking to posts is a little trickier, but not too bad. The idea is, I somewhat
frequently want to link to another post I wrote previously on the blog. Using
the {filename}
expansion, it's not too bad:
I've shown that `in a previous post`__.
.. __: {filename}08-task-context-en.rst
But it could certainly be better. The {filename}
expansion is relative to
the current file, so if I'm referencing a post from the same year, it's like in
the example above, but referencing one from a different year requires an
additional ../<year>/
in the path. Also, I shouldn't need to write the whole
filename. Ideally I'd only need to type what's unique to the post, that is, its
post_id
. Yes, even the language suffix (-en
or -br
) can be omitted,
since I can derive it from the current post's language.
So far so good, it could be done with a bit more logic on top of what was done
for {image}
. But since I'm already improving things, I'd also like to take
this chance to better standardize the text I use in the links. Sure, using a
text like "in a previous post" blends well with the surrounding text, but it
isn't immediately obvious that the link is to another post in my blog.
So the idea is to have a link expansion that not only maps to the right path to the post, but also automatically sets its text to have the post title. One final touch is that when setting the text I want it to also take into account the language of the post: if it's in English, the text should be in the format '"Title of the post" post' and if it's in Portuguese, 'artigo "Title of the post"'.
Updating the link's text is not something the linker plugin can do natively, so
I first needed to extend it to enable that in this commit. With the basic
mechanism in place, I actually implemented the new {article}
expansion in
this commit.
All that work pays off, as now I can show you how it looks by pointing to the
very first post I wrote on this blog through a simple {article}tasklist
:
"Creating movie and game lists using Taskwarrior" post
Of course I also changed all references to blog posts to this new amazing expansion in this commit.
This is where it gets messy... You see, it's quite common for my blog posts,
being technical, to have code blocks amidst the text. The way I include the code
is by using the include
rst directive and giving it the path to a separate
file containing the code.
The issue is, unlike the image
rst directive and rst links whose target
appears in the final HTML, which makes it simple to edit them inside pelican,
the include
directive and its path are processed by the rst reader at an
earlier stage. This means that the normal link tweaking methods in pelican can't
be used here (like the linker plugin).
I could put the code inline instead of using the include
directive and avoid
this problem altogether, but when the code is more than a few lines, I feel like
it would pollute the post source file too much to have it inline.
Well, if you've read the "Blog customizations" post you might remember that I
already have a custom rst reader. So in order to implement a link expansion for
code, I had to extend this reader to substitute any {code}
occurrence inside
an include
directive with the code file's path. That's what I did in this
commit, by copying the code from pelican's rst reader and making a few
changes.
It's worth saying that this kind of customization I'm doing (and was already doing) by overriding the rst reader is not really stable. If there are changes in pelican's rst reader, I might need to either stop updating pelican or reimplement their changes in my own custom reader. But since there isn't really a less intrusive way of implementing this, I'm willing to take the risk.
Despite the ugly changes, in the end it's all worth it when you look at the
improvements in the post sources. This is the commit where I updated them to
use the new {code}
expansion. I dare you to say it wasn't worth it (please
don't say it).
And here we are. Took some work, but after these changes, internal linking on my blog is effortless! Anything that makes writing blog posts easier is well worth it to me.