summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurentRDC <>2020-01-23 13:24:00 (GMT)
committerhdiff <hdiff@hdiff.luite.com>2020-01-23 13:24:00 (GMT)
commit314ccbe9380ebd0d6eba8c3e9ad4aa936a3f63b9 (patch)
treede5e1fc873733cf0d6cc054267fa5c2d820dee6c
parent336abc44c24c91fc8e972321009f9a76d567b112 (diff)
version 0.2.0.00.2.0.0
-rw-r--r--CHANGELOG.md8
-rw-r--r--README.md40
-rw-r--r--example-config.yml56
-rw-r--r--executable/Main.hs15
-rw-r--r--pandoc-plot.cabal6
-rw-r--r--src/Text/Pandoc/Filter/Plot.hs63
-rw-r--r--src/Text/Pandoc/Filter/Plot/Configuration.hs25
-rw-r--r--src/Text/Pandoc/Filter/Plot/Parse.hs39
-rw-r--r--src/Text/Pandoc/Filter/Plot/Renderers.hs28
-rw-r--r--src/Text/Pandoc/Filter/Plot/Renderers/GNUPlot.hs41
-rw-r--r--src/Text/Pandoc/Filter/Plot/Renderers/Mathematica.hs2
-rw-r--r--src/Text/Pandoc/Filter/Plot/Renderers/Matlab.hs11
-rw-r--r--src/Text/Pandoc/Filter/Plot/Renderers/Prelude.hs10
-rw-r--r--src/Text/Pandoc/Filter/Plot/Scripting.hs17
-rw-r--r--src/Text/Pandoc/Filter/Plot/Types.hs35
-rw-r--r--tests/Common.hs126
-rw-r--r--tests/Main.hs3
17 files changed, 400 insertions, 125 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index df14244..0e03c37 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,14 @@
pandoc-plot uses [Semantic Versioning](http://semver.org/spec/v2.0.0.html)
+Release 0.2.0.0
+---------------
+
+* Added support for gnuplot.
+* Added more tests for all toolkits.
+
+* Fixed an issue where the package could not be installed because a source file was not included in the cabal file.
+
Release 0.1.0.0
---------------
diff --git a/README.md b/README.md
index 43673a8..c9d15e8 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
# pandoc-plot
-## A Pandoc filter to generate figures from code blocks in documents using your plotting toolkit of choice
+## A Pandoc filter to generate figures from code blocks in documents
-[![Build status](https://ci.appveyor.com/api/projects/status/mmgiuk52j356e6jp?svg=true)](https://ci.appveyor.com/project/LaurentRDC/pandoc-plot) [![Build Status](https://dev.azure.com/laurentdecotret/pandoc-plot/_apis/build/status/LaurentRDC.pandoc-plot?branchName=master)](https://dev.azure.com/laurentdecotret/pandoc-plot/_build/latest?definitionId=5&branchName=master) ![GitHub](https://img.shields.io/github/license/LaurentRDC/pandoc-plot)
+[![Hackage version](https://img.shields.io/hackage/v/pandoc-plot.svg)](http://hackage.haskell.org/package/pandoc-plot) [![Build status](https://ci.appveyor.com/api/projects/status/mmgiuk52j356e6jp?svg=true)](https://ci.appveyor.com/project/LaurentRDC/pandoc-plot) [![Build Status](https://dev.azure.com/laurentdecotret/pandoc-plot/_apis/build/status/LaurentRDC.pandoc-plot?branchName=master)](https://dev.azure.com/laurentdecotret/pandoc-plot/_build/latest?definitionId=5&branchName=master) ![GitHub](https://img.shields.io/github/license/LaurentRDC/pandoc-plot)
-`pandoc-plot` turns code blocks present in your documents into embedded figures, using your plotting toolkit of choice, including Matplotlib, ggplot2, MATLAB, Mathematica, and more.
+`pandoc-plot` turns code blocks present in your documents (Markdown, LaTeX, etc.) into embedded figures, using your plotting toolkit of choice, including Matplotlib, ggplot2, MATLAB, Mathematica, and more.
## Table of content
@@ -47,9 +47,11 @@ Putting the above in `input.md`, we can then generate the plot and embed it in a
pandoc --filter pandoc-plot input.md --output output.html
```
+*Note that pandoc-plot only works with pandoc >= 2.8 because of some breaking changes in pandoc's API.*
+
## Supported toolkits
-`pandoc-plot` currently supports the following plotting toolkits (installed separately):
+`pandoc-plot` currently supports the following plotting toolkits (**installed separately**):
* `matplotlib`: plots using the [matplotlib](https://matplotlib.org/) Python library;
* `plotly_python` : plots using the [plotly](https://plot.ly/python/) Python library;
@@ -57,6 +59,8 @@ pandoc --filter pandoc-plot input.md --output output.html
* `mathplot` : plots using [Mathematica](https://www.wolfram.com/mathematica/);
* `octaveplot`: plots using [GNU Octave](https://www.gnu.org/software/octave/);
* `ggplot2`: plots using [ggplot2](https://ggplot2.tidyverse.org/);
+* `gnuplot`: plots using [gnuplot](http://www.gnuplot.info/);
+
To know which toolkits are useable on *your machine* (and which ones are not available), you can check with the `--toolkits/-t` flag:
@@ -64,13 +68,6 @@ To know which toolkits are useable on *your machine* (and which ones are not ava
pandoc-plot --toolkits
```
-### In progress
-
-Support for the following plotting toolkits is coming:
-
-* [gnuplot](http://www.gnuplot.info/)
-* [Plotly R](https://plot.ly/r/)
-
**Wish your plotting toolkit of choice was available? Please [raise an issue](https://github.com/LaurentRDC/pandoc-plot/issues)!**
## Features
@@ -82,7 +79,7 @@ You can also specify a caption for your image. This is done using the optional `
__Markdown__:
~~~markdown
-```{.matlabplot caption="This is a simple figure"}
+```{.matlabplot caption="This is a simple figure with a **Markdown** caption"}
x = 0: .1 : 2*pi;
y1 = cos(x);
y2 = sin(x);
@@ -95,7 +92,7 @@ plot(x, y1, 'b', x, y2, 'r-.', 'LineWidth', 2)
__LaTex__:
```latex
-\begin{minted}[caption=This is a simple figure]{matlabplot}
+\begin{minted}[caption=This is a simple figure with a \LaTeX caption]{matlabplot}
x = 0: .1 : 2*pi;
y1 = cos(x);
y2 = sin(x);
@@ -105,7 +102,7 @@ plot(x, y1, 'b', x, y2, 'r-.', 'LineWidth', 2)
\end{minted}
```
-Caption formatting is either plain text or Markdown. LaTeX-style math is also support in captions (using dollar signs $...$).
+Caption formatting should match the document formatting.
### Link to source code
@@ -273,17 +270,20 @@ matlabplot:
### Binaries
-Windows binaries are available on [GitHub](https://github.com/LaurentRDC/pandoc-plot/releases). Place the executable in a location that is in your PATH to be able to call it.
-
-If you can show me how to generate binaries for other platform using e.g. Azure Pipelines, let me know!
+*Coming soon*
### Installers (Windows)
-Windows installers are made available thanks to [Inno Setup](http://www.jrsoftware.org/isinfo.php). You can download them from the [release page](https://github.com/LaurentRDC/pandoc-plot/releases/latest).
+*Coming soon*
-### From Hackage/Stackage
+### From Hackage
-*Coming soon*
+`pandoc-plot` is available on [Hackage](http://hackage.haskell.org/package/pandoc-plot). Using the [`cabal-install`](https://www.haskell.org/cabal/) tool:
+
+```bash
+cabal update
+cabal install pandoc-plot
+```
### From source
diff --git a/example-config.yml b/example-config.yml
new file mode 100644
index 0000000..4736399
--- /dev/null
+++ b/example-config.yml
@@ -0,0 +1,56 @@
+
+# This is an example configuration. Everything in this file is optional.
+# Please refer to the documentation to know about the parameters herein.
+#
+# The `executable` parameter for all toolkits can be either the
+# executable name (if it is present on the PATH), or
+# the full path to the executable.
+# E.g.:
+# executable: python3
+# executable: "C:\Python37\Scripts\python.exe"
+#
+# Note that this file should be re-named to ".pandoc-plot.yml" before pandoc-plot
+# notices it.
+
+# The following parameters affect all toolkits
+directory: plots/
+source: false
+dpi: 80
+format: PNG
+
+# The possible parameters for the Matplotlib toolkit
+matplotlib:
+ # preamble: matplotlib.py
+ tight_bbox: false
+ transparent: false
+ executable: python
+
+# The possible parameters for the MATLAB toolkit
+matlabplot:
+ # preamble: matlab.m
+ executable: matlab
+
+# The possible parameters for the Plotly/Python toolkit
+plotly_python:
+ # preamble: plotly-python.py
+ executable: python
+
+# The possible parameters for the Mathematica toolkit
+mathplot:
+ # preamble: mathematica.m
+ executable: math
+
+# The possible parameters for the GNU Octave toolkit
+octaveplot:
+ # preamble: octave.m
+ executable: octave
+
+# The possible parameters for the ggplot2 toolkit
+ggplot2:
+ # preamble: ggplot2.r
+ executable: Rscript
+
+# The possible parameters for the gnuplot toolkit
+gnuplot:
+ # preamble: gnuplot.gp
+ executable: gnuplot
diff --git a/executable/Main.hs b/executable/Main.hs
index d15bdf5..3f85006 100644
--- a/executable/Main.hs
+++ b/executable/Main.hs
@@ -8,7 +8,7 @@ import Control.Applicative ((<|>))
import Control.Monad (join)
import Data.Default.Class (def)
-import Data.List (intersperse)
+import Data.List (intersperse, (\\))
import Data.Monoid ((<>))
import qualified Data.Text as T
import qualified Data.Text.IO as T
@@ -20,11 +20,10 @@ import System.Directory (doesFileExist)
import System.IO.Temp (writeSystemTempFile)
import Text.Pandoc.Filter.Plot (availableToolkits,
- plotTransform,
- unavailableToolkits)
+ plotTransform)
import Text.Pandoc.Filter.Plot.Internal (Toolkit (..), cls, Configuration(..),
supportedSaveFormats,
- configuration)
+ configuration, toolkits)
import Text.Pandoc.JSON (toJSONFilter)
@@ -106,9 +105,13 @@ run = do
go (Just Toolkits) _ = do
c <- config
putStrLn "\nAVAILABLE TOOLKITS\n"
- availableToolkits c >>= mapM_ toolkitInfo
+ available <- availableToolkits c
+ return available >>= mapM_ toolkitInfo
putStrLn "\nUNAVAILABLE TOOLKITS\n"
- unavailableToolkits c >>= mapM_ toolkitInfo
+ -- We don't use unavailableToolkits because this would force
+ -- more IO actions
+ let unavailable = toolkits \\ available
+ return unavailable >>= mapM_ toolkitInfo
go (Just Config) _ = T.writeFile ".example-pandoc-plot.yml" exampleConfig
go Nothing _ = toJSONFilterWithConfig
diff --git a/pandoc-plot.cabal b/pandoc-plot.cabal
index 008846a..e485882 100644
--- a/pandoc-plot.cabal
+++ b/pandoc-plot.cabal
@@ -1,5 +1,5 @@
name: pandoc-plot
-version: 0.1.0.0
+version: 0.2.0.0
cabal-version: >= 1.12
synopsis: A Pandoc filter to include figures generated from code blocks using your plotting toolkit of choice.
description: A Pandoc filter to include figures generated from code blocks. Keep the document and code in the same location. Output is captured and included as a figure.
@@ -16,6 +16,7 @@ extra-source-files:
LICENSE
README.md
stack.yaml
+ example-config.yml
source-repository head
type: git
@@ -39,6 +40,7 @@ library
Text.Pandoc.Filter.Plot.Renderers.Mathematica
Text.Pandoc.Filter.Plot.Renderers.Octave
Text.Pandoc.Filter.Plot.Renderers.GGPlot2
+ Text.Pandoc.Filter.Plot.Renderers.GNUPlot
hs-source-dirs:
src
ghc-options: -Wall -Wcompat
@@ -51,9 +53,11 @@ library
, hashable >= 1 && < 2
, pandoc >= 2.8 && < 3
, pandoc-types >= 1.20 && < 2
+ , parallel-io >= 0.3.3 && < 0.4
, shakespeare >= 2.0 && < 3
, temporary
, text >= 1 && < 2
+ , turtle >= 1.5 && < 2
, typed-process >= 0.2.1 && < 1
, yaml >= 0.8 && < 1
, mtl >= 2.2 && < 2.3
diff --git a/src/Text/Pandoc/Filter/Plot.hs b/src/Text/Pandoc/Filter/Plot.hs
index cab51b9..6446129 100644
--- a/src/Text/Pandoc/Filter/Plot.hs
+++ b/src/Text/Pandoc/Filter/Plot.hs
@@ -54,8 +54,13 @@ module Text.Pandoc.Filter.Plot (
, unavailableToolkits
) where
+import Control.Monad.IO.Class (liftIO)
import Control.Monad.Reader (runReaderT)
+import Data.Maybe (fromMaybe)
+
+import System.IO (hPutStrLn, stderr)
+
import Text.Pandoc.Definition
import Text.Pandoc.Walk (walkM)
@@ -65,34 +70,54 @@ import Text.Pandoc.Filter.Plot.Internal
-- | Highest-level function that can be walked over a Pandoc tree.
-- All code blocks that have the @.plot@ / @.plotly@ class will be considered
-- figures.
-makePlot :: Configuration -> Block -> IO Block
-makePlot conf block =
- maybe (return block) (\tk -> make tk conf block) (plotToolkit block)
+--
+-- The target document format determines how the figure captions should be parsed.
+-- By default (i.e. if Nothing), captions will be parsed as Markdown with LaTeX math @$...$@,
+makePlot :: Configuration -- ^ Configuration for default values
+ -> Maybe Format -- ^ Input document format
+ -> Block
+ -> IO Block
+makePlot conf mfmt block =
+ let fmt = fromMaybe (Format "markdown+tex_math_dollars") mfmt
+ in maybe (return block) (\tk -> make tk conf fmt block) (plotToolkit block)
-- | Walk over an entire Pandoc document, changing appropriate code blocks
-- into figures. Default configuration is used.
-plotTransform :: Configuration -> Pandoc -> IO Pandoc
-plotTransform = walkM . makePlot
+plotTransform :: Configuration -- ^ Configuration for default values
+ -> Maybe Format -- ^ Input document format
+ -> Pandoc
+ -> IO Pandoc
+plotTransform conf mfmt = walkM $ makePlot conf mfmt
-- | Force to use a particular toolkit to render appropriate code blocks.
-make :: Toolkit -> Configuration -> Block -> IO Block
-make tk conf block = do
- let runEnv = PlotEnv tk conf
- runReaderT (makePlot' block >>= either (fail . show) return) runEnv
+--
+-- Failing to render a figure does not stop the filter, so that you may run the filter
+-- on documents without having all necessary toolkits installed. In this case, error
+-- messages are printed to stderr, and blocks are left unchanged.
+make :: Toolkit
+ -> Configuration -- ^ Configuration for default values
+ -> Format -- ^ Input document format
+ -> Block
+ -> IO Block
+make tk conf fmt block = runReaderT (makePlot' block) (PlotEnv tk conf)
where
- makePlot' blk = do
- parsed <- parseFigureSpec blk
- maybe
- (return $ Right blk)
- (\s -> handleResult s <$> runScriptIfNecessary s)
- parsed
+ makePlot' :: Block -> PlotM Block
+ makePlot' blk
+ = parseFigureSpec blk
+ >>= maybe
+ (return blk)
+ (\s -> runScriptIfNecessary s >>= handleResult s)
where
- handleResult _ (ScriptChecksFailed msg) = Left $ ScriptChecksFailedError msg
- handleResult _ (ScriptFailure cmd code) = Left $ ScriptError cmd code
- handleResult spec ScriptSuccess = Right $ toImage spec
-
+ handleResult spec ScriptSuccess = return $ toImage fmt spec
+ handleResult _ (ScriptChecksFailed msg) = do
+ liftIO $ hPutStrLn stderr $ "pandoc-plot: The script check failed with message: " <> msg
+ return blk
+ handleResult _ (ScriptFailure _ code) = do
+ liftIO $ hPutStrLn stderr $ "pandoc-plot: The script failed with exit code " <> show code
+ return blk
+
-- | Possible errors returned by the filter
data PandocPlotError
diff --git a/src/Text/Pandoc/Filter/Plot/Configuration.hs b/src/Text/Pandoc/Filter/Plot/Configuration.hs
index 2fc34b0..908f386 100644
--- a/src/Text/Pandoc/Filter/Plot/Configuration.hs
+++ b/src/Text/Pandoc/Filter/Plot/Configuration.hs
@@ -50,6 +50,7 @@ data ConfigPrecursor = ConfigPrecursor
, _mathematicaPrec :: !MathematicaPrecursor
, _octavePrec :: !OctavePrecursor
, _ggplot2Prec :: !GGPlot2Precursor
+ , _gnuplotPrec :: !GNUPlotPrecursor
}
instance Default ConfigPrecursor where
@@ -65,6 +66,7 @@ instance Default ConfigPrecursor where
, _mathematicaPrec = def
, _octavePrec = def
, _ggplot2Prec = def
+ , _gnuplotPrec = def
}
@@ -80,6 +82,7 @@ data PlotlyPythonPrecursor = PlotlyPythonPrecursor {_plotlyPythonPreamble :: !(
data MathematicaPrecursor = MathematicaPrecursor {_mathematicaPreamble :: !(Maybe FilePath), _mathematicaExe :: !FilePath}
data OctavePrecursor = OctavePrecursor {_octavePreamble :: !(Maybe FilePath), _octaveExe :: !FilePath}
data GGPlot2Precursor = GGPlot2Precursor {_ggplot2Preamble :: !(Maybe FilePath), _ggplot2Exe :: !FilePath}
+data GNUPlotPrecursor = GNUPlotPrecursor {_gnuplotPreamble :: !(Maybe FilePath), _gnuplotExe :: !FilePath}
instance Default MatplotlibPrecursor where
@@ -90,36 +93,41 @@ instance Default PlotlyPythonPrecursor where def = PlotlyPythonPrecursor Nothin
instance Default MathematicaPrecursor where def = MathematicaPrecursor Nothing (mathematicaExe def)
instance Default OctavePrecursor where def = OctavePrecursor Nothing (octaveExe def)
instance Default GGPlot2Precursor where def = GGPlot2Precursor Nothing (ggplot2Exe def)
+instance Default GNUPlotPrecursor where def = GNUPlotPrecursor Nothing (gnuplotExe def)
instance FromJSON MatplotlibPrecursor where
parseJSON (Object v) =
MatplotlibPrecursor
- <$> v .:? (tshow MatplotlibPreambleK)
+ <$> v .:? (tshow PreambleK)
<*> v .:? (tshow MatplotlibTightBBoxK) .!= (matplotlibTightBBox def)
<*> v .:? (tshow MatplotlibTransparentK) .!= (matplotlibTransparent def)
- <*> v .:? (tshow MatplotlibExecutableK) .!= (matplotlibExe def)
+ <*> v .:? (tshow ExecutableK) .!= (matplotlibExe def)
parseJSON _ = fail $ mconcat ["Could not parse ", show Matplotlib, " configuration."]
instance FromJSON MatlabPrecursor where
- parseJSON (Object v) = MatlabPrecursor <$> v .:? (tshow MatlabPreambleK) <*> v .:? (tshow MatlabExecutableK) .!= (matlabExe def)
+ parseJSON (Object v) = MatlabPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (matlabExe def)
parseJSON _ = fail $ mconcat ["Could not parse ", show Matlab, " configuration."]
instance FromJSON PlotlyPythonPrecursor where
- parseJSON (Object v) = PlotlyPythonPrecursor <$> v .:? (tshow PlotlyPythonPreambleK) <*> v .:? (tshow PlotlyPythonExecutableK) .!= (plotlyPythonExe def)
+ parseJSON (Object v) = PlotlyPythonPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (plotlyPythonExe def)
parseJSON _ = fail $ mconcat ["Could not parse ", show PlotlyPython, " configuration."]
instance FromJSON MathematicaPrecursor where
- parseJSON (Object v) = MathematicaPrecursor <$> v .:? (tshow MathematicaPreambleK) <*> v .:? (tshow MathematicaExecutableK) .!= (mathematicaExe def)
+ parseJSON (Object v) = MathematicaPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (mathematicaExe def)
parseJSON _ = fail $ mconcat ["Could not parse ", show Mathematica, " configuration."]
instance FromJSON OctavePrecursor where
- parseJSON (Object v) = OctavePrecursor <$> v .:? (tshow OctavePreambleK) <*> v .:? (tshow OctaveExecutableK) .!= (octaveExe def)
+ parseJSON (Object v) = OctavePrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (octaveExe def)
parseJSON _ = fail $ mconcat ["Could not parse ", show Octave, " configuration."]
instance FromJSON GGPlot2Precursor where
- parseJSON (Object v) = GGPlot2Precursor <$> v .:? (tshow GGPlot2PreambleK) <*> v .:? (tshow GGPlot2ExecutableK) .!= (ggplot2Exe def)
+ parseJSON (Object v) = GGPlot2Precursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (ggplot2Exe def)
parseJSON _ = fail $ mconcat ["Could not parse ", show GGPlot2, " configuration."]
+instance FromJSON GNUPlotPrecursor where
+ parseJSON (Object v) = GNUPlotPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (ggplot2Exe def)
+ parseJSON _ = fail $ mconcat ["Could not parse ", show GNUPlot, " configuration."]
+
instance FromJSON ConfigPrecursor where
parseJSON (Null) = return def -- In case of empty file
@@ -136,6 +144,7 @@ instance FromJSON ConfigPrecursor where
_mathematicaPrec <- v .:? (cls Mathematica) .!= def
_octavePrec <- v .:? (cls Octave) .!= def
_ggplot2Prec <- v .:? (cls GGPlot2) .!= def
+ _gnuplotPrec <- v .:? (cls GNUPlot) .!= def
return $ ConfigPrecursor{..}
parseJSON _ = fail "Could not parse configuration."
@@ -157,6 +166,7 @@ renderConfig ConfigPrecursor{..} = do
mathematicaExe = _mathematicaExe _mathematicaPrec
octaveExe = _octaveExe _octavePrec
ggplot2Exe = _ggplot2Exe _ggplot2Prec
+ gnuplotExe = _gnuplotExe _gnuplotPrec
matplotlibPreamble <- readPreamble (_matplotlibPreamble _matplotlibPrec)
matlabPreamble <- readPreamble (_matlabPreamble _matlabPrec)
@@ -164,6 +174,7 @@ renderConfig ConfigPrecursor{..} = do
mathematicaPreamble <- readPreamble (_mathematicaPreamble _mathematicaPrec)
octavePreamble <- readPreamble (_octavePreamble _octavePrec)
ggplot2Preamble <- readPreamble (_ggplot2Preamble _ggplot2Prec)
+ gnuplotPreamble <- readPreamble (_gnuplotPreamble _gnuplotPrec)
return Configuration{..}
where
diff --git a/src/Text/Pandoc/Filter/Plot/Parse.hs b/src/Text/Pandoc/Filter/Plot/Parse.hs
index 5cf29d1..fd9f268 100644
--- a/src/Text/Pandoc/Filter/Plot/Parse.hs
+++ b/src/Text/Pandoc/Filter/Plot/Parse.hs
@@ -39,13 +39,11 @@ import Paths_pandoc_plot (version)
import System.FilePath (makeValid)
import Text.Pandoc.Definition (Block (..), Inline,
- Pandoc (..))
+ Pandoc (..), Format(..))
import Text.Pandoc.Class (runPure)
-import Text.Pandoc.Extensions (Extension (..),
- extensionsFromList)
import Text.Pandoc.Options (ReaderOptions (..))
-import Text.Pandoc.Readers (readMarkdown)
+import Text.Pandoc.Readers (getReader, Reader(..))
import Text.Pandoc.Filter.Plot.Renderers
import Text.Pandoc.Filter.Plot.Types
@@ -74,13 +72,12 @@ parseFigureSpec (CodeBlock (id', classes, attrs) content) = do
let extraAttrs' = parseExtraAttrs toolkit attrs'
header = comment toolkit $ "Generated by pandoc-plot " <> ((pack . showVersion) version)
defaultPreamble = preambleSelector toolkit conf
- -- Note that the default preamble changes based on the RendererM
- -- which is why we use @preambleSelector@ as the default value
+
includeScript <- fromMaybe
(return defaultPreamble)
((liftIO . TIO.readFile) <$> preamblePath)
let -- Filtered attributes that are not relevant to pandoc-plot
- -- This presumes that inclusionKeys includes ALL possible keys, for all renderers
+ -- This presumes that inclusionKeys includes ALL possible keys, for all toolkits
filteredAttrs = filter (\(k, _) -> k `notElem` (tshow <$> inclusionKeys)) attrs
defWithSource = defaultWithSource conf
defSaveFmt = defaultSaveFormat conf
@@ -112,26 +109,16 @@ plotToolkit (CodeBlock (_, classes, _) _) =
plotToolkit _ = Nothing
--- | Reader options for captions.
-readerOptions :: ReaderOptions
-readerOptions = def
- {readerExtensions =
- extensionsFromList
- [ Ext_tex_math_dollars
- , Ext_superscript
- , Ext_subscript
- , Ext_raw_tex
- ]
- }
-
-
--- | Read a figure caption in Markdown format. LaTeX math @$...$@ is supported,
--- as are Markdown subscripts and superscripts.
-captionReader :: Text -> Maybe [Inline]
-captionReader t = either (const Nothing) (Just . extractFromBlocks) $ runPure $ readMarkdown' t
+-- | Reader a caption, based on input document format
+captionReader :: Format -> Text -> Maybe [Inline]
+captionReader (Format f) t = either (const Nothing) (Just . extractFromBlocks) $ runPure $ do
+ (reader, exts) <- getReader f
+ let readerOpts = def {readerExtensions = exts}
+ -- Assuming no ByteString readers...
+ case reader of
+ TextReader fct -> fct readerOpts t
+ _ -> return mempty
where
- readMarkdown' = readMarkdown readerOptions
-
extractFromBlocks (Pandoc _ blocks) = mconcat $ extractInlines <$> blocks
extractInlines (Plain inlines) = inlines
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers.hs b/src/Text/Pandoc/Filter/Plot/Renderers.hs
index 51604df..ca03161 100644
--- a/src/Text/Pandoc/Filter/Plot/Renderers.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers.hs
@@ -25,10 +25,11 @@ module Text.Pandoc.Filter.Plot.Renderers (
, unavailableToolkits
) where
-import Control.Monad (filterM)
+import Control.Concurrent.ParallelIO.Local
import Data.List ((\\))
import Data.Map.Strict (Map)
+import Data.Maybe (catMaybes)
import Data.Text (Text)
import Text.Pandoc.Filter.Plot.Renderers.Mathematica
@@ -37,6 +38,7 @@ import Text.Pandoc.Filter.Plot.Renderers.Matplotlib
import Text.Pandoc.Filter.Plot.Renderers.Octave
import Text.Pandoc.Filter.Plot.Renderers.Plotly
import Text.Pandoc.Filter.Plot.Renderers.GGPlot2
+import Text.Pandoc.Filter.Plot.Renderers.GNUPlot
import Text.Pandoc.Filter.Plot.Types
@@ -49,6 +51,7 @@ scriptExtension Matlab = ".m"
scriptExtension Mathematica = ".m"
scriptExtension Octave = ".m"
scriptExtension GGPlot2 = ".r"
+scriptExtension GNUPlot = ".gp"
-- Make a string into a comment
@@ -59,6 +62,7 @@ comment Matlab = mappend "% "
comment Mathematica = \t -> mconcat ["(*", t, "*)"]
comment Octave = mappend "% "
comment GGPlot2 = mappend "# "
+comment GNUPlot = mappend "# "
-- | The function that maps from configuration to the preamble.
@@ -69,6 +73,7 @@ preambleSelector Matlab = matlabPreamble
preambleSelector Mathematica = mathematicaPreamble
preambleSelector Octave = octavePreamble
preambleSelector GGPlot2 = ggplot2Preamble
+preambleSelector GNUPlot = gnuplotPreamble
-- | Save formats supported by this renderer.
@@ -79,6 +84,7 @@ supportedSaveFormats Matlab = matlabSupportedSaveFormats
supportedSaveFormats Mathematica = mathematicaSupportedSaveFormats
supportedSaveFormats Octave = octaveSupportedSaveFormats
supportedSaveFormats GGPlot2 = ggplot2SupportedSaveFormats
+supportedSaveFormats GNUPlot = gnuplotSupportedSaveFormats
-- Checks to perform before running a script. If ANY check fails,
@@ -103,6 +109,7 @@ command Matlab = matlabCommand
command Mathematica = mathematicaCommand
command Octave = octaveCommand
command GGPlot2 = ggplot2Command
+command GNUPlot = gnuplotCommand
-- | Script fragment required to capture a figure.
@@ -113,6 +120,7 @@ capture Matlab = matlabCapture
capture Mathematica = mathematicaCapture
capture Octave = octaveCapture
capture GGPlot2 = ggplot2Capture
+capture GNUPlot = gnuplotCapture
-- | Check if a toolkit is available, based on the current configuration
@@ -123,14 +131,28 @@ toolkitAvailable Matlab = matlabAvailable
toolkitAvailable Mathematica = mathematicaAvailable
toolkitAvailable Octave = octaveAvailable
toolkitAvailable GGPlot2 = ggplot2Available
+toolkitAvailable GNUPlot = gnuplotAvailable
-- | List of toolkits available on this machine.
-- The executables to look for are taken from the configuration.
availableToolkits :: Configuration -> IO [Toolkit]
-availableToolkits conf = filterM (\tk -> toolkitAvailable tk conf) toolkits
-
+availableToolkits conf = do
+ -- Certain toolkits (e.g. Python-based toolkits)
+ -- may take a long time to startup.
+ -- Therefore, we check for available of toolkits in parallel.
+ -- TODO: benchmark. Is this overkill?
+ maybeToolkits <- withPool (length toolkits) $
+ \pool -> parallel pool (maybeToolkit <$> toolkits)
+ return $ catMaybes maybeToolkits
+ where
+ maybeToolkit tk = do
+ available <- toolkitAvailable tk conf
+ if available
+ then return $ Just tk
+ else return Nothing
+
-- | List of toolkits not available on this machine.
-- The executables to look for are taken from the configuration.
unavailableToolkits :: Configuration -> IO [Toolkit]
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/GNUPlot.hs b/src/Text/Pandoc/Filter/Plot/Renderers/GNUPlot.hs
new file mode 100644
index 0000000..70beab7
--- /dev/null
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/GNUPlot.hs
@@ -0,0 +1,41 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE RecordWildCards #-}
+{-|
+Module : $header$
+Copyright : (c) Laurent P René de Cotret, 2020
+License : GNU GPL, version 2 or above
+Maintainer : laurent.decotret@outlook.com
+Stability : internal
+Portability : portable
+
+Rendering gnuplot plots code blocks
+-}
+
+module Text.Pandoc.Filter.Plot.Renderers.GNUPlot (
+ gnuplotSupportedSaveFormats
+ , gnuplotCommand
+ , gnuplotCapture
+ , gnuplotAvailable
+) where
+
+import Text.Pandoc.Filter.Plot.Renderers.Prelude
+
+gnuplotSupportedSaveFormats :: [SaveFormat]
+gnuplotSupportedSaveFormats = [PNG] -- TODO: support more save formats
+
+
+gnuplotCommand :: Configuration -> FigureSpec -> FilePath -> Text
+gnuplotCommand Configuration{..} _ fp = [st|#{gnuplotExe} -c #{fp}|]
+
+
+gnuplotAvailable :: Configuration -> IO Bool
+gnuplotAvailable Configuration{..} = commandSuccess [st|#{gnuplotExe} -h|] -- TODO: test this
+
+
+gnuplotCapture :: FigureSpec -> FilePath -> Script
+gnuplotCapture FigureSpec{..} fname = [st|
+set term png
+set output '#{fname}'
+|]
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/Mathematica.hs b/src/Text/Pandoc/Filter/Plot/Renderers/Mathematica.hs
index de22386..5660c22 100644
--- a/src/Text/Pandoc/Filter/Plot/Renderers/Mathematica.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/Mathematica.hs
@@ -36,5 +36,5 @@ mathematicaAvailable Configuration{..} = commandSuccess [st|#{mathematicaExe} -h
mathematicaCapture :: FigureSpec -> FilePath -> Script
mathematicaCapture FigureSpec{..} fname = [st|
-Export["#{fname}", %, show saveFormat]
+Export["#{fname}", %, #{show saveFormat}]
|]
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/Matlab.hs b/src/Text/Pandoc/Filter/Plot/Renderers/Matlab.hs
index 62c00b7..f518855 100644
--- a/src/Text/Pandoc/Filter/Plot/Renderers/Matlab.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/Matlab.hs
@@ -31,8 +31,17 @@ matlabCommand :: Configuration -> FigureSpec -> FilePath -> Text
matlabCommand Configuration{..} _ fp = [st|#{matlabExe} -batch "run('#{fp}')"|]
+-- On Windows at least, "matlab -help" actually returns -1, even though the
+-- help text is shown successfully!
+-- Therefore, we cannot rely on this behavior to know if matlab is present,
+-- like other toolkits.
matlabAvailable :: Configuration -> IO Bool
-matlabAvailable Configuration{..} = commandSuccess [st|#{matlabExe} -h|]
+matlabAvailable Configuration{..} = existsOnPath (matlabExe <> ext)
+ where
+ -- The @which@ function from Turtle only works on
+ -- windows if the executable extension is included.
+ ext = if isWindows then ".exe" else mempty
+
matlabCapture :: FigureSpec -> FilePath -> Script
matlabCapture FigureSpec{..} fname = [st|
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/Prelude.hs b/src/Text/Pandoc/Filter/Plot/Renderers/Prelude.hs
index 38c1ce0..b0faeb1 100644
--- a/src/Text/Pandoc/Filter/Plot/Renderers/Prelude.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/Prelude.hs
@@ -18,8 +18,10 @@ module Text.Pandoc.Filter.Plot.Renderers.Prelude (
, st
, unpack
, commandSuccess
+ , existsOnPath
) where
+import Data.Maybe (isJust)
import Data.Text (Text, unpack)
import System.Exit (ExitCode(..))
import System.Process.Typed (runProcess, shell,
@@ -27,6 +29,7 @@ import System.Process.Typed (runProcess, shell,
nullStream)
import Text.Shakespeare.Text (st)
+import qualified Turtle as Sh
import Text.Pandoc.Filter.Plot.Types
@@ -39,4 +42,9 @@ commandSuccess s = do
$ setStdout nullStream
$ setStderr nullStream
$ shell (unpack s)
- return $ ec == ExitSuccess \ No newline at end of file
+ return $ ec == ExitSuccess
+
+
+-- | Checks that an executable is available on path, at all.
+existsOnPath :: FilePath -> IO Bool
+existsOnPath fp = Sh.which (Sh.fromString fp) >>= fmap isJust . return \ No newline at end of file
diff --git a/src/Text/Pandoc/Filter/Plot/Scripting.hs b/src/Text/Pandoc/Filter/Plot/Scripting.hs
index 9a089cb..3fa90b5 100644
--- a/src/Text/Pandoc/Filter/Plot/Scripting.hs
+++ b/src/Text/Pandoc/Filter/Plot/Scripting.hs
@@ -38,7 +38,7 @@ import System.Process.Typed (runProcess, shell, setStdout
import Text.Pandoc.Builder (fromList, imageWith, link,
para, toList)
-import Text.Pandoc.Definition (Block (..))
+import Text.Pandoc.Definition (Block (..), Format)
import Text.Pandoc.Filter.Plot.Parse (captionReader)
import Text.Pandoc.Filter.Plot.Renderers
@@ -84,7 +84,12 @@ runTempScript spec@FigureSpec{..} = do
-- so that there is never any collision
scriptPath <- tempScriptPath spec
let captureFragment = (capture tk) spec (figurePath spec)
- scriptWithCapture = mconcat [script, "\n", captureFragment]
+ -- Note: for gnuplot, the capture string must be placed
+ -- BEFORE plotting happens. Since this is only really an
+ -- issue for gnuplot, we have a special case.
+ scriptWithCapture = if (tk == GNUPlot)
+ then mconcat [captureFragment, "\n", script]
+ else mconcat [script, "\n", captureFragment]
liftIO $ T.writeFile scriptPath scriptWithCapture
let command_ = T.unpack $ command tk conf spec scriptPath
@@ -100,8 +105,10 @@ runTempScript spec@FigureSpec{..} = do
-- | Convert a @FigureSpec@ to a Pandoc block component.
-- Note that the script to generate figure files must still
-- be run in another function.
-toImage :: FigureSpec -> Block
-toImage spec = head . toList $ para $ imageWith attrs' (T.pack target') "fig:" caption'
+toImage :: Format -- ^ text format of the caption
+ -> FigureSpec
+ -> Block
+toImage fmt spec = head . toList $ para $ imageWith attrs' (T.pack target') "fig:" caption'
-- To render images as figures with captions, the target title
-- must be "fig:"
-- Janky? yes
@@ -110,7 +117,7 @@ toImage spec = head . toList $ para $ imageWith attrs' (T.pack target') "fig:" c
target' = figurePath spec
withSource' = withSource spec
srcLink = link (T.pack $ replaceExtension target' ".txt") mempty "Source code"
- captionText = fromList $ fromMaybe mempty (captionReader $ caption spec)
+ captionText = fromList $ fromMaybe mempty (captionReader fmt $ caption spec)
captionLinks = mconcat [" (", srcLink, ")"]
caption' = if withSource' then captionText <> captionLinks else captionText
diff --git a/src/Text/Pandoc/Filter/Plot/Types.hs b/src/Text/Pandoc/Filter/Plot/Types.hs
index 78b3660..faa1ccc 100644
--- a/src/Text/Pandoc/Filter/Plot/Types.hs
+++ b/src/Text/Pandoc/Filter/Plot/Types.hs
@@ -58,6 +58,7 @@ data Toolkit
| Mathematica
| Octave
| GGPlot2
+ | GNUPlot
deriving (Bounded, Eq, Enum, Generic)
-- | This instance should only be used to display toolkit names
@@ -68,6 +69,7 @@ instance Show Toolkit where
show Mathematica = "Mathematica"
show Octave = "GNU Octave"
show GGPlot2 = "ggplot2"
+ show GNUPlot = "gnuplot"
-- | Class name which will trigger the filter
cls :: Toolkit -> Text
@@ -77,13 +79,14 @@ cls PlotlyPython = "plotly_python"
cls Mathematica = "mathplot"
cls Octave = "octaveplot"
cls GGPlot2 = "ggplot2"
+cls GNUPlot = "gnuplot"
type PlotM a = ReaderT PlotEnv IO a
data PlotEnv
- = PlotEnv { toolkit :: !Toolkit
- , config :: !Configuration
+ = PlotEnv { toolkit :: !Toolkit
+ , config :: !Configuration
}
data Configuration = Configuration
@@ -98,6 +101,7 @@ data Configuration = Configuration
, mathematicaPreamble :: !Script
, octavePreamble :: !Script
, ggplot2Preamble :: !Script
+ , gnuplotPreamble :: !Script
-- Toolkit executables
, matplotlibExe :: !FilePath
, matlabExe :: !FilePath
@@ -105,6 +109,7 @@ data Configuration = Configuration
, mathematicaExe :: !FilePath
, octaveExe :: !FilePath
, ggplot2Exe :: !FilePath
+ , gnuplotExe :: !FilePath
-- Toolkit-specific options
, matplotlibTightBBox :: !Bool
, matplotlibTransparent :: !Bool
@@ -123,6 +128,7 @@ instance Default Configuration where
, mathematicaPreamble = mempty
, octavePreamble = mempty
, ggplot2Preamble = mempty
+ , gnuplotPreamble = mempty
-- Toolkit executables
-- Default values are executable names as if on the PATH
, matplotlibExe = if isWindows then "python" else "python3"
@@ -131,6 +137,7 @@ instance Default Configuration where
, mathematicaExe = "math"
, octaveExe = "octave"
, ggplot2Exe = "Rscript"
+ , gnuplotExe = "gnuplot"
-- Toolkit-specific
, matplotlibTightBBox = False
, matplotlibTransparent = False
@@ -164,18 +171,6 @@ data InclusionKey
| ExecutableK
| MatplotlibTightBBoxK
| MatplotlibTransparentK
- | MatplotlibPreambleK
- | MatplotlibExecutableK
- | PlotlyPythonPreambleK
- | PlotlyPythonExecutableK
- | MatlabPreambleK
- | MatlabExecutableK
- | MathematicaPreambleK
- | MathematicaExecutableK
- | OctavePreambleK
- | OctaveExecutableK
- | GGPlot2PreambleK
- | GGPlot2ExecutableK
deriving (Bounded, Eq, Enum)
-- | Keys that pandoc-plot will look for in code blocks.
@@ -190,18 +185,6 @@ instance Show InclusionKey where
show ExecutableK = "executable"
show MatplotlibTightBBoxK = "tight_bbox"
show MatplotlibTransparentK = "transparent"
- show MatplotlibPreambleK = show PreambleK
- show PlotlyPythonPreambleK = show PreambleK
- show MatlabPreambleK = show PreambleK
- show MathematicaPreambleK = show PreambleK
- show OctavePreambleK = show PreambleK
- show GGPlot2PreambleK = show PreambleK
- show MatplotlibExecutableK = show ExecutableK
- show MatlabExecutableK = show ExecutableK
- show PlotlyPythonExecutableK = show ExecutableK
- show MathematicaExecutableK = show ExecutableK
- show OctaveExecutableK = show ExecutableK
- show GGPlot2ExecutableK = show ExecutableK
-- | List of all keys related to pandoc-plot that
diff --git a/tests/Common.hs b/tests/Common.hs
index c1741e3..1321399 100644
--- a/tests/Common.hs
+++ b/tests/Common.hs
@@ -9,6 +9,7 @@ import Control.Monad.Reader
import Data.Default.Class (def)
import Data.List (isInfixOf, isSuffixOf)
import Data.Monoid ((<>))
+import Data.String (fromString)
import Data.Text (Text, pack, unpack)
import Test.Tasty
@@ -30,17 +31,20 @@ import System.Directory (createDirectory,
import System.FilePath (takeExtensions, (</>))
import System.IO.Temp (getCanonicalTemporaryDirectory)
+-- Default caption format
+defFmt = B.Format "markdown+tex_math_dollars"
-------------------------------------------------------------------------------
-- Test that plot files and source files are created when the filter is run
testFileCreation :: Toolkit -> TestTree
testFileCreation tk =
testCase "writes output files in appropriate directory" $ do
- tempDir <- (</> "test-file-creation") <$> getCanonicalTemporaryDirectory
+ let postfix = unpack . cls $ tk
+ tempDir <- (</> "test-file-creation-" <> postfix) <$> getCanonicalTemporaryDirectory
ensureDirectoryExistsAndEmpty tempDir
let cb = (addDirectory tempDir $ codeBlock tk (trivialContent tk))
- _ <- (make tk) def cb
+ _ <- (make tk) def defFmt cb
filesCreated <- length <$> listDirectory tempDir
assertEqual "" 2 filesCreated
@@ -49,12 +53,13 @@ testFileCreation tk =
testFileInclusion :: Toolkit -> TestTree
testFileInclusion tk =
testCase "includes plot inclusions" $ do
- tempDir <- (</> "test-file-inclusion") <$> getCanonicalTemporaryDirectory
+ let postfix = unpack . cls $ tk
+ tempDir <- (</> "test-file-inclusion-" <> postfix) <$> getCanonicalTemporaryDirectory
ensureDirectoryExistsAndEmpty tempDir
- let cb = (addInclusion (include tk) $
+ let cb = (addPreamble (include tk) $
addDirectory tempDir $ codeBlock tk (trivialContent tk))
- _ <- (make tk) def cb
+ _ <- (make tk) def defFmt cb
inclusion <- readFile (include tk)
sourcePath <- head . filter (isExtensionOf "txt") <$> listDirectory tempDir
src <- readFile (tempDir </> sourcePath)
@@ -66,23 +71,118 @@ testFileInclusion tk =
include Mathematica = "tests/includes/mathplot.m"
include Octave = "tests/includes/octave.m"
include GGPlot2 = "tests/includes/ggplot2.r"
+ include GNUPlot = "tests/includes/gnuplot.gp"
-------------------------------------------------------------------------------
-- Test that the files are saved in the appropriate format
testSaveFormat :: Toolkit -> TestTree
testSaveFormat tk =
testCase "saves in the appropriate format" $ do
- tempDir <- (</> "test-safe-format") <$> getCanonicalTemporaryDirectory
+ let postfix = unpack . cls $ tk
+ tempDir <- (</> "test-safe-format-" <> postfix) <$> getCanonicalTemporaryDirectory
ensureDirectoryExistsAndEmpty tempDir
let fmt = head (supportedSaveFormats tk)
cb = (addSaveFormat fmt $
addDirectory tempDir $ codeBlock tk (trivialContent tk))
- _ <- (make tk) def cb
+ _ <- (make tk) def defFmt cb
numberjpgFiles <-
length <$> filter (isExtensionOf (extension fmt)) <$>
listDirectory tempDir
assertEqual "" numberjpgFiles 1
+-------------------------------------------------------------------------------
+-- Test that it is possible to not render source links in captions
+testWithSource :: Toolkit -> TestTree
+testWithSource tk =
+ testCase "appropriately omits links to source code" $ do
+ let postfix = unpack . cls $ tk
+ tempDir <- (</> "test-caption-links-" <> postfix) <$> getCanonicalTemporaryDirectory
+ ensureDirectoryExistsAndEmpty tempDir
+
+ let expected = "caption content"
+ noSource = addWithSource False
+ $ addDirectory tempDir
+ $ addCaption expected
+ $ codeBlock tk (trivialContent tk)
+ withSource = addWithSource True
+ $ addDirectory tempDir
+ $ addCaption expected
+ $ codeBlock tk (trivialContent tk)
+ blockNoSource <- (make tk) def defFmt noSource
+ blockWithSource <- (make tk) def defFmt withSource
+
+ -- In the case where source=false, the caption is used verbatim.
+ -- Otherwise, links will be appended to the caption; hence, the caption
+ -- is no longer equal to the initial value
+ assertEqual "" (B.toList $ fromString expected) (extractCaption blockNoSource)
+ assertNotEqual "" (B.toList $ fromString expected) (extractCaption blockWithSource)
+
+ where
+ extractCaption (B.Para blocks) = extractImageCaption . head $ blocks
+ extractCaption _ = mempty
+
+ extractImageCaption (Image _ c _) = c
+ extractImageCaption _ = mempty
+
+-------------------------------------------------------------------------------
+-- Test that parameters in code blocks will override the defaults in configuration
+testOverrideConfiguration :: Toolkit -> TestTree
+testOverrideConfiguration tk =
+ -- We set the default save format to JPG via the configuration,
+ -- but set the code block parameter to PNG.
+ -- Therefore, after the filter has been used, there should be one PNG file and
+ -- no JPG files.
+ testCase "code block attributes override configuration defaults" $ do
+ let postfix = unpack . cls $ tk
+ tempDir <- (</> "test-caption-links-" <> postfix) <$> getCanonicalTemporaryDirectory
+ ensureDirectoryExistsAndEmpty tempDir
+
+ let config = (def::Configuration) { defaultDirectory = tempDir
+ , defaultSaveFormat = JPG}
+
+ -- Not all toolkits support both save formats
+ when ( JPG `elem` supportedSaveFormats tk
+ && PNG `elem` supportedSaveFormats tk
+ ) $ do
+
+ let cb = addDirectory tempDir
+ $ addSaveFormat PNG
+ $ codeBlock tk (trivialContent tk)
+ _ <- (make tk) config defFmt cb
+
+ numberPngFiles <-
+ length <$> filter (isExtensionOf (extension PNG)) <$>
+ listDirectory (defaultDirectory config)
+ numberJpgFiles <-
+ length <$> filter (isExtensionOf (extension JPG)) <$>
+ listDirectory (defaultDirectory config)
+ assertEqual "" numberPngFiles 1
+ assertEqual "" numberJpgFiles 0
+
+-------------------------------------------------------------------------------
+-- Test that Markdown formatting in captions is correctly rendered
+testMarkdownFormattingCaption :: Toolkit -> TestTree
+testMarkdownFormattingCaption tk =
+ testCase "appropriately parses captions" $ do
+ let postfix = unpack . cls $ tk
+ tempDir <- (</> "test-caption-parsing-" <> postfix) <$> getCanonicalTemporaryDirectory
+ ensureDirectoryExistsAndEmpty tempDir
+
+ -- Note that this test is fragile, in the sense that the expected result must be carefully
+ -- constructed
+ let expected = [B.Strong [B.Str "caption"]]
+ cb = addDirectory tempDir
+ $ addCaption "**caption**"
+ $ codeBlock tk (trivialContent tk)
+ fmt = B.Format "markdown+tex_math_dollars"
+ result <- (make tk) def fmt cb
+ assertIsInfix expected (extractCaption result)
+ where
+ extractCaption (B.Para blocks) = extractImageCaption . head $ blocks
+ extractCaption _ = mempty
+
+ extractImageCaption (Image _ c _) = c
+ extractImageCaption _ = mempty
codeBlock :: Toolkit -> Script -> Block
@@ -96,6 +196,7 @@ trivialContent Matlab = "figure('visible', 'off')\n"
trivialContent Mathematica = "\n"
trivialContent Octave = "figure('visible', 'off')\nplot (-10:0.1:10);"
trivialContent GGPlot2 = "library(ggplot2)\nggplot()\n"
+trivialContent GNUPlot = "plot sin(x)"
addCaption :: String -> Block -> Block
@@ -108,8 +209,8 @@ addDirectory dir (CodeBlock (id', cls, attrs) script) =
CodeBlock (id', cls, attrs ++ [(tshow DirectoryK, pack dir)]) script
-addInclusion :: FilePath -> Block -> Block
-addInclusion inclusionPath (CodeBlock (id', cls, attrs) script) =
+addPreamble :: FilePath -> Block -> Block
+addPreamble inclusionPath (CodeBlock (id', cls, attrs) script) =
CodeBlock (id', cls, attrs ++ [(tshow PreambleK, pack inclusionPath)]) script
@@ -137,6 +238,13 @@ assertFileExists filepath = do
msg = mconcat ["File ", filepath, " does not exist."]
+-- | Assert not equal
+assertNotEqual :: (HasCallStack, Eq a, Show a) => String -> a -> a -> Assertion
+assertNotEqual msg expected actual =
+ unless (expected /= actual)
+ (assertFailure $ mconcat [msg, ": expected ", show expected, " but got ", show actual])
+
+
-- | Not available with GHC < 8.4
-- since this function was added in filepath-1.4.2
-- but GHC 8.2.2 comes with filepath-1.4.1.2
diff --git a/tests/Main.hs b/tests/Main.hs
index 5d105b7..0251bc7 100644
--- a/tests/Main.hs
+++ b/tests/Main.hs
@@ -38,6 +38,9 @@ toolkitSuite tk =
[ testFileCreation
, testFileInclusion
, testSaveFormat
+ , testWithSource
+ , testOverrideConfiguration
+ , testMarkdownFormattingCaption
] <*> [tk]