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

-- |

-- Module      : $header$

-- Copyright   : (c) Laurent P René de Cotret, 2019 - 2021

-- 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
import Control.Monad.Reader (local)
import Control.Monad.State.Strict
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.Renderers.Bokeh
import Text.Pandoc.Filter.Plot.Renderers.GGPlot2
import Text.Pandoc.Filter.Plot.Renderers.GNUPlot
import Text.Pandoc.Filter.Plot.Renderers.Graphviz
import Text.Pandoc.Filter.Plot.Renderers.Mathematica
import Text.Pandoc.Filter.Plot.Renderers.Matlab
import Text.Pandoc.Filter.Plot.Renderers.Matplotlib
import Text.Pandoc.Filter.Plot.Renderers.Octave
import Text.Pandoc.Filter.Plot.Renderers.PlotlyPython
import Text.Pandoc.Filter.Plot.Renderers.PlotlyR
import Text.Pandoc.Filter.Plot.Renderers.Plotsjl

-- | 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 -> PlotM ()
debug (Text -> PlotM ()) -> Text -> PlotM ()
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 -> PlotM ()
debug (Text -> PlotM ()) -> Text -> PlotM ()
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 () -> PlotM ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> PlotM ()) -> IO () -> PlotM ()
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 -> PlotM ()
forall s (m :: * -> *). MonadState s m => s -> m ()
put (PlotState -> PlotM ()) -> PlotState -> PlotM ()
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

-- | 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

-- | 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

-- | 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 = Configuration -> PlotM [Toolkit] -> IO [Toolkit]
forall a. Configuration -> PlotM a -> IO a
runPlotM 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 = Configuration -> PlotM [Toolkit] -> IO [Toolkit]
forall a. Configuration -> PlotM a -> IO a
runPlotM 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. PlotM a -> PlotM a
silence (PlotM [Toolkit] -> PlotM [Toolkit])
-> PlotM [Toolkit] -> PlotM [Toolkit]
forall a b. (a -> b) -> a -> b
$
  PlotM [Toolkit] -> PlotM [Toolkit]
forall a. PlotM a -> PlotM a
asNonStrict (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
    asNonStrict :: StateT PlotState (ReaderT RuntimeEnv IO) a
-> StateT PlotState (ReaderT RuntimeEnv IO) a
asNonStrict = (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 Configuration
c Logger
l FilePath
d) -> Configuration -> Logger -> FilePath -> RuntimeEnv
RuntimeEnv Configuration
c {strictMode :: Bool
strictMode = Bool
False} Logger
l 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