{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}
module Rib.Parser.Pandoc
(
parse,
parsePure,
render,
renderPandocInlines,
extractMeta,
getH1,
getToC,
getFirstImg,
Pandoc,
module Text.Pandoc.Readers,
)
where
import Control.Monad.Except (MonadError, liftEither, runExcept)
import Data.Aeson
import Development.Shake (Action, readFile')
import Lucid (HtmlT, toHtmlRaw)
import Relude
import Rib.Shake (ribInputDir)
import System.FilePath
import Text.Pandoc
import Text.Pandoc.Filter.IncludeCode (includeCode)
import qualified Text.Pandoc.Readers
import Text.Pandoc.Walk (query, walkM)
import Text.Pandoc.Writers.Shared (toTableOfContents)
parsePure ::
(ReaderOptions -> Text -> PandocPure Pandoc) ->
Text ->
Pandoc
parsePure textReader s =
either (error . show) id $ runExcept $ do
runPure' $ textReader readerSettings s
parse ::
(ReaderOptions -> Text -> PandocIO Pandoc) ->
FilePath ->
Action Pandoc
parse textReader f =
either fail pure =<< do
inputDir <- ribInputDir
content <- toText <$> readFile' (inputDir </> f)
fmap (first show) $ runExceptT $ do
v' <- runIO' $ textReader readerSettings content
liftIO $ walkM includeSources v'
where
includeSources = includeCode $ Just $ Format "html5"
render :: Monad m => Pandoc -> HtmlT m ()
render doc =
either error id $ first show $ runExcept $ do
runPure'
$ fmap toHtmlRaw
$ writeHtml5String writerSettings doc
extractMeta :: Pandoc -> Maybe (Either Text Value)
extractMeta (Pandoc meta _) = flattenMeta meta
runPure' :: MonadError PandocError m => PandocPure a -> m a
runPure' = liftEither . runPure
runIO' :: (MonadError PandocError m, MonadIO m) => PandocIO a -> m a
runIO' = liftEither <=< liftIO . runIO
renderPandocInlines :: Monad m => [Inline] -> HtmlT m ()
renderPandocInlines =
renderPandocBlocks . pure . Plain
renderPandocBlocks :: Monad m => [Block] -> HtmlT m ()
renderPandocBlocks =
toHtmlRaw . render . Pandoc mempty
getH1 :: Monad m => Pandoc -> Maybe (HtmlT m ())
getH1 (Pandoc _ bs) = fmap renderPandocInlines $ flip query bs $ \case
Header 1 _ xs -> Just xs
_ -> Nothing
getToC :: Monad m => Pandoc -> HtmlT m ()
getToC (Pandoc _ bs) = renderPandocBlocks [toc]
where
toc = toTableOfContents writerSettings bs
getFirstImg ::
Pandoc ->
Maybe Text
getFirstImg (Pandoc _ bs) = listToMaybe $ flip query bs $ \case
Image _ _ (url, _) -> [toText url]
_ -> []
exts :: Extensions
exts =
mconcat
[ extensionsFromList
[ Ext_yaml_metadata_block,
Ext_fenced_code_attributes,
Ext_auto_identifiers,
Ext_smart
],
githubMarkdownExtensions
]
readerSettings :: ReaderOptions
readerSettings = def {readerExtensions = exts}
writerSettings :: WriterOptions
writerSettings = def {writerExtensions = exts}
flattenMeta :: Meta -> Maybe (Either Text Value)
flattenMeta (Meta meta) = fmap toJSON . traverse go <$> guarded (not . null) meta
where
go :: MetaValue -> Either Text Value
go (MetaMap m) = toJSON <$> traverse go m
go (MetaList m) = toJSONList <$> traverse go m
go (MetaBool m) = pure $ toJSON m
go (MetaString m) = pure $ toJSON m
go (MetaInlines m) =
bimap show toJSON
$ runPure . plainWriter
$ Pandoc mempty [Plain m]
go (MetaBlocks m) =
bimap show toJSON
$ runPure . plainWriter
$ Pandoc mempty m
plainWriter = writePlain def