Cross References in Notes
My notes are continuous struggle. Because I render my notes with Hugo,
which is a fabulous static site generator, I kept links in Hugo-friendly
form: [foo]({{</* ref "bar.md" */>}})
. Unfortunately this is only understood by
Hugo and sometimes I want to read and navigate notes from my phone. But fear
no more - I fixed it!
I create Makefiles for almost all of my projects. They’re great: they provide easy to remember facade to complex tasks and have shell completion for their targets, so choosing what to run usually boils down to a little tabbing through the list. How does it relates to having and not having Hugo-like references? What should we do to have our cake and eat it? Add a little preprocessing in Makefile of course!
First of all I needed to convert back all of my Hugo-like refs to ordinary
markdown links. I used some magic sed for it which worked for ~95% of all
original refs the rest I fixed manually. It was one time thing and I don’t
remember the exact regular expression unfortunately, but it was something
like sed -i -E -e 's/{{</* ref "(.+.md)" */>}}/\1/g'
. Unhandled cases?
relref
shortcode (obviously, I just HAD to use it at least once somewhere)
and several refs in a single line (.+
is a greedy match).
Next, I wanted Hugo to understand that any links to .md files are in fact cross-references, which means that we must somehow replace bare file name with a shortcode, without changing – without even touching a working copy (because who likes when his text editor screams at you that file has changed when it hasn’t).
First I thought that I’ll be able to use these fancy pipelines which Hugo uses to pre-process assets, but apparently they’re special snowflakes, specialized for minifying Javascript and compiling SASS to CSS. Not good. So I decided to do the simplest thing ever: copy the whole content aside, run sed through it and use it to generate my site.
PUBDIR := /var/www/docs
CONTENT_TMP := /tmp/docs_content_tmp
build:
@rm -rf "$(PUBDIR)"
@mkdir -p "$(PUBDIR)"
@rm -rf "$(CONTENT_TMP)"
cp -r content "$(CONTENT_TMP)"
sed -i -E\
-e 's,]\(([^).]+.md)\),]({{</* ref "\1" */>}}),g' \
-e 's,]\((files/[^)]+)\),]({{</* file "\1" */>}}),g' \
`find "$(CONTENT_TMP)" -name "*.md" -type f`
hugo -d "$(PUBDIR)" -c "$(CONTENT_TMP)"
@rm -rf "$(CONTENT_TMP)"
.PHONY: build
I hate that sed, but it does the job and probably it’s not the worst one I’ve
ever written. And hey, it even works for several links in a single line,
which wasn’t that trivial (see that [^)]
block? That’s a hack for catching
all unicode characters in a non-greedy way, which is generally unsupported by
BRE and ERE used by GNU tools. Or it depends on locale. Or something else
obscure enough that I don’t want to think about it).
Second -e
script additionally detects file attachments, which are stored
inside each repo in files directory, and wraps them with a custom {{</* file */>}}
shortcode which handles conversions to absolute paths. So for
example image ![foo](files/bar.jpg)
gets converted to ![foo]({{</*
"files/bar.jpg" */>}})
, which in turn is generated to <img
src="http://localhost/docs/0/files/bar.jpg" />
.
Shortcode implementation is one-liner:
{{-/* printf "%s/%s/%s" .Site.BaseURL .Page.Section (.Get 0) */-}}
I like this approach a lot because finally content of my notes is independent from any external system/renderer. And now they work rather nicely with Markor.
One thing which I’ll for sure improve in future: I won’t blindly copy the whole content directory, but only Markdown files. All other files (“attachments”) will be symlinked. It should save a little disk space and a little time spent on unnecessary copying.