{-
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.
-}

{- |
   Module      : Text.Pandoc.CrossRef
   Copyright   : Copyright (C) 2015 Nikolay Yakimov
   License     : GNU GPL, version 2 or above

   Maintainer  : Nikolay Yakimov <root@livid.pp.ru>
   Stability   : alpha
   Portability : portable

Public interface to pandoc-crossref library

Example of use:

> import Text.Pandoc
> import Text.Pandoc.JSON
>
> import Text.Pandoc.CrossRef
>
> main :: IO ()
> main = toJSONFilter go
>   where
>     go fmt p@(Pandoc meta _) = runCrossRefIO meta fmt action p
>       where
>         action (Pandoc _ bs) = do
>           meta' <- crossRefMeta
>           bs' <- crossRefBlocks bs
>           return $ Pandoc meta' bs'

This module also exports utility functions for setting up meta-settings for
pandoc-crossref. Refer to documentation for a complete list of metadata field
names. All functions accept a single argument of type, returned by
"Text.Pandoc.Builder" functions, and return 'Meta'.

Example:

> runCrossRefIO meta fmt crossRefBlocks blocks
>   where
>     meta =
>          figureTitle (str "Figura")
>       <> tableTitle (str "Tabla")
>       <> figPrefix (str "fig.")
>       <> eqnPrefix (str "ec.")
>       <> tblPrefix (str "tbl.")
>       <> loftitle (header 1 $ text "Lista de figuras")
>       <> lotTitle (header 1 $ text "Lista de tablas")
>       <> chaptersDepth (MetaString "2")

-}
{-# LANGUAGE RankNTypes #-}

module Text.Pandoc.CrossRef (
    crossRefBlocks
  , crossRefMeta
  , defaultCrossRefAction
  , runCrossRef
  , runCrossRefIO
  , module SG
  , defaultMeta
  , CrossRefM
  , CrossRefEnv(..)
  ) where

import qualified Control.Monad.Reader as R
import Control.Monad.State
import Text.Pandoc

import Text.Pandoc.CrossRef.References
import Text.Pandoc.CrossRef.References.Monad
import Text.Pandoc.CrossRef.Util.CodeBlockCaptions
import Text.Pandoc.CrossRef.Util.ModifyMeta
import Text.Pandoc.CrossRef.Util.Options as O
import Text.Pandoc.CrossRef.Util.Settings
import Text.Pandoc.CrossRef.Util.Settings.Gen as SG

-- | Enviromnent for 'CrossRefM'
data CrossRefEnv = CrossRefEnv {
                      CrossRefEnv -> Meta
creSettings :: Meta -- ^Metadata settings
                    , CrossRefEnv -> Options
creOptions :: Options -- ^Internal pandoc-crossref options
                   }

-- | Essentially a reader monad for basic pandoc-crossref environment
type CrossRefM a = R.Reader CrossRefEnv a

{- | Walk over blocks, while inserting cross-references, list-of, etc.

Works in 'CrossRefM' monad. -}
crossRefBlocks :: [Block] -> CrossRefM [Block]
crossRefBlocks :: [Block] -> CrossRefM [Block]
crossRefBlocks [Block]
blocks = do
  Options
opts <- (CrossRefEnv -> Options) -> ReaderT CrossRefEnv Identity Options
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
R.asks CrossRefEnv -> Options
creOptions
  let
    doWalk :: WS [Block]
doWalk =
      ([Block] -> WS [Block]) -> [Block] -> WS [Block]
forall (m :: * -> *) a b.
(Monad m, Data a, Data b) =>
(a -> m a) -> b -> m b
bottomUpM [Block] -> WS [Block]
mkCodeBlockCaptions [Block]
blocks
      WS [Block] -> ([Block] -> WS [Block]) -> WS [Block]
forall a b. WS a -> (a -> WS b) -> WS b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= [Block] -> WS [Block]
forall a. Data a => a -> WS a
replaceAll
      WS [Block] -> ([Block] -> WS [Block]) -> WS [Block]
forall a b. WS a -> (a -> WS b) -> WS b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ([Inline] -> WS [Inline]) -> [Block] -> WS [Block]
forall (m :: * -> *) a b.
(Monad m, Data a, Data b) =>
(a -> m a) -> b -> m b
bottomUpM [Inline] -> WS [Inline]
replaceRefs
      WS [Block] -> ([Block] -> WS [Block]) -> WS [Block]
forall a b. WS a -> (a -> WS b) -> WS b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ([Block] -> WS [Block]) -> [Block] -> WS [Block]
forall (m :: * -> *) a b.
(Monad m, Data a, Data b) =>
(a -> m a) -> b -> m b
bottomUpM [Block] -> WS [Block]
listOf
    ([Block]
result, References
st) = (State References [Block] -> References -> ([Block], References))
-> References -> State References [Block] -> ([Block], References)
forall a b c. (a -> b -> c) -> b -> a -> c
flip State References [Block] -> References -> ([Block], References)
forall s a. State s a -> s -> (a, s)
runState References
forall a. Default a => a
def (State References [Block] -> ([Block], References))
-> State References [Block] -> ([Block], References)
forall a b. (a -> b) -> a -> b
$ (ReaderT Options (StateT References Identity) [Block]
 -> Options -> State References [Block])
-> Options
-> ReaderT Options (StateT References Identity) [Block]
-> State References [Block]
forall a b c. (a -> b -> c) -> b -> a -> c
flip ReaderT Options (StateT References Identity) [Block]
-> Options -> State References [Block]
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
R.runReaderT Options
opts (ReaderT Options (StateT References Identity) [Block]
 -> State References [Block])
-> ReaderT Options (StateT References Identity) [Block]
-> State References [Block]
forall a b. (a -> b) -> a -> b
$ WS [Block] -> ReaderT Options (StateT References Identity) [Block]
forall a. WS a -> ReaderT Options (StateT References Identity) a
runWS WS [Block]
doWalk
  References
st References -> CrossRefM [Block] -> CrossRefM [Block]
forall a b. a -> b -> b
`seq` [Block] -> CrossRefM [Block]
forall a. a -> ReaderT CrossRefEnv Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return [Block]
result

{- | Modifies metadata for LaTeX output, adding header-includes instructions
to setup custom and builtin environments.

Note, that if output format is not "latex", this function does nothing.

Works in 'CrossRefM' monad. -}
crossRefMeta :: CrossRefM Meta
crossRefMeta :: CrossRefM Meta
crossRefMeta = do
  Options
opts <- (CrossRefEnv -> Options) -> ReaderT CrossRefEnv Identity Options
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
R.asks CrossRefEnv -> Options
creOptions
  Meta
dtv <- (CrossRefEnv -> Meta) -> CrossRefM Meta
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
R.asks CrossRefEnv -> Meta
creSettings
  Meta -> CrossRefM Meta
forall a. a -> ReaderT CrossRefEnv Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return (Meta -> CrossRefM Meta) -> Meta -> CrossRefM Meta
forall a b. (a -> b) -> a -> b
$ Options -> Meta -> Meta
modifyMeta Options
opts Meta
dtv

{- | Combines 'crossRefMeta' and 'crossRefBlocks'

Works in 'CrossRefM' monad. -}
defaultCrossRefAction :: Pandoc -> CrossRefM Pandoc
defaultCrossRefAction :: Pandoc -> CrossRefM Pandoc
defaultCrossRefAction (Pandoc Meta
_ [Block]
bs) = do
  Meta
meta' <- CrossRefM Meta
crossRefMeta
  [Block]
bs' <- [Block] -> CrossRefM [Block]
crossRefBlocks [Block]
bs
  Pandoc -> CrossRefM Pandoc
forall a. a -> ReaderT CrossRefEnv Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return (Pandoc -> CrossRefM Pandoc) -> Pandoc -> CrossRefM Pandoc
forall a b. (a -> b) -> a -> b
$ Meta -> [Block] -> Pandoc
Pandoc Meta
meta' [Block]
bs'

{- | Run an action in 'CrossRefM' monad with argument, and return pure result.

This is primary function to work with 'CrossRefM' -}
runCrossRef :: forall a b. Meta -> Maybe Format -> (a -> CrossRefM b) -> a -> b
runCrossRef :: forall a b. Meta -> Maybe Format -> (a -> CrossRefM b) -> a -> b
runCrossRef Meta
meta Maybe Format
fmt a -> CrossRefM b
action a
arg = CrossRefM b -> CrossRefEnv -> b
forall r a. Reader r a -> r -> a
R.runReader (a -> CrossRefM b
action a
arg) CrossRefEnv
env
  where
    settings :: Meta
settings = Meta
defaultMeta Meta -> Meta -> Meta
forall a. Semigroup a => a -> a -> a
<> Meta
meta
    env :: CrossRefEnv
env = CrossRefEnv {
            creSettings :: Meta
creSettings = Meta
settings
          , creOptions :: Options
creOptions = Meta -> Maybe Format -> Options
getOptions Meta
settings Maybe Format
fmt
         }

{- | Run an action in 'CrossRefM' monad with argument, and return 'IO' result.

This function will attempt to read pandoc-crossref settings from settings
file specified by crossrefYaml metadata field. -}
runCrossRefIO :: forall a b. Meta -> Maybe Format -> (a -> CrossRefM b) -> a -> IO b
runCrossRefIO :: forall a b. Meta -> Maybe Format -> (a -> CrossRefM b) -> a -> IO b
runCrossRefIO Meta
meta Maybe Format
fmt a -> CrossRefM b
action a
arg = do
  Meta
settings <- Maybe Format -> Meta -> IO Meta
getSettings Maybe Format
fmt Meta
meta
  let
    env :: CrossRefEnv
env = CrossRefEnv {
            creSettings :: Meta
creSettings = Meta
settings
          , creOptions :: Options
creOptions = Meta -> Maybe Format -> Options
getOptions Meta
settings Maybe Format
fmt
         }
  b -> IO b
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (b -> IO b) -> b -> IO b
forall a b. (a -> b) -> a -> b
$ CrossRefM b -> CrossRefEnv -> b
forall r a. Reader r a -> r -> a
R.runReader (a -> CrossRefM b
action a
arg) CrossRefEnv
env