I've got a Blog, generated using the Hakyll static site generator.
Hakyll uses Pandoc's markdown parser, which by default, generates IDs for each of the heading elements, based on their contents. These IDs can be used to link to individual sections, by appending an anchor with the ID to the page URL.
However, without inspecting the page source, it's difficult to tell what the ID for a given heading is. I'd like to add a self link to each heading, so visitors to the blog can use each heading as a link to that section in the post.
Here's how I achieved this. First off, some imports:
import Text.Pandoc ( Pandoc, Inline(Link), Block(Header) )
import Text.Pandoc.Walk (walk)
import Text.Pandoc.Shared (stringify)
We want a function that transforms Pandoc's Block
s.
We want to match headers that have a level which is greater than 1 (because h1
elements only appear at the top of blog posts, so there's no point in linking to them).
For these headers, we want to transform them, inserting a link.
The link will have a destination generated from the ID of the header (with a #
prepended to make it an anchor), and a title generated from the header contents.
We also assign a class to the link, to help styling it, and set the contents of the link using the contents of the original header.
transformHeader :: Block -> Block
transformHeader header@(Header level attr@(identifier, _, _) contents) | level > 1 =
let linkClass = "headerLink"
linkAttr = ("", [linkClass], [])
linkDestination = "#" <> identifier
linkTitle = stringify contents
in Header level attr
[ Link linkAttr contents (linkDestination, linkTitle)
]
transformHeader block = block
We can lift this into a function over Pandoc documents using walk
.
buildHeaderLinks :: Pandoc -> Pandoc
buildHeaderLinks = walk transformHeader
This can then be integrated into a Hakyll app using pandocCompilerWithTransform
, or otherwise.
I've tested this using pandoc-2.14.0.3
, but it should be relatively flexible.