Adding a Wiki to the Site
Some things on the blog are supposed to have a longer shelf-life. But the nature of a blog is to present things in a timeline.
I employ cross-links from older posts to newer posts to encourage exploration with the introduction of the “linked posts” part at the bottom of each post. And I have a structured overview to help with discovery. Even then I branched out into other topical pages, like the TextKit overview, or the even larger FastSpring/Distribute outside the MAS page. To make sense of the timeline, I introduce what’s basically a ‘garden’ to my ‘stream’. It’s not a new idea, but I find not having these overview pages to hamper my writing. Some things need systematic overviews, and I enjoy making these, but there’s no good place for them.
After Denis Defreyne, maker of the static site generator nanoc I use, mentioned in passing that he’s experimenting with wiki links in his own project to adopt the Zettelkasten style, I wanted to look into this again after failed attempts in the past.
To make this addition work, [[wiki links]]
are very helpful. A regex applied to the text doesn’t cut it, though, otherwise the verbatim <code>
tag just now would be treated as a link, too. Extending the Markdown parser for inline elements is way better. Denis pointed out that the kramdown Ruby Markdown library supports syntax extensions. I was able to whip up a first attempt to inject wiki link detection as inline elements and maintain compatibility with pipes used for table block elements. I published this as a Gist, the code is:
require 'kramdown/parser/kramdown'
require 'kramdown-parser-gfm'
# Based on the API doc comment: https://github.com/gettalong/kramdown/blob/master/lib/kramdown/parser/kramdown.rb
class Kramdown::Parser::GFMWikiLink < Kramdown::Parser::GFM
def initialize(source, options)
super
# Override existing Table parser to use our own start Regex which adds a check for wikilinks
@@parsers.delete(:table) #Data(:table, TABLE_START, nil, "parse_table")
self.class.define_parser(:table, TABLE_START)
@span_parsers.unshift(:wikilinks)
end
# Override Kramdown table pipe check so we can write `[[pagename|Anchor Text]]`.
# https://github.com/gettalong/kramdown/blob/master/lib/kramdown/parser/kramdown/table.rb
# Regex test suite: https://regexr.com/5rb9q
TABLE_PIPE_CHECK = /^(?:\|(?!\[\[)|[^\[]*?(?!\[\[)[^\[]*?\||.*?(?:\[\[[^\]]+\]\]).*?\|)/.freeze # Fail for wikilinks in same line
TABLE_LINE = /#{TABLE_PIPE_CHECK}.*?\n/.freeze # Unchanged
TABLE_START = /^#{OPT_SPACE}(?=\S)#{TABLE_LINE}/.freeze # Unchanged
WIKILINKS_MATCH = /\[\[(.*?)\]\]/.freeze
define_parser(:wikilinks, WIKILINKS_MATCH, '\[\[')
def parse_wikilinks
line_number = @src.current_line_number
# Advance parser position
@src.pos += @src.matched_size
wikilink = Wikilink.parse(@src[1])
el = Element.new(:a, nil, {'href' => wikilink.url, 'title' => wikilink.title}, location: line_number)
add_text(wikilink.title, el)
@tree.children << el
el
end
# [[page_name|Optional title]]
# For a converter that uses the available pages, see: <https://github.com/metala/jekyll-wikilinks-plugin/blob/master/wikilinks.rb>
class Wikilink
def self.parse(text)
name, title = text.split('|', 2)
title = name if title.nil?
self.new(name, title)
end
attr_accessor :name, :title
attr_reader :match
def initialize(name, title)
@name = name.strip.gsub(/ +/, '-')
@title = title
end
def title
@title || @name
end
def url
"/wiki/#{@name.downcase}"
end
end
end
The original regex to see if a line of text denotes a table needed to be replaced, though. I patched this in by overriding the table detection, but keeping the table handling as-is.
All the lookaheads and lookbehinds in the TABLE_PIPE_CHECK
make compilation of my page even slower, so I limit the GFMWikiParser
to the /wiki/**/*
route in nanoc’s compilation rules.
It’s functional already, but the result doesn’t have very many things to look at just yet. I’ll add the new wiki to the navigation once I ported old wiki stuff I had abandoned over.