Writing Blog Posts in Vimwiki
Thu Apr 23, 2020

For a while I've disliked the workflow I have for writing blog posts for this site. To create a new post it requires making a file in the posts directory of my blog, writing front matter, and then writing the post. This works great (and is the standard for static site generators for a reason) but I find it's not immediate enough for my needs.

I decided to try and improve this using vimwiki, as I already use it for my todo lists, note taking and other personal organisation tasks. It has a built in diary, which is a simple way to make small documents with a date and title. Sounds perfect for blog posts! It has some site generating functions of it's own, but I use some features of my current site generator (Zola) that can't be recreated using them.

I decided that I wanted every vimwiki diary entry that included the :blogpost tag to be seen as a blog post and bought into zola, and I did this with a python script. This requires your vimwiki to be using markdown syntax.

This script has 2 main parts: reading tags, and generating front matter/copying files. It's fairly trivial, but I hope there's some useful information here.

Reading Vimwiki Tags

The first step in creating this script is reading vimwiki's tags. I want to use these as the tags in the blog post, as well as to identify which entries should be published to the blog. In a file, vimwiki tags look like this:

:tag1:tag2:tag3

It would be fairly simple to just search the diary for these, but vimwiki has a tag index (created with :VimwikiRebuildTags). May as well use it. The file is called .vimwiki_tags in the vimwiki base directory. This is a ctags file, and could be parsed by a real ctags parser. For our needs, We can just do it simple string tools.

import os
VIMWIKI_BASE="/home/nihilazo/vimwiki"

tags = {}
with open(os.path.join(VIMWIKI_BASE, ".vimwiki_tags"),"r") as tagfile:
    for line in tagfile.readlines():
        if not line.startswith("!"):
            s = line.split("\t")
            if s[1] in tags:
                tags[s[1]].append(s[0])
            else:
                tags[s[1]] = [s[0]]

posts = {k: v for k, v in tags.items() if 'blogpost' in v}

This creates a dictionary in the form:

{"filename1":["tag1","tag2","tag3"],
 "filename2":["tag2","tag4","tag3"]}

for all our posts.

This simplifies the information (getting rid of the location of the tag in the file, for example) but it's good enough for what this script needs to do.

Generating front-matter and writing posts

Now that we have our tags, we can generate front-matter and write posts. Zola uses the TOML language for front matter. Other site generators use YAML, but this could be easily adapted. Like before, we could use an actual reader/writer to do this, but I'm just using string functions.

# Remove the replace if you use the .md extention for your wiki.
POSTS_DIR="/home/nihilazo/website/blog/content/posts"
for f, tags in posts.items():
    sf = f.split("/")[-1:][0] # just the last part of the file path
    with open(os.path.join(VIMWIKI_BASE, f),"r") as i:
        l = i.readlines()
        title = l[0][1:].strip()   # works because the first line of a diary entry is "# Title"
        date = sf.split(".")[0]   # works because the file is named the date it was made
        content = "".join(l[2:]) # works because this skips the title and tags
        with open(os.path.join(POSTS_DIR, sf.replace(".wiki",".md")), "w+") as o:
            o.write(
f"""+++
title=\"{title}\"
date={date}
[taxonomies]
tags=[""".strip())
            for tag in tags:
                if tag != "blogpost":
                    o.write(f"\"{tag}\",")
            o.write("]\n+++\n")
            o.write(content)

The title and tags are written using f-strings, to avoid having to use an actual library for these front matter formats. And... that's it!

Future improvements

This script works but has a lot of problems. These would all be ideal improvements:

I hope this contained at least something that made it worth your time. If you have any suggestions, leave a comment!



photos · music · code · posts · notes · about · home