packages feed

pandoc-crossref-0.3.21: lib-internal/Text/Pandoc/CrossRef/Util/Generic.hs

{-
pandoc-crossref is a pandoc filter for numbering figures,
equations, tables and cross-references to them.
Copyright (C) 2015  Nikolay Yakimov <root@livid.pp.ru>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-}

{-# LANGUAGE MonoLocalBinds, TypeAbstractions #-}

module Text.Pandoc.CrossRef.Util.Generic
  ( module Data.Generics
  , runReplace
  , replaceRecurse
  , replaceNoRecurse
  , ReplacedResult
  , noReplaceRecurse
  , mkRR
  , extRR
  , replaceList
  , fixRefs
  , fixRefs'
  , replaceRefsWalk
  ) where

import Data.Generics
import Text.Pandoc.Builder hiding ((<>))
import Text.Pandoc.CrossRef.References.Monad
import Text.Pandoc.CrossRef.References.Types
import Text.Pandoc.CrossRef.References.Refs
import Text.Pandoc.CrossRef.Util.Options
import Lens.Micro.Mtl
import Lens.Micro
import qualified Text.Pandoc.Builder as B

data ReplacedResult a where
  Replaced :: Recurse -> a -> ReplacedResult a
  ReplacedList :: FixRefs a b => ([a] -> WS [a]) -> b -> b -> ReplacedResult [a]
  NotReplaced :: ReplacedResult a
data Recurse = Recurse | NoRecurse
newtype RR m a = RR {unRR :: a -> m (ReplacedResult a)}

class Monoid b => FixRefs a b where
  lens' :: Lens' b [a]

instance FixRefs Inline Inlines where
  lens' = lens B.toList (const B.fromList)

instance FixRefs Block Blocks where
  lens' = lens B.toList (const B.fromList)

instance FixRefs a [a] where
  lens' = id

fixRefs :: (Data a, FixRefs a b, Monad m) => m (Maybe b) -> [a] -> m (ReplacedResult [a])
fixRefs x rest =
  -- NB: must be very-very careful to not inspect the result beyond Maybe,
  -- otherwise fix in deferred part will loop
  x >>= \case
    Just res' -> replaceList' pure res' rest
    Nothing -> noReplaceRecurse

fixRefs' :: Inline -> [Inline] -> WS (ReplacedResult [Inline])
fixRefs' = fixRefs . replaceRefsInline

replaceRefsInline :: Inline -> WS (Maybe Inlines)
replaceRefsInline (Cite cits _) = Just <$> do
  opts <- use wsOptions
  -- recurse into prefix/suffix
  cits' <- doReplaceRefs cits
  liftF $ replaceRefs cits' opts
replaceRefsInline _ = pure Nothing

replaceRefsWalk :: Data a => Options -> References -> a -> a
replaceRefsWalk o r x = fst $ unwrapWS (doReplaceRefs x) (WState o r) r

doReplaceRefs :: Data a => a -> WS a
doReplaceRefs = runReplace $ mkRR \case
  (x:xs) -> fixRefs' x xs
  [] -> noReplaceRecurse

runReplace
  :: Data a
  => (forall d. Data d => (d -> WS (ReplacedResult d)))
  -> a -> WS a
runReplace f x = f x >>= \case
  Replaced Recurse x' -> gmapM (runReplace f) x'
  Replaced NoRecurse x' -> pure x'
  NotReplaced -> gmapM (runReplace f) x
  ReplacedList @a hdT hd tl -> do
    hd' <- hdT `lens'` hd
    tl' <- runReplace @[a] f `lens'` tl
    pure $ view lens' $ hd' <> tl'

mkRR :: (Monad m, Typeable a, Typeable b)
     => (b -> m (ReplacedResult b))
     -> (a -> m (ReplacedResult a))
mkRR = extRR (const noReplaceRecurse)

extRR :: ( Monad m, Typeable a, Typeable b)
     => (a -> m (ReplacedResult a))
     -> (b -> m (ReplacedResult b))
     -> (a -> m (ReplacedResult a))
extRR def' ext = unRR (RR def' `ext0` RR ext)

replaceRecurse :: Monad m => a -> m (ReplacedResult a)
replaceRecurse = pure . Replaced Recurse

replaceNoRecurse :: Data a => a -> WS (ReplacedResult a)
replaceNoRecurse = fmap (Replaced NoRecurse) . doReplaceRefs

replaceList :: (FixRefs a b, Monad m, Monoid b, Data a) => b -> [a] -> m (ReplacedResult [a])
replaceList = replaceList' doReplaceRefs

replaceList' :: (FixRefs a b, Monad m, Monoid b, Data a) => ([a] -> WS [a]) -> b -> [a] -> m (ReplacedResult [a])
replaceList' hdT hd tl = pure $ ReplacedList hdT hd (mempty & lens' .~ tl)

noReplaceRecurse :: Monad m => m (ReplacedResult a)
noReplaceRecurse = pure NotReplaced