Add variants of pandoc renderer and compiler for items
Add renderPandocItemWithTransformM and pandocItemCompilerWithTransformM, which work like the respective functions without the "item" infix, but the transformation function is a monadic "Item Pandoc -> Item Pandoc" instead of just a "Pandoc -> Pandoc". This allows one to more seamlessly compose functions that do require the extra information that an item provides, like bibliographic transformations.
My specific use-case for this is exactly adding a nicely formatted bibliography at the end of certain pages. Here is a real world example with entirely too much code:
myPandocCompiler :: Compiler (Item String)
myPandocCompiler =
pandocItemCompilerWithTransformM -- ⋘ HERE
readerOpts
writerOpts
(traverse moreSettings <=< processBib)
where
processBib :: Item Pandoc -> Compiler (Item Pandoc) -- Due to this type
processBib pandoc = do
csl <- load @CSL "bib/style.csl"
bib <- load @Biblio "bib/bibliography.bib"
-- We do want to link citations.
p <- withItemBody
(\(Pandoc (Meta meta) bs) -> pure $
Pandoc (Meta $ Map.insert "link-citations" (MetaBool True) meta)
bs)
pandoc
fmap (tableiseBib . insertRefHeading) <$> processPandocBiblio csl bib p
where
-- Insert a heading for the citations.
insertRefHeading :: Pandoc -> Pandoc
insertRefHeading = walk $ concatMap \case
d@(Div ("refs", _, _) _) -> [Header 1 ("references", [], []) [Str "References"], d]
block -> [block]
-- Arrange all citations in a table, so that they are nicely aligned.
-- This probably only works with label or numerical styles.
tableiseBib :: Pandoc -> Pandoc
tableiseBib = walk \case
Div a@("refs", _, _) body -> Div a (Many.toList (simpleTable [] (map citToRow body)))
body -> body
where
citToRow :: Block -> [Many Block]
citToRow = map Many.singleton . \case
Div attr [Para (s1 : ss)] -> [Div attr [Plain [s1]], Plain [Space], Plain ss]
d -> error $ "citToRow: unexpected citation format: " <> show d
Out of curiosity, what would the code look like if you used renderPandocItemWithTransformM and pandocItemCompilerWithTransformM?
Would it be simpler than implementing a Compiler (Item String) function directly without bothering with a derived compiler like pandocItemCompilerWithTransformM? E.g., I do something similar with my own website:
articleCompiler :: Compiler (Item String)
articleCompiler = do
let readerOptions = mathReaderWith defaultHakyllReaderOptions
writerOptions <- getTocOptionsWith $ mathWriterWith defaultHakyllWriterOptions
bibFile <- load "article/bibliography/references.bib"
cslFile <- load "article/bibliography/acm.csl"
getResourceBody
>>= readPandocWith readerOptions
>>= pure . fmap (setMeta "link-citations" True)
>>= processPandocBiblio cslFile bibFile
>>= pure . writePandocWith writerOptions
Out of curiosity, what would the code look like if you used
renderPandocItemWithTransformMandpandocItemCompilerWithTransformM?
The basic setup wouldn't be much more difficult, if a bit uglier in my opinion:
myPandocCompiler :: Compiler (Item String)
myPandocCompiler = do
csl <- load @CSL "bib/style.csl"
bib <- load @Biblio "bib/bibliography.bib"
getResourceBody
>>= readPandocWith defaultHakyllReaderOptions
>>= traverse ( pure . usingSideNotesHTML myWriter
<=< pygmentsHighlight
. addSectionLinks
. smallCaps
. setMeta "link-citations" True
)
>>= ((fmap . fmap) (tableiseBib . insertRefHeading) . processPandocBiblio csl bib)
<&> writePandocWith myWriter
The bigger problem is that this doesn't work with my setup, as I also have a dedicated RSS compiler that skips many of these steps, as e.g. sidenotes do not make sense without CSS. Since now the main compiler has bibliographic information baked into it, I would have to rewrite this in some clever way, and make the big traverse block more modular; I reckon I would end up at exactly something like pandocItemCompilerWithTransformM.