Well, recently I started using Obsidian (a Markdown-based personal wiki). Since my writing is mostly code-related, it includes a lot of code-snippets. As I like having my code neatly formatted, and hate formatting it by hand, I wanted a tool to do that for me. The best tool I found was mdformat. It’s Python based, formats Python, Rust, and Go code snippets, and even formats the Markdown itself.
The only problem being: it formats a lovely wiki
\[\[link\]\], breaking it in the process.
To mitigate that, I had to add wikilink support2.
If you just want to see the code, go to mdformat-wikilink.
There are 2 types of mdformat plugins:
- Code formatter plugins, used for formatting the code inside fenced blocks;
- Parser extension plugins, used to add support for new nodes.
Since we’re adding support for a new type of syntax, a wikilink, we’ll be writing a parser extension plugin. To do so, we’ll follow the mdformat guide for developing plugins.
That said, our mdformat plugin will only do the rendering. For parsing, we need to write a markdown-it-py plugin.
After a bit of reading, experimenting, and finding out - it seems that we only need very little code to make things work. We can do something more complex, but for our needs (ensuring mdformat doesn’t modify wikilinks) we can hack something quick. We’re going to create a new parser token for wikilinks, and make mdformat render it as-is.
The code will consist of 3 parts:
- markdown-it-py plugin, to parse the wikilinks as a new token
- mdformat plugin, to use the previous plugin, and render the new token as-is
- A bit of
pyproject.tomlconfig to make
mdformatrecognize the plugin.
Since we’re only parsing wikilinks to keep them unmodified, we’re not going to break them up into parts. Instead, we’ll just keep them as a block of text. This means that we can write a simplistic parser that does the following:
- Using regex, we check whether the string currently fed to the parser is a link
- If it isn’t, we do nothing and report that it isn’t.
- If it is, we push a
wikilinktoken with the entire link as its content, and increment the parser position part the link.
The last (and most important) part of the code is registering the parser we just wrote. We register it as an “inline” rule (as it is an inline element, not a block), and we register it last, as it doesn’t replace any other elements (well, plain text…).
Our mdformat plugin is even more simplistic.
update_mditfunction is used to load our wikilink-parsing plugin into the current instance of markdown-it-py
_render_wikilinkfunction returns the content of the wikilink token (or node, in mdformat terminology) that we pushed
The last thing we need to do is register the right entry-point for our plugin, so that mdformat will know to load and use it.
We can do it in our
pyproject.toml file (I’m using Poetry, other tools have similar options).
And with that, we’re done.
You can see the whole project at mdformat-wikilink.