quick update: writing tools

Published on 30 Dec 2018

My daily driver for writing in markdown is Vim. That’s how I usually write updates on my blogs. That’s also how I prepare document for Caspershire Atlas (converted from markdown to dokuwiki format, thanks to pandoc).

The only thing about writing markdown in terminal is that I can’t really see the preview of the product. With either Atom or VS Code, previewing document is easier because they both have built-in basic markdown viewer. So, can I come up with a solution?

I could, and I did.

Enter, a modular setup that depends on pandoc, entr, and browser-sync.

brief introduction

But why? Why not just use VS Code or Atom or Sublime Text?

Well, why not. I like experimenting, and that’s how I ended up in grad school (spoiler alert: not half as fun as you would think).

So, here is the idea. Whenever changes being made on a markdown file, the program entr detects it, and tells pandoc to compile it into a HTML file, styled according a CSS, and hardcoded the output into an index.html.

The second part is the browser-sync, which is a HTTP server, spins a local HTTP server (localhost, port 9000) and live-reloads the browser every time it sees changes in the index.html file.

The pandoc part was adapted from my previous attempt with pandoc. The CSS file I am currently using is also available here (the github.css). I learned how to use live-reload from a Medium blog post written by Scott Vinkle.


sudo apt install pandoc
sudo apt install entr
sudo npm -g install browser-sync

I know some people are bothered by the sudo npm and they prefer to not use it with sudo. Fun fact: I am too lazy to make npm invocation sudo-less.

the initial setup: bash aliases

Since the pandoc and the browser-sync commands can be run single-line-ly, the simplest implementation would be shell aliases, which makes invoking commands less of a marathon for my fingers.

Because my shell session runs on fish, here is how the aliases look like:

# run browser-sync with reload command
alias reload='browser-sync start --server --files index.html --no-notify --no-open --port 9000'

# run pandoc with pen command
function pen
  find . | entr pandoc $argv[1] --smart --self-contained --css=/home/aixnr/.pandoc/github.css --highlight-style=haddock --output="index.html"

Note that if the shell session runs on bash (so vanilla) or zsh (a step towards being a rebel), the function declaration is going to look a little different.

Also note that I put the github.css inside the directory ~/.pandoc/github.css, where ~/ is the shortform for /home/aixnr, where aixnr is the username. I heard that it is always a good thing to hard-link things in script.

It works. Sort of.

I now can open a terminal session, run tmux first, then invoke pen document-name.md on one pane, then reload on another pane. Oh, before I decided to go with reload and pen aliases, I ran a quick check to make sure no available binaries with the same name. This can be easily done by running which pen or which reload. If terminal returns nothing, then binaries with the aforementioned names do not exist.

This setup works, except, too many steps involved.

final product

terminal output

Of course, a bash script. I call this panda.sh, because I was reminded by Azusagawa Kaede.


# ------------------------------------
# A dead simple bash script by @aixnr
# Run 2 programs in parallel
#   1) pandoc with entr
#   2) browser-sync for live-reload
# ------------------------------------

find . | entr pandoc "$1" --smart \
        --self-contained \
        --css=/home/aixnr/.pandoc/github.css \
        --highlight-style=haddock \
        --output="index.html" &

browser-sync start --server \
        --files index.html \
        --no-notify \
        --no-open \
        --port 9000

Now, I can just run ./panda.sh document-name.md and it works!

browser proof

By the way, having more LOC (lines of code) is not necessarily bad. In fact, it is better to make it much more readable.

And, without a doubt, the next logical step was to put panda.sh in $PATH for global invocation. You don’t want to cast a magic spell that only works in a specific locale. You want to make it global!

extra note

I tried inotifywait. I personally think that entr is simpler, but I am open to learn how to properly unleash the power of inotify-tools in the near future. It looks much more programmable to me.

Let me know on Twitter (@aixnr) if there is actually a much more obvious way to do this. Nevertheless, I am quite happy with the result.