Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add variants of pandoc renderer and compiler for items #1020

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

slotThe
Copy link
Contributor

@slotThe slotThe commented Feb 21, 2024

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

Add renderPandocItemWithTransformM and pandocItemCompilerWithTransformM,
which work like the respective functions without the `item' infix, but
the transformation function is an 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.
@chungyc
Copy link
Contributor

chungyc commented Feb 22, 2024

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

@slotThe
Copy link
Contributor Author

slotThe commented Feb 22, 2024

Out of curiosity, what would the code look like if you used renderPandocItemWithTransformM and pandocItemCompilerWithTransformM?

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants