{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

-- |
-- Module      : $header$
-- Copyright   : (c) Laurent P René de Cotret, 2019 - present
-- License     : GNU GPL, version 2 or above
-- Maintainer  : laurent.decotret@outlook.com
-- Stability   : internal
-- Portability : portable
--
-- Specification of renderers.
module Text.Pandoc.Filter.Plot.Renderers
  ( renderer,
    preambleSelector,
    parseExtraAttrs,
    executable,
    availableToolkits,
    availableToolkitsM,
    unavailableToolkits,
    unavailableToolkitsM,
    supportedSaveFormats,
    OutputSpec (..),
    Executable (..),
    Renderer (..),
  )
where

import Control.Concurrent.Async.Lifted (forConcurrently)
import Control.Concurrent.MVar (putMVar, takeMVar)
import Control.Monad.Reader (local)
import Control.Monad.State.Strict
  ( MonadState (get, put),
  )
import Data.List ((\\))
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Data.Maybe (catMaybes, isJust)
import Data.Text (Text, pack)
import Text.Pandoc.Filter.Plot.Monad
import Text.Pandoc.Filter.Plot.Monad.Logging
  ( Logger (lVerbosity),
  )
import Text.Pandoc.Filter.Plot.Renderers.Bokeh
    ( bokeh, bokehSupportedSaveFormats )
import Text.Pandoc.Filter.Plot.Renderers.GGPlot2
    ( ggplot2, ggplot2SupportedSaveFormats )
import Text.Pandoc.Filter.Plot.Renderers.GNUPlot
    ( gnuplot, gnuplotSupportedSaveFormats )
import Text.Pandoc.Filter.Plot.Renderers.Graphviz
    ( graphviz, graphvizSupportedSaveFormats )
import Text.Pandoc.Filter.Plot.Renderers.Mathematica
    ( mathematica, mathematicaSupportedSaveFormats )
import Text.Pandoc.Filter.Plot.Renderers.Matlab
    ( matlab, matlabSupportedSaveFormats )
import Text.Pandoc.Filter.Plot.Renderers.Matplotlib
    ( matplotlib, matplotlibSupportedSaveFormats )
import Text.Pandoc.Filter.Plot.Renderers.Octave
    ( octave, octaveSupportedSaveFormats )
import Text.Pandoc.Filter.Plot.Renderers.PlantUML
    ( plantuml, plantumlSupportedSaveFormats )
import Text.Pandoc.Filter.Plot.Renderers.PlotlyPython
    ( plotlyPython, plotlyPythonSupportedSaveFormats )
import Text.Pandoc.Filter.Plot.Renderers.PlotlyR
    ( plotlyR, plotlyRSupportedSaveFormats )
import Text.Pandoc.Filter.Plot.Renderers.Plotsjl
    ( plotsjl, plotsjlSupportedSaveFormats )
import Text.Pandoc.Filter.Plot.Renderers.SageMath
    ( sagemath, sagemathSupportedSaveFormats )

-- | Get the renderer associated with a toolkit.
-- If the renderer has not been used before,
-- initialize it and store where it is. It will be re-used.
renderer :: Toolkit -> PlotM (Maybe Renderer)
renderer :: Toolkit -> PlotM (Maybe Renderer)
renderer Toolkit
tk = do
  PlotState MVar (Map FilePath FileHash)
varHashes MVar (Map Toolkit (Maybe Renderer))
varRenderers <- StateT PlotState (ReaderT RuntimeEnv IO) PlotState
forall s (m :: * -> *). MonadState s m => m s
get
  Map Toolkit (Maybe Renderer)
renderers <- IO (Map Toolkit (Maybe Renderer))
-> StateT
     PlotState (ReaderT RuntimeEnv IO) (Map Toolkit (Maybe Renderer))
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Map Toolkit (Maybe Renderer))
 -> StateT
      PlotState (ReaderT RuntimeEnv IO) (Map Toolkit (Maybe Renderer)))
-> IO (Map Toolkit (Maybe Renderer))
-> StateT
     PlotState (ReaderT RuntimeEnv IO) (Map Toolkit (Maybe Renderer))
forall a b. (a -> b) -> a -> b
$ MVar (Map Toolkit (Maybe Renderer))
-> IO (Map Toolkit (Maybe Renderer))
forall a. MVar a -> IO a
takeMVar MVar (Map Toolkit (Maybe Renderer))
varRenderers
  (Maybe Renderer
r', Map Toolkit (Maybe Renderer)
rs') <- case Toolkit -> Map Toolkit (Maybe Renderer) -> Maybe (Maybe Renderer)
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup Toolkit
tk Map Toolkit (Maybe Renderer)
renderers of
    Maybe (Maybe Renderer)
Nothing -> do
      Text -> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall (m :: * -> *). (MonadLogger m, MonadIO m) => Text -> m ()
debug (Text -> StateT PlotState (ReaderT RuntimeEnv IO) ())
-> Text -> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat [Text
"Looking for renderer for ", FilePath -> Text
pack (FilePath -> Text) -> FilePath -> Text
forall a b. (a -> b) -> a -> b
$ Toolkit -> FilePath
forall a. Show a => a -> FilePath
show Toolkit
tk]
      Maybe Renderer
r' <- Toolkit -> PlotM (Maybe Renderer)
sel Toolkit
tk
      let rs' :: Map Toolkit (Maybe Renderer)
rs' = Toolkit
-> Maybe Renderer
-> Map Toolkit (Maybe Renderer)
-> Map Toolkit (Maybe Renderer)
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert Toolkit
tk Maybe Renderer
r' Map Toolkit (Maybe Renderer)
renderers
      (Maybe Renderer, Map Toolkit (Maybe Renderer))
-> StateT
     PlotState
     (ReaderT RuntimeEnv IO)
     (Maybe Renderer, Map Toolkit (Maybe Renderer))
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Renderer
r', Map Toolkit (Maybe Renderer)
rs')
    Just Maybe Renderer
e -> do
      Text -> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall (m :: * -> *). (MonadLogger m, MonadIO m) => Text -> m ()
debug (Text -> StateT PlotState (ReaderT RuntimeEnv IO) ())
-> Text -> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat [Text
"Renderer for \"", FilePath -> Text
pack (FilePath -> Text) -> FilePath -> Text
forall a b. (a -> b) -> a -> b
$ Toolkit -> FilePath
forall a. Show a => a -> FilePath
show Toolkit
tk, Text
"\" already initialized."]
      (Maybe Renderer, Map Toolkit (Maybe Renderer))
-> StateT
     PlotState
     (ReaderT RuntimeEnv IO)
     (Maybe Renderer, Map Toolkit (Maybe Renderer))
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Renderer
e, Map Toolkit (Maybe Renderer)
renderers)
  IO () -> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> StateT PlotState (ReaderT RuntimeEnv IO) ())
-> IO () -> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall a b. (a -> b) -> a -> b
$ MVar (Map Toolkit (Maybe Renderer))
-> Map Toolkit (Maybe Renderer) -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar (Map Toolkit (Maybe Renderer))
varRenderers Map Toolkit (Maybe Renderer)
rs'
  PlotState -> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall s (m :: * -> *). MonadState s m => s -> m ()
put (PlotState -> StateT PlotState (ReaderT RuntimeEnv IO) ())
-> PlotState -> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall a b. (a -> b) -> a -> b
$ MVar (Map FilePath FileHash)
-> MVar (Map Toolkit (Maybe Renderer)) -> PlotState
PlotState MVar (Map FilePath FileHash)
varHashes MVar (Map Toolkit (Maybe Renderer))
varRenderers
  Maybe Renderer -> PlotM (Maybe Renderer)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Renderer
r'
  where
    sel :: Toolkit -> PlotM (Maybe Renderer)
    sel :: Toolkit -> PlotM (Maybe Renderer)
sel Toolkit
Matplotlib = PlotM (Maybe Renderer)
matplotlib
    sel Toolkit
PlotlyPython = PlotM (Maybe Renderer)
plotlyPython
    sel Toolkit
PlotlyR = PlotM (Maybe Renderer)
plotlyR
    sel Toolkit
Matlab = PlotM (Maybe Renderer)
matlab
    sel Toolkit
Mathematica = PlotM (Maybe Renderer)
mathematica
    sel Toolkit
Octave = PlotM (Maybe Renderer)
octave
    sel Toolkit
GGPlot2 = PlotM (Maybe Renderer)
ggplot2
    sel Toolkit
GNUPlot = PlotM (Maybe Renderer)
gnuplot
    sel Toolkit
Graphviz = PlotM (Maybe Renderer)
graphviz
    sel Toolkit
Bokeh = PlotM (Maybe Renderer)
bokeh
    sel Toolkit
Plotsjl = PlotM (Maybe Renderer)
plotsjl
    sel Toolkit
PlantUML = PlotM (Maybe Renderer)
plantuml
    sel Toolkit
SageMath = PlotM (Maybe Renderer)
sagemath

-- | Save formats supported by this renderer.
supportedSaveFormats :: Toolkit -> [SaveFormat]
supportedSaveFormats :: Toolkit -> [SaveFormat]
supportedSaveFormats Toolkit
Matplotlib = [SaveFormat]
matplotlibSupportedSaveFormats
supportedSaveFormats Toolkit
PlotlyPython = [SaveFormat]
plotlyPythonSupportedSaveFormats
supportedSaveFormats Toolkit
PlotlyR = [SaveFormat]
plotlyRSupportedSaveFormats
supportedSaveFormats Toolkit
Matlab = [SaveFormat]
matlabSupportedSaveFormats
supportedSaveFormats Toolkit
Mathematica = [SaveFormat]
mathematicaSupportedSaveFormats
supportedSaveFormats Toolkit
Octave = [SaveFormat]
octaveSupportedSaveFormats
supportedSaveFormats Toolkit
GGPlot2 = [SaveFormat]
ggplot2SupportedSaveFormats
supportedSaveFormats Toolkit
GNUPlot = [SaveFormat]
gnuplotSupportedSaveFormats
supportedSaveFormats Toolkit
Graphviz = [SaveFormat]
graphvizSupportedSaveFormats
supportedSaveFormats Toolkit
Bokeh = [SaveFormat]
bokehSupportedSaveFormats
supportedSaveFormats Toolkit
Plotsjl = [SaveFormat]
plotsjlSupportedSaveFormats
supportedSaveFormats Toolkit
PlantUML = [SaveFormat]
plantumlSupportedSaveFormats
supportedSaveFormats Toolkit
SageMath = [SaveFormat]
sagemathSupportedSaveFormats

-- | The function that maps from configuration to the preamble.
preambleSelector :: Toolkit -> (Configuration -> Script)
preambleSelector :: Toolkit -> Configuration -> Text
preambleSelector Toolkit
Matplotlib = Configuration -> Text
matplotlibPreamble
preambleSelector Toolkit
PlotlyPython = Configuration -> Text
plotlyPythonPreamble
preambleSelector Toolkit
PlotlyR = Configuration -> Text
plotlyRPreamble
preambleSelector Toolkit
Matlab = Configuration -> Text
matlabPreamble
preambleSelector Toolkit
Mathematica = Configuration -> Text
mathematicaPreamble
preambleSelector Toolkit
Octave = Configuration -> Text
octavePreamble
preambleSelector Toolkit
GGPlot2 = Configuration -> Text
ggplot2Preamble
preambleSelector Toolkit
GNUPlot = Configuration -> Text
gnuplotPreamble
preambleSelector Toolkit
Graphviz = Configuration -> Text
graphvizPreamble
preambleSelector Toolkit
Bokeh = Configuration -> Text
bokehPreamble
preambleSelector Toolkit
Plotsjl = Configuration -> Text
plotsjlPreamble
preambleSelector Toolkit
PlantUML = Configuration -> Text
plantumlPreamble
preambleSelector Toolkit
SageMath = Configuration -> Text
sagemathPreamble

-- | Parse code block headers for extra attributes that are specific
-- to this renderer. By default, no extra attributes are parsed.
parseExtraAttrs :: Toolkit -> Map Text Text -> Map Text Text
parseExtraAttrs :: Toolkit -> Map Text Text -> Map Text Text
parseExtraAttrs Toolkit
Matplotlib = (Text -> Text -> Bool) -> Map Text Text -> Map Text Text
forall k a. (k -> a -> Bool) -> Map k a -> Map k a
M.filterWithKey (\Text
k Text
_ -> Text
k Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text
"tight_bbox", Text
"transparent"])
parseExtraAttrs Toolkit
_ = Map Text Text -> Map Text Text -> Map Text Text
forall (m :: * -> *) a. Monad m => a -> m a
return Map Text Text
forall a. Monoid a => a
mempty

-- | List of toolkits available on this machine.
-- The executables to look for are taken from the configuration.
availableToolkits :: Configuration -> IO [Toolkit]
availableToolkits :: Configuration -> IO [Toolkit]
availableToolkits Configuration
conf = Maybe Format -> Configuration -> PlotM [Toolkit] -> IO [Toolkit]
forall a. Maybe Format -> Configuration -> PlotM a -> IO a
runPlotM Maybe Format
forall a. Maybe a
Nothing Configuration
conf PlotM [Toolkit]
availableToolkitsM

-- | List of toolkits not available on this machine.
-- The executables to look for are taken from the configur
unavailableToolkits :: Configuration -> IO [Toolkit]
unavailableToolkits :: Configuration -> IO [Toolkit]
unavailableToolkits Configuration
conf = Maybe Format -> Configuration -> PlotM [Toolkit] -> IO [Toolkit]
forall a. Maybe Format -> Configuration -> PlotM a -> IO a
runPlotM Maybe Format
forall a. Maybe a
Nothing Configuration
conf PlotM [Toolkit]
unavailableToolkitsM

-- | Monadic version of @availableToolkits@.
--
-- Note that logging is disabled
availableToolkitsM :: PlotM [Toolkit]
availableToolkitsM :: PlotM [Toolkit]
availableToolkitsM = PlotM [Toolkit] -> PlotM [Toolkit]
forall a.
StateT PlotState (ReaderT RuntimeEnv IO) a
-> StateT PlotState (ReaderT RuntimeEnv IO) a
asNonStrictAndSilent (PlotM [Toolkit] -> PlotM [Toolkit])
-> PlotM [Toolkit] -> PlotM [Toolkit]
forall a b. (a -> b) -> a -> b
$ do
    [Maybe Toolkit]
mtks <- [Toolkit]
-> (Toolkit
    -> StateT PlotState (ReaderT RuntimeEnv IO) (Maybe Toolkit))
-> StateT PlotState (ReaderT RuntimeEnv IO) [Maybe Toolkit]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, MonadBaseControl IO m) =>
t a -> (a -> m b) -> m (t b)
forConcurrently [Toolkit]
toolkits ((Toolkit
  -> StateT PlotState (ReaderT RuntimeEnv IO) (Maybe Toolkit))
 -> StateT PlotState (ReaderT RuntimeEnv IO) [Maybe Toolkit])
-> (Toolkit
    -> StateT PlotState (ReaderT RuntimeEnv IO) (Maybe Toolkit))
-> StateT PlotState (ReaderT RuntimeEnv IO) [Maybe Toolkit]
forall a b. (a -> b) -> a -> b
$ \Toolkit
tk -> do
      Bool
available <- Maybe Renderer -> Bool
forall a. Maybe a -> Bool
isJust (Maybe Renderer -> Bool)
-> PlotM (Maybe Renderer)
-> StateT PlotState (ReaderT RuntimeEnv IO) Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Toolkit -> PlotM (Maybe Renderer)
renderer Toolkit
tk
      if Bool
available
        then Maybe Toolkit
-> StateT PlotState (ReaderT RuntimeEnv IO) (Maybe Toolkit)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Toolkit
 -> StateT PlotState (ReaderT RuntimeEnv IO) (Maybe Toolkit))
-> Maybe Toolkit
-> StateT PlotState (ReaderT RuntimeEnv IO) (Maybe Toolkit)
forall a b. (a -> b) -> a -> b
$ Toolkit -> Maybe Toolkit
forall a. a -> Maybe a
Just Toolkit
tk
        else Maybe Toolkit
-> StateT PlotState (ReaderT RuntimeEnv IO) (Maybe Toolkit)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Toolkit
forall a. Maybe a
Nothing
    [Toolkit] -> PlotM [Toolkit]
forall (m :: * -> *) a. Monad m => a -> m a
return ([Toolkit] -> PlotM [Toolkit]) -> [Toolkit] -> PlotM [Toolkit]
forall a b. (a -> b) -> a -> b
$ [Maybe Toolkit] -> [Toolkit]
forall a. [Maybe a] -> [a]
catMaybes [Maybe Toolkit]
mtks
  where
    asNonStrictAndSilent :: StateT PlotState (ReaderT RuntimeEnv IO) a
-> StateT PlotState (ReaderT RuntimeEnv IO) a
asNonStrictAndSilent = (RuntimeEnv -> RuntimeEnv)
-> StateT PlotState (ReaderT RuntimeEnv IO) a
-> StateT PlotState (ReaderT RuntimeEnv IO) a
forall r (m :: * -> *) a. MonadReader r m => (r -> r) -> m a -> m a
local (\(RuntimeEnv Maybe Format
f Configuration
c Logger
l FilePath
d) -> Maybe Format -> Configuration -> Logger -> FilePath -> RuntimeEnv
RuntimeEnv Maybe Format
f (Configuration
c{strictMode :: Bool
strictMode = Bool
False}) (Logger
l{lVerbosity :: Verbosity
lVerbosity = Verbosity
Silent}) FilePath
d)

-- | Monadic version of @unavailableToolkits@
unavailableToolkitsM :: PlotM [Toolkit]
unavailableToolkitsM :: PlotM [Toolkit]
unavailableToolkitsM = [Toolkit] -> [Toolkit] -> [Toolkit]
forall a. Eq a => [a] -> [a] -> [a]
(\\) [Toolkit]
toolkits ([Toolkit] -> [Toolkit]) -> PlotM [Toolkit] -> PlotM [Toolkit]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> PlotM [Toolkit]
availableToolkitsM