Published on

Streamlining blogging using neovim

Authors
  • avatar
    Name
    Luke Wood
    Twitter

Streamlining blogging using neovim

Recently, I've gotten into using neovim & other unix utilities to minimize friction across all of the things I regularly do. One thing I've always wanted to more of, but have always been to lazy to actually do, is blogging! In particular, I found that the friction of creating a new blog post was high enough to prevent me from wanting to do it. Often times I only have a few paragraphs that I'd like to write up, and it just doesn't feel worth it spending 10 minutes creating a new post.

Today I finally decided to remove this friction by writing a custom neovim plugin. The goal of this plugin is to allow me to switch from coding to blog writing in an instant, with absolutely zero friction. My hope is that this will make the cost of writing a blog post low enough that I do it much more often. At the time of writing, I haven't published a blog post in almost 2 years - ouch.

Neovim plugin overview

I'll spare you the details, but neovim plugins can mostly be thought of as single files of lua that modify your neovim environment in some way. In this case, I think it would be really nice to be able to run :Blog, enter a title, and be able to immediately start writing. By doing this all from my editor hopefully I can prevent myself from exiting what programmers often call "flow state" and be able to immediately jot down whatever it is that I'm hoping to share.

So, here's how I decided to tackle this. First, I created a file call blog.lua:

# blog.lua
local M = {}

I know my filesystem structure, so I just hard coded a target directory for these posts:

local function get_root()
  local home = os.getenv('HOME')
  return home .. '/workspace/lukewood.github.io/data/blog'
end

Next, I needed a way to go from "Title" to "filename":

local function convert_title_to_filename(title)
  local filename = title:lower()
  filename = filename:gsub('[^%w%s]', '')
  filename = filename:gsub('%s+', '-')
  filename = filename:gsub('%-+', '-')
  filename = filename:gsub('^%-+', ''):gsub('%-+$', '')
  return filename .. '.mdx'
end

I strung together some neovim commands to read input after the command is run, create a buffer with the correct filepath assigned to it, template out my blog post, and focus the buffer:

function M.create_file_with_template()
  local root_dir = get_root()

  local title = vim.fn.input('Enter Title: ')
  local filename = convert_title_to_filename(title)

  if title == '' then
    print('No filename provided. Aborting.')
    return
  end

  local full_path = root_dir .. '/' .. filename

  vim.cmd('enew')
  vim.api.nvim_buf_set_name(0, full_path)

  local current_date = os.date("%Y-%m-%d")
  local template = {
    "---",
    "title: " .. title,
    "date: " .. current_date,
    "tags: []",
    "summary: TODO",
    "---",
    "# " .. title,
  }
  vim.api.nvim_buf_set_lines(0, 0, -1, false, template)
end

and finally I bound it to a neovim command:

vim.api.nvim_create_user_command('Blog', M.create_file_with_template, {})

and voila - that's the entire plugin.

Demo

Here's the plugin in action:

Remaining friction

I'm pretty happy with this workflow for blogging - in fact without the plugin I never would've taken the time to write this post. One thing I really wanted to show off was how this all looks in the terminal. I used asciinema, which is nice - but I'd really like to be able to easily attach screen recordings to my blog posts. Unfortunately, recording, compressing and hosting videos is pretty annoying.

Looks like I'll have to tackle that another day...