pandoc-plot 1.0.0.0 → 1.0.1.0
raw patch · 40 files changed
+3234/−2818 lines, 40 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Text.Pandoc.Filter.Plot: [sourceCodeLabel] :: Configuration -> !Text
+ Text.Pandoc.Filter.Plot.Internal: SourceCodeLabelK :: InclusionKey
+ Text.Pandoc.Filter.Plot.Internal: [sourceCodeLabel] :: Configuration -> !Text
+ Text.Pandoc.Filter.Plot.Internal: language :: Toolkit -> Text
+ Text.Pandoc.Filter.Plot.Internal: sourceCodePath :: FigureSpec -> PlotM FilePath
- Text.Pandoc.Filter.Plot: Configuration :: !FilePath -> !Bool -> !Int -> !SaveFormat -> ![FilePath] -> !Format -> !Verbosity -> !LogSink -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !Bool -> !Bool -> Configuration
+ Text.Pandoc.Filter.Plot: Configuration :: !FilePath -> !Bool -> !Int -> !SaveFormat -> ![FilePath] -> !Format -> !Text -> !Verbosity -> !LogSink -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !Bool -> !Bool -> Configuration
- Text.Pandoc.Filter.Plot.Internal: Configuration :: !FilePath -> !Bool -> !Int -> !SaveFormat -> ![FilePath] -> !Format -> !Verbosity -> !LogSink -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !Bool -> !Bool -> Configuration
+ Text.Pandoc.Filter.Plot.Internal: Configuration :: !FilePath -> !Bool -> !Int -> !SaveFormat -> ![FilePath] -> !Format -> !Text -> !Verbosity -> !LogSink -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !Script -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !FilePath -> !Bool -> !Bool -> Configuration
Files
- CHANGELOG.md +7/−0
- MANUAL.md +44/−37
- README.md +1/−1
- benchmark/MatplotlibGallery.hs +11/−14
- benchmark/bench.hs +39/−22
- data/srctemplate.html +241/−0
- docs/MANUAL.html +134/−126
- example-config.yml +6/−0
- executable/ExampleConfig.hs +11/−13
- executable/Main.hs +253/−223
- executable/ManPage.hs +24/−19
- executable/OpenFile.hs +24/−30
- pandoc-plot.cabal +11/−3
- src/Text/Pandoc/Filter/Plot.hs +157/−132
- src/Text/Pandoc/Filter/Plot/Clean.hs +123/−124
- src/Text/Pandoc/Filter/Plot/Configuration.hs +231/−233
- src/Text/Pandoc/Filter/Plot/Embed.hs +101/−97
- src/Text/Pandoc/Filter/Plot/Internal.hs +24/−25
- src/Text/Pandoc/Filter/Plot/Monad.hs +253/−212
- src/Text/Pandoc/Filter/Plot/Monad/Logging.hs +89/−88
- src/Text/Pandoc/Filter/Plot/Monad/Types.hs +168/−164
- src/Text/Pandoc/Filter/Plot/Parse.hs +120/−120
- src/Text/Pandoc/Filter/Plot/Renderers.hs +138/−137
- src/Text/Pandoc/Filter/Plot/Renderers/Bokeh.hs +45/−49
- src/Text/Pandoc/Filter/Plot/Renderers/GGPlot2.hs +34/−37
- src/Text/Pandoc/Filter/Plot/Renderers/GNUPlot.hs +30/−34
- src/Text/Pandoc/Filter/Plot/Renderers/Graphviz.hs +31/−35
- src/Text/Pandoc/Filter/Plot/Renderers/Mathematica.hs +28/−31
- src/Text/Pandoc/Filter/Plot/Renderers/Matlab.hs +25/−30
- src/Text/Pandoc/Filter/Plot/Renderers/Matplotlib.hs +57/−62
- src/Text/Pandoc/Filter/Plot/Renderers/Octave.hs +28/−32
- src/Text/Pandoc/Filter/Plot/Renderers/PlotlyPython.hs +38/−42
- src/Text/Pandoc/Filter/Plot/Renderers/PlotlyR.hs +39/−43
- src/Text/Pandoc/Filter/Plot/Renderers/Plotsjl.hs +28/−32
- src/Text/Pandoc/Filter/Plot/Renderers/Prelude.hs +50/−51
- src/Text/Pandoc/Filter/Plot/Scripting.hs +168/−143
- src/Text/Pandoc/Filter/Plot/Scripting/Template.hs +17/−0
- stack.yaml +9/−6
- tests/Common.hs +275/−248
- tests/Main.hs +122/−123
CHANGELOG.md view
@@ -2,6 +2,13 @@ pandoc-plot uses [Semantic Versioning](http://semver.org/spec/v2.0.0.html) +Release 1.0.1.0 +--------------- + +* Added the ability to change the "Source code" label to other languages via configuration. +* Added syntax highlighting to the linked source code. +* Fixed an issue where code blocks with unicode symbols (e.g. greek letters) would trip up pandoc-plot (#16). + Release 1.0.0.0 ---------------
MANUAL.md view
@@ -6,21 +6,21 @@ `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. - - [Features Overview](#features-overview) - - [Captions](#captions) - - [Link to source code](#link-to-source-code) - - [Preamble scripts](#preamble-scripts) - - [Support for interactive plots](#support-for-interactive-plots) - - [Performance](#performance) - - [Compatibility with pandoc-crossref](#compatibility-with-pandoc-crossref) - - [Detailed usage](#detailed-usage) - - [As a filter](#as-a-filter) - - [Syntax](#syntax) - - [Parameters and options](#parameters-and-options) - - [Interactive HTML figures](#interactive-html-figures) - - [Configuration](#configuration) - - [Other commands](#other-commands) - - [Warning](#warning) +- [Features Overview](#features-overview) + - [Captions](#captions) + - [Link to source code](#link-to-source-code) + - [Preamble scripts](#preamble-scripts) + - [Support for interactive plots](#support-for-interactive-plots) + - [Performance](#performance) + - [Compatibility with pandoc-crossref](#compatibility-with-pandoc-crossref) +- [Detailed usage](#detailed-usage) + - [As a filter](#as-a-filter) + - [Syntax](#syntax) + - [Parameters and options](#parameters-and-options) + - [Interactive HTML figures](#interactive-html-figures) + - [Configuration](#configuration) + - [Other commands](#other-commands) +- [Warning](#warning) ## Features Overview @@ -120,7 +120,7 @@ \end{minted} ``` -This `preamble` parameter is perfect for longer documents with many plots. Simply define the style you want in a separate script\! You can also import packages this way, or define functions you often use. +This `preamble` parameter is perfect for longer documents with many plots. Simply define the style you want in a separate script! You can also import packages this way, or define functions you often use. ### Support for interactive plots @@ -128,7 +128,7 @@ ### Performance -`pandoc-plot` minimizes work, only generating figures if it absolutely must, i.e. if the content has changed. `pandoc-plot` will save the hash of the source code used to generate a figure in its filename. Before generating a figure, `pandoc-plot` will check it this figure already exists based on the hash of its source\! This also means that there is no way to directly name figures. +`pandoc-plot` minimizes work, only generating figures if it absolutely must, i.e. if the content has changed. `pandoc-plot` will save the hash of the source code used to generate a figure in its filename. Before generating a figure, `pandoc-plot` will check it this figure already exists based on the hash of its source! This also means that there is no way to directly name figures. Moreover, starting with version 0.5.0.0, `pandoc-plot` takes advantage of multicore CPUs, rendering figures **in parallel**. @@ -159,7 +159,7 @@ `pandoc-plot` is a command line executable with a few functions. You can take a look at the help using the `-h`/`--help` flag: ``` bash -pandoc-plot 0.9.4.0 - generate figures directly in documents +pandoc-plot 1.0.1.0 - generate figures directly in documents Usage: pandoc-plot.EXE [(-v|--version) | --full-version | (-m|--manual)] [COMMAND] [AST] @@ -235,7 +235,8 @@ directory=(path) caption=(text) format=(PNG|PDF|SVG|JPG|EPS|GIF|TIF|WEBP|HTML) - source=(true|false) + source=(true|True|false|False) + source_label=(text) preamble=(path) dpi=(integer) dependencies=[...] @@ -247,21 +248,22 @@ ``` ```` - - `cls` must be one of the following: `matplotlib`, `matlabplot`, `plotly_python`, `plotly_r`, `mathplot`, `octaveplot`, `ggplot2`, `gnuplot`, `graphviz`, `bokeh`, `plotsjl`. +- `cls` must be one of the following: `matplotlib`, `matlabplot`, `plotly_python`, `plotly_r`, `mathplot`, `octaveplot`, `ggplot2`, `gnuplot`, `graphviz`, `bokeh`, `plotsjl`. -All following parameters are optional, with their default values controlled by the [configuration](#configuration) +All following parameters are optional, with their default values controlled by the [configuration](#configuration). - - `language` specifies the programming language used in this block. This parameter is ignored by `pandoc-plot`, but your text editor may use it to highlight code. See [Code highlighting](#code-highlighting) below. - - `directory` is a path to the directory where the figure and source code will be saved. You cannot control the file name. This path is either absolute, or relative from the working directory where you call `pandoc-plot`. - - `caption` is the caption text. The format of the caption is specified in the `caption_format` parameter, described below. - - `format` is the desired filetype for the resulting figure. Possible values for `format` are \[`PNG`, `PDF`, `SVG`, `JPG`, `EPS`, `GIF`, `TIF`, `WEBP`, `HTML`\]. Not all toolkits support all formats. See `pandoc-plot toolkits` for toolkit-specific information regarding save formats. The `HTML` format is special; it can produce standalone, offline, interactive plots. As such, it only makes sense to use this format when creating HTML documents. - - `source` is a boolean toggle that determines whether the source code should be linked in the caption or not. Possible values are \[`true`, `True`, `false`, `False`\]. - - `preamble` is a path to a script that will be included as a preamble to the content of the code block. This path is either absolute, or relative from the working directory where you call `pandoc-plot`. - - `dpi` is the pixel density of the figure in dots-per-inch. Possible values are positive integers. Not all toolkits respect this. - - `dependencies` is a list of files/directories that affect the figure, for example data files. If one of those files/directories changes, `pandoc-plot` will re-render the associated figure. Format is a comma-separated list, e.g. `dependencies=[data.txt, foo.bar, ~/wtv]`. Values for `dependencies` in the configuration file will be appended to the values in a code block. - - `file` is a path to a file from which to read the figure content. If this parameter is used, the content of the code block is ignored. By using this parameter, you can use all the standard tooling of your plotting toolkit of choice, which is especially useful for complex figures. - - `executable` is a path to the executable to use (e.g. `C:\\python3.exe`) or the name of the executable (e.g. `python3`). - - `caption_format` is the text format of the caption. Possible values are exactly the same as `pandoc`’s format specification, usually `FORMAT+EXTENSION-EXTENSION`. For example, captions in Markdown with raw LaTeX would be parsed correctly provided that `caption_format=markdown+raw_tex`. See Pandoc’s guide on [Specifying formats](https://pandoc.org/MANUAL.html#specifying-formats). +- `language` specifies the programming language used in this block. This parameter is ignored by `pandoc-plot`, but your text editor may use it to highlight code. See [Code highlighting](#code-highlighting) below. +- `directory` is a path to the directory where the figure and source code will be saved. You cannot control the file name. This path is either absolute, or relative from the working directory where you call `pandoc-plot`. +- `caption` is the caption text. The format of the caption is specified in the `caption_format` parameter, described below. +- `format` is the desired filetype for the resulting figure. Possible values for `format` are \[`PNG`, `PDF`, `SVG`, `JPG`, `EPS`, `GIF`, `TIF`, `WEBP`, `HTML`\]. Not all toolkits support all formats. See `pandoc-plot toolkits` for toolkit-specific information regarding save formats. The `HTML` format is special; it can produce standalone, offline, interactive plots. As such, it only makes sense to use this format when creating HTML documents. +- `source` is a boolean toggle that determines whether the source code should be linked in the caption or not. Possible values are \[`true`, `True`, `false`, `False`\]. +- `source_label` is the text that links to source code (if `source=true`). This is useful if you are writing text in a different language. The default is `source_label="Source Code"`. You might want to set this via the [configuration](#configuration) instead. +- `preamble` is a path to a script that will be included as a preamble to the content of the code block. This path is either absolute, or relative from the working directory where you call `pandoc-plot`. +- `dpi` is the pixel density of the figure in dots-per-inch. Possible values are positive integers. Not all toolkits respect this. +- `dependencies` is a list of files/directories that affect the figure, for example data files. If one of those files/directories changes, `pandoc-plot` will re-render the associated figure. Format is a comma-separated list, e.g. `dependencies=[data.txt, foo.bar, ~/wtv]`. Values for `dependencies` in the configuration file will be appended to the values in a code block. +- `file` is a path to a file from which to read the figure content. If this parameter is used, the content of the code block is ignored. By using this parameter, you can use all the standard tooling of your plotting toolkit of choice, which is especially useful for complex figures. +- `executable` is a path to the executable to use (e.g. `C:\\python3.exe`) or the name of the executable (e.g. `python3`). +- `caption_format` is the text format of the caption. Possible values are exactly the same as `pandoc`’s format specification, usually `FORMAT+EXTENSION-EXTENSION`. For example, captions in Markdown with raw LaTeX would be parsed correctly provided that `caption_format=markdown+raw_tex`. See Pandoc’s guide on [Specifying formats](https://pandoc.org/MANUAL.html#specifying-formats). #### Code highlighting @@ -319,7 +321,6 @@ files. Here are **all** the possible parameters: ``` yaml - # This is an example configuration. Everything in this file is optional. # Please refer to the documentation to know about the parameters herein. # @@ -339,6 +340,11 @@ # Particularly useful for HTML output. source: false +# Text label for links to source code. +# You can change this label if you are writing a document in a non-English language. +# This only matters if `source` is set to `true`. +source_label: Source code + # Default density of figures in dots per inches (DPI). # This can be changed in the document specifically as well. dpi: 80 @@ -361,6 +367,7 @@ # Example: markdown, rst+raw_tex caption_format: markdown+tex_math_dollars + # Logging configuration logging: # Possible verbosity values: debug, error, warning, info, silent @@ -469,8 +476,8 @@ ##### Matplotlib - - `tight_bbox` is a boolean that determines whether to use `bbox_inches="tight"` or not when saving Matplotlib figures. For example, `tight_bbox: true`. See [here](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.savefig.html) for details. - - `transparent` is a boolean that determines whether to make Matplotlib figure background transparent or not. This is useful, for example, for displaying a plot on top of a colored background on a web page. High-resolution figures are not affected. For example, `transparent: true`. +- `tight_bbox` is a boolean that determines whether to use `bbox_inches="tight"` or not when saving Matplotlib figures. For example, `tight_bbox: true`. See [here](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.savefig.html) for details. +- `transparent` is a boolean that determines whether to make Matplotlib figure background transparent or not. This is useful, for example, for displaying a plot on top of a colored background on a web page. High-resolution figures are not affected. For example, `transparent: true`. #### Logging @@ -529,7 +536,7 @@ #### Configuration template -Because `pandoc-plot` supports a few toolkits, there are a lot of configuration options. Don’t start from scratch\! The `write-example-config` command will create a file for you, which you can then modify: +Because `pandoc-plot` supports a few toolkits, there are a lot of configuration options. Don’t start from scratch! The `write-example-config` command will create a file for you, which you can then modify: ``` bash pandoc-plot write-example-config @@ -552,4 +559,4 @@ ## Warning Do not run this filter on unknown documents. There is nothing in -`pandoc-plot` that can stop a script from performing **evil actions**.+`pandoc-plot` that can stop a script from performing **evil actions**.
README.md view
@@ -59,7 +59,7 @@ - `gnuplot`: plots using [gnuplot](http://www.gnuplot.info/); - `graphviz`: graphs using [Graphviz](http://graphviz.org/); - `bokeh`: plots using the [Bokeh](https://bokeh.org/) visualization library; - - `plotsjl`: plots using the [Julia `Plots.jl`](http://docs.plotsjl.org/latest/) package. + - `plotsjl`: plots using the [Julia `Plots.jl`](https://docs.juliaplots.org/latest/) package. To know which toolkits are useable on *your machine* (and which ones are not available), you can check with the `toolkits` command:
benchmark/MatplotlibGallery.hs view
@@ -2,25 +2,22 @@ module MatplotlibGallery where -import Data.String (fromString) - -import qualified Data.Text as T -import qualified Data.Text.IO as T - -import Language.Haskell.TH.Syntax - +import Data.String (fromString) +import qualified Data.Text as T +import qualified Data.Text.IO as T +import Language.Haskell.TH.Syntax galleryItem :: FilePath -> Q Exp galleryItem fp = do - qAddDependentFile fp - txt <- runIO $ T.readFile fp - strToExp $ T.unpack txt - where - strToExp :: String -> Q Exp - strToExp s = return $ VarE 'fromString `AppE` LitE (StringL s) + qAddDependentFile fp + txt <- runIO $ T.readFile fp + strToExp $ T.unpack txt + where + strToExp :: String -> Q Exp + strToExp s = return $ VarE 'fromString `AppE` LitE (StringL s) galleryItem1, galleryItem2, galleryItem3, galleryItem4 :: Q Exp galleryItem1 = galleryItem "benchmark/gallery_item_1.py" galleryItem2 = galleryItem "benchmark/gallery_item_2.py" galleryItem3 = galleryItem "benchmark/gallery_item_3.py" -galleryItem4 = galleryItem "benchmark/gallery_item_4.py"+galleryItem4 = galleryItem "benchmark/gallery_item_4.py"
benchmark/bench.hs view
@@ -1,39 +1,56 @@ {-# LANGUAGE TemplateHaskell #-} import Criterion.Main - -import Text.Pandoc.Definition + ( bench, + bgroup, + defaultMain, + envWithCleanup, + nfIO, + ) +import MatplotlibGallery + ( galleryItem1, + galleryItem2, + galleryItem3, + galleryItem4, + ) +import Text.Pandoc.Definition (Block (CodeBlock), Pandoc (..)) import Text.Pandoc.Filter.Plot -import Text.Pandoc.Filter.Plot.Internal - -import MatplotlibGallery ( galleryItem1, galleryItem2 - , galleryItem3, galleryItem4 - ) + ( Configuration (logSink, logVerbosity), + LogSink (StdErr), + Script, + Toolkit (Matplotlib), + Verbosity (Silent), + cleanOutputDirs, + defaultConfiguration, + plotTransform, + ) +import Text.Pandoc.Filter.Plot.Internal (cls) main :: IO () -main = - defaultMain [ - envWithCleanup (return ()) (\_ -> cleanupEnv) $ \_ -> - bgroup "main" [ - bench "filter-async" $ nfIO (plotTransform plotConfig benchDoc) - , bench "filter" $ nfIO (makePlot plotConfig benchDoc) - ] +main = + defaultMain + [ envWithCleanup (return ()) (\_ -> cleanupEnv) $ \_ -> + bgroup + "main" + [ bench "filter" $ nfIO (plotTransform plotConfig benchDoc) + ] ] plotConfig :: Configuration -plotConfig = defaultConfiguration {logVerbosity=Silent, logSink =StdErr} +plotConfig = defaultConfiguration {logVerbosity = Silent, logSink = StdErr} cleanupEnv :: IO () cleanupEnv = cleanOutputDirs plotConfig benchDoc >> return () - codeBlock :: Script -> Block codeBlock = CodeBlock (mempty, [cls Matplotlib], mempty) - benchDoc :: Pandoc -benchDoc = Pandoc mempty [ codeBlock $(galleryItem1) - , codeBlock $(galleryItem2) - , codeBlock $(galleryItem3) - , codeBlock $(galleryItem4) - ]+benchDoc = + Pandoc + mempty + [ codeBlock $(galleryItem1), + codeBlock $(galleryItem2), + codeBlock $(galleryItem3), + codeBlock $(galleryItem4) + ]
+ data/srctemplate.html view
@@ -0,0 +1,241 @@+<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml" lang="$lang$" xml:lang="$lang$"$if(dir)$ dir="$dir$"$endif$> +<head> + <meta charset="utf-8" /> + <meta name="generator" content="pandoc-plot" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> +$for(author-meta)$ + <meta name="author" content="$author-meta$" /> +$endfor$ +$if(date-meta)$ + <meta name="dcterms.date" content="$date-meta$" /> +$endif$ +$if(keywords)$ + <meta name="keywords" content="$for(keywords)$$keywords$$sep$, $endfor$" /> +$endif$ + <title>Generated figure</title> + <style> +$if(document-css)$ +html { + line-height: $if(linestretch)$$linestretch$$else$1.5$endif$; + font-family: $if(mainfont)$$mainfont$$else$Georgia, serif$endif$; + font-size: $if(fontsize)$$fontsize$$else$20px$endif$; + color: $if(fontcolor)$$fontcolor$$else$#1a1a1a$endif$; + background-color: $if(backgroundcolor)$$backgroundcolor$$else$#fdfdfd$endif$; +} +body { + margin: 0 auto; + max-width: 36em; + padding-left: $if(margin-left)$$margin-left$$else$50px$endif$; + padding-right: $if(margin-right)$$margin-right$$else$50px$endif$; + padding-top: $if(margin-top)$$margin-top$$else$50px$endif$; + padding-bottom: $if(margin-bottom)$$margin-bottom$$else$50px$endif$; + hyphens: auto; + word-wrap: break-word; + text-rendering: optimizeLegibility; + font-kerning: normal; +} +@media (max-width: 600px) { + body { + font-size: 0.9em; + padding: 1em; + } +} +@media print { + body { + background-color: transparent; + color: black; + font-size: 12pt; + } + p, h2, h3 { + orphans: 3; + widows: 3; + } + h2, h3, h4 { + page-break-after: avoid; + } +} +p { + margin: 1em 0; +} +a { + color: $if(linkcolor)$$linkcolor$$else$#1a1a1a$endif$; +} +a:visited { + color: $if(linkcolor)$$linkcolor$$else$#1a1a1a$endif$; +} +img { + max-width: 100%; +} +h1, h2, h3, h4, h5, h6 { + margin-top: 1.4em; +} +h5, h6 { + font-size: 1em; + font-style: italic; +} +h6 { + font-weight: normal; +} +ol, ul { + padding-left: 1.7em; + margin-top: 1em; +} +li > ol, li > ul { + margin-top: 0; +} +blockquote { + margin: 1em 0 1em 1.7em; + padding-left: 1em; + border-left: 2px solid #e6e6e6; + color: #606060; +} +code { + font-family: $if(monofont)$$monofont$$else$Menlo, Monaco, 'Lucida Console', Consolas, monospace$endif$; +$if(monobackgroundcolor)$ + background-color: $monobackgroundcolor$; + padding: .2em .4em; +$endif$ + font-size: 85%; + margin: 0; +} +pre { + margin: 1em 0; +$if(monobackgroundcolor)$ + background-color: $monobackgroundcolor$; + padding: 1em; +$endif$ + overflow: auto; +} +pre code { + padding: 0; + overflow: visible; +} +.sourceCode { + background-color: transparent; + overflow: visible; +} +hr { + background-color: #1a1a1a; + border: none; + height: 1px; + margin: 1em 0; +} +table { + margin: 1em 0; + border-collapse: collapse; + width: 100%; + overflow-x: auto; + display: block; + font-variant-numeric: lining-nums tabular-nums; +} +table caption { + margin-bottom: 0.75em; +} +tbody { + margin-top: 0.5em; + border-top: 1px solid $if(fontcolor)$$fontcolor$$else$#1a1a1a$endif$; + border-bottom: 1px solid $if(fontcolor)$$fontcolor$$else$#1a1a1a$endif$; +} +th { + border-top: 1px solid $if(fontcolor)$$fontcolor$$else$#1a1a1a$endif$; + padding: 0.25em 0.5em 0.25em 0.5em; +} +td { + padding: 0.125em 0.5em 0.25em 0.5em; +} +header { + margin-bottom: 4em; + text-align: center; +} +#TOC li { + list-style: none; +} +#TOC a:not(:hover) { + text-decoration: none; +} +$endif$ +code{white-space: pre-wrap;} +span.smallcaps{font-variant: small-caps;} +span.underline{text-decoration: underline;} +div.column{display: inline-block; vertical-align: top; width: 50%;} +div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;} +ul.task-list{list-style: none;} +$if(quotes)$ +q { quotes: "“" "”" "‘" "’"; } +$endif$ +$if(highlighting-css)$ +$highlighting-css$ +$endif$ +$if(displaymath-css)$ +.display.math{display: block; text-align: center; margin: 0.5rem auto;} +$endif$ +$if(csl-css)$ +div.csl-bib-body { } +div.csl-entry { + clear: both; +$if(csl-entry-spacing)$ + margin-bottom: $csl-entry-spacing$; +$endif$ +} +.hanging div.csl-entry { + margin-left:2em; + text-indent:-2em; +} +div.csl-left-margin { + min-width:2em; + float:left; +} +div.csl-right-inline { + margin-left:2em; + padding-left:1em; +} +div.csl-indent { + margin-left: 2em; +} +$endif$ + </style> +$for(css)$ + <link rel="stylesheet" href="$css$" /> +$endfor$ +$if(math)$ + $math$ +$endif$ + +$for(header-includes)$ + $header-includes$ +$endfor$ +</head> +<body> +$for(include-before)$ +$include-before$ +$endfor$ +$if(title)$ +<header id="title-block-header"> +<h1 class="title">$title$</h1> +$if(subtitle)$ +<p class="subtitle">$subtitle$</p> +$endif$ +$for(author)$ +<p class="author">$author$</p> +$endfor$ +$if(date)$ +<p class="date">$date$</p> +$endif$ +</header> +$endif$ +$if(toc)$ +<nav id="$idprefix$TOC" role="doc-toc"> +$if(toc-title)$ +<h2 id="$idprefix$toc-title">$toc-title$</h2> +$endif$ +$table-of-contents$ +</nav> +$endif$ +$body$ +$for(include-after)$ +$include-after$ +$endfor$ +<a href="https://github.com/LaurentRDC/pandoc-plot">Click here to see how this plot was generated.</a> +</body> +</html>
docs/MANUAL.html view
@@ -4,7 +4,7 @@ <meta charset="utf-8" /> <meta name="generator" content="pandoc" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" /> - <title>pandoc-plot 1.0.0.0 manual</title> + <title>pandoc-plot 1.0.1.0 manual</title> <style> code{white-space: pre-wrap;} span.smallcaps{font-variant: small-caps;} @@ -15,6 +15,7 @@ pre > code.sourceCode { white-space: pre; position: relative; } pre > code.sourceCode > span { display: inline-block; line-height: 1.25; } pre > code.sourceCode > span:empty { height: 1.2em; } + .sourceCode { overflow: visible; } code.sourceCode > span { color: inherit; text-decoration: inherit; } div.sourceCode { margin: 1em 0; } pre.sourceCode { margin: 0; } @@ -76,14 +77,14 @@ code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */ .display.math{display: block; text-align: center; margin: 0.5rem auto;} </style> - <style type="text/css">html { font-size: 100%; overflow-y: scroll; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; }body{color:#444;font-family:Georgia, Palatino, 'Palatino Linotype', Times, 'Times New Roman', serif;font-size:12px;line-height:1.5em;padding:1em;margin:auto;max-width:42em;background:#fefefe;}a{ color: #0645ad; text-decoration:none;}a:visited{ color: #0b0080; }a:hover{ color: #06e; }a:active{ color:#faa700; }a:focus{ outline: thin dotted; }a:hover, a:active{ outline: 0; }::-moz-selection{background:rgba(255,255,0,0.3);color:#000}::selection{background:rgba(255,255,0,0.3);color:#000}a::-moz-selection{background:rgba(255,255,0,0.3);color:#0645ad}a::selection{background:rgba(255,255,0,0.3);color:#0645ad}p{margin:1em 0;}img{max-width:100%;}h1,h2,h3,h4,h5,h6{font-weight:normal;color:#111;line-height:1em;}h4,h5,h6{ font-weight: bold; }h1{ font-size:2.5em; }h2{ font-size:2em; }h3{ font-size:1.5em; }h4{ font-size:1.2em; }h5{ font-size:1em; }h6{ font-size:0.9em; }blockquote{color:#666666;margin:0;padding-left: 3em;border-left: 0.5em #EEE solid;}hr { display: block; height: 2px; border: 0; border-top: 1px solid #aaa;border-bottom: 1px solid #eee; margin: 1em 0; padding: 0; }pre, code, kbd, samp { color: #000; font-family: monospace, monospace; _font-family: 'courier new', monospace; font-size: 0.98em; }pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; }b, strong { font-weight: bold; }dfn { font-style: italic; }ins { background: #ff9; color: #000; text-decoration: none; }mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; }sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }sup { top: -0.5em; }sub { bottom: -0.25em; }ul, ol { margin: 1em 0; padding: 0 0 0 2em; }li p:last-child { margin:0 }dd { margin: 0 0 0 2em; }img { border: 0; -ms-interpolation-mode: bicubic; vertical-align: middle; }table {border-collapse: collapse;border-spacing: 0;width: 100%;}th { border-bottom: 1px solid black; }td { vertical-align: top; }@media only screen and (min-width: 480px) {body{font-size:14px;}}@media only screen and (min-width: 768px) {body{font-size:16px;}}@media print {* { background: transparent !important; color: black !important; filter:none !important; -ms-filter: none !important; }body{font-size:12pt; max-width:100%;}a, a:visited { text-decoration: underline; }hr { height: 1px; border:0; border-bottom:1px solid black; }a[href]:after { content: " (" attr(href) ")"; }abbr[title]:after { content: " (" attr(title) ")"; }.ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; }pre, blockquote { border: 1px solid #999; padding-right: 1em; page-break-inside: avoid; }tr, img { page-break-inside: avoid; }img { max-width: 100% !important; }@page :left { margin: 15mm 20mm 15mm 10mm; }@page :right { margin: 15mm 10mm 15mm 20mm; }p, h2, h3 { orphans: 3; widows: 3; }h2, h3 { page-break-after: avoid; }}</style> + <link rel="stylesheet" href="data:text/css,html%20%7B%20font%2Dsize%3A%20100%25%3B%20overflow%2Dy%3A%20scroll%3B%20%2Dwebkit%2Dtext%2Dsize%2Dadjust%3A%20100%25%3B%20%2Dms%2Dtext%2Dsize%2Dadjust%3A%20100%25%3B%20%7Dbody%7Bcolor%3A%23444%3Bfont%2Dfamily%3AGeorgia%2C%20Palatino%2C%20%27Palatino%20Linotype%27%2C%20Times%2C%20%27Times%20New%20Roman%27%2C%20serif%3Bfont%2Dsize%3A12px%3Bline%2Dheight%3A1%2E5em%3Bpadding%3A1em%3Bmargin%3Aauto%3Bmax%2Dwidth%3A42em%3Bbackground%3A%23fefefe%3B%7Da%7B%20color%3A%20%230645ad%3B%20text%2Ddecoration%3Anone%3B%7Da%3Avisited%7B%20color%3A%20%230b0080%3B%20%7Da%3Ahover%7B%20color%3A%20%2306e%3B%20%7Da%3Aactive%7B%20color%3A%23faa700%3B%20%7Da%3Afocus%7B%20outline%3A%20thin%20dotted%3B%20%7Da%3Ahover%2C%20a%3Aactive%7B%20outline%3A%200%3B%20%7D%3A%3A%2Dmoz%2Dselection%7Bbackground%3Argba%28255%2C255%2C0%2C0%2E3%29%3Bcolor%3A%23000%7D%3A%3Aselection%7Bbackground%3Argba%28255%2C255%2C0%2C0%2E3%29%3Bcolor%3A%23000%7Da%3A%3A%2Dmoz%2Dselection%7Bbackground%3Argba%28255%2C255%2C0%2C0%2E3%29%3Bcolor%3A%230645ad%7Da%3A%3Aselection%7Bbackground%3Argba%28255%2C255%2C0%2C0%2E3%29%3Bcolor%3A%230645ad%7Dp%7Bmargin%3A1em%200%3B%7Dimg%7Bmax%2Dwidth%3A100%25%3B%7Dh1%2Ch2%2Ch3%2Ch4%2Ch5%2Ch6%7Bfont%2Dweight%3Anormal%3Bcolor%3A%23111%3Bline%2Dheight%3A1em%3B%7Dh4%2Ch5%2Ch6%7B%20font%2Dweight%3A%20bold%3B%20%7Dh1%7B%20font%2Dsize%3A2%2E5em%3B%20%7Dh2%7B%20font%2Dsize%3A2em%3B%20%7Dh3%7B%20font%2Dsize%3A1%2E5em%3B%20%7Dh4%7B%20font%2Dsize%3A1%2E2em%3B%20%7Dh5%7B%20font%2Dsize%3A1em%3B%20%7Dh6%7B%20font%2Dsize%3A0%2E9em%3B%20%7Dblockquote%7Bcolor%3A%23666666%3Bmargin%3A0%3Bpadding%2Dleft%3A%203em%3Bborder%2Dleft%3A%200%2E5em%20%23EEE%20solid%3B%7Dhr%20%7B%20display%3A%20block%3B%20height%3A%202px%3B%20border%3A%200%3B%20border%2Dtop%3A%201px%20solid%20%23aaa%3Bborder%2Dbottom%3A%201px%20solid%20%23eee%3B%20margin%3A%201em%200%3B%20padding%3A%200%3B%20%7Dpre%2C%20code%2C%20kbd%2C%20samp%20%7B%20color%3A%20%23000%3B%20font%2Dfamily%3A%20monospace%2C%20monospace%3B%20%5Ffont%2Dfamily%3A%20%27courier%20new%27%2C%20monospace%3B%20font%2Dsize%3A%200%2E98em%3B%20%7Dpre%20%7B%20white%2Dspace%3A%20pre%3B%20white%2Dspace%3A%20pre%2Dwrap%3B%20word%2Dwrap%3A%20break%2Dword%3B%20%7Db%2C%20strong%20%7B%20font%2Dweight%3A%20bold%3B%20%7Ddfn%20%7B%20font%2Dstyle%3A%20italic%3B%20%7Dins%20%7B%20background%3A%20%23ff9%3B%20color%3A%20%23000%3B%20text%2Ddecoration%3A%20none%3B%20%7Dmark%20%7B%20background%3A%20%23ff0%3B%20color%3A%20%23000%3B%20font%2Dstyle%3A%20italic%3B%20font%2Dweight%3A%20bold%3B%20%7Dsub%2C%20sup%20%7B%20font%2Dsize%3A%2075%25%3B%20line%2Dheight%3A%200%3B%20position%3A%20relative%3B%20vertical%2Dalign%3A%20baseline%3B%20%7Dsup%20%7B%20top%3A%20%2D0%2E5em%3B%20%7Dsub%20%7B%20bottom%3A%20%2D0%2E25em%3B%20%7Dul%2C%20ol%20%7B%20margin%3A%201em%200%3B%20padding%3A%200%200%200%202em%3B%20%7Dli%20p%3Alast%2Dchild%20%7B%20margin%3A0%20%7Ddd%20%7B%20margin%3A%200%200%200%202em%3B%20%7Dimg%20%7B%20border%3A%200%3B%20%2Dms%2Dinterpolation%2Dmode%3A%20bicubic%3B%20vertical%2Dalign%3A%20middle%3B%20%7Dtable%20%7Bborder%2Dcollapse%3A%20collapse%3Bborder%2Dspacing%3A%200%3Bwidth%3A%20100%25%3B%7Dth%20%7B%20border%2Dbottom%3A%201px%20solid%20black%3B%20%7Dtd%20%7B%20vertical%2Dalign%3A%20top%3B%20%7D%40media%20only%20screen%20and%20%28min%2Dwidth%3A%20480px%29%20%7Bbody%7Bfont%2Dsize%3A14px%3B%7D%7D%40media%20only%20screen%20and%20%28min%2Dwidth%3A%20768px%29%20%7Bbody%7Bfont%2Dsize%3A16px%3B%7D%7D%40media%20print%20%7B%2A%20%7B%20background%3A%20transparent%20%21important%3B%20color%3A%20black%20%21important%3B%20filter%3Anone%20%21important%3B%20%2Dms%2Dfilter%3A%20none%20%21important%3B%20%7Dbody%7Bfont%2Dsize%3A12pt%3B%20max%2Dwidth%3A100%25%3B%7Da%2C%20a%3Avisited%20%7B%20text%2Ddecoration%3A%20underline%3B%20%7Dhr%20%7B%20height%3A%201px%3B%20border%3A0%3B%20border%2Dbottom%3A1px%20solid%20black%3B%20%7Da%5Bhref%5D%3Aafter%20%7B%20content%3A%20%22%20%28%22%20attr%28href%29%20%22%29%22%3B%20%7Dabbr%5Btitle%5D%3Aafter%20%7B%20content%3A%20%22%20%28%22%20attr%28title%29%20%22%29%22%3B%20%7D%2Eir%20a%3Aafter%2C%20a%5Bhref%5E%3D%22javascript%3A%22%5D%3Aafter%2C%20a%5Bhref%5E%3D%22%23%22%5D%3Aafter%20%7B%20content%3A%20%22%22%3B%20%7Dpre%2C%20blockquote%20%7B%20border%3A%201px%20solid%20%23999%3B%20padding%2Dright%3A%201em%3B%20page%2Dbreak%2Dinside%3A%20avoid%3B%20%7Dtr%2C%20img%20%7B%20page%2Dbreak%2Dinside%3A%20avoid%3B%20%7Dimg%20%7B%20max%2Dwidth%3A%20100%25%20%21important%3B%20%7D%40page%20%3Aleft%20%7B%20margin%3A%2015mm%2020mm%2015mm%2010mm%3B%20%7D%40page%20%3Aright%20%7B%20margin%3A%2015mm%2010mm%2015mm%2020mm%3B%20%7Dp%2C%20h2%2C%20h3%20%7B%20orphans%3A%203%3B%20widows%3A%203%3B%20%7Dh2%2C%20h3%20%7B%20page%2Dbreak%2Dafter%3A%20avoid%3B%20%7D%7D" /> <!--[if lt IE 9]> <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script> <![endif]--> </head> <body> <header id="title-block-header"> -<h1 class="title">pandoc-plot 1.0.0.0 manual</h1> +<h1 class="title">pandoc-plot 1.0.1.0 manual</h1> </header> <!-- The file MANUAL.md is automatically generated by the tools/mkmanual.ps1 script. Do not edit manually. @@ -184,23 +185,23 @@ <span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>As you can see in @fig:myexample, ...</span></code></pre></div> <p>If the above source is located in file <code>myfile.md</code>, you can render the figure and references by applying <code>pandoc-plot</code> <strong>first</strong>, and then <code>pandoc-crossref</code>. For example:</p> -<div class="sourceCode" id="cb10"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="ex">pandoc</span> --filter pandoc-plot --filter pandoc-crossref -i myfile.md -o myfile.html</span></code></pre></div> +<div class="sourceCode" id="cb10"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="ex">pandoc</span> <span class="at">--filter</span> pandoc-plot <span class="at">--filter</span> pandoc-crossref <span class="at">-i</span> myfile.md <span class="at">-o</span> myfile.html</span></code></pre></div> <h2 id="detailed-usage">Detailed usage</h2> <p><code>pandoc-plot</code> is a command line executable with a few functions. You can take a look at the help using the <code>-h</code>/<code>--help</code> flag:</p> -<div class="sourceCode" id="cb11"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="ex">pandoc-plot</span> 0.9.4.0 - generate figures directly in documents</span> +<div class="sourceCode" id="cb11"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="ex">pandoc-plot</span> 1.0.1.0 <span class="at">-</span> generate figures directly in documents</span> <span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a><span class="ex">Usage</span>: pandoc-plot.EXE [(-v<span class="kw">|</span><span class="ex">--version</span>) <span class="kw">|</span> <span class="ex">--full-version</span> <span class="kw">|</span> <span class="kw">(</span><span class="ex">-m</span><span class="kw">|</span><span class="ex">--manual</span><span class="kw">)</span>] </span> -<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a> [<span class="ex">COMMAND</span>] [AST]</span> +<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a><span class="ex">Usage:</span> pandoc-plot.EXE [<span class="er">(</span><span class="ex">-v</span><span class="kw">|</span><span class="ex">--version</span><span class="kw">)</span> <span class="kw">|</span> <span class="ex">--full-version</span> <span class="kw">|</span> <span class="kw">(</span><span class="ex">-m</span><span class="kw">|</span><span class="ex">--manual</span><span class="kw">)</span><span class="ex">]</span> </span> +<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a> <span class="ex">[COMMAND]</span> [AST]</span> <span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a> <span class="ex">This</span> pandoc filter generates plots from code blocks using a multitude of</span> <span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a> <span class="ex">possible</span> renderers. This allows to keep documentation and figures in perfect</span> <span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a> <span class="ex">synchronicity.</span></span> <span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a></span> <span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a><span class="ex">Available</span> options:</span> -<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a> <span class="ex">-v</span>,--version Show version number and exit.</span> +<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a> <span class="ex">-v,--version</span> Show version number and exit.</span> <span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a> <span class="ex">--full-version</span> Show full version information and exit.</span> -<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a> <span class="ex">-m</span>,--manual Open the manual page in the default web browser and</span> +<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a> <span class="ex">-m,--manual</span> Open the manual page in the default web browser and</span> <span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a> <span class="ex">exit.</span></span> -<span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a> <span class="ex">-h</span>,--help Show this help text</span> +<span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a> <span class="ex">-h,--help</span> Show this help text</span> <span id="cb11-15"><a href="#cb11-15" aria-hidden="true" tabindex="-1"></a></span> <span id="cb11-16"><a href="#cb11-16" aria-hidden="true" tabindex="-1"></a><span class="ex">Available</span> commands:</span> <span id="cb11-17"><a href="#cb11-17" aria-hidden="true" tabindex="-1"></a> <span class="ex">toolkits</span> Show information on toolkits and exit.</span> @@ -209,14 +210,14 @@ <span id="cb11-20"><a href="#cb11-20" aria-hidden="true" tabindex="-1"></a> <span class="ex">those</span> directories will be deleted.</span> <span id="cb11-21"><a href="#cb11-21" aria-hidden="true" tabindex="-1"></a> <span class="ex">write-example-config</span> Write example configuration to a file and exit.</span> <span id="cb11-22"><a href="#cb11-22" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb11-23"><a href="#cb11-23" aria-hidden="true" tabindex="-1"></a><span class="ex">More</span> information can be found via the manual (pandoc-plot --manual) <span class="ex">or</span> the</span> +<span id="cb11-23"><a href="#cb11-23" aria-hidden="true" tabindex="-1"></a><span class="ex">More</span> information can be found via the manual <span class="er">(</span><span class="ex">pandoc-plot</span> <span class="at">--manual</span><span class="kw">)</span> <span class="ex">or</span> the</span> <span id="cb11-24"><a href="#cb11-24" aria-hidden="true" tabindex="-1"></a><span class="ex">repository</span> README, located at https://github.com/LaurentRDC/pandoc-plot</span></code></pre></div> <h3 id="as-a-filter">As a filter</h3> <p>The most common use for <code>pandoc-plot</code> is as a pandoc filter, in which case it should be called without arguments. For example:</p> -<div class="sourceCode" id="cb12"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="ex">pandoc</span> --filter pandoc-plot -i input.md -o output.html</span></code></pre></div> +<div class="sourceCode" id="cb12"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="ex">pandoc</span> <span class="at">--filter</span> pandoc-plot <span class="at">-i</span> input.md <span class="at">-o</span> output.html</span></code></pre></div> <p>If <code>pandoc-plot</code> fails to render a code block into a figure, the filtering will not stop. Your code blocks will stay unchanged.</p> <p>You can chain other filters with it (e.g., <a href="https://github.com/lierdakil/pandoc-crossref"><code>pandoc-crossref</code></a>) like so:</p> -<div class="sourceCode" id="cb13"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="ex">pandoc</span> --filter pandoc-plot --filter pandoc-crossref -i input.md -o output.html</span></code></pre></div> +<div class="sourceCode" id="cb13"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="ex">pandoc</span> <span class="at">--filter</span> pandoc-plot <span class="at">--filter</span> pandoc-crossref <span class="at">-i</span> input.md <span class="at">-o</span> output.html</span></code></pre></div> <h3 id="syntax">Syntax</h3> <p>The syntax for code blocks in documents is shown below. <code>pandoc-plot</code> looks for code blocks with a specific class, depending on the toolkit you want to use. <code>pandoc-plot</code> will run the code and capture the figure output. There can only be <strong>one</strong> figure per code block.</p> <p>The possible parameters and options are described in <a href="#parameters-and-options">further below</a>.</p> @@ -236,26 +237,28 @@ <span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a><span class="in"> directory=(path) </span></span> <span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a><span class="in"> caption=(text) </span></span> <span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a><span class="in"> format=(PNG|PDF|SVG|JPG|EPS|GIF|TIF|WEBP|HTML) </span></span> -<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a><span class="in"> source=(true|false) </span></span> -<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a><span class="in"> preamble=(path) </span></span> -<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a><span class="in"> dpi=(integer)</span></span> -<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a><span class="in"> dependencies=[...]</span></span> -<span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a><span class="in"> file=(path)</span></span> -<span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a><span class="in"> executable=(path) </span></span> -<span id="cb16-12"><a href="#cb16-12" aria-hidden="true" tabindex="-1"></a><span class="in"> caption_format=(text)</span></span> -<span id="cb16-13"><a href="#cb16-13" aria-hidden="true" tabindex="-1"></a><span class="in"> }</span></span> -<span id="cb16-14"><a href="#cb16-14" aria-hidden="true" tabindex="-1"></a><span class="in"> # script content</span></span> -<span id="cb16-15"><a href="#cb16-15" aria-hidden="true" tabindex="-1"></a><span class="in"> ```</span></span></code></pre></div> +<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a><span class="in"> source=(true|True|false|False) </span></span> +<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a><span class="in"> source_label=(text)</span></span> +<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a><span class="in"> preamble=(path) </span></span> +<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a><span class="in"> dpi=(integer)</span></span> +<span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a><span class="in"> dependencies=[...]</span></span> +<span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a><span class="in"> file=(path)</span></span> +<span id="cb16-12"><a href="#cb16-12" aria-hidden="true" tabindex="-1"></a><span class="in"> executable=(path) </span></span> +<span id="cb16-13"><a href="#cb16-13" aria-hidden="true" tabindex="-1"></a><span class="in"> caption_format=(text)</span></span> +<span id="cb16-14"><a href="#cb16-14" aria-hidden="true" tabindex="-1"></a><span class="in"> }</span></span> +<span id="cb16-15"><a href="#cb16-15" aria-hidden="true" tabindex="-1"></a><span class="in"> # script content</span></span> +<span id="cb16-16"><a href="#cb16-16" aria-hidden="true" tabindex="-1"></a><span class="in"> ```</span></span></code></pre></div> <ul> <li><code>cls</code> must be one of the following: <code>matplotlib</code>, <code>matlabplot</code>, <code>plotly_python</code>, <code>plotly_r</code>, <code>mathplot</code>, <code>octaveplot</code>, <code>ggplot2</code>, <code>gnuplot</code>, <code>graphviz</code>, <code>bokeh</code>, <code>plotsjl</code>.</li> </ul> -<p>All following parameters are optional, with their default values controlled by the <a href="#configuration">configuration</a></p> +<p>All following parameters are optional, with their default values controlled by the <a href="#configuration">configuration</a>.</p> <ul> <li><code>language</code> specifies the programming language used in this block. This parameter is ignored by <code>pandoc-plot</code>, but your text editor may use it to highlight code. See <a href="#code-highlighting">Code highlighting</a> below.</li> <li><code>directory</code> is a path to the directory where the figure and source code will be saved. You cannot control the file name. This path is either absolute, or relative from the working directory where you call <code>pandoc-plot</code>.</li> <li><code>caption</code> is the caption text. The format of the caption is specified in the <code>caption_format</code> parameter, described below.</li> <li><code>format</code> is the desired filetype for the resulting figure. Possible values for <code>format</code> are [<code>PNG</code>, <code>PDF</code>, <code>SVG</code>, <code>JPG</code>, <code>EPS</code>, <code>GIF</code>, <code>TIF</code>, <code>WEBP</code>, <code>HTML</code>]. Not all toolkits support all formats. See <code>pandoc-plot toolkits</code> for toolkit-specific information regarding save formats. The <code>HTML</code> format is special; it can produce standalone, offline, interactive plots. As such, it only makes sense to use this format when creating HTML documents.</li> <li><code>source</code> is a boolean toggle that determines whether the source code should be linked in the caption or not. Possible values are [<code>true</code>, <code>True</code>, <code>false</code>, <code>False</code>].</li> +<li><code>source_label</code> is the text that links to source code (if <code>source=true</code>). This is useful if you are writing text in a different language. The default is <code>source_label="Source Code"</code>. You might want to set this via the <a href="#configuration">configuration</a> instead.</li> <li><code>preamble</code> is a path to a script that will be included as a preamble to the content of the code block. This path is either absolute, or relative from the working directory where you call <code>pandoc-plot</code>.</li> <li><code>dpi</code> is the pixel density of the figure in dots-per-inch. Possible values are positive integers. Not all toolkits respect this.</li> <li><code>dependencies</code> is a list of files/directories that affect the figure, for example data files. If one of those files/directories changes, <code>pandoc-plot</code> will re-render the associated figure. Format is a comma-separated list, e.g. <code>dependencies=[data.txt, foo.bar, ~/wtv]</code>. Values for <code>dependencies</code> in the configuration file will be appended to the values in a code block.</li> @@ -285,115 +288,120 @@ <p>Interactive HTML figures are available for a few toolkits, e.g. <code>bokeh</code>. To make a figure interactive, use the output format <code>format=html</code>. This only makes sense if your output file is also HTML.</p> <p>You can take a look at the <a href="https://laurentrdc.github.io/pandoc-plot/">demonstration page</a> for an example result.</p> <p>Many interactive plots rely on javascript scripts stored on the internet. If you want to have a self-contained document that can be viewed offline – or you want your document to work for the next 10 years –, you can use pandoc’s <code>--self-contained</code> flag:</p> -<div class="sourceCode" id="cb21"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="ex">pandoc</span> --self-contained --filter pandoc-plot -i mydoc.md -o webpage.html </span></code></pre></div> +<div class="sourceCode" id="cb21"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="ex">pandoc</span> <span class="at">--self-contained</span> <span class="at">--filter</span> pandoc-plot <span class="at">-i</span> mydoc.md <span class="at">-o</span> webpage.html </span></code></pre></div> <p>The resulting output <code>webpage.html</code> will contain everything, at the cost of size.</p> <h3 id="configuration">Configuration</h3> <p>To avoid repetition, <code>pandoc-plot</code> can be configured using simple YAML files. Here are <strong>all</strong> the possible parameters:</p> -<div class="sourceCode" id="cb22"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a><span class="co"># This is an example configuration. Everything in this file is optional.</span></span> -<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Please refer to the documentation to know about the parameters herein.</span></span> -<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span> -<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a><span class="co"># The `executable` parameter for all toolkits can be either the</span></span> -<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a><span class="co"># executable name (if it is present on the PATH), or</span></span> -<span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a><span class="co"># the full path to the executable.</span></span> -<span id="cb22-8"><a href="#cb22-8" aria-hidden="true" tabindex="-1"></a><span class="co"># E.g.:</span></span> -<span id="cb22-9"><a href="#cb22-9" aria-hidden="true" tabindex="-1"></a><span class="co"># executable: python3</span></span> -<span id="cb22-10"><a href="#cb22-10" aria-hidden="true" tabindex="-1"></a><span class="co"># executable: "C:\Python37\Scripts\python.exe"</span></span> -<span id="cb22-11"><a href="#cb22-11" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-12"><a href="#cb22-12" aria-hidden="true" tabindex="-1"></a><span class="co"># The following parameters affect all toolkits</span></span> -<span id="cb22-13"><a href="#cb22-13" aria-hidden="true" tabindex="-1"></a><span class="co"># Directory where to save the plots. The path can be relative to pandoc-plot's</span></span> -<span id="cb22-14"><a href="#cb22-14" aria-hidden="true" tabindex="-1"></a><span class="co"># current working directory, or absolute.</span></span> -<span id="cb22-15"><a href="#cb22-15" aria-hidden="true" tabindex="-1"></a><span class="fu">directory</span><span class="kw">:</span><span class="at"> plots/</span></span> -<span id="cb22-16"><a href="#cb22-16" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-17"><a href="#cb22-17" aria-hidden="true" tabindex="-1"></a><span class="co"># Whether or not to include a link to the source script in the caption. </span></span> -<span id="cb22-18"><a href="#cb22-18" aria-hidden="true" tabindex="-1"></a><span class="co"># Particularly useful for HTML output.</span></span> -<span id="cb22-19"><a href="#cb22-19" aria-hidden="true" tabindex="-1"></a><span class="fu">source</span><span class="kw">:</span><span class="at"> </span><span class="ch">false</span></span> -<span id="cb22-20"><a href="#cb22-20" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-21"><a href="#cb22-21" aria-hidden="true" tabindex="-1"></a><span class="co"># Default density of figures in dots per inches (DPI). </span></span> -<span id="cb22-22"><a href="#cb22-22" aria-hidden="true" tabindex="-1"></a><span class="co"># This can be changed in the document specifically as well.</span></span> -<span id="cb22-23"><a href="#cb22-23" aria-hidden="true" tabindex="-1"></a><span class="fu">dpi</span><span class="kw">:</span><span class="at"> </span><span class="dv">80</span></span> +<div class="sourceCode" id="cb22"><pre class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="co"># This is an example configuration. Everything in this file is optional.</span></span> +<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a><span class="co"># Please refer to the documentation to know about the parameters herein.</span></span> +<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a><span class="co">#</span></span> +<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a><span class="co"># The `executable` parameter for all toolkits can be either the</span></span> +<span id="cb22-5"><a href="#cb22-5" aria-hidden="true" tabindex="-1"></a><span class="co"># executable name (if it is present on the PATH), or</span></span> +<span id="cb22-6"><a href="#cb22-6" aria-hidden="true" tabindex="-1"></a><span class="co"># the full path to the executable.</span></span> +<span id="cb22-7"><a href="#cb22-7" aria-hidden="true" tabindex="-1"></a><span class="co"># E.g.:</span></span> +<span id="cb22-8"><a href="#cb22-8" aria-hidden="true" tabindex="-1"></a><span class="co"># executable: python3</span></span> +<span id="cb22-9"><a href="#cb22-9" aria-hidden="true" tabindex="-1"></a><span class="co"># executable: "C:\Python37\Scripts\python.exe"</span></span> +<span id="cb22-10"><a href="#cb22-10" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb22-11"><a href="#cb22-11" aria-hidden="true" tabindex="-1"></a><span class="co"># The following parameters affect all toolkits</span></span> +<span id="cb22-12"><a href="#cb22-12" aria-hidden="true" tabindex="-1"></a><span class="co"># Directory where to save the plots. The path can be relative to pandoc-plot's</span></span> +<span id="cb22-13"><a href="#cb22-13" aria-hidden="true" tabindex="-1"></a><span class="co"># current working directory, or absolute.</span></span> +<span id="cb22-14"><a href="#cb22-14" aria-hidden="true" tabindex="-1"></a><span class="fu">directory</span><span class="kw">:</span><span class="at"> plots/</span></span> +<span id="cb22-15"><a href="#cb22-15" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb22-16"><a href="#cb22-16" aria-hidden="true" tabindex="-1"></a><span class="co"># Whether or not to include a link to the source script in the caption. </span></span> +<span id="cb22-17"><a href="#cb22-17" aria-hidden="true" tabindex="-1"></a><span class="co"># Particularly useful for HTML output.</span></span> +<span id="cb22-18"><a href="#cb22-18" aria-hidden="true" tabindex="-1"></a><span class="fu">source</span><span class="kw">:</span><span class="at"> </span><span class="ch">false</span></span> +<span id="cb22-19"><a href="#cb22-19" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb22-20"><a href="#cb22-20" aria-hidden="true" tabindex="-1"></a><span class="co"># Text label for links to source code.</span></span> +<span id="cb22-21"><a href="#cb22-21" aria-hidden="true" tabindex="-1"></a><span class="co"># You can change this label if you are writing a document in a non-English language. </span></span> +<span id="cb22-22"><a href="#cb22-22" aria-hidden="true" tabindex="-1"></a><span class="co"># This only matters if `source` is set to `true`.</span></span> +<span id="cb22-23"><a href="#cb22-23" aria-hidden="true" tabindex="-1"></a><span class="fu">source_label</span><span class="kw">:</span><span class="at"> Source code</span></span> <span id="cb22-24"><a href="#cb22-24" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-25"><a href="#cb22-25" aria-hidden="true" tabindex="-1"></a><span class="co"># Default format in which to save the figures. This can be specified </span></span> -<span id="cb22-26"><a href="#cb22-26" aria-hidden="true" tabindex="-1"></a><span class="co"># individually as well.</span></span> -<span id="cb22-27"><a href="#cb22-27" aria-hidden="true" tabindex="-1"></a><span class="fu">format</span><span class="kw">:</span><span class="at"> PNG</span></span> +<span id="cb22-25"><a href="#cb22-25" aria-hidden="true" tabindex="-1"></a><span class="co"># Default density of figures in dots per inches (DPI). </span></span> +<span id="cb22-26"><a href="#cb22-26" aria-hidden="true" tabindex="-1"></a><span class="co"># This can be changed in the document specifically as well.</span></span> +<span id="cb22-27"><a href="#cb22-27" aria-hidden="true" tabindex="-1"></a><span class="fu">dpi</span><span class="kw">:</span><span class="at"> </span><span class="dv">80</span></span> <span id="cb22-28"><a href="#cb22-28" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-29"><a href="#cb22-29" aria-hidden="true" tabindex="-1"></a><span class="co"># Default files/directories on which all figures depend. If any of these files/directories</span></span> -<span id="cb22-30"><a href="#cb22-30" aria-hidden="true" tabindex="-1"></a><span class="co"># changes, all figures will be re-rendered.</span></span> -<span id="cb22-31"><a href="#cb22-31" aria-hidden="true" tabindex="-1"></a><span class="co"># Dependencies specified in code blocks will be appended to this list.</span></span> -<span id="cb22-32"><a href="#cb22-32" aria-hidden="true" tabindex="-1"></a><span class="fu">dependencies</span><span class="kw">:</span></span> -<span id="cb22-33"><a href="#cb22-33" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> file1.txt</span></span> -<span id="cb22-34"><a href="#cb22-34" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> file2.txt</span></span> -<span id="cb22-35"><a href="#cb22-35" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-36"><a href="#cb22-36" aria-hidden="true" tabindex="-1"></a><span class="co"># Text format for the captions. Unfortunately, there is no way to detect</span></span> -<span id="cb22-37"><a href="#cb22-37" aria-hidden="true" tabindex="-1"></a><span class="co"># this automatically. You can use the same notation as Pandoc's --from </span></span> -<span id="cb22-38"><a href="#cb22-38" aria-hidden="true" tabindex="-1"></a><span class="co"># parameter, specified here: </span></span> -<span id="cb22-39"><a href="#cb22-39" aria-hidden="true" tabindex="-1"></a><span class="co"># https://pandoc.org/MANUAL.html#option--from</span></span> -<span id="cb22-40"><a href="#cb22-40" aria-hidden="true" tabindex="-1"></a><span class="co"># Example: markdown, rst+raw_tex</span></span> -<span id="cb22-41"><a href="#cb22-41" aria-hidden="true" tabindex="-1"></a><span class="fu">caption_format</span><span class="kw">:</span><span class="at"> markdown+tex_math_dollars</span></span> -<span id="cb22-42"><a href="#cb22-42" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-43"><a href="#cb22-43" aria-hidden="true" tabindex="-1"></a><span class="co"># Logging configuration</span></span> -<span id="cb22-44"><a href="#cb22-44" aria-hidden="true" tabindex="-1"></a><span class="fu">logging</span><span class="kw">:</span></span> -<span id="cb22-45"><a href="#cb22-45" aria-hidden="true" tabindex="-1"></a><span class="co"> # Possible verbosity values: debug, error, warning, info, silent</span></span> -<span id="cb22-46"><a href="#cb22-46" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">verbosity</span><span class="kw">:</span><span class="at"> warning</span></span> -<span id="cb22-47"><a href="#cb22-47" aria-hidden="true" tabindex="-1"></a><span class="co"> # If the filepath below is not present, then pandoc-plot will log to stderr</span></span> -<span id="cb22-48"><a href="#cb22-48" aria-hidden="true" tabindex="-1"></a><span class="co"> # Otherwise, log messages will be appended to the filepath.</span></span> -<span id="cb22-49"><a href="#cb22-49" aria-hidden="true" tabindex="-1"></a><span class="co"> # filepath: path/to/file.txt</span></span> -<span id="cb22-50"><a href="#cb22-50" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-51"><a href="#cb22-51" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the Matplotlib toolkit</span></span> -<span id="cb22-52"><a href="#cb22-52" aria-hidden="true" tabindex="-1"></a><span class="fu">matplotlib</span><span class="kw">:</span></span> -<span id="cb22-53"><a href="#cb22-53" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: matplotlib.py</span></span> -<span id="cb22-54"><a href="#cb22-54" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">tight_bbox</span><span class="kw">:</span><span class="at"> </span><span class="ch">false</span></span> -<span id="cb22-55"><a href="#cb22-55" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">transparent</span><span class="kw">:</span><span class="at"> </span><span class="ch">false</span></span> -<span id="cb22-56"><a href="#cb22-56" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> python</span></span> -<span id="cb22-57"><a href="#cb22-57" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-58"><a href="#cb22-58" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the MATLAB toolkit</span></span> -<span id="cb22-59"><a href="#cb22-59" aria-hidden="true" tabindex="-1"></a><span class="fu">matlabplot</span><span class="kw">:</span></span> -<span id="cb22-60"><a href="#cb22-60" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: matlab.m</span></span> -<span id="cb22-61"><a href="#cb22-61" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> matlab</span></span> +<span id="cb22-29"><a href="#cb22-29" aria-hidden="true" tabindex="-1"></a><span class="co"># Default format in which to save the figures. This can be specified </span></span> +<span id="cb22-30"><a href="#cb22-30" aria-hidden="true" tabindex="-1"></a><span class="co"># individually as well.</span></span> +<span id="cb22-31"><a href="#cb22-31" aria-hidden="true" tabindex="-1"></a><span class="fu">format</span><span class="kw">:</span><span class="at"> PNG</span></span> +<span id="cb22-32"><a href="#cb22-32" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb22-33"><a href="#cb22-33" aria-hidden="true" tabindex="-1"></a><span class="co"># Default files/directories on which all figures depend. If any of these files/directories</span></span> +<span id="cb22-34"><a href="#cb22-34" aria-hidden="true" tabindex="-1"></a><span class="co"># changes, all figures will be re-rendered.</span></span> +<span id="cb22-35"><a href="#cb22-35" aria-hidden="true" tabindex="-1"></a><span class="co"># Dependencies specified in code blocks will be appended to this list.</span></span> +<span id="cb22-36"><a href="#cb22-36" aria-hidden="true" tabindex="-1"></a><span class="fu">dependencies</span><span class="kw">:</span></span> +<span id="cb22-37"><a href="#cb22-37" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> file1.txt</span></span> +<span id="cb22-38"><a href="#cb22-38" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> file2.txt</span></span> +<span id="cb22-39"><a href="#cb22-39" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb22-40"><a href="#cb22-40" aria-hidden="true" tabindex="-1"></a><span class="co"># Text format for the captions. Unfortunately, there is no way to detect</span></span> +<span id="cb22-41"><a href="#cb22-41" aria-hidden="true" tabindex="-1"></a><span class="co"># this automatically. You can use the same notation as Pandoc's --from </span></span> +<span id="cb22-42"><a href="#cb22-42" aria-hidden="true" tabindex="-1"></a><span class="co"># parameter, specified here: </span></span> +<span id="cb22-43"><a href="#cb22-43" aria-hidden="true" tabindex="-1"></a><span class="co"># https://pandoc.org/MANUAL.html#option--from</span></span> +<span id="cb22-44"><a href="#cb22-44" aria-hidden="true" tabindex="-1"></a><span class="co"># Example: markdown, rst+raw_tex</span></span> +<span id="cb22-45"><a href="#cb22-45" aria-hidden="true" tabindex="-1"></a><span class="fu">caption_format</span><span class="kw">:</span><span class="at"> markdown+tex_math_dollars</span></span> +<span id="cb22-46"><a href="#cb22-46" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb22-47"><a href="#cb22-47" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb22-48"><a href="#cb22-48" aria-hidden="true" tabindex="-1"></a><span class="co"># Logging configuration</span></span> +<span id="cb22-49"><a href="#cb22-49" aria-hidden="true" tabindex="-1"></a><span class="fu">logging</span><span class="kw">:</span></span> +<span id="cb22-50"><a href="#cb22-50" aria-hidden="true" tabindex="-1"></a><span class="co"> # Possible verbosity values: debug, error, warning, info, silent</span></span> +<span id="cb22-51"><a href="#cb22-51" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">verbosity</span><span class="kw">:</span><span class="at"> warning</span></span> +<span id="cb22-52"><a href="#cb22-52" aria-hidden="true" tabindex="-1"></a><span class="co"> # If the filepath below is not present, then pandoc-plot will log to stderr</span></span> +<span id="cb22-53"><a href="#cb22-53" aria-hidden="true" tabindex="-1"></a><span class="co"> # Otherwise, log messages will be appended to the filepath.</span></span> +<span id="cb22-54"><a href="#cb22-54" aria-hidden="true" tabindex="-1"></a><span class="co"> # filepath: path/to/file.txt</span></span> +<span id="cb22-55"><a href="#cb22-55" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb22-56"><a href="#cb22-56" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the Matplotlib toolkit</span></span> +<span id="cb22-57"><a href="#cb22-57" aria-hidden="true" tabindex="-1"></a><span class="fu">matplotlib</span><span class="kw">:</span></span> +<span id="cb22-58"><a href="#cb22-58" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: matplotlib.py</span></span> +<span id="cb22-59"><a href="#cb22-59" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">tight_bbox</span><span class="kw">:</span><span class="at"> </span><span class="ch">false</span></span> +<span id="cb22-60"><a href="#cb22-60" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">transparent</span><span class="kw">:</span><span class="at"> </span><span class="ch">false</span></span> +<span id="cb22-61"><a href="#cb22-61" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> python</span></span> <span id="cb22-62"><a href="#cb22-62" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-63"><a href="#cb22-63" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the Plotly/Python toolkit</span></span> -<span id="cb22-64"><a href="#cb22-64" aria-hidden="true" tabindex="-1"></a><span class="fu">plotly_python</span><span class="kw">:</span></span> -<span id="cb22-65"><a href="#cb22-65" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: plotly-python.py</span></span> -<span id="cb22-66"><a href="#cb22-66" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> python</span></span> +<span id="cb22-63"><a href="#cb22-63" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the MATLAB toolkit</span></span> +<span id="cb22-64"><a href="#cb22-64" aria-hidden="true" tabindex="-1"></a><span class="fu">matlabplot</span><span class="kw">:</span></span> +<span id="cb22-65"><a href="#cb22-65" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: matlab.m</span></span> +<span id="cb22-66"><a href="#cb22-66" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> matlab</span></span> <span id="cb22-67"><a href="#cb22-67" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-68"><a href="#cb22-68" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the Plotly/R toolkit</span></span> -<span id="cb22-69"><a href="#cb22-69" aria-hidden="true" tabindex="-1"></a><span class="fu">plotly_r</span><span class="kw">:</span></span> -<span id="cb22-70"><a href="#cb22-70" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: plotly-r.r</span></span> -<span id="cb22-71"><a href="#cb22-71" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> Rscript</span></span> +<span id="cb22-68"><a href="#cb22-68" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the Plotly/Python toolkit</span></span> +<span id="cb22-69"><a href="#cb22-69" aria-hidden="true" tabindex="-1"></a><span class="fu">plotly_python</span><span class="kw">:</span></span> +<span id="cb22-70"><a href="#cb22-70" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: plotly-python.py</span></span> +<span id="cb22-71"><a href="#cb22-71" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> python</span></span> <span id="cb22-72"><a href="#cb22-72" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-73"><a href="#cb22-73" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the Mathematica toolkit</span></span> -<span id="cb22-74"><a href="#cb22-74" aria-hidden="true" tabindex="-1"></a><span class="fu">mathplot</span><span class="kw">:</span></span> -<span id="cb22-75"><a href="#cb22-75" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: mathematica.m</span></span> -<span id="cb22-76"><a href="#cb22-76" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> math</span></span> +<span id="cb22-73"><a href="#cb22-73" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the Plotly/R toolkit</span></span> +<span id="cb22-74"><a href="#cb22-74" aria-hidden="true" tabindex="-1"></a><span class="fu">plotly_r</span><span class="kw">:</span></span> +<span id="cb22-75"><a href="#cb22-75" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: plotly-r.r</span></span> +<span id="cb22-76"><a href="#cb22-76" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> Rscript</span></span> <span id="cb22-77"><a href="#cb22-77" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-78"><a href="#cb22-78" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the GNU Octave toolkit</span></span> -<span id="cb22-79"><a href="#cb22-79" aria-hidden="true" tabindex="-1"></a><span class="fu">octaveplot</span><span class="kw">:</span></span> -<span id="cb22-80"><a href="#cb22-80" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: octave.m</span></span> -<span id="cb22-81"><a href="#cb22-81" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> octave</span></span> +<span id="cb22-78"><a href="#cb22-78" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the Mathematica toolkit</span></span> +<span id="cb22-79"><a href="#cb22-79" aria-hidden="true" tabindex="-1"></a><span class="fu">mathplot</span><span class="kw">:</span></span> +<span id="cb22-80"><a href="#cb22-80" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: mathematica.m</span></span> +<span id="cb22-81"><a href="#cb22-81" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> math</span></span> <span id="cb22-82"><a href="#cb22-82" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-83"><a href="#cb22-83" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the ggplot2 toolkit</span></span> -<span id="cb22-84"><a href="#cb22-84" aria-hidden="true" tabindex="-1"></a><span class="fu">ggplot2</span><span class="kw">:</span></span> -<span id="cb22-85"><a href="#cb22-85" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: ggplot2.r</span></span> -<span id="cb22-86"><a href="#cb22-86" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> Rscript</span></span> +<span id="cb22-83"><a href="#cb22-83" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the GNU Octave toolkit</span></span> +<span id="cb22-84"><a href="#cb22-84" aria-hidden="true" tabindex="-1"></a><span class="fu">octaveplot</span><span class="kw">:</span></span> +<span id="cb22-85"><a href="#cb22-85" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: octave.m</span></span> +<span id="cb22-86"><a href="#cb22-86" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> octave</span></span> <span id="cb22-87"><a href="#cb22-87" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-88"><a href="#cb22-88" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the gnuplot toolkit</span></span> -<span id="cb22-89"><a href="#cb22-89" aria-hidden="true" tabindex="-1"></a><span class="fu">gnuplot</span><span class="kw">:</span></span> -<span id="cb22-90"><a href="#cb22-90" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: gnuplot.gp</span></span> -<span id="cb22-91"><a href="#cb22-91" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> gnuplot</span></span> +<span id="cb22-88"><a href="#cb22-88" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the ggplot2 toolkit</span></span> +<span id="cb22-89"><a href="#cb22-89" aria-hidden="true" tabindex="-1"></a><span class="fu">ggplot2</span><span class="kw">:</span></span> +<span id="cb22-90"><a href="#cb22-90" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: ggplot2.r</span></span> +<span id="cb22-91"><a href="#cb22-91" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> Rscript</span></span> <span id="cb22-92"><a href="#cb22-92" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-93"><a href="#cb22-93" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the graphviz toolkit</span></span> -<span id="cb22-94"><a href="#cb22-94" aria-hidden="true" tabindex="-1"></a><span class="fu">graphviz</span><span class="kw">:</span></span> -<span id="cb22-95"><a href="#cb22-95" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: graphviz.dot</span></span> -<span id="cb22-96"><a href="#cb22-96" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> dot</span></span> +<span id="cb22-93"><a href="#cb22-93" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the gnuplot toolkit</span></span> +<span id="cb22-94"><a href="#cb22-94" aria-hidden="true" tabindex="-1"></a><span class="fu">gnuplot</span><span class="kw">:</span></span> +<span id="cb22-95"><a href="#cb22-95" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: gnuplot.gp</span></span> +<span id="cb22-96"><a href="#cb22-96" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> gnuplot</span></span> <span id="cb22-97"><a href="#cb22-97" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-98"><a href="#cb22-98" aria-hidden="true" tabindex="-1"></a><span class="fu">bokeh</span><span class="kw">:</span></span> -<span id="cb22-99"><a href="#cb22-99" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: bokeh.py</span></span> -<span id="cb22-100"><a href="#cb22-100" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> python</span></span> -<span id="cb22-101"><a href="#cb22-101" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb22-102"><a href="#cb22-102" aria-hidden="true" tabindex="-1"></a><span class="fu">plotsjl</span><span class="kw">:</span></span> -<span id="cb22-103"><a href="#cb22-103" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: plotsjl.jl</span></span> -<span id="cb22-104"><a href="#cb22-104" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> julia</span></span></code></pre></div> +<span id="cb22-98"><a href="#cb22-98" aria-hidden="true" tabindex="-1"></a><span class="co"># The possible parameters for the graphviz toolkit</span></span> +<span id="cb22-99"><a href="#cb22-99" aria-hidden="true" tabindex="-1"></a><span class="fu">graphviz</span><span class="kw">:</span></span> +<span id="cb22-100"><a href="#cb22-100" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: graphviz.dot</span></span> +<span id="cb22-101"><a href="#cb22-101" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> dot</span></span> +<span id="cb22-102"><a href="#cb22-102" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb22-103"><a href="#cb22-103" aria-hidden="true" tabindex="-1"></a><span class="fu">bokeh</span><span class="kw">:</span></span> +<span id="cb22-104"><a href="#cb22-104" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: bokeh.py</span></span> +<span id="cb22-105"><a href="#cb22-105" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> python</span></span> +<span id="cb22-106"><a href="#cb22-106" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb22-107"><a href="#cb22-107" aria-hidden="true" tabindex="-1"></a><span class="fu">plotsjl</span><span class="kw">:</span></span> +<span id="cb22-108"><a href="#cb22-108" aria-hidden="true" tabindex="-1"></a><span class="co"> # preamble: plotsjl.jl</span></span> +<span id="cb22-109"><a href="#cb22-109" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">executable</span><span class="kw">:</span><span class="at"> julia</span></span></code></pre></div> <p>A file like the above sets the <strong>default</strong> values; you can still override them in documents directly.</p> <p>The easiest way to specify configuration for <code>pandoc-plot</code> is to place a <code>.pandoc-plot.yml</code> file in the current working directory. You can also specify a configuration file in document metadata, under the <code>plot-configuration</code> key. For example, in Markdown:</p> <div class="sourceCode" id="cb23"><pre class="sourceCode markdown"><code class="sourceCode markdown"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="co">---</span></span> @@ -402,7 +410,7 @@ <span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a><span class="an">plot-configuration:</span><span class="co"> /path/to/file.yml</span></span> <span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a><span class="co">---</span></span></code></pre></div> <p>or on the command line, using the pandoc <code>-M/--metadata</code> flag:</p> -<div class="sourceCode" id="cb24"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="ex">pandoc</span> --filter pandoc-plot -M plot-configuration=/path/to/file.yml ...</span></code></pre></div> +<div class="sourceCode" id="cb24"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="ex">pandoc</span> <span class="at">--filter</span> pandoc-plot <span class="at">-M</span> plot-configuration=/path/to/file.yml ...</span></code></pre></div> <p>The hierarchy of configuration files is as follows:</p> <ol type="1"> <li>A configuration file specified in the metadata under the <code>plot-configuration</code> key;</li> @@ -437,36 +445,36 @@ <h3 id="other-commands">Other commands</h3> <h4 id="finding-installed-toolkits">Finding installed toolkits</h4> <p>You can determine which toolkits are available on your current machine using the <code>pandoc-plot toolkits</code> command. Here is the full help text:</p> -<div class="sourceCode" id="cb28"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="ex">Usage</span>: pandoc-plot.EXE toolkits [--config PATH]</span> +<div class="sourceCode" id="cb28"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="ex">Usage:</span> pandoc-plot.EXE toolkits [--config PATH]</span> <span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a> <span class="ex">Show</span> information on toolkits and exit.</span> <span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb28-4"><a href="#cb28-4" aria-hidden="true" tabindex="-1"></a><span class="ex">Available</span> options:</span> <span id="cb28-5"><a href="#cb28-5" aria-hidden="true" tabindex="-1"></a> <span class="ex">--config</span> PATH Path to optional configuration file.</span> -<span id="cb28-6"><a href="#cb28-6" aria-hidden="true" tabindex="-1"></a> <span class="ex">-h</span>,--help Show this help text</span></code></pre></div> +<span id="cb28-6"><a href="#cb28-6" aria-hidden="true" tabindex="-1"></a> <span class="ex">-h,--help</span> Show this help text</span></code></pre></div> <h4 id="cleaning-output">Cleaning output</h4> <p>Figures produced by <code>pandoc-plot</code> can be placed in a few different locations. You can set a default location in the <a href="#configuration">Configuration</a>, but you can also re-direct specific figures in other directories if you use the <code>directory=...</code> argument in code blocks. These figures will build up over time. You can use the <code>clean</code> command to scan documents and delete the associated <code>pandoc-plot</code> output files. For example, to delete the figures generated from the <code>input.md</code> file:</p> <div class="sourceCode" id="cb29"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a><span class="ex">pandoc-plot</span> clean input.md</span></code></pre></div> <p>This sill remove all directories where a figure <em>could</em> have been placed. <strong>WARNING</strong>: all files will be removed.</p> <p>Here is the full help text for the <code>clean</code> command:</p> -<div class="sourceCode" id="cb30"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="ex">Usage</span>: pandoc-plot.EXE clean [--config PATH] FILE</span> +<div class="sourceCode" id="cb30"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a><span class="ex">Usage:</span> pandoc-plot.EXE clean [--config PATH] FILE</span> <span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a> <span class="ex">Clean</span> output directories where figures from FILE and log files might be</span> <span id="cb30-3"><a href="#cb30-3" aria-hidden="true" tabindex="-1"></a> <span class="ex">stored.</span> WARNING: All files in those directories will be deleted.</span> <span id="cb30-4"><a href="#cb30-4" aria-hidden="true" tabindex="-1"></a></span> <span id="cb30-5"><a href="#cb30-5" aria-hidden="true" tabindex="-1"></a><span class="ex">Available</span> options:</span> <span id="cb30-6"><a href="#cb30-6" aria-hidden="true" tabindex="-1"></a> <span class="ex">--config</span> PATH Path to optional configuration file.</span> -<span id="cb30-7"><a href="#cb30-7" aria-hidden="true" tabindex="-1"></a> <span class="ex">-h</span>,--help Show this help text</span></code></pre></div> +<span id="cb30-7"><a href="#cb30-7" aria-hidden="true" tabindex="-1"></a> <span class="ex">-h,--help</span> Show this help text</span></code></pre></div> <h4 id="configuration-template">Configuration template</h4> <p>Because <code>pandoc-plot</code> supports a few toolkits, there are a lot of configuration options. Don’t start from scratch! The <code>write-example-config</code> command will create a file for you, which you can then modify:</p> <div class="sourceCode" id="cb31"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a><span class="ex">pandoc-plot</span> write-example-config</span></code></pre></div> <p>You will need to re-name the file to <code>.pandoc-ploy.yml</code> to be able to use it, so don’t worry about overwriting your own configuration.</p> <p>Here is the full help text for the <code>write-example-config</code> command:</p> -<div class="sourceCode" id="cb32"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="ex">Usage</span>: pandoc-plot.EXE write-example-config [--path FILE]</span> +<div class="sourceCode" id="cb32"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="ex">Usage:</span> pandoc-plot.EXE write-example-config [--path FILE]</span> <span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a> <span class="ex">Write</span> example configuration to a file and exit.</span> <span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a></span> <span id="cb32-4"><a href="#cb32-4" aria-hidden="true" tabindex="-1"></a><span class="ex">Available</span> options:</span> <span id="cb32-5"><a href="#cb32-5" aria-hidden="true" tabindex="-1"></a> <span class="ex">--path</span> FILE Target location of the configuration file. Default is</span> <span id="cb32-6"><a href="#cb32-6" aria-hidden="true" tabindex="-1"></a> <span class="st">".example-pandoc-plot.yml"</span></span> -<span id="cb32-7"><a href="#cb32-7" aria-hidden="true" tabindex="-1"></a> <span class="ex">-h</span>,--help Show this help text</span></code></pre></div> +<span id="cb32-7"><a href="#cb32-7" aria-hidden="true" tabindex="-1"></a> <span class="ex">-h,--help</span> Show this help text</span></code></pre></div> <h2 id="warning">Warning</h2> <p>Do not run this filter on unknown documents. There is nothing in <code>pandoc-plot</code> that can stop a script from performing <strong>evil actions</strong>.</p>
example-config.yml view
@@ -18,6 +18,11 @@ # Particularly useful for HTML output. source: false +# Text label for links to source code. +# You can change this label if you are writing a document in a non-English language. +# This only matters if `source` is set to `true`. +source_label: Source code + # Default density of figures in dots per inches (DPI). # This can be changed in the document specifically as well. dpi: 80 @@ -39,6 +44,7 @@ # https://pandoc.org/MANUAL.html#option--from # Example: markdown, rst+raw_tex caption_format: markdown+tex_math_dollars + # Logging configuration logging:
executable/ExampleConfig.hs view
@@ -1,13 +1,11 @@ {-# LANGUAGE TemplateHaskellQuotes #-} -module ExampleConfig ( embedExampleConfig ) where - - -import Data.String -import Data.Text (unpack) -import qualified Data.Text.IO as TIO +module ExampleConfig (embedExampleConfig) where -import Language.Haskell.TH.Syntax +import Data.String +import Data.Text (unpack) +import qualified Data.Text.IO as TIO +import Language.Haskell.TH.Syntax docFile :: FilePath docFile = "example-config.yml" @@ -17,9 +15,9 @@ embedExampleConfig :: Q Exp embedExampleConfig = do - qAddDependentFile docFile - s <- runIO readDocFile - strToExp s - where - strToExp :: String -> Q Exp - strToExp s = return $ VarE 'fromString `AppE` LitE (StringL s)+ qAddDependentFile docFile + s <- runIO readDocFile + strToExp s + where + strToExp :: String -> Q Exp + strToExp s = return $ VarE 'fromString `AppE` LitE (StringL s)
executable/Main.hs view
@@ -1,282 +1,312 @@-{-# LANGUAGE ApplicativeDo #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE ApplicativeDo #-} {-# LANGUAGE CPP #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TemplateHaskell #-} module Main where -import Control.Monad (join, when, msum) - -import Data.List (intersperse, (\\)) -import Data.Maybe (fromJust) -import Data.Text (unpack) -import qualified Data.Text.IO as TIO -import Data.Version (parseVersion, showVersion) - -import GHC.IO.Encoding (setLocaleEncoding, utf8) -import GitHash as Git - -import Options.Applicative -import qualified Options.Applicative.Help.Pretty as P - -import System.Directory (doesFileExist, getTemporaryDirectory) -import System.Environment (lookupEnv) -import System.FilePath ((</>)) -import System.IO (hPutStrLn, stderr) - -import Text.Pandoc.Filter.Plot (availableToolkits, - plotTransform, - defaultConfiguration, - configuration, Configuration(..), - pandocPlotVersion, ) -import Text.Pandoc.Filter.Plot.Internal (cls, - supportedSaveFormats, - toolkits, readDoc, - cleanOutputDirs, - configurationPathMeta, - executable, runPlotM, Executable(..)) - -import Text.Pandoc (pandocVersion) -import Text.Pandoc.Definition (pandocTypesVersion) -import Text.Pandoc.JSON (toJSONFilter) - -import Text.ParserCombinators.ReadP (readP_to_S) - -import OpenFile (openFile) - -import qualified Data.Version as V - -import ManPage (embedManualHtml) -import ExampleConfig (embedExampleConfig) +import Control.Monad (join, msum, when) +import Data.List (intersperse, (\\)) +import Data.Maybe (fromJust) +import Data.Text (unpack) +import qualified Data.Text.IO as TIO +import Data.Version (parseVersion, showVersion) +import qualified Data.Version as V +import ExampleConfig (embedExampleConfig) +import GHC.IO.Encoding (setLocaleEncoding, utf8) +import GitHash as Git +import ManPage (embedManualHtml) +import OpenFile (openFile) +import Options.Applicative +import qualified Options.Applicative.Help.Pretty as P +import System.Directory (doesFileExist, getTemporaryDirectory) +import System.Environment (lookupEnv) +import System.FilePath ((</>)) +import System.IO (hPutStrLn, stderr) +import Text.Pandoc (pandocVersion) +import Text.Pandoc.Definition (pandocTypesVersion) +import Text.Pandoc.Filter.Plot + ( Configuration (..), + availableToolkits, + configuration, + defaultConfiguration, + pandocPlotVersion, + plotTransform, + ) +import Text.Pandoc.Filter.Plot.Internal + ( Executable (..), + cleanOutputDirs, + cls, + configurationPathMeta, + executable, + readDoc, + runPlotM, + supportedSaveFormats, + toolkits, + ) +import Text.Pandoc.JSON (toJSONFilter) +import Text.ParserCombinators.ReadP (readP_to_S) -- The difference between commands and flags is that commands require knowledge of -- the configuration, while flags only display static information. -data Command = Clean (Maybe FilePath) FilePath - | WriteConfig FilePath - | Toolkits (Maybe FilePath) - -data Flag = Version - | FullVersion - | Manual - deriving (Eq) +data Command + = Clean (Maybe FilePath) FilePath + | WriteConfig FilePath + | Toolkits (Maybe FilePath) +data Flag + = Version + | FullVersion + | Manual + deriving (Eq) main :: IO () -main = join $ execParser opts - where - opts = info (optparse <**> helper) - (fullDesc - <> progDesc (unlines - ["This pandoc filter generates plots from code blocks using a multitude of " - , "possible renderers. This allows to keep documentation and figures in" - , "perfect synchronicity." - ] - ) +main = do + setLocaleEncoding utf8 + join $ execParser opts + where + opts = + info + (optparse <**> helper) + ( fullDesc + <> progDesc + ( unlines + [ "This pandoc filter generates plots from code blocks using a multitude of ", + "possible renderers. This allows to keep documentation and figures in", + "perfect synchronicity." + ] + ) <> header (mconcat ["pandoc-plot ", V.showVersion pandocPlotVersion, " - generate figures directly in documents"]) <> footerDoc (Just footer') - ) - - optparse = do - flag_ <- flagParser - command_ <- commandParser - -- The extra optional input below only serves to show - -- to the user that the last argument is the AST from pandoc - -- The parsed input is never used - input <- optional $ strArgument (metavar "AST") - return $ go flag_ command_ input - - go :: Maybe Flag -> Maybe Command -> Maybe String -> IO () - go (Just Version) _ _ = putStrLn (V.showVersion pandocPlotVersion) - go (Just FullVersion) _ _ = showFullVersion - go (Just Manual) _ _ = showManPage - go _ (Just (Toolkits mfp)) _ = showAvailableToolkits mfp - go _ (Just (Clean mfp fp)) _ = clean mfp fp - go _ (Just (WriteConfig fp)) _ = writeFile fp $(embedExampleConfig) - go Nothing Nothing _ = toJSONFilterWithConfig + ) + optparse = do + flag_ <- flagParser + command_ <- commandParser + -- The extra optional input below only serves to show + -- to the user that the last argument is the AST from pandoc + -- The parsed input is never used + input <- optional $ strArgument (metavar "AST") + return $ go flag_ command_ input + + go :: Maybe Flag -> Maybe Command -> Maybe String -> IO () + go (Just Version) _ _ = putStrLn (V.showVersion pandocPlotVersion) + go (Just FullVersion) _ _ = showFullVersion + go (Just Manual) _ _ = showManPage + go _ (Just (Toolkits mfp)) _ = showAvailableToolkits mfp + go _ (Just (Clean mfp fp)) _ = clean mfp fp + go _ (Just (WriteConfig fp)) _ = writeFile fp $(embedExampleConfig) + go Nothing Nothing _ = toJSONFilterWithConfig + flagParser :: Parser (Maybe Flag) flagParser = versionP <|> fullVersionP <|> manualP - where - versionP = flag Nothing (Just Version) (mconcat - [ long "version" - , short 'v' - , help "Show version number and exit." - ]) - - fullVersionP = flag Nothing (Just FullVersion) (mconcat - [ long "full-version" - , help "Show full version information and exit." - ]) + where + versionP = + flag + Nothing + (Just Version) + ( mconcat + [ long "version", + short 'v', + help "Show version number and exit." + ] + ) - manualP = flag Nothing (Just Manual) (mconcat - [ long "manual" - , short 'm' - , help "Open the manual page in the default web browser and exit." - ]) + fullVersionP = + flag + Nothing + (Just FullVersion) + ( mconcat + [ long "full-version", + help "Show full version information and exit." + ] + ) + manualP = + flag + Nothing + (Just Manual) + ( mconcat + [ long "manual", + short 'm', + help "Open the manual page in the default web browser and exit." + ] + ) + commandParser :: Parser (Maybe Command) -commandParser = optional $ subparser $ mconcat - [ command "toolkits" ( - info (toolkitsP <**> helper) (progDesc "Show information on toolkits and exit.") - ) - , command "clean" ( - info (cleanP <**> helper) ( - progDesc (unlines - [ "Clean output directories where figures from FILE and log files might be stored." - , "WARNING: All files in those directories will be deleted." +commandParser = + optional $ + subparser $ + mconcat + [ command + "toolkits" + ( info (toolkitsP <**> helper) (progDesc "Show information on toolkits and exit.") + ), + command + "clean" + ( info + (cleanP <**> helper) + ( progDesc + ( unlines + [ "Clean output directories where figures from FILE and log files might be stored.", + "WARNING: All files in those directories will be deleted." ] - ) + ) ) + ), + command + "write-example-config" + ( info (writeConfigP <**> helper) (progDesc "Write example configuration to a file and exit.") ) - , command "write-example-config" ( - info (writeConfigP <**> helper) (progDesc "Write example configuration to a file and exit.") - ) - ] - where - configP = optional $ strOption (mconcat [long "config", metavar "PATH", help "Path to optional configuration file."]) - toolkitsP = Toolkits <$> configP - cleanP = Clean <$> configP <*> strArgument (metavar "FILE") - writeConfigP = WriteConfig <$> - strOption ( - mconcat [ long "path" - , metavar "FILE" - , value ".example-pandoc-plot.yml" - , help "Target location of the configuration file. Default is \".example-pandoc-plot.yml\"" - ] - ) + ] + where + configP = optional $ strOption (mconcat [long "config", metavar "PATH", help "Path to optional configuration file."]) + toolkitsP = Toolkits <$> configP + cleanP = Clean <$> configP <*> strArgument (metavar "FILE") + writeConfigP = + WriteConfig + <$> strOption + ( mconcat + [ long "path", + metavar "FILE", + value ".example-pandoc-plot.yml", + help "Target location of the configuration file. Default is \".example-pandoc-plot.yml\"" + ] + ) -- | Determine configuration and run filter. -- -- Priority for configuration: --- +-- -- (1) Loaded from filepath stored in document metadata, under the key @plot-configuration@; -- -- (2) Loaded from file @.pandoc-plot.yml@ in current work directory; -- -- (3) Default configuration --- toJSONFilterWithConfig :: IO () toJSONFilterWithConfig = do - upToDatePandoc <- checkRuntimePandocVersion - when upToDatePandoc $ toJSONFilter $ \doc -> do - c <- maybe localConfig configuration (configurationPathMeta doc) - plotTransform c doc - + upToDatePandoc <- checkRuntimePandocVersion + when upToDatePandoc $ + toJSONFilter $ \doc -> do + c <- maybe localConfig configuration (configurationPathMeta doc) + plotTransform c doc -- | Check that the runtime version of Pandoc is at least 2.11. The return value -- indicates whether the Pandoc version is new enough or not. checkRuntimePandocVersion :: IO Bool checkRuntimePandocVersion = do - -- Please note that for some reason, makeVersion [2, 11, 0, 0] > makeVersion [2, 11] - let minimumPandocVersion = V.makeVersion [2, 11] - - -- Pandoc runs filters in an environment with two variables: - -- PANDOV_VERSION and PANDOC_READER_OPTS - -- We can use the former to ensure that people are not using pandoc < 2.11 - pandocV <- lookupEnv "PANDOC_VERSION" - case pandocV >>= readVersion of - Nothing -> return True - Just v -> if (v < minimumPandocVersion) - then do - hPutStrLn stderr $ mconcat - [ "ERROR (pandoc-plot) The pandoc-plot filter only " - , "supports Pandoc 2.11 and newer. " - , "but you are using Pandoc " - , showVersion v - ] - return False - else return True - where - readVersion = fmap fst . lastMaybe . readP_to_S parseVersion - lastMaybe xs = if length xs > 1 then Just (last xs) else Nothing + -- Please note that for some reason, makeVersion [2, 11, 0, 0] > makeVersion [2, 11] + let minimumPandocVersion = V.makeVersion [2, 11] + -- Pandoc runs filters in an environment with two variables: + -- PANDOV_VERSION and PANDOC_READER_OPTS + -- We can use the former to ensure that people are not using pandoc < 2.11 + pandocV <- lookupEnv "PANDOC_VERSION" + case pandocV >>= readVersion of + Nothing -> return True + Just v -> + if (v < minimumPandocVersion) + then do + hPutStrLn stderr $ + mconcat + [ "ERROR (pandoc-plot) The pandoc-plot filter only ", + "supports Pandoc 2.11 and newer. ", + "but you are using Pandoc ", + showVersion v + ] + return False + else return True + where + readVersion = fmap fst . lastMaybe . readP_to_S parseVersion + lastMaybe xs = if length xs > 1 then Just (last xs) else Nothing --- | Load configuration from local file @.pandoc-plot.yml@. +-- | Load configuration from local file @.pandoc-plot.yml@. -- If the file does not exist, the default configuration will be used. localConfig :: IO Configuration -localConfig = do - configExists <- doesFileExist ".pandoc-plot.yml" - if configExists - then configuration ".pandoc-plot.yml" - else return defaultConfiguration - +localConfig = do + configExists <- doesFileExist ".pandoc-plot.yml" + if configExists + then configuration ".pandoc-plot.yml" + else return defaultConfiguration showFullVersion :: IO () showFullVersion = do - putStrLn $ "pandoc-plot " <> (V.showVersion pandocPlotVersion) - putStrLn $ "Git revision " <> gitrev - putStrLn $ mconcat - [ "Compiled with pandoc " , (unpack pandocVersion) - , " and pandoc-types " , V.showVersion pandocTypesVersion - , " using GHC ", TOOL_VERSION_ghc -- Constant defined by CPP - ] - where - -- In certain environments (e.g. Hackage when building documentation), - -- there is no git information. - gitrev = either (const "unknown") Git.giHash ($$tGitInfoCwdTry) - + putStrLn $ "pandoc-plot " <> (V.showVersion pandocPlotVersion) + putStrLn $ "Git revision " <> gitrev + putStrLn $ + mconcat + [ "Compiled with pandoc ", + (unpack pandocVersion), + " and pandoc-types ", + V.showVersion pandocTypesVersion, + " using GHC ", + TOOL_VERSION_ghc -- Constant defined by CPP + ] + where + -- In certain environments (e.g. Hackage when building documentation), + -- there is no git information. + gitrev = either (const "unknown") Git.giHash ($$tGitInfoCwdTry) showAvailableToolkits :: Maybe FilePath -> IO () showAvailableToolkits mfp = do - c <- case mfp of - Nothing -> localConfig - Just fp -> configuration fp - - putStrLn "\nAVAILABLE TOOLKITS\n" - available <- availableToolkits c - return available >>= mapM_ (availToolkitInfo c) - putStrLn "\nUNAVAILABLE TOOLKITS\n" - -- We don't use unavailableToolkits because this would force - -- more IO actions - let unavailable = toolkits \\ available - return unavailable >>= mapM_ (unavailToolkitInfo c) - where - toolkitInfo avail conf tk = do - putStrLn $ "Toolkit: " <> show tk - when avail $ do - Executable dir exe <- fmap fromJust $ runPlotM conf $ executable tk - putStrLn $ " Executable: " <> (dir </> unpack exe) - putStrLn $ " Code block trigger: " <> (unpack . cls $ tk) - putStrLn $ " Supported save formats: " <> (mconcat . intersperse ", " . fmap show $ supportedSaveFormats tk) - putStrLn mempty - availToolkitInfo = toolkitInfo True - unavailToolkitInfo = toolkitInfo False + c <- case mfp of + Nothing -> localConfig + Just fp -> configuration fp + putStrLn "\nAVAILABLE TOOLKITS\n" + available <- availableToolkits c + return available >>= mapM_ (availToolkitInfo c) + putStrLn "\nUNAVAILABLE TOOLKITS\n" + -- We don't use unavailableToolkits because this would force + -- more IO actions + let unavailable = toolkits \\ available + return unavailable >>= mapM_ (unavailToolkitInfo c) + where + toolkitInfo avail conf tk = do + putStrLn $ "Toolkit: " <> show tk + when avail $ do + Executable dir exe <- fmap fromJust $ runPlotM conf $ executable tk + putStrLn $ " Executable: " <> (dir </> unpack exe) + putStrLn $ " Code block trigger: " <> (unpack . cls $ tk) + putStrLn $ " Supported save formats: " <> (mconcat . intersperse ", " . fmap show $ supportedSaveFormats tk) + putStrLn mempty + availToolkitInfo = toolkitInfo True + unavailToolkitInfo = toolkitInfo False -- | Clean output directories associated with a file --- +-- -- Priority for configuration are the same as @toJSONFilterWithConfig@. -clean :: Maybe FilePath -- Use configuration file? - -> FilePath -- Document to clean - -> IO () +clean :: + Maybe FilePath -> -- Use configuration file? + FilePath -> -- Document to clean + IO () clean mfp fp = do - doc <- readDoc fp - -- Note the priority for configuration: - -- (1) path of argument --config - -- (2) document metadata - -- (3) local .pandoc-plot.yml - -- (4) default config - conf <- maybe localConfig configuration $ firstJusts [configurationPathMeta doc, mfp] - cleanOutputDirs conf doc >> return () - where - firstJusts :: [Maybe a] -> Maybe a - firstJusts = msum - + doc <- readDoc fp + -- Note the priority for configuration: + -- (1) path of argument --config + -- (2) document metadata + -- (3) local .pandoc-plot.yml + -- (4) default config + conf <- maybe localConfig configuration $ firstJusts [configurationPathMeta doc, mfp] + cleanOutputDirs conf doc >> return () + where + firstJusts :: [Maybe a] -> Maybe a + firstJusts = msum showManPage :: IO () showManPage = do - setLocaleEncoding utf8 -- This is required to write the manual file, for some reason. - manualPath <- (</> "pandoc-plot-manual.html") <$> getTemporaryDirectory - TIO.writeFile manualPath $(embedManualHtml) - openFile ("file:///" <> manualPath) + manualPath <- (</> "pandoc-plot-manual.html") <$> getTemporaryDirectory + TIO.writeFile manualPath $(embedManualHtml) + openFile ("file:///" <> manualPath) -- | Use Doc type directly because of newline formatting footer' :: P.Doc -footer' = mconcat - [ P.text "More information can be found via the manual (pandoc-plot --manual) or the" - , P.line - , P.text "repository README, located at https://github.com/LaurentRDC/pandoc-plot" - ]+footer' = + mconcat + [ P.text "More information can be found via the manual (pandoc-plot --manual) or the", + P.line, + P.text "repository README, located at https://github.com/LaurentRDC/pandoc-plot" + ]
executable/ManPage.hs view
@@ -1,30 +1,35 @@-{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE OverloadedStrings #-} -{-| -This module was inspired by pandoc-crossref -|-} - -module ManPage ( embedManualHtml ) where - -import Data.String (fromString) -import Data.Text (unpack) -import qualified Data.Text.IO as TIO +{-# LANGUAGE TemplateHaskell #-} -import System.FilePath ((</>)) +-- | +-- This module was inspired by pandoc-crossref +-- | +module ManPage (embedManualHtml) where -import Language.Haskell.TH.Syntax +import Control.Monad (unless) +import Data.String (fromString) +import Data.Text (unpack) +import qualified Data.Text.IO as TIO +import Language.Haskell.TH.Syntax +import System.Directory (doesFileExist) +import System.FilePath ((</>)) docFile :: FilePath docFile = "docs" </> "MANUAL.html" readDocFile :: IO String -readDocFile = TIO.readFile docFile >>= return . unpack +readDocFile = TIO.readFile docFile >>= return . unpack embedManualHtml :: Q Exp embedManualHtml = do - qAddDependentFile docFile - d <- runIO readDocFile - strToExp d - where - strToExp :: String -> Q Exp - strToExp s = return $ VarE 'fromString `AppE` LitE (StringL s) + -- Ensure that the manual file exists + -- even if it is empty + manualExists <- runIO $ doesFileExist docFile + unless manualExists (runIO $ TIO.writeFile docFile mempty) + + qAddDependentFile docFile + d <- runIO readDocFile + strToExp d + where + strToExp :: String -> Q Exp + strToExp s = return $ VarE 'fromString `AppE` LitE (StringL s)
executable/OpenFile.hs view
@@ -1,43 +1,37 @@-{-| -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 - -Open a file in its default program. --} - +-- | +-- 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 +-- +-- Open a file in its default program. module OpenFile (openFile) where -import Data.List (isInfixOf) -import System.Info (os) -import System.Process.Typed (runProcess_, shell, proc) - +import Data.List (isInfixOf) +import System.Info (os) +import System.Process.Typed (proc, runProcess_, shell) openFile :: FilePath -> IO () openFile - -- Aliases taken from cabal's Distribution.System module - | os `elem` ["mingw32", "win32", "cygwin32"] = openFileWindows - | any (`isInfixOf` os) ["linux", "bsd"] = openFileLinux - | os `elem` ["darwin"] = openFileMacOS - | otherwise = error $ "Unsupported OS: " <> os - + -- Aliases taken from cabal's Distribution.System module + | os `elem` ["mingw32", "win32", "cygwin32"] = openFileWindows + | any (`isInfixOf` os) ["linux", "bsd"] = openFileLinux + | os `elem` ["darwin"] = openFileMacOS + | otherwise = error $ "Unsupported OS: " <> os openFileWindows :: FilePath -> IO () -openFileWindows fp = - -- Call looks like: cmd /c 'start "" "my_filepath.html"' - runProcess_ $ shell $ mconcat ["cmd /c start ", quoted mempty, " ", quoted fp] - where - quoted f = mconcat ["\"", f, "\""] - +openFileWindows fp = + -- Call looks like: cmd /c 'start "" "my_filepath.html"' + runProcess_ $ shell $ mconcat ["cmd /c start ", quoted mempty, " ", quoted fp] + where + quoted f = mconcat ["\"", f, "\""] openFileLinux :: FilePath -> IO () openFileLinux fp = - runProcess_ (proc "sh" ["-c", "xdg-open \"$0\" 2>&1 > /dev/null", fp]) - + runProcess_ (proc "sh" ["-c", "xdg-open \"$0\" 2>&1 > /dev/null", fp]) openFileMacOS :: FilePath -> IO () openFileMacOS fp = - runProcess_ (proc "open" [fp])+ runProcess_ (proc "open" [fp])
pandoc-plot.cabal view
@@ -1,8 +1,10 @@ cabal-version: 2.2 name: pandoc-plot -version: 1.0.0.0 +version: 1.0.1.0 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. +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. category: Text copyright: (c) 2019-2020 Laurent P. René de Cotret homepage: https://github.com/LaurentRDC/pandoc-plot#readme @@ -12,7 +14,7 @@ license: GPL-2.0-or-later license-file: LICENSE build-type: Simple -tested-with: GHC == 8.6.5, GHC == 8.8.3, GHC == 8.10.1 +tested-with: GHC == 8.10.1 extra-source-files: CHANGELOG.md LICENSE @@ -21,11 +23,15 @@ docs/MANUAL.html stack.yaml example-config.yml + + data/srctemplate.html + tests/fixtures/.config-meta.yml tests/fixtures/.empty-config.yml tests/fixtures/.verbose-config.yml tests/fixtures/plotfile.py tests/fixtures/data.csv + tests/includes/bokeh.py tests/includes/ggplot2.r tests/includes/gnuplot.gp @@ -58,6 +64,7 @@ Text.Pandoc.Filter.Plot.Configuration Text.Pandoc.Filter.Plot.Parse Text.Pandoc.Filter.Plot.Scripting + Text.Pandoc.Filter.Plot.Scripting.Template Text.Pandoc.Filter.Plot.Renderers Text.Pandoc.Filter.Plot.Renderers.Prelude Text.Pandoc.Filter.Plot.Renderers.Matplotlib @@ -96,6 +103,7 @@ , lifted-base >= 0.2 && < 1 , shakespeare >= 2.0 && < 3 , tagsoup >= 0.14 && < 1 + , template-haskell > 2.7 && < 3 , text >= 1 && < 2 , typed-process >= 0.2.1 && < 1 , yaml >= 0.8 && < 1
src/Text/Pandoc/Filter/Plot.hs view
@@ -1,117 +1,143 @@+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE FlexibleContexts #-} -{-| -Module : $header$ -Description : Pandoc filter to create figures from code blocks using your plotting toolkit of choice -Copyright : (c) Laurent P René de Cotret, 2020 -License : GNU GPL, version 2 or above -Maintainer : laurent.decotret@outlook.com -Stability : unstable -Portability : portable -This module defines a Pandoc filter @plotTransform@ and related functions -that can be used to walk over a Pandoc document and generate figures from -code blocks, using a multitude of plotting toolkits. - -The syntax for code blocks is simple. Code blocks with the appropriate class -attribute will trigger the filter: - -* @matplotlib@ for matplotlib-based Python plots; -* @plotly_python@ for Plotly-based Python plots; -* @plotly_r@ for Plotly-based R plots; -* @matlabplot@ for MATLAB plots; -* @mathplot@ for Mathematica plots; -* @octaveplot@ for GNU Octave plots; -* @ggplot2@ for ggplot2-based R plots; -* @gnuplot@ for gnuplot plots; -* @graphviz@ for Graphviz graphs; -* @bokeh@ for Bokeh-based Python plots; -* @plotsjl@ for Plots.jl-based Julia plots; - -For example, in Markdown: - -@ - This is a paragraph. - - ```{.matlabplot} - figure() - plot([1,2,3,4,5], [1,2,3,4,5], '-k') - ``` -@ - -The code block will be reworked into a script and the output figure will be captured. Optionally, the source code - used to generate the figure will be linked in the caption. - -Here are the possible attributes what pandoc-plot understands for ALL toolkits: - - * @directory=...@ : Directory where to save the figure. This path should be specified with - respect to the current working directory, and not with respect to the document. - * @source=true|false@ : Whether or not to link the source code of this figure in the caption. - Ideal for web pages, for example. Default is false. - * @format=...@: Format of the generated figure. This can be an extension or an acronym, - e.g. @format=PNG@. - * @caption="..."@: Specify a plot caption (or alternate text). Format - for captions is specified in the documentation for the @Configuration@ type. - * @dpi=...@: Specify a value for figure resolution, or dots-per-inch. Certain toolkits ignore this. - * @dependencies=[...]@: Specify files/directories on which a figure depends, e.g. data file. - Figures will be re-rendered if one of those file/directory changes. These paths should - be specified with respect to the current working directory, and not with respect to the document. - * @preamble=...@: Path to a file to include before the code block. Ideal to avoid repetition over - many figures. - * @file=...@: Path to a file from which to read the content of the figure. The content of the - code block will be ignored. This path should be specified with respect to the current working - directory, and not with respect to the document. - -Default values for the above attributes are stored in the @Configuration@ datatype. These can be specified in a -YAML file. - -Here is an example code block which will render a figure using gnuplot, in Markdown: - -@ - ```{.gnuplot format=png caption="Sinusoidal function" source=true} - sin(x) +-- | +-- Module : $header$ +-- Description : Pandoc filter to create figures from code blocks using your plotting toolkit of choice +-- Copyright : (c) Laurent P René de Cotret, 2020 +-- License : GNU GPL, version 2 or above +-- Maintainer : laurent.decotret@outlook.com +-- Stability : unstable +-- Portability : portable +-- +-- This module defines a Pandoc filter @plotTransform@ and related functions +-- that can be used to walk over a Pandoc document and generate figures from +-- code blocks, using a multitude of plotting toolkits. +-- +-- The syntax for code blocks is simple. Code blocks with the appropriate class +-- attribute will trigger the filter: +-- +-- * @matplotlib@ for matplotlib-based Python plots; +-- * @plotly_python@ for Plotly-based Python plots; +-- * @plotly_r@ for Plotly-based R plots; +-- * @matlabplot@ for MATLAB plots; +-- * @mathplot@ for Mathematica plots; +-- * @octaveplot@ for GNU Octave plots; +-- * @ggplot2@ for ggplot2-based R plots; +-- * @gnuplot@ for gnuplot plots; +-- * @graphviz@ for Graphviz graphs; +-- * @bokeh@ for Bokeh-based Python plots; +-- * @plotsjl@ for Plots.jl-based Julia plots; +-- +-- For example, in Markdown: +-- +-- @ +-- This is a paragraph. +-- +-- ```{.matlabplot} +-- figure() +-- plot([1,2,3,4,5], [1,2,3,4,5], '-k') +-- ``` +-- @ +-- +-- The code block will be reworked into a script and the output figure will be captured. Optionally, the source code +-- used to generate the figure will be linked in the caption. +-- +-- Here are the possible attributes what pandoc-plot understands for ALL toolkits: +-- +-- * @directory=...@ : Directory where to save the figure. This path should be specified with +-- respect to the current working directory, and not with respect to the document. +-- * @source=true|false@ : Whether or not to link the source code of this figure in the caption. +-- Ideal for web pages, for example. Default is false. +-- * @format=...@: Format of the generated figure. This can be an extension or an acronym, +-- e.g. @format=PNG@. +-- * @caption="..."@: Specify a plot caption (or alternate text). Format +-- for captions is specified in the documentation for the @Configuration@ type. +-- * @dpi=...@: Specify a value for figure resolution, or dots-per-inch. Certain toolkits ignore this. +-- * @dependencies=[...]@: Specify files/directories on which a figure depends, e.g. data file. +-- Figures will be re-rendered if one of those file/directory changes. These paths should +-- be specified with respect to the current working directory, and not with respect to the document. +-- * @preamble=...@: Path to a file to include before the code block. Ideal to avoid repetition over +-- many figures. +-- * @file=...@: Path to a file from which to read the content of the figure. The content of the +-- code block will be ignored. This path should be specified with respect to the current working +-- directory, and not with respect to the document. +-- +-- Default values for the above attributes are stored in the @Configuration@ datatype. These can be specified in a +-- YAML file. +-- +-- Here is an example code block which will render a figure using gnuplot, in Markdown: +-- +-- @ +-- ```{.gnuplot format=png caption="Sinusoidal function" source=true} +-- sin(x) +-- +-- set xlabel "x" +-- set ylabel "y" +-- ``` +-- @ +module Text.Pandoc.Filter.Plot + ( -- * Operating on whole Pandoc documents + plotTransform, - set xlabel "x" - set ylabel "y" - ``` -@ --} -module Text.Pandoc.Filter.Plot ( - -- * Operating on whole Pandoc documents - plotTransform -- * Cleaning output directories - , cleanOutputDirs + cleanOutputDirs, + -- * Runtime configuration - , configuration - , defaultConfiguration - , Configuration(..) - , Verbosity(..) - , LogSink(..) - , SaveFormat(..) - , Script - -- * Determining available plotting toolkits - , Toolkit(..) - , availableToolkits - , unavailableToolkits - , toolkits - , supportedSaveFormats - -- * Version information - , pandocPlotVersion - -- * For embedding, testing and internal purposes ONLY. Might change without notice. - , make - , makeEither - , PandocPlotError(..) - ) where + configuration, + defaultConfiguration, + Configuration (..), + Verbosity (..), + LogSink (..), + SaveFormat (..), + Script, -import Control.Concurrent.Async.Lifted (mapConcurrently) -import Data.Text (Text, unpack) -import Data.Version (Version) + -- * Determining available plotting toolkits + Toolkit (..), + availableToolkits, + unavailableToolkits, + toolkits, + supportedSaveFormats, -import Paths_pandoc_plot (version) + -- * Version information + pandocPlotVersion, -import Text.Pandoc.Definition (Pandoc(..), Block) + -- * For embedding, testing and internal purposes ONLY. Might change without notice. + make, + makeEither, + PandocPlotError (..), + ) +where +import Control.Concurrent.Async.Lifted (mapConcurrently) +import Data.Text (Text, unpack) +import Data.Version (Version) +import Paths_pandoc_plot (version) +import Text.Pandoc.Definition (Block, Pandoc (..)) import Text.Pandoc.Filter.Plot.Internal + ( Configuration (..), + FigureSpec, + LogSink (..), + PlotM, + RuntimeEnv (envConfig), + SaveFormat (..), + Script, + ScriptResult (..), + Toolkit (..), + Verbosity (..), + asks, + availableToolkits, + cleanOutputDirs, + configuration, + defaultConfiguration, + parseFigureSpec, + runPlotM, + runScriptIfNecessary, + supportedSaveFormats, + toFigure, + toolkits, + unavailableToolkits, + ) -- | Walk over an entire Pandoc document, transforming appropriate code blocks -- into figures. This function will operate on blocks in parallel if possible. @@ -119,12 +145,14 @@ -- 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. -plotTransform :: Configuration -- ^ Configuration for default values - -> Pandoc -- ^ Input document - -> IO Pandoc -plotTransform conf (Pandoc meta blocks) = - runPlotM conf $ mapConcurrently make blocks >>= return . Pandoc meta - +plotTransform :: + -- | Configuration for default values + Configuration -> + -- | Input document + Pandoc -> + IO Pandoc +plotTransform conf (Pandoc meta blocks) = + runPlotM conf $ mapConcurrently make blocks >>= return . Pandoc meta -- | The version of the pandoc-plot package. -- @@ -132,35 +160,32 @@ pandocPlotVersion :: Version pandocPlotVersion = version - -- | Try to process the block with `pandoc-plot`. If a failure happens (or the block) -- was not meant to become a figure, return the block as-is. make :: Block -> PlotM Block -make blk = either (const (return blk) ) return =<< makeEither blk - +make blk = either (const (return blk)) return =<< makeEither blk -- | Try to process the block with `pandoc-plot`, documenting the error. makeEither :: Block -> PlotM (Either PandocPlotError Block) -makeEither block = - parseFigureSpec block - >>= maybe - (return $ Right block) - (\s -> runScriptIfNecessary s >>= handleResult s) - where - -- Logging of errors has been taken care of in @runScriptIfNecessary@ - handleResult :: FigureSpec -> ScriptResult -> PlotM (Either PandocPlotError Block) - handleResult _ (ScriptFailure msg code) = return $ Left (ScriptRuntimeError msg code) - handleResult _ (ScriptChecksFailed msg) = return $ Left (ScriptChecksFailedError msg) - handleResult _ (ToolkitNotInstalled tk') = return $ Left (ToolkitNotInstalledError tk') - handleResult spec ScriptSuccess = asks envConfig >>= \c -> Right <$> toFigure (captionFormat c) spec - +makeEither block = + parseFigureSpec block + >>= maybe + (return $ Right block) + (\s -> runScriptIfNecessary s >>= handleResult s) + where + -- Logging of errors has been taken care of in @runScriptIfNecessary@ + handleResult :: FigureSpec -> ScriptResult -> PlotM (Either PandocPlotError Block) + handleResult _ (ScriptFailure msg code) = return $ Left (ScriptRuntimeError msg code) + handleResult _ (ScriptChecksFailed msg) = return $ Left (ScriptChecksFailedError msg) + handleResult _ (ToolkitNotInstalled tk') = return $ Left (ToolkitNotInstalledError tk') + handleResult spec ScriptSuccess = asks envConfig >>= \c -> Right <$> toFigure (captionFormat c) spec data PandocPlotError - = ScriptRuntimeError Text Int - | ScriptChecksFailedError Text - | ToolkitNotInstalledError Toolkit + = ScriptRuntimeError Text Int + | ScriptChecksFailedError Text + | ToolkitNotInstalledError Toolkit instance Show PandocPlotError where - show (ScriptRuntimeError _ exitcode) = "ERROR (pandoc-plot) The script failed with exit code " <> show exitcode <> "." - show (ScriptChecksFailedError msg) = "ERROR (pandoc-plot) A script check failed with message: " <> unpack msg <> "." - show (ToolkitNotInstalledError tk) = "ERROR (pandoc-plot) The " <> show tk <> " toolkit is required but not installed."+ show (ScriptRuntimeError _ exitcode) = "ERROR (pandoc-plot) The script failed with exit code " <> show exitcode <> "." + show (ScriptChecksFailedError msg) = "ERROR (pandoc-plot) A script check failed with message: " <> unpack msg <> "." + show (ToolkitNotInstalledError tk) = "ERROR (pandoc-plot) The " <> show tk <> " toolkit is required but not installed."
src/Text/Pandoc/Filter/Plot/Clean.hs view
@@ -1,105 +1,104 @@+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE FlexibleContexts #-} -{-| -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 - -Utilities to clean pandoc-plot output directories. --} - -module Text.Pandoc.Filter.Plot.Clean ( - cleanOutputDirs - , outputDirs - , readDoc -) where +-- | +-- 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 +-- +-- Utilities to clean pandoc-plot output directories. +module Text.Pandoc.Filter.Plot.Clean + ( cleanOutputDirs, + outputDirs, + readDoc, + ) +where -- TODO: forConcurrently -import Control.Monad.Reader (forM) - -import qualified Data.ByteString.Lazy as B -import Data.Char (toLower) -import Data.Default (def) -import Data.List (nub) -import Data.Maybe (fromMaybe, catMaybes) - -import Data.Text (Text, pack) -import qualified Data.Text.IO as Text - -import System.Directory (removePathForcibly) - -import System.FilePath (takeExtension) - -import Text.Pandoc.Class (runIO) -import Text.Pandoc.Definition -import Text.Pandoc.Error (handleError) -import qualified Text.Pandoc.Readers as P -import qualified Text.Pandoc.Options as P -import Text.Pandoc.Walk (query, Walkable) - -import Text.Pandoc.Filter.Plot.Parse +import Control.Monad.Reader (forM) +import qualified Data.ByteString.Lazy as B +import Data.Char (toLower) +import Data.Default (def) +import Data.List (nub) +import Data.Maybe (catMaybes, fromMaybe) +import Data.Text (Text, pack) +import qualified Data.Text.IO as Text +import System.Directory (removePathForcibly) +import System.FilePath (takeExtension) +import Text.Pandoc.Class (runIO) +import Text.Pandoc.Definition +import Text.Pandoc.Error (handleError) import Text.Pandoc.Filter.Plot.Monad - +import Text.Pandoc.Filter.Plot.Parse +import qualified Text.Pandoc.Options as P +import qualified Text.Pandoc.Readers as P +import Text.Pandoc.Walk (Walkable, query) --- | Clean all output related to pandoc-plot. This includes output directories specified --- in the configuration and in the document/block, as well as log files. +-- | Clean all output related to pandoc-plot. This includes output directories specified +-- in the configuration and in the document/block, as well as log files. -- Note that *all* files in pandoc-plot output directories will be removed. -- -- The cleaned directories are returned. -cleanOutputDirs :: Walkable Block b - => Configuration -> b -> IO [FilePath] +cleanOutputDirs :: + Walkable Block b => + Configuration -> + b -> + IO [FilePath] cleanOutputDirs conf doc = do - dirs <- runPlotM conf . cleanOutputDirsM $ doc - -- Deletion of the log file must be done outside of PlotM - -- to ensure the log file has been closed. - case logSink conf of - LogFile path -> removePathForcibly path - _ -> return () - return dirs - + dirs <- runPlotM conf . cleanOutputDirsM $ doc + -- Deletion of the log file must be done outside of PlotM + -- to ensure the log file has been closed. + case logSink conf of + LogFile path -> removePathForcibly path + _ -> return () + return dirs -- | Analyze a document to determine where would the pandoc-plot output directories be. -outputDirs :: Walkable Block b - => b -> PlotM [FilePath] -outputDirs = fmap (nub . catMaybes) - . sequence - . query (\b -> [parseFigureSpec b >>= return . fmap directory]) - +outputDirs :: + Walkable Block b => + b -> + PlotM [FilePath] +outputDirs = + fmap (nub . catMaybes) + . sequence + . query (\b -> [parseFigureSpec b >>= return . fmap directory]) -- PlotM version of @cleanOutputDirs@ -cleanOutputDirsM :: Walkable Block b - => b -> PlotM [FilePath] +cleanOutputDirsM :: + Walkable Block b => + b -> + PlotM [FilePath] cleanOutputDirsM doc = do - directories <- outputDirs doc - forM directories $ \fp -> do - info $ "Removing directory " <> pack fp - -- It is important to use `removePathForcibly` here, because it does - -- not throw exceptions if the directory doesn't exist. This means - -- we do not have to check in advance if directories are nested in our - -- list of directories. - liftIO $ removePathForcibly fp - return fp - + directories <- outputDirs doc + forM directories $ \fp -> do + info $ "Removing directory " <> pack fp + -- It is important to use `removePathForcibly` here, because it does + -- not throw exceptions if the directory doesn't exist. This means + -- we do not have to check in advance if directories are nested in our + -- list of directories. + liftIO $ removePathForcibly fp + return fp -- | Read a document, guessing what extensions and reader options are appropriate. If -- the file cannot be read for any reason, an error is thrown. readDoc :: FilePath -> IO Pandoc -readDoc fp = handleError =<< (runIO $ do - let fmt = fromMaybe mempty (formatFromFilePath fp) - (reader, exts) <- P.getReader fmt - let readerOpts = def {P.readerExtensions = exts} - case reader of - P.TextReader fct -> do - t <- liftIO $ Text.readFile fp +readDoc fp = + handleError + =<< ( runIO $ do + let fmt = fromMaybe mempty (formatFromFilePath fp) + (reader, exts) <- P.getReader fmt + let readerOpts = def {P.readerExtensions = exts} + case reader of + P.TextReader fct -> do + t <- liftIO $ Text.readFile fp fct readerOpts t - P.ByteStringReader bst -> do + P.ByteStringReader bst -> do b <- liftIO $ B.readFile fp - bst readerOpts b) - + bst readerOpts b + ) -- Determine format based on file extension -- Note : this is exactly the heuristic used by pandoc here: @@ -109,49 +108,49 @@ formatFromFilePath :: FilePath -> Maybe Text formatFromFilePath x = case takeExtension (map toLower x) of - ".adoc" -> Just "asciidoc" + ".adoc" -> Just "asciidoc" ".asciidoc" -> Just "asciidoc" - ".context" -> Just "context" - ".ctx" -> Just "context" - ".db" -> Just "docbook" - ".doc" -> Just "doc" -- so we get an "unknown reader" error - ".docx" -> Just "docx" + ".context" -> Just "context" + ".ctx" -> Just "context" + ".db" -> Just "docbook" + ".doc" -> Just "doc" -- so we get an "unknown reader" error + ".docx" -> Just "docx" ".dokuwiki" -> Just "dokuwiki" - ".epub" -> Just "epub" - ".fb2" -> Just "fb2" - ".htm" -> Just "html" - ".html" -> Just "html" - ".icml" -> Just "icml" - ".json" -> Just "json" - ".latex" -> Just "latex" - ".lhs" -> Just "markdown+lhs" - ".ltx" -> Just "latex" + ".epub" -> Just "epub" + ".fb2" -> Just "fb2" + ".htm" -> Just "html" + ".html" -> Just "html" + ".icml" -> Just "icml" + ".json" -> Just "json" + ".latex" -> Just "latex" + ".lhs" -> Just "markdown+lhs" + ".ltx" -> Just "latex" ".markdown" -> Just "markdown" - ".md" -> Just "markdown" - ".ms" -> Just "ms" - ".muse" -> Just "muse" - ".native" -> Just "native" - ".odt" -> Just "odt" - ".opml" -> Just "opml" - ".org" -> Just "org" - ".pdf" -> Just "pdf" -- so we get an "unknown reader" error - ".pptx" -> Just "pptx" - ".roff" -> Just "ms" - ".rst" -> Just "rst" - ".rtf" -> Just "rtf" - ".s5" -> Just "s5" - ".t2t" -> Just "t2t" - ".tei" -> Just "tei" - ".tei.xml" -> Just "tei" - ".tex" -> Just "latex" - ".texi" -> Just "texinfo" - ".texinfo" -> Just "texinfo" - ".text" -> Just "markdown" - ".textile" -> Just "textile" - ".txt" -> Just "markdown" - ".wiki" -> Just "mediawiki" - ".xhtml" -> Just "html" - ".ipynb" -> Just "ipynb" - ".csv" -> Just "csv" - ['.',y] | y `elem` ['1'..'9'] -> Just "man" - _ -> Nothing+ ".md" -> Just "markdown" + ".ms" -> Just "ms" + ".muse" -> Just "muse" + ".native" -> Just "native" + ".odt" -> Just "odt" + ".opml" -> Just "opml" + ".org" -> Just "org" + ".pdf" -> Just "pdf" -- so we get an "unknown reader" error + ".pptx" -> Just "pptx" + ".roff" -> Just "ms" + ".rst" -> Just "rst" + ".rtf" -> Just "rtf" + ".s5" -> Just "s5" + ".t2t" -> Just "t2t" + ".tei" -> Just "tei" + ".tei.xml" -> Just "tei" + ".tex" -> Just "latex" + ".texi" -> Just "texinfo" + ".texinfo" -> Just "texinfo" + ".text" -> Just "markdown" + ".textile" -> Just "textile" + ".txt" -> Just "markdown" + ".wiki" -> Just "mediawiki" + ".xhtml" -> Just "html" + ".ipynb" -> Just "ipynb" + ".csv" -> Just "csv" + ['.', y] | y `elem` ['1' .. '9'] -> Just "man" + _ -> Nothing
src/Text/Pandoc/Filter/Plot/Configuration.hs view
@@ -1,33 +1,30 @@ {-# LANGUAGE OverloadedStrings #-} -{-# 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 - -Reading configuration from file --} - -module Text.Pandoc.Filter.Plot.Configuration ( - configuration - , configurationPathMeta - , defaultConfiguration -) where - -import Data.Maybe (fromMaybe) -import Data.Text (Text, pack, unpack) -import qualified Data.Text.IO as TIO -import Data.Yaml ((.!=), (.:?), FromJSON(parseJSON), Value(Object, Null)) -import Data.Yaml.Config (ignoreEnv, loadYamlSettings) - -import System.FilePath (normalise) +{-# LANGUAGE RecordWildCards #-} -import Text.Pandoc.Definition (Format(..), Pandoc(..), MetaValue(..), Inline(..), lookupMeta) +-- | +-- 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 +-- +-- Reading configuration from file +module Text.Pandoc.Filter.Plot.Configuration + ( configuration, + configurationPathMeta, + defaultConfiguration, + ) +where -import Text.Pandoc.Filter.Plot.Monad +import Data.Maybe (fromMaybe) +import Data.Text (Text, pack, unpack) +import qualified Data.Text.IO as TIO +import Data.Yaml (FromJSON (parseJSON), Value (Null, Object), (.!=), (.:?)) +import Data.Yaml.Config (ignoreEnv, loadYamlSettings) +import System.FilePath (normalise) +import Text.Pandoc.Definition (Format (..), Inline (..), MetaValue (..), Pandoc (..), lookupMeta) +import Text.Pandoc.Filter.Plot.Monad -- | Read configuration from a YAML file. The -- keys are exactly the same as for code blocks. @@ -37,52 +34,48 @@ configuration :: FilePath -> IO Configuration configuration fp = (loadYamlSettings [normalise fp] [] ignoreEnv) >>= renderConfig - -- | Default configuration values. -- -- @since 0.5.0.0 defaultConfiguration :: Configuration -defaultConfiguration = - Configuration - { defaultDirectory = "plots/" - , defaultWithSource = False - , defaultDPI = 80 - , defaultSaveFormat = PNG - , defaultDependencies = mempty - , captionFormat = Format "markdown+tex_math_dollars" - - , logVerbosity = Warning - , logSink = StdErr - - , matplotlibPreamble = mempty - , plotlyPythonPreamble= mempty - , plotlyRPreamble = mempty - , matlabPreamble = mempty - , mathematicaPreamble = mempty - , octavePreamble = mempty - , ggplot2Preamble = mempty - , gnuplotPreamble = mempty - , graphvizPreamble = mempty - , bokehPreamble = mempty - , plotsjlPreamble = mempty - - , matplotlibExe = python - , matlabExe = "matlab" - , plotlyPythonExe = python - , plotlyRExe = "Rscript" - , mathematicaExe = "math" - , octaveExe = "octave" - , ggplot2Exe = "Rscript" - , gnuplotExe = "gnuplot" - , graphvizExe = "dot" - , bokehExe = python - , plotsjlExe = "julia" - - , matplotlibTightBBox = False - , matplotlibTransparent = False - } - where - python = if isWindows then "python" else "python3" +defaultConfiguration = + Configuration + { defaultDirectory = "plots/", + defaultWithSource = False, + defaultDPI = 80, + defaultSaveFormat = PNG, + defaultDependencies = mempty, + captionFormat = Format "markdown+tex_math_dollars", + sourceCodeLabel = "Source code", + logVerbosity = Warning, + logSink = StdErr, + matplotlibPreamble = mempty, + plotlyPythonPreamble = mempty, + plotlyRPreamble = mempty, + matlabPreamble = mempty, + mathematicaPreamble = mempty, + octavePreamble = mempty, + ggplot2Preamble = mempty, + gnuplotPreamble = mempty, + graphvizPreamble = mempty, + bokehPreamble = mempty, + plotsjlPreamble = mempty, + matplotlibExe = python, + matlabExe = "matlab", + plotlyPythonExe = python, + plotlyRExe = "Rscript", + mathematicaExe = "math", + octaveExe = "octave", + ggplot2Exe = "Rscript", + gnuplotExe = "gnuplot", + graphvizExe = "dot", + bokehExe = python, + plotsjlExe = "julia", + matplotlibTightBBox = False, + matplotlibTransparent = False + } + where + python = if isWindows then "python" else "python3" -- | Extact path to configuration from the metadata in a Pandoc document. -- The path to the configuration file should be under the @plot-configuration@ key. @@ -95,7 +88,7 @@ -- title: My document -- author: John Doe -- plot-configuration: /path/to/file.yml --- --- +-- --- -- @ -- -- The same can be specified via the command line using Pandoc's @-M@ flag: @@ -104,216 +97,221 @@ -- -- @since 0.6.0.0 configurationPathMeta :: Pandoc -> Maybe FilePath -configurationPathMeta (Pandoc meta _) = - lookupMeta "plot-configuration" meta >>= getPath - where - getPath (MetaString t) = Just (unpack t) - getPath (MetaInlines [Str s]) = Just (unpack s) - getPath _ = Nothing - +configurationPathMeta (Pandoc meta _) = + lookupMeta "plot-configuration" meta >>= getPath + where + getPath (MetaString t) = Just (unpack t) + getPath (MetaInlines [Str s]) = Just (unpack s) + getPath _ = Nothing -- We define a precursor type because preambles are best specified as file paths, -- but we want to read those files before building a full -- @Configuration@ value. data ConfigPrecursor = ConfigPrecursor - { _defaultDirectory :: !FilePath - , _defaultWithSource :: !Bool - , _defaultDPI :: !Int - , _defaultSaveFormat :: !SaveFormat - , _defaultDependencies :: ![FilePath] - , _captionFormat :: !Format - - , _logPrec :: !LoggingPrecursor + { _defaultDirectory :: !FilePath, + _defaultWithSource :: !Bool, + _defaultDPI :: !Int, + _defaultSaveFormat :: !SaveFormat, + _defaultDependencies :: ![FilePath], + _captionFormat :: !Format, + _sourceCodeLabel :: !Text, + _logPrec :: !LoggingPrecursor, + _matplotlibPrec :: !MatplotlibPrecursor, + _matlabPrec :: !MatlabPrecursor, + _plotlyPythonPrec :: !PlotlyPythonPrecursor, + _plotlyRPrec :: !PlotlyRPrecursor, + _mathematicaPrec :: !MathematicaPrecursor, + _octavePrec :: !OctavePrecursor, + _ggplot2Prec :: !GGPlot2Precursor, + _gnuplotPrec :: !GNUPlotPrecursor, + _graphvizPrec :: !GraphvizPrecursor, + _bokehPrec :: !BokehPrecursor, + _plotsjlPrec :: !PlotsjlPrecursor + } - , _matplotlibPrec :: !MatplotlibPrecursor - , _matlabPrec :: !MatlabPrecursor - , _plotlyPythonPrec :: !PlotlyPythonPrecursor - , _plotlyRPrec :: !PlotlyRPrecursor - , _mathematicaPrec :: !MathematicaPrecursor - , _octavePrec :: !OctavePrecursor - , _ggplot2Prec :: !GGPlot2Precursor - , _gnuplotPrec :: !GNUPlotPrecursor - , _graphvizPrec :: !GraphvizPrecursor - , _bokehPrec :: !BokehPrecursor - , _plotsjlPrec :: !PlotsjlPrecursor +defaultConfigPrecursor :: ConfigPrecursor +defaultConfigPrecursor = + ConfigPrecursor + { _defaultDirectory = defaultDirectory defaultConfiguration, + _defaultWithSource = defaultWithSource defaultConfiguration, + _defaultDPI = defaultDPI defaultConfiguration, + _defaultSaveFormat = defaultSaveFormat defaultConfiguration, + _defaultDependencies = defaultDependencies defaultConfiguration, + _captionFormat = captionFormat defaultConfiguration, + _sourceCodeLabel = sourceCodeLabel defaultConfiguration, + _logPrec = LoggingPrecursor (logVerbosity defaultConfiguration) Nothing, -- _logFilePath=Nothing implies log to stderr + _matplotlibPrec = MatplotlibPrecursor Nothing (matplotlibTightBBox defaultConfiguration) (matplotlibTransparent defaultConfiguration) (matplotlibExe defaultConfiguration), + _matlabPrec = MatlabPrecursor Nothing (matlabExe defaultConfiguration), + _plotlyPythonPrec = PlotlyPythonPrecursor Nothing (plotlyPythonExe defaultConfiguration), + _plotlyRPrec = PlotlyRPrecursor Nothing (plotlyRExe defaultConfiguration), + _mathematicaPrec = MathematicaPrecursor Nothing (mathematicaExe defaultConfiguration), + _octavePrec = OctavePrecursor Nothing (octaveExe defaultConfiguration), + _ggplot2Prec = GGPlot2Precursor Nothing (ggplot2Exe defaultConfiguration), + _gnuplotPrec = GNUPlotPrecursor Nothing (gnuplotExe defaultConfiguration), + _graphvizPrec = GraphvizPrecursor Nothing (graphvizExe defaultConfiguration), + _bokehPrec = BokehPrecursor Nothing (bokehExe defaultConfiguration), + _plotsjlPrec = PlotsjlPrecursor Nothing (plotsjlExe defaultConfiguration) } -defaultConfigPrecursor :: ConfigPrecursor -defaultConfigPrecursor = - ConfigPrecursor - { _defaultDirectory = defaultDirectory defaultConfiguration - , _defaultWithSource = defaultWithSource defaultConfiguration - , _defaultDPI = defaultDPI defaultConfiguration - , _defaultSaveFormat = defaultSaveFormat defaultConfiguration - , _defaultDependencies = defaultDependencies defaultConfiguration - , _captionFormat = captionFormat defaultConfiguration +data LoggingPrecursor = LoggingPrecursor + { _logVerbosity :: !Verbosity, + _logFilePath :: !(Maybe FilePath) + } - , _logPrec = LoggingPrecursor (logVerbosity defaultConfiguration) Nothing -- _logFilePath=Nothing implies log to stderr - - , _matplotlibPrec = MatplotlibPrecursor Nothing (matplotlibTightBBox defaultConfiguration) (matplotlibTransparent defaultConfiguration) (matplotlibExe defaultConfiguration) - , _matlabPrec = MatlabPrecursor Nothing (matlabExe defaultConfiguration) - , _plotlyPythonPrec = PlotlyPythonPrecursor Nothing (plotlyPythonExe defaultConfiguration) - , _plotlyRPrec = PlotlyRPrecursor Nothing (plotlyRExe defaultConfiguration) - , _mathematicaPrec = MathematicaPrecursor Nothing (mathematicaExe defaultConfiguration) - , _octavePrec = OctavePrecursor Nothing (octaveExe defaultConfiguration) - , _ggplot2Prec = GGPlot2Precursor Nothing (ggplot2Exe defaultConfiguration) - , _gnuplotPrec = GNUPlotPrecursor Nothing (gnuplotExe defaultConfiguration) - , _graphvizPrec = GraphvizPrecursor Nothing (graphvizExe defaultConfiguration) - , _bokehPrec = BokehPrecursor Nothing (bokehExe defaultConfiguration) - , _plotsjlPrec = PlotsjlPrecursor Nothing (plotsjlExe defaultConfiguration) - } +-- Separate YAML clauses have their own types. +data MatplotlibPrecursor = MatplotlibPrecursor + { _matplotlibPreamble :: !(Maybe FilePath), + _matplotlibTightBBox :: !Bool, + _matplotlibTransparent :: !Bool, + _matplotlibExe :: !FilePath + } +data MatlabPrecursor = MatlabPrecursor {_matlabPreamble :: !(Maybe FilePath), _matlabExe :: !FilePath} -data LoggingPrecursor = LoggingPrecursor { _logVerbosity :: !Verbosity - , _logFilePath :: !(Maybe FilePath) - } +data PlotlyPythonPrecursor = PlotlyPythonPrecursor {_plotlyPythonPreamble :: !(Maybe FilePath), _plotlyPythonExe :: !FilePath} --- Separate YAML clauses have their own types. -data MatplotlibPrecursor = MatplotlibPrecursor - { _matplotlibPreamble :: !(Maybe FilePath) - , _matplotlibTightBBox :: !Bool - , _matplotlibTransparent :: !Bool - , _matplotlibExe :: !FilePath - } -data MatlabPrecursor = MatlabPrecursor {_matlabPreamble :: !(Maybe FilePath), _matlabExe :: !FilePath} -data PlotlyPythonPrecursor = PlotlyPythonPrecursor {_plotlyPythonPreamble :: !(Maybe FilePath), _plotlyPythonExe :: !FilePath} -data PlotlyRPrecursor = PlotlyRPrecursor {_plotlyRPreamble :: !(Maybe FilePath), _plotlyRExe :: !FilePath} -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} -data GraphvizPrecursor = GraphvizPrecursor {_graphvizPreamble :: !(Maybe FilePath), _graphvizExe :: !FilePath} -data BokehPrecursor = BokehPrecursor {_bokehPreamble :: !(Maybe FilePath), _bokehExe :: !FilePath} -data PlotsjlPrecursor = PlotsjlPrecursor {_plotsjlPreamble :: !(Maybe FilePath), _plotsjlExe :: !FilePath} +data PlotlyRPrecursor = PlotlyRPrecursor {_plotlyRPreamble :: !(Maybe FilePath), _plotlyRExe :: !FilePath} +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} + +data GraphvizPrecursor = GraphvizPrecursor {_graphvizPreamble :: !(Maybe FilePath), _graphvizExe :: !FilePath} + +data BokehPrecursor = BokehPrecursor {_bokehPreamble :: !(Maybe FilePath), _bokehExe :: !FilePath} + +data PlotsjlPrecursor = PlotsjlPrecursor {_plotsjlPreamble :: !(Maybe FilePath), _plotsjlExe :: !FilePath} + instance FromJSON LoggingPrecursor where - parseJSON (Object v) = - LoggingPrecursor <$> v .:? "verbosity" .!= (logVerbosity defaultConfiguration) - <*> v .:? "filepath" - parseJSON _ = fail $ mconcat ["Could not parse logging configuration. "] + parseJSON (Object v) = + LoggingPrecursor <$> v .:? "verbosity" .!= (logVerbosity defaultConfiguration) + <*> v .:? "filepath" + parseJSON _ = fail $ mconcat ["Could not parse logging configuration. "] instance FromJSON MatplotlibPrecursor where - parseJSON (Object v) = - MatplotlibPrecursor - <$> v .:? (tshow PreambleK) - <*> v .:? (tshow MatplotlibTightBBoxK) .!= (matplotlibTightBBox defaultConfiguration) - <*> v .:? (tshow MatplotlibTransparentK) .!= (matplotlibTransparent defaultConfiguration) - <*> v .:? (tshow ExecutableK) .!= (matplotlibExe defaultConfiguration) - parseJSON _ = fail $ mconcat ["Could not parse ", show Matplotlib, " configuration."] + parseJSON (Object v) = + MatplotlibPrecursor + <$> v .:? (tshow PreambleK) + <*> v .:? (tshow MatplotlibTightBBoxK) .!= (matplotlibTightBBox defaultConfiguration) + <*> v .:? (tshow MatplotlibTransparentK) .!= (matplotlibTransparent defaultConfiguration) + <*> v .:? (tshow ExecutableK) .!= (matplotlibExe defaultConfiguration) + parseJSON _ = fail $ mconcat ["Could not parse ", show Matplotlib, " configuration."] instance FromJSON MatlabPrecursor where - parseJSON (Object v) = MatlabPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (matlabExe defaultConfiguration) - parseJSON _ = fail $ mconcat ["Could not parse ", show Matlab, " configuration."] + parseJSON (Object v) = MatlabPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (matlabExe defaultConfiguration) + parseJSON _ = fail $ mconcat ["Could not parse ", show Matlab, " configuration."] instance FromJSON PlotlyPythonPrecursor where - parseJSON (Object v) = PlotlyPythonPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (plotlyPythonExe defaultConfiguration) - parseJSON _ = fail $ mconcat ["Could not parse ", show PlotlyPython, " configuration."] + parseJSON (Object v) = PlotlyPythonPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (plotlyPythonExe defaultConfiguration) + parseJSON _ = fail $ mconcat ["Could not parse ", show PlotlyPython, " configuration."] instance FromJSON PlotlyRPrecursor where - parseJSON (Object v) = PlotlyRPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (plotlyRExe defaultConfiguration) - parseJSON _ = fail $ mconcat ["Could not parse ", show PlotlyR, " configuration."] + parseJSON (Object v) = PlotlyRPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (plotlyRExe defaultConfiguration) + parseJSON _ = fail $ mconcat ["Could not parse ", show PlotlyR, " configuration."] instance FromJSON MathematicaPrecursor where - parseJSON (Object v) = MathematicaPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (mathematicaExe defaultConfiguration) - parseJSON _ = fail $ mconcat ["Could not parse ", show Mathematica, " configuration."] + parseJSON (Object v) = MathematicaPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (mathematicaExe defaultConfiguration) + parseJSON _ = fail $ mconcat ["Could not parse ", show Mathematica, " configuration."] instance FromJSON OctavePrecursor where - parseJSON (Object v) = OctavePrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (octaveExe defaultConfiguration) - parseJSON _ = fail $ mconcat ["Could not parse ", show Octave, " configuration."] + parseJSON (Object v) = OctavePrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (octaveExe defaultConfiguration) + parseJSON _ = fail $ mconcat ["Could not parse ", show Octave, " configuration."] instance FromJSON GGPlot2Precursor where - parseJSON (Object v) = GGPlot2Precursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (ggplot2Exe defaultConfiguration) - parseJSON _ = fail $ mconcat ["Could not parse ", show GGPlot2, " configuration."] + parseJSON (Object v) = GGPlot2Precursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (ggplot2Exe defaultConfiguration) + parseJSON _ = fail $ mconcat ["Could not parse ", show GGPlot2, " configuration."] instance FromJSON GNUPlotPrecursor where - parseJSON (Object v) = GNUPlotPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (gnuplotExe defaultConfiguration) - parseJSON _ = fail $ mconcat ["Could not parse ", show GNUPlot, " configuration."] + parseJSON (Object v) = GNUPlotPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (gnuplotExe defaultConfiguration) + parseJSON _ = fail $ mconcat ["Could not parse ", show GNUPlot, " configuration."] instance FromJSON GraphvizPrecursor where - parseJSON (Object v) = GraphvizPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (graphvizExe defaultConfiguration) - parseJSON _ = fail $ mconcat ["Could not parse ", show Graphviz, " configuration."] + parseJSON (Object v) = GraphvizPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (graphvizExe defaultConfiguration) + parseJSON _ = fail $ mconcat ["Could not parse ", show Graphviz, " configuration."] instance FromJSON BokehPrecursor where - parseJSON (Object v) = BokehPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (bokehExe defaultConfiguration) - parseJSON _ = fail $ mconcat ["Could not parse ", show Bokeh, " configuration."] + parseJSON (Object v) = BokehPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (bokehExe defaultConfiguration) + parseJSON _ = fail $ mconcat ["Could not parse ", show Bokeh, " configuration."] instance FromJSON PlotsjlPrecursor where - parseJSON (Object v) = PlotsjlPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (plotsjlExe defaultConfiguration) - parseJSON _ = fail $ mconcat ["Could not parse ", show Plotsjl, " configuration."] - + parseJSON (Object v) = PlotsjlPrecursor <$> v .:? (tshow PreambleK) <*> v .:? (tshow ExecutableK) .!= (plotsjlExe defaultConfiguration) + parseJSON _ = fail $ mconcat ["Could not parse ", show Plotsjl, " configuration."] instance FromJSON ConfigPrecursor where - parseJSON (Null) = return defaultConfigPrecursor -- In case of empty file - parseJSON (Object v) = do - - _defaultDirectory <- v .:? (tshow DirectoryK) .!= (_defaultDirectory defaultConfigPrecursor) - _defaultWithSource <- v .:? (tshow WithSourceK) .!= (_defaultWithSource defaultConfigPrecursor) - _defaultDPI <- v .:? (tshow DpiK) .!= (_defaultDPI defaultConfigPrecursor) - _defaultSaveFormat <- v .:? (tshow SaveFormatK) .!= (_defaultSaveFormat defaultConfigPrecursor) - _defaultDependencies <- v .:? (tshow DependenciesK) .!= (_defaultDependencies defaultConfigPrecursor) - _captionFormat <- v .:? (tshow CaptionFormatK) .!= (_captionFormat defaultConfigPrecursor) - - _logPrec <- v .:? "logging" .!= _logPrec defaultConfigPrecursor + parseJSON (Null) = return defaultConfigPrecursor -- In case of empty file + parseJSON (Object v) = do + _defaultDirectory <- v .:? (tshow DirectoryK) .!= (_defaultDirectory defaultConfigPrecursor) + _defaultWithSource <- v .:? (tshow WithSourceK) .!= (_defaultWithSource defaultConfigPrecursor) + _defaultDPI <- v .:? (tshow DpiK) .!= (_defaultDPI defaultConfigPrecursor) + _defaultSaveFormat <- v .:? (tshow SaveFormatK) .!= (_defaultSaveFormat defaultConfigPrecursor) + _defaultDependencies <- v .:? (tshow DependenciesK) .!= (_defaultDependencies defaultConfigPrecursor) + _captionFormat <- v .:? (tshow CaptionFormatK) .!= (_captionFormat defaultConfigPrecursor) + _sourceCodeLabel <- v .:? (tshow SourceCodeLabelK) .!= (_sourceCodeLabel defaultConfigPrecursor) - _matplotlibPrec <- v .:? (cls Matplotlib) .!= _matplotlibPrec defaultConfigPrecursor - _matlabPrec <- v .:? (cls Matlab) .!= _matlabPrec defaultConfigPrecursor - _plotlyPythonPrec <- v .:? (cls PlotlyPython) .!= _plotlyPythonPrec defaultConfigPrecursor - _plotlyRPrec <- v .:? (cls PlotlyR) .!= _plotlyRPrec defaultConfigPrecursor - _mathematicaPrec <- v .:? (cls Mathematica) .!= _mathematicaPrec defaultConfigPrecursor - _octavePrec <- v .:? (cls Octave) .!= _octavePrec defaultConfigPrecursor - _ggplot2Prec <- v .:? (cls GGPlot2) .!= _ggplot2Prec defaultConfigPrecursor - _gnuplotPrec <- v .:? (cls GNUPlot) .!= _gnuplotPrec defaultConfigPrecursor - _graphvizPrec <- v .:? (cls Graphviz) .!= _graphvizPrec defaultConfigPrecursor - _bokehPrec <- v .:? (cls Bokeh) .!= _bokehPrec defaultConfigPrecursor - _plotsjlPrec <- v .:? (cls Plotsjl) .!= _plotsjlPrec defaultConfigPrecursor + _logPrec <- v .:? "logging" .!= _logPrec defaultConfigPrecursor - return $ ConfigPrecursor{..} - parseJSON _ = fail "Could not parse configuration." + _matplotlibPrec <- v .:? (cls Matplotlib) .!= _matplotlibPrec defaultConfigPrecursor + _matlabPrec <- v .:? (cls Matlab) .!= _matlabPrec defaultConfigPrecursor + _plotlyPythonPrec <- v .:? (cls PlotlyPython) .!= _plotlyPythonPrec defaultConfigPrecursor + _plotlyRPrec <- v .:? (cls PlotlyR) .!= _plotlyRPrec defaultConfigPrecursor + _mathematicaPrec <- v .:? (cls Mathematica) .!= _mathematicaPrec defaultConfigPrecursor + _octavePrec <- v .:? (cls Octave) .!= _octavePrec defaultConfigPrecursor + _ggplot2Prec <- v .:? (cls GGPlot2) .!= _ggplot2Prec defaultConfigPrecursor + _gnuplotPrec <- v .:? (cls GNUPlot) .!= _gnuplotPrec defaultConfigPrecursor + _graphvizPrec <- v .:? (cls Graphviz) .!= _graphvizPrec defaultConfigPrecursor + _bokehPrec <- v .:? (cls Bokeh) .!= _bokehPrec defaultConfigPrecursor + _plotsjlPrec <- v .:? (cls Plotsjl) .!= _plotsjlPrec defaultConfigPrecursor + return $ ConfigPrecursor {..} + parseJSON _ = fail "Could not parse configuration." renderConfig :: ConfigPrecursor -> IO Configuration -renderConfig ConfigPrecursor{..} = do - let defaultDirectory = _defaultDirectory - defaultWithSource = _defaultWithSource - defaultDPI = _defaultDPI - defaultSaveFormat = _defaultSaveFormat - defaultDependencies = _defaultDependencies - captionFormat = _captionFormat +renderConfig ConfigPrecursor {..} = do + let defaultDirectory = _defaultDirectory + defaultWithSource = _defaultWithSource + defaultDPI = _defaultDPI + defaultSaveFormat = _defaultSaveFormat + defaultDependencies = _defaultDependencies + captionFormat = _captionFormat + sourceCodeLabel = _sourceCodeLabel - logVerbosity = _logVerbosity _logPrec - logSink = maybe StdErr LogFile (_logFilePath _logPrec) + logVerbosity = _logVerbosity _logPrec + logSink = maybe StdErr LogFile (_logFilePath _logPrec) - matplotlibTightBBox = _matplotlibTightBBox _matplotlibPrec - matplotlibTransparent = _matplotlibTransparent _matplotlibPrec + matplotlibTightBBox = _matplotlibTightBBox _matplotlibPrec + matplotlibTransparent = _matplotlibTransparent _matplotlibPrec - matplotlibExe = _matplotlibExe _matplotlibPrec - matlabExe = _matlabExe _matlabPrec - plotlyPythonExe = _plotlyPythonExe _plotlyPythonPrec - plotlyRExe = _plotlyRExe _plotlyRPrec - mathematicaExe = _mathematicaExe _mathematicaPrec - octaveExe = _octaveExe _octavePrec - ggplot2Exe = _ggplot2Exe _ggplot2Prec - gnuplotExe = _gnuplotExe _gnuplotPrec - graphvizExe = _graphvizExe _graphvizPrec - bokehExe = _bokehExe _bokehPrec - plotsjlExe = _plotsjlExe _plotsjlPrec - - matplotlibPreamble <- readPreamble (_matplotlibPreamble _matplotlibPrec) - matlabPreamble <- readPreamble (_matlabPreamble _matlabPrec) - plotlyPythonPreamble <- readPreamble (_plotlyPythonPreamble _plotlyPythonPrec) - plotlyRPreamble <- readPreamble (_plotlyRPreamble _plotlyRPrec) - mathematicaPreamble <- readPreamble (_mathematicaPreamble _mathematicaPrec) - octavePreamble <- readPreamble (_octavePreamble _octavePrec) - ggplot2Preamble <- readPreamble (_ggplot2Preamble _ggplot2Prec) - gnuplotPreamble <- readPreamble (_gnuplotPreamble _gnuplotPrec) - graphvizPreamble <- readPreamble (_graphvizPreamble _graphvizPrec) - bokehPreamble <- readPreamble (_bokehPreamble _bokehPrec) - plotsjlPreamble <- readPreamble (_plotsjlPreamble _plotsjlPrec) + matplotlibExe = _matplotlibExe _matplotlibPrec + matlabExe = _matlabExe _matlabPrec + plotlyPythonExe = _plotlyPythonExe _plotlyPythonPrec + plotlyRExe = _plotlyRExe _plotlyRPrec + mathematicaExe = _mathematicaExe _mathematicaPrec + octaveExe = _octaveExe _octavePrec + ggplot2Exe = _ggplot2Exe _ggplot2Prec + gnuplotExe = _gnuplotExe _gnuplotPrec + graphvizExe = _graphvizExe _graphvizPrec + bokehExe = _bokehExe _bokehPrec + plotsjlExe = _plotsjlExe _plotsjlPrec - return Configuration{..} - where - readPreamble fp = fromMaybe mempty $ TIO.readFile <$> fp + matplotlibPreamble <- readPreamble (_matplotlibPreamble _matplotlibPrec) + matlabPreamble <- readPreamble (_matlabPreamble _matlabPrec) + plotlyPythonPreamble <- readPreamble (_plotlyPythonPreamble _plotlyPythonPrec) + plotlyRPreamble <- readPreamble (_plotlyRPreamble _plotlyRPrec) + mathematicaPreamble <- readPreamble (_mathematicaPreamble _mathematicaPrec) + octavePreamble <- readPreamble (_octavePreamble _octavePrec) + ggplot2Preamble <- readPreamble (_ggplot2Preamble _ggplot2Prec) + gnuplotPreamble <- readPreamble (_gnuplotPreamble _gnuplotPrec) + graphvizPreamble <- readPreamble (_graphvizPreamble _graphvizPrec) + bokehPreamble <- readPreamble (_bokehPreamble _bokehPrec) + plotsjlPreamble <- readPreamble (_plotsjlPreamble _plotsjlPrec) + return Configuration {..} + where + readPreamble fp = fromMaybe mempty $ TIO.readFile <$> fp tshow :: Show a => a -> Text -tshow = pack . show+tshow = pack . show
src/Text/Pandoc/Filter/Plot/Embed.hs view
@@ -1,88 +1,97 @@ {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE QuasiQuotes #-} -{-| -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 - -Embedding HTML content --} - -module Text.Pandoc.Filter.Plot.Embed ( - extractPlot - , toFigure -) where - -import Data.Default (def) -import Data.List (nub) -import Data.Maybe (fromMaybe) -import Data.Text (Text, pack) -import qualified Data.Text.IO as T - -import System.FilePath (replaceExtension) - -import Text.HTML.TagSoup - -import Text.Pandoc.Builder (fromList, imageWith, link, - para, toList, Inlines) -import Text.Pandoc.Class (runPure) -import Text.Pandoc.Definition (Pandoc(..), Block (..), Format, Attr) -import Text.Pandoc.Error (handleError) -import Text.Pandoc.Writers.HTML (writeHtml5String) - -import Text.Pandoc.Filter.Plot.Parse (captionReader) -import Text.Pandoc.Filter.Plot.Monad -import Text.Pandoc.Filter.Plot.Scripting (figurePath) +{-# LANGUAGE QuasiQuotes #-} -import Text.Shakespeare.Text (st) +-- | +-- 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 +-- +-- Embedding HTML content +module Text.Pandoc.Filter.Plot.Embed + ( extractPlot, + toFigure, + ) +where +import Data.Default (def) +import Data.List (nub) +import Data.Maybe (fromMaybe) +import Data.Text (Text, pack) +import qualified Data.Text.IO as T +import Text.HTML.TagSoup +import Text.Pandoc.Builder + ( Inlines, + fromList, + imageWith, + link, + para, + str, + toList, + ) +import Text.Pandoc.Class (runPure) +import Text.Pandoc.Definition (Attr, Block (..), Format, Pandoc (..)) +import Text.Pandoc.Error (handleError) +import Text.Pandoc.Filter.Plot.Monad +import Text.Pandoc.Filter.Plot.Parse (captionReader) +import Text.Pandoc.Filter.Plot.Scripting (figurePath, sourceCodePath) +import Text.Pandoc.Writers.HTML (writeHtml5String) +import Text.Shakespeare.Text (st) -- | Convert a @FigureSpec@ to a Pandoc figure component. -- Note that the script to generate figure files must still -- be run in another function. -toFigure :: Format -- ^ text format of the caption - -> FigureSpec - -> PlotM Block +toFigure :: + -- | text format of the caption + Format -> + FigureSpec -> + PlotM Block toFigure fmt spec = do - target <- figurePath spec - let srcLink = link (pack $ replaceExtension target ".txt") mempty "Source code" - attrs' = blockAttrs spec - withSource' = withSource spec - captionText = fromList $ fromMaybe mempty (captionReader fmt $ caption spec) - captionLinks = mconcat [" (", srcLink, ")"] - caption' = if withSource' then captionText <> captionLinks else captionText - builder attrs' target caption' - where - builder = if saveFormat spec == HTML - then interactiveBlock - else figure - + target <- figurePath spec + scp <- pack <$> sourceCodePath spec + sourceLabel <- asksConfig sourceCodeLabel -- Allow the possibility for non-english labels + let srcLink = link scp mempty (str sourceLabel) + attrs' = blockAttrs spec + withSource' = withSource spec + captionText = fromList $ fromMaybe mempty (captionReader fmt $ caption spec) + captionLinks = mconcat [" (", srcLink, ")"] + caption' = if withSource' then captionText <> captionLinks else captionText + builder attrs' target caption' + where + builder = + if saveFormat spec == HTML + then interactiveBlock + else figure -figure :: Attr - -> FilePath - -> Inlines - -> PlotM Block +figure :: + Attr -> + FilePath -> + Inlines -> + PlotM Block -- To render images as figures with captions, the target title -- must be "fig:" -- Janky? yes -figure as fp caption' = return . head . toList . para $ - imageWith as (pack fp) "fig:" caption' - +figure as fp caption' = + return . head . toList . para $ + imageWith as (pack fp) "fig:" caption' -interactiveBlock :: Attr - -> FilePath - -> Inlines - -> PlotM Block +interactiveBlock :: + Attr -> + FilePath -> + Inlines -> + PlotM Block interactiveBlock _ fp caption' = do - -- TODO: should we instead include the scripts in the "include-after" - -- template variable? - -- See https://github.com/jgm/pandoc/issues/6582 - htmlpage <- liftIO $ T.readFile fp - renderedCaption <- writeHtml caption' - return $ RawBlock "html5" [st| + -- TODO: should we instead include the scripts in the "include-after" + -- template variable? + -- See https://github.com/jgm/pandoc/issues/6582 + htmlpage <- liftIO $ T.readFile fp + renderedCaption <- writeHtml caption' + return $ + RawBlock + "html5" + [st| <figure> <div> #{extractPlot htmlpage} @@ -91,57 +100,52 @@ </figure> |] - -- | Convert Pandoc inlines to html writeHtml :: Inlines -> PlotM Text writeHtml is = liftIO $ handleError $ runPure $ writeHtml5String def document - where - document = Pandoc mempty [Para . toList $ is] - + where + document = Pandoc mempty [Para . toList $ is] -- | Extract the plot-relevant content from inside of a full HTML document. -- Scripts contained in the <head> tag are extracted, as well as the entirety of the -- <body> tag. extractPlot :: Text -> Text -extractPlot t = let tags = canonicalizeTags $ parseTagsOptions parseOptionsFast t - extracted = headScripts tags <> [inside "body" $ tags] - in mconcat $ renderTags <$> (deferScripts <$> extracted) - where - headScripts = partitions (~== ("<script>"::String)) . inside "head" - +extractPlot t = + let tags = canonicalizeTags $ parseTagsOptions parseOptionsFast t + extracted = headScripts tags <> [inside "body" $ tags] + in mconcat $ renderTags <$> (deferScripts <$> extracted) + where + headScripts = partitions (~== ("<script>" :: String)) . inside "head" -- | Get content inside a tag, e.g. /inside "body"/ returns all tags -- between /<body>/ and /</body>/ inside :: Text -> [Tag Text] -> [Tag Text] inside t = init . tail . tgs - where - tgs = takeWhile (~/= TagClose t) . dropWhile (~/= TagOpen t []) - - -data ScriptTag = InlineScript [Attribute Text] - | ExternalScript [Attribute Text] + where + tgs = takeWhile (~/= TagClose t) . dropWhile (~/= TagOpen t []) +data ScriptTag + = InlineScript [Attribute Text] + | ExternalScript [Attribute Text] fromTag :: Tag Text -> Maybe ScriptTag -fromTag (TagOpen "script" attrs) = - Just $ if "src" `elem` (fst . unzip $ attrs) - then ExternalScript attrs - else InlineScript attrs +fromTag (TagOpen "script" attrs) = + Just $ + if "src" `elem` (fst . unzip $ attrs) + then ExternalScript attrs + else InlineScript attrs fromTag _ = Nothing - toTag :: ScriptTag -> Tag Text -toTag (InlineScript t) = TagOpen "script" t +toTag (InlineScript t) = TagOpen "script" t toTag (ExternalScript t) = TagOpen "script" t - deferScript :: ScriptTag -> ScriptTag -deferScript (InlineScript attrs) = InlineScript $ nub $ attrs <> [("type", "module")] +deferScript (InlineScript attrs) = InlineScript $ nub $ attrs <> [("type", "module")] deferScript (ExternalScript attrs) = ExternalScript $ nub $ attrs <> [("defer", mempty)] - -- | Replace /<script src=...>/ tags with /<script src=... defer>/, -- and inline scripts as /<script type="module">/. -- This makes scripts execute only after HTML parsing has finished. deferScripts :: [Tag Text] -> [Tag Text] -deferScripts = fmap (\t -> maybe t (toTag . deferScript) (fromTag t))+deferScripts = fmap (\t -> maybe t (toTag . deferScript) (fromTag t))
src/Text/Pandoc/Filter/Plot/Internal.hs view
@@ -1,29 +1,28 @@-{-| -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 - -This module re-exports internal pandoc-plot functionality. -The external use of content from this module is discouraged. --} - -module Text.Pandoc.Filter.Plot.Internal ( - module Text.Pandoc.Filter.Plot.Renderers - , module Text.Pandoc.Filter.Plot.Scripting - , module Text.Pandoc.Filter.Plot.Parse - , module Text.Pandoc.Filter.Plot.Configuration - , module Text.Pandoc.Filter.Plot.Clean - , module Text.Pandoc.Filter.Plot.Monad - , module Text.Pandoc.Filter.Plot.Embed - ) where +-- | +-- 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 +-- +-- This module re-exports internal pandoc-plot functionality. +-- The external use of content from this module is discouraged. +module Text.Pandoc.Filter.Plot.Internal + ( module Text.Pandoc.Filter.Plot.Renderers, + module Text.Pandoc.Filter.Plot.Scripting, + module Text.Pandoc.Filter.Plot.Parse, + module Text.Pandoc.Filter.Plot.Configuration, + module Text.Pandoc.Filter.Plot.Clean, + module Text.Pandoc.Filter.Plot.Monad, + module Text.Pandoc.Filter.Plot.Embed, + ) +where +import Text.Pandoc.Filter.Plot.Clean +import Text.Pandoc.Filter.Plot.Configuration +import Text.Pandoc.Filter.Plot.Embed +import Text.Pandoc.Filter.Plot.Monad import Text.Pandoc.Filter.Plot.Parse import Text.Pandoc.Filter.Plot.Renderers import Text.Pandoc.Filter.Plot.Scripting -import Text.Pandoc.Filter.Plot.Configuration -import Text.Pandoc.Filter.Plot.Clean -import Text.Pandoc.Filter.Plot.Monad -import Text.Pandoc.Filter.Plot.Embed
src/Text/Pandoc/Filter/Plot/Monad.hs view
@@ -1,237 +1,247 @@ {-# LANGUAGE OverloadedStrings #-} -{-| -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 -This module defines the @PlotM@ monad and related capabilities. --} +-- | +-- 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 +-- +-- This module defines the @PlotM@ monad and related capabilities. +module Text.Pandoc.Filter.Plot.Monad + ( Configuration (..), + PlotM, + RuntimeEnv (..), + runPlotM, -module Text.Pandoc.Filter.Plot.Monad ( - Configuration(..) - , PlotM - , RuntimeEnv(..) - , runPlotM -- * Running external commands - , runCommand - -- * Getting file hashes - , fileHash - -- * Getting executables - , executable - -- * Logging - , Verbosity(..) - , LogSink(..) - , debug - , err - , warning - , info - -- * Lifting and other monadic operations - , liftIO - , ask - , asks - , asksConfig - , silence - -- * Base types - , module Text.Pandoc.Filter.Plot.Monad.Types -) where - - -import Control.Concurrent.Chan (writeChan) -import Control.Concurrent.MVar + runCommand, -import Control.Monad.Reader -import Control.Monad.State.Strict + -- * Getting file hashes + fileHash, -import Data.ByteString.Lazy (toStrict) -import Data.Hashable (hash) -import Data.Map.Strict (Map) -import qualified Data.Map.Strict as M + -- * Getting executables + executable, -import Data.Text (Text, pack, unpack) -import qualified Data.Text as T -import Data.Text.Encoding (decodeUtf8With) -import Data.Text.Encoding.Error (lenientDecode) + -- * Logging + Verbosity (..), + LogSink (..), + debug, + err, + warning, + info, -import System.Directory ( doesFileExist, getModificationTime - , findExecutable, getCurrentDirectory - ) -import System.Exit (ExitCode (..)) -import System.Process.Typed ( readProcessStderr, shell, nullStream - , setStdout, setStderr, byteStringOutput - , setWorkingDir - ) -import Text.Pandoc.Definition (Format(..)) + -- * Lifting and other monadic operations + liftIO, + ask, + asks, + asksConfig, + silence, -import Prelude hiding (log, fst, snd) + -- * Base types + module Text.Pandoc.Filter.Plot.Monad.Types, + ) +where +import Control.Concurrent.Chan (writeChan) +import Control.Concurrent.MVar +import Control.Monad.Reader +import Control.Monad.State.Strict +import Data.ByteString.Lazy (toStrict) +import Data.Hashable (hash) +import Data.Map.Strict (Map) +import qualified Data.Map.Strict as M +import Data.Text (Text, pack, unpack) +import qualified Data.Text as T +import Data.Text.Encoding (decodeUtf8With) +import Data.Text.Encoding.Error (lenientDecode) +import System.Directory + ( doesFileExist, + findExecutable, + getCurrentDirectory, + getModificationTime, + ) +import System.Exit (ExitCode (..)) +import System.Process.Typed + ( byteStringOutput, + nullStream, + readProcessStderr, + setStderr, + setStdout, + setWorkingDir, + shell, + ) +import Text.Pandoc.Definition (Format (..)) import Text.Pandoc.Filter.Plot.Monad.Logging as Log import Text.Pandoc.Filter.Plot.Monad.Types - +import Prelude hiding (fst, log, snd) -- | pandoc-plot monad type PlotM a = StateT PlotState (ReaderT RuntimeEnv IO) a - -data RuntimeEnv = - RuntimeEnv { envConfig :: Configuration - , envLogger :: Logger - , envCWD :: FilePath - } - +data RuntimeEnv = RuntimeEnv + { envConfig :: Configuration, + envLogger :: Logger, + envCWD :: FilePath + } -- | Modify the runtime environment to be silent. silence :: PlotM a -> PlotM a -silence = local (\(RuntimeEnv c l d) -> RuntimeEnv c l{lVerbosity = Silent} d) - +silence = local (\(RuntimeEnv c l d) -> RuntimeEnv c l {lVerbosity = Silent} d) -- | Get access to configuration within the @PlotM@ monad. asksConfig :: (Configuration -> a) -> PlotM a asksConfig f = asks (f . envConfig) - -- | Evaluate a @PlotM@ action. runPlotM :: Configuration -> PlotM a -> IO a runPlotM conf v = do - cwd <- getCurrentDirectory - st <- PlotState <$> newMVar mempty - <*> newMVar mempty - let verbosity = logVerbosity conf - sink = logSink conf - withLogger verbosity sink $ - \logger -> runReaderT (evalStateT v st) (RuntimeEnv conf logger cwd) - + cwd <- getCurrentDirectory + st <- + PlotState <$> newMVar mempty + <*> newMVar mempty + let verbosity = logVerbosity conf + sink = logSink conf + withLogger verbosity sink $ + \logger -> runReaderT (evalStateT v st) (RuntimeEnv conf logger cwd) debug, err, warning, info :: Text -> PlotM () -debug = log "DEBUG | " Debug -err = log "ERROR | " Error -warning = log "WARN | " Warning -info = log "INFO | " Info - +debug = log "DEBUG | " Debug +err = log "ERROR | " Error +warning = log "WARN | " Warning +info = log "INFO | " Info --- | General purpose logging. -log :: Text -- ^ Header. - -> Verbosity -- ^ Verbosity of the message. - -> Text -- ^ Message (can be multiple lines). - -> PlotM () +-- | General purpose logging. +log :: + -- | Header. + Text -> + -- | Verbosity of the message. + Verbosity -> + -- | Message (can be multiple lines). + Text -> + PlotM () log h v t = do - logger <- asks envLogger - when (v >= lVerbosity logger) $ - liftIO $ do - forM_ ( T.lines t) $ \l -> writeChan (lChannel logger) (Just (h <> l <> "\n")) - + logger <- asks envLogger + when (v >= lVerbosity logger) $ + liftIO $ do + forM_ (T.lines t) $ \l -> writeChan (lChannel logger) (Just (h <> l <> "\n")) -- | Run a command within the @PlotM@ monad. Stderr stream --- is read and decoded, while Stdout is ignored. +-- is read and decoded, while Stdout is ignored. -- Logging happens at the debug level if the command succeeds, or at -- the error level if it does not succeed. -runCommand :: FilePath -- Directory from which to run the command - -> Text -- Command to run, including executable - -> PlotM (ExitCode, Text) +runCommand :: + FilePath -> -- Directory from which to run the command + Text -> -- Command to run, including executable + PlotM (ExitCode, Text) runCommand wordir command = do - (ec, processOutput') <- liftIO - $ readProcessStderr - $ setStdout nullStream - $ setStderr byteStringOutput - $ setWorkingDir wordir - $ shell (unpack command) - let processOutput = decodeUtf8With lenientDecode $ toStrict processOutput' - logFunc = if ec == ExitSuccess - then debug - else err - message = T.unlines [ "Running command" - , " " <> command - , "ended with exit code " <> (pack . show $ ec) - ] - errorMessage = if processOutput == mempty - then mempty - else T.unlines [ "*******" - , processOutput - , "*******" - ] - - logFunc $ message <> errorMessage - return (ec, processOutput) + (ec, processOutput') <- + liftIO $ + readProcessStderr $ + setStdout nullStream $ + setStderr byteStringOutput $ + setWorkingDir wordir $ + shell (unpack command) + let processOutput = decodeUtf8With lenientDecode $ toStrict processOutput' + logFunc = + if ec == ExitSuccess + then debug + else err + message = + T.unlines + [ "Running command", + " " <> command, + "ended with exit code " <> (pack . show $ ec) + ] + errorMessage = + if processOutput == mempty + then mempty + else + T.unlines + [ "*******", + processOutput, + "*******" + ] + logFunc $ message <> errorMessage + return (ec, processOutput) -- Plot state is used for caching. -- One part consists of a map of filepaths to hashes -- This allows multiple plots to depend on the same file/directory, and the file hashes -- will only be calculated once. This is OK because pandoc-plot will not run for long. --- We note that because figures are rendered possibly in parallel, access to +-- We note that because figures are rendered possibly in parallel, access to -- the state must be synchronized; otherwise, each thread might compute its own -- hashes. -- The other part is comprised of a map of toolkits to executables (possibly missing) -- This means that executable will be search for only once. -type FileHash = Word -data PlotState = - PlotState (MVar (Map FilePath FileHash)) - (MVar (Map Toolkit (Maybe Executable))) +type FileHash = Word +data PlotState + = PlotState + (MVar (Map FilePath FileHash)) + (MVar (Map Toolkit (Maybe Executable))) -- | Get a filehash. If the file hash has been computed before, -- it is reused. Otherwise, the filehash is calculated and stored. fileHash :: FilePath -> PlotM FileHash fileHash path = do - PlotState varHashes varExes <- get - hashes <- liftIO $ takeMVar varHashes - (fh, hashes') <- case M.lookup path hashes of - Nothing -> do - debug $ mconcat ["Calculating hash of dependency ", pack path] - fh <- fileHash' path - let hashes' = M.insert path fh hashes - return (fh, hashes') - Just h -> do - debug $ mconcat ["Hash of dependency ", pack path, " already calculated."] - return (h, hashes) - liftIO $ putMVar varHashes hashes' - put $ PlotState varHashes varExes - return fh - where + PlotState varHashes varExes <- get + hashes <- liftIO $ takeMVar varHashes + (fh, hashes') <- case M.lookup path hashes of + Nothing -> do + debug $ mconcat ["Calculating hash of dependency ", pack path] + fh <- fileHash' path + let hashes' = M.insert path fh hashes + return (fh, hashes') + Just h -> do + debug $ mconcat ["Hash of dependency ", pack path, " already calculated."] + return (h, hashes) + liftIO $ putMVar varHashes hashes' + put $ PlotState varHashes varExes + return fh + where -- As a proxy for the state of a file dependency, we use the modification time -- This is much faster than actual file hashing fileHash' :: FilePath -> PlotM FileHash fileHash' fp = do - fileExists <- liftIO $ doesFileExist fp - if fileExists - then liftIO . fmap (fromIntegral . hash . show) . getModificationTime $ fp - else err (mconcat ["Dependency ", pack fp, " does not exist."]) >> return 0 - + fileExists <- liftIO $ doesFileExist fp + if fileExists + then liftIO . fmap (fromIntegral . hash . show) . getModificationTime $ fp + else err (mconcat ["Dependency ", pack fp, " does not exist."]) >> return 0 --- | Get an executable. If the executable has not been used before, +-- | Get an executable. If the executable has not been used before, -- find it and store where it is. It will be re-used. executable :: Toolkit -> PlotM (Maybe Executable) executable tk = do - name <- exeSelector tk - PlotState varHashes varExes <- get - exes <- liftIO $ takeMVar varExes - (exe', exes') <- case M.lookup tk exes of - Nothing -> do - debug $ mconcat ["Looking for executable \"", pack name, "\" for ", pack $ show tk] - exe' <- liftIO $ findExecutable name >>= return . fmap exeFromPath - let exes' = M.insert tk exe' exes - return (exe', exes') - Just e -> do - debug $ mconcat ["Executable \"", pack name, "\" already found."] - return (e, exes) - liftIO $ putMVar varExes exes' - put $ PlotState varHashes varExes - return exe' - where - exeSelector Matplotlib = asksConfig matplotlibExe - exeSelector PlotlyPython = asksConfig plotlyPythonExe - exeSelector PlotlyR = asksConfig plotlyRExe - exeSelector Matlab = asksConfig matlabExe - exeSelector Mathematica = asksConfig mathematicaExe - exeSelector Octave = asksConfig octaveExe - exeSelector GGPlot2 = asksConfig ggplot2Exe - exeSelector GNUPlot = asksConfig gnuplotExe - exeSelector Graphviz = asksConfig graphvizExe - exeSelector Bokeh = asksConfig bokehExe - exeSelector Plotsjl = asksConfig plotsjlExe - + name <- exeSelector tk + PlotState varHashes varExes <- get + exes <- liftIO $ takeMVar varExes + (exe', exes') <- case M.lookup tk exes of + Nothing -> do + debug $ mconcat ["Looking for executable \"", pack name, "\" for ", pack $ show tk] + exe' <- liftIO $ findExecutable name >>= return . fmap exeFromPath + let exes' = M.insert tk exe' exes + return (exe', exes') + Just e -> do + debug $ mconcat ["Executable \"", pack name, "\" already found."] + return (e, exes) + liftIO $ putMVar varExes exes' + put $ PlotState varHashes varExes + return exe' + where + exeSelector Matplotlib = asksConfig matplotlibExe + exeSelector PlotlyPython = asksConfig plotlyPythonExe + exeSelector PlotlyR = asksConfig plotlyRExe + exeSelector Matlab = asksConfig matlabExe + exeSelector Mathematica = asksConfig mathematicaExe + exeSelector Octave = asksConfig octaveExe + exeSelector GGPlot2 = asksConfig ggplot2Exe + exeSelector GNUPlot = asksConfig gnuplotExe + exeSelector Graphviz = asksConfig graphvizExe + exeSelector Bokeh = asksConfig bokehExe + exeSelector Plotsjl = asksConfig plotsjlExe -- | The @Configuration@ type holds the default values to use -- when running pandoc-plot. These values can be overridden in code blocks. @@ -245,7 +255,7 @@ -- title: My document -- author: John Doe -- plot-configuration: path\to\file.yml --- --- +-- --- -- @ -- -- The same can be specified via the command line using Pandoc's @-M@ flag: @@ -254,40 +264,71 @@ -- -- In this case, use @configurationPathMeta@ to extact the path from @Pandoc@ documents. data Configuration = Configuration - { defaultDirectory :: !FilePath -- ^ The default directory where figures will be saved. - , defaultWithSource :: !Bool -- ^ The default behavior of whether or not to include links to source code and high-res - , defaultDPI :: !Int -- ^ The default dots-per-inch value for generated figures. Renderers might ignore this. - , defaultSaveFormat :: !SaveFormat -- ^ The default save format of generated figures. - , defaultDependencies :: ![FilePath] -- ^ List of files/directories on which all figures depend. - , captionFormat :: !Format -- ^ Caption format, in the same notation as Pandoc format, e.g. "markdown+tex_math_dollars" - - , logVerbosity :: !Verbosity -- ^ Level of logging verbosity. - , logSink :: !LogSink -- ^ Method of logging, i.e. printing to stderr or file. - - , matplotlibPreamble :: !Script -- ^ The default preamble script for the matplotlib toolkit. - , plotlyPythonPreamble :: !Script -- ^ The default preamble script for the Plotly/Python toolkit. - , plotlyRPreamble :: !Script -- ^ The default preamble script for the Plotly/R toolkit. - , matlabPreamble :: !Script -- ^ The default preamble script for the MATLAB toolkit. - , mathematicaPreamble :: !Script -- ^ The default preamble script for the Mathematica toolkit. - , octavePreamble :: !Script -- ^ The default preamble script for the GNU Octave toolkit. - , ggplot2Preamble :: !Script -- ^ The default preamble script for the GGPlot2 toolkit. - , gnuplotPreamble :: !Script -- ^ The default preamble script for the gnuplot toolkit. - , graphvizPreamble :: !Script -- ^ The default preamble script for the Graphviz toolkit. - , bokehPreamble :: !Script -- ^ The default preamble script for the Python/Bokeh toolkit. - , plotsjlPreamble :: !Script -- ^ The default preamble script for the Julia/Plots.jl toolkit. - - , matplotlibExe :: !FilePath -- ^ The executable to use to generate figures using the matplotlib toolkit. - , matlabExe :: !FilePath -- ^ The executable to use to generate figures using the MATLAB toolkit. - , plotlyPythonExe :: !FilePath -- ^ The executable to use to generate figures using the Plotly/Python toolkit. - , plotlyRExe :: !FilePath -- ^ The executable to use to generate figures using the Plotly/R toolkit. - , mathematicaExe :: !FilePath -- ^ The executable to use to generate figures using the Mathematica toolkit. - , octaveExe :: !FilePath -- ^ The executable to use to generate figures using the GNU Octave toolkit. - , ggplot2Exe :: !FilePath -- ^ The executable to use to generate figures using the GGPlot2 toolkit. - , gnuplotExe :: !FilePath -- ^ The executable to use to generate figures using the gnuplot toolkit. - , graphvizExe :: !FilePath -- ^ The executable to use to generate figures using the Graphviz toolkit. - , bokehExe :: !FilePath -- ^ The executable to use to generate figures using the Python/Bokeh toolkit. - , plotsjlExe :: !FilePath -- ^ The executable to use to generate figures using the Julia/Plots.jl toolkit. - - , matplotlibTightBBox :: !Bool -- ^ Whether or not to make Matplotlib figures tight by default. - , matplotlibTransparent :: !Bool -- ^ Whether or not to make Matplotlib figures transparent by default. - } deriving (Eq, Show)+ { -- | The default directory where figures will be saved. + defaultDirectory :: !FilePath, + -- | The default behavior of whether or not to include links to source code and high-res + defaultWithSource :: !Bool, + -- | The default dots-per-inch value for generated figures. Renderers might ignore this. + defaultDPI :: !Int, + -- | The default save format of generated figures. + defaultSaveFormat :: !SaveFormat, + -- | List of files/directories on which all figures depend. + defaultDependencies :: ![FilePath], + -- | Caption format, in the same notation as Pandoc format, e.g. "markdown+tex_math_dollars" + captionFormat :: !Format, + -- | The text label to which the source code is linked. Change this if you are writing non-english documents. + sourceCodeLabel :: !Text, + -- | Level of logging verbosity. + logVerbosity :: !Verbosity, + -- | Method of logging, i.e. printing to stderr or file. + logSink :: !LogSink, + -- | The default preamble script for the matplotlib toolkit. + matplotlibPreamble :: !Script, + -- | The default preamble script for the Plotly/Python toolkit. + plotlyPythonPreamble :: !Script, + -- | The default preamble script for the Plotly/R toolkit. + plotlyRPreamble :: !Script, + -- | The default preamble script for the MATLAB toolkit. + matlabPreamble :: !Script, + -- | The default preamble script for the Mathematica toolkit. + mathematicaPreamble :: !Script, + -- | The default preamble script for the GNU Octave toolkit. + octavePreamble :: !Script, + -- | The default preamble script for the GGPlot2 toolkit. + ggplot2Preamble :: !Script, + -- | The default preamble script for the gnuplot toolkit. + gnuplotPreamble :: !Script, + -- | The default preamble script for the Graphviz toolkit. + graphvizPreamble :: !Script, + -- | The default preamble script for the Python/Bokeh toolkit. + bokehPreamble :: !Script, + -- | The default preamble script for the Julia/Plots.jl toolkit. + plotsjlPreamble :: !Script, + -- | The executable to use to generate figures using the matplotlib toolkit. + matplotlibExe :: !FilePath, + -- | The executable to use to generate figures using the MATLAB toolkit. + matlabExe :: !FilePath, + -- | The executable to use to generate figures using the Plotly/Python toolkit. + plotlyPythonExe :: !FilePath, + -- | The executable to use to generate figures using the Plotly/R toolkit. + plotlyRExe :: !FilePath, + -- | The executable to use to generate figures using the Mathematica toolkit. + mathematicaExe :: !FilePath, + -- | The executable to use to generate figures using the GNU Octave toolkit. + octaveExe :: !FilePath, + -- | The executable to use to generate figures using the GGPlot2 toolkit. + ggplot2Exe :: !FilePath, + -- | The executable to use to generate figures using the gnuplot toolkit. + gnuplotExe :: !FilePath, + -- | The executable to use to generate figures using the Graphviz toolkit. + graphvizExe :: !FilePath, + -- | The executable to use to generate figures using the Python/Bokeh toolkit. + bokehExe :: !FilePath, + -- | The executable to use to generate figures using the Julia/Plots.jl toolkit. + plotsjlExe :: !FilePath, + -- | Whether or not to make Matplotlib figures tight by default. + matplotlibTightBBox :: !Bool, + -- | Whether or not to make Matplotlib figures transparent by default. + matplotlibTransparent :: !Bool + } + deriving (Eq, Show)
src/Text/Pandoc/Filter/Plot/Monad/Logging.hs view
@@ -1,111 +1,112 @@ {-# LANGUAGE OverloadedStrings #-} -{-# 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 - -Logging primitives. --} - -module Text.Pandoc.Filter.Plot.Monad.Logging - ( Verbosity(..) - , LogSink(..) - , Logger(..) - , withLogger - ) where - - -import Control.Concurrent (forkIO) -import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan) -import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar) - -import Control.Monad (forever) - -import Data.Char (toLower) -import Data.List (intercalate) -import Data.String (IsString(..)) -import Data.Text (Text, unpack) -import Data.Text.IO (hPutStr) -import Data.Yaml (FromJSON(parseJSON), Value(String)) - -import System.IO (stderr, withFile, IOMode (AppendMode) ) +{-# 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 +-- +-- Logging primitives. +module Text.Pandoc.Filter.Plot.Monad.Logging + ( Verbosity (..), + LogSink (..), + Logger (..), + withLogger, + ) +where +import Control.Concurrent (forkIO) +import Control.Concurrent.Chan (Chan, newChan, readChan, writeChan) +import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar) +import Control.Monad (forever) +import Data.Char (toLower) +import Data.List (intercalate) +import Data.String (IsString (..)) +import Data.Text (Text, unpack) +import Data.Text.IO (hPutStr) +import Data.Yaml (FromJSON (parseJSON), Value (String)) +import System.IO (IOMode (AppendMode), stderr, withFile) -- | Verbosity of the logger. -data Verbosity = Debug -- ^ Log all messages, including debug messages. - | Info -- ^ Log information, warning, and error messages. - | Warning -- ^ Log warning and error messages. - | Error -- ^ Only log errors. - | Silent -- ^ Don't log anything. - deriving (Eq, Ord, Show, Enum, Bounded) - +data Verbosity + = -- | Log all messages, including debug messages. + Debug + | -- | Log information, warning, and error messages. + Info + | -- | Log warning and error messages. + Warning + | -- | Only log errors. + Error + | -- | Don't log anything. + Silent + deriving (Eq, Ord, Show, Enum, Bounded) -- | Description of the possible ways to sink log messages. -data LogSink = StdErr -- ^ Standard error stream. - | LogFile FilePath -- ^ Appended to file. - deriving (Eq, Show) - +data LogSink + = -- | Standard error stream. + StdErr + | -- | Appended to file. + LogFile FilePath + deriving (Eq, Show) -- | The logging implementation is very similar to Hakyll's. data Logger = Logger - { lVerbosity :: Verbosity - , lChannel :: Chan (Maybe Text) - , lSink :: Text -> IO () - , lSync :: MVar () - } - + { lVerbosity :: Verbosity, + lChannel :: Chan (Maybe Text), + lSink :: Text -> IO (), + lSync :: MVar () + } -- | Perform an IO action with a logger. Using this function -- ensures that logging will be gracefully shut down. withLogger :: Verbosity -> LogSink -> (Logger -> IO a) -> IO a withLogger v s f = do - logger <- Logger <$> pure v - <*> newChan - <*> pure (sink s) - <*> newEmptyMVar - - -- The logger either logs messages (if Just "message"), - -- or stops working on Nothing. - _ <- forkIO $ forever $ - readChan (lChannel logger) - >>= maybe (putMVar (lSync logger) ()) (lSink logger) + logger <- + Logger <$> pure v + <*> newChan + <*> pure (sink s) + <*> newEmptyMVar - result <- f logger - - -- Flushing the logger - -- To signal to the logger that logging duties are over, - -- we append Nothing to the channel, and wait for it to finish - -- dealing with all items in the channel. - writeChan (lChannel logger) Nothing - () <- takeMVar (lSync logger) + -- The logger either logs messages (if Just "message"), + -- or stops working on Nothing. + _ <- + forkIO $ + forever $ + readChan (lChannel logger) + >>= maybe (putMVar (lSync logger) ()) (lSink logger) - return result + result <- f logger - where - sink StdErr = hPutStr stderr - sink (LogFile fp) = \t -> withFile fp AppendMode $ \h -> hPutStr h t + -- Flushing the logger + -- To signal to the logger that logging duties are over, + -- we append Nothing to the channel, and wait for it to finish + -- dealing with all items in the channel. + writeChan (lChannel logger) Nothing + () <- takeMVar (lSync logger) + return result + where + sink StdErr = hPutStr stderr + sink (LogFile fp) = \t -> withFile fp AppendMode $ \h -> hPutStr h t instance IsString Verbosity where - fromString s - | ls == "silent" = Silent - | ls == "info" = Info - | ls == "warning" = Warning - | ls == "error" = Error - | ls == "debug" = Debug - | otherwise = errorWithoutStackTrace $ mconcat ["Unrecognized verbosity '", s, "'. Valid choices are: " ] <> choices - where - ls = toLower <$> s - choices = intercalate ", " - $ fmap (fmap toLower . show) - $ enumFromTo minBound (maxBound::Verbosity) + fromString s + | ls == "silent" = Silent + | ls == "info" = Info + | ls == "warning" = Warning + | ls == "error" = Error + | ls == "debug" = Debug + | otherwise = errorWithoutStackTrace $ mconcat ["Unrecognized verbosity '", s, "'. Valid choices are: "] <> choices + where + ls = toLower <$> s + choices = + intercalate ", " $ + fmap (fmap toLower . show) $ + enumFromTo minBound (maxBound :: Verbosity) instance FromJSON Verbosity where - parseJSON (String t) = pure $ fromString . unpack $ t - parseJSON _ = fail $ "Could not parse the logging verbosity."+ parseJSON (String t) = pure $ fromString . unpack $ t + parseJSON _ = fail $ "Could not parse the logging verbosity."
src/Text/Pandoc/Filter/Plot/Monad/Types.hs view
@@ -1,231 +1,235 @@-{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-} -{-# 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 - -This module defines base types in use in pandoc-plot --} +{-# LANGUAGE RecordWildCards #-} -module Text.Pandoc.Filter.Plot.Monad.Types ( - Toolkit(..) - , Script - , CheckResult(..) - , InclusionKey(..) - , FigureSpec(..) - , SaveFormat(..) - , cls - , extension - , toolkits - , inclusionKeys - , Executable(..) - , exeFromPath +-- | +-- 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 +-- +-- This module defines base types in use in pandoc-plot +module Text.Pandoc.Filter.Plot.Monad.Types + ( Toolkit (..), + Script, + CheckResult (..), + InclusionKey (..), + FigureSpec (..), + SaveFormat (..), + cls, + extension, + toolkits, + inclusionKeys, + Executable (..), + exeFromPath, -- Utilities - , isWindows -) where - -import Data.Char (toLower) -import Data.List (intersperse) -import Data.String (IsString (..)) -import Data.Text (Text, pack) -import Data.Yaml (FromJSON, ToJSON(toJSON)) - -import GHC.Generics (Generic) -import System.FilePath (splitFileName) -import System.Info (os) + isWindows, + ) +where -import Text.Pandoc.Definition (Attr) +import Data.Char (toLower) +import Data.List (intersperse) +import Data.String (IsString (..)) +import Data.Text (Text, pack) +import Data.Yaml (FromJSON, ToJSON (toJSON)) +import GHC.Generics (Generic) +import System.FilePath (splitFileName) +import System.Info (os) +import Text.Pandoc.Definition (Attr) -- | List of supported toolkits. toolkits :: [Toolkit] toolkits = enumFromTo minBound maxBound - -- | Enumeration of supported toolkits data Toolkit - = Matplotlib - | Matlab - | PlotlyPython - | PlotlyR - | Mathematica - | Octave - | GGPlot2 - | GNUPlot - | Graphviz - | Bokeh - | Plotsjl - deriving (Bounded, Eq, Enum, Generic, Ord) - + = Matplotlib + | Matlab + | PlotlyPython + | PlotlyR + | Mathematica + | Octave + | GGPlot2 + | GNUPlot + | Graphviz + | Bokeh + | Plotsjl + deriving (Bounded, Eq, Enum, Generic, Ord) -- | This instance should only be used to display toolkit names instance Show Toolkit where - show Matplotlib = "Python/Matplotlib" - show Matlab = "MATLAB" - show PlotlyPython = "Python/Plotly" - show PlotlyR = "R/Plotly" - show Mathematica = "Mathematica" - show Octave = "GNU Octave" - show GGPlot2 = "ggplot2" - show GNUPlot = "gnuplot" - show Graphviz = "graphviz" - show Bokeh = "Python/Bokeh" - show Plotsjl = "Julia/Plots.jl" - + show Matplotlib = "Python/Matplotlib" + show Matlab = "MATLAB" + show PlotlyPython = "Python/Plotly" + show PlotlyR = "R/Plotly" + show Mathematica = "Mathematica" + show Octave = "GNU Octave" + show GGPlot2 = "ggplot2" + show GNUPlot = "gnuplot" + show Graphviz = "graphviz" + show Bokeh = "Python/Bokeh" + show Plotsjl = "Julia/Plots.jl" -- | Class name which will trigger the filter cls :: Toolkit -> Text -cls Matplotlib = "matplotlib" -cls Matlab = "matlabplot" +cls Matplotlib = "matplotlib" +cls Matlab = "matlabplot" cls PlotlyPython = "plotly_python" -cls PlotlyR = "plotly_r" -cls Mathematica = "mathplot" -cls Octave = "octaveplot" -cls GGPlot2 = "ggplot2" -cls GNUPlot = "gnuplot" -cls Graphviz = "graphviz" -cls Bokeh = "bokeh" -cls Plotsjl = "plotsjl" - +cls PlotlyR = "plotly_r" +cls Mathematica = "mathplot" +cls Octave = "octaveplot" +cls GGPlot2 = "ggplot2" +cls GNUPlot = "gnuplot" +cls Graphviz = "graphviz" +cls Bokeh = "bokeh" +cls Plotsjl = "plotsjl" -- | Executable program and directory where it can be found. data Executable = Executable FilePath Text - exeFromPath :: FilePath -> Executable -exeFromPath fp = let (dir, name) = splitFileName fp - in Executable dir (pack name) - +exeFromPath fp = + let (dir, name) = splitFileName fp + in Executable dir (pack name) -- | Source context for plotting scripts type Script = Text - -- | Result of checking scripts for problems data CheckResult - = CheckPassed - | CheckFailed Text - deriving (Eq) + = CheckPassed + | CheckFailed Text + deriving (Eq) instance Semigroup CheckResult where - (<>) CheckPassed a = a - (<>) a CheckPassed = a - (<>) (CheckFailed msg1) (CheckFailed msg2) = CheckFailed (msg1 <> msg2) + (<>) CheckPassed a = a + (<>) a CheckPassed = a + (<>) (CheckFailed msg1) (CheckFailed msg2) = CheckFailed (msg1 <> msg2) instance Monoid CheckResult where - mempty = CheckPassed - + mempty = CheckPassed -- | Description of any possible inclusion key, both in documents -- and in configuration files. data InclusionKey - = DirectoryK - | CaptionK - | SaveFormatK - | WithSourceK - | CaptionFormatK - | PreambleK - | DpiK - | ExecutableK - | DependenciesK - | FileK - | MatplotlibTightBBoxK - | MatplotlibTransparentK - deriving (Bounded, Eq, Enum) + = DirectoryK + | CaptionK + | SaveFormatK + | WithSourceK + | CaptionFormatK + | PreambleK + | DpiK + | SourceCodeLabelK + | ExecutableK + | DependenciesK + | FileK + | MatplotlibTightBBoxK + | MatplotlibTransparentK + deriving (Bounded, Eq, Enum) -- | Keys that pandoc-plot will look for in code blocks. -- These are only exported for testing purposes. instance Show InclusionKey where - show DirectoryK = "directory" - show CaptionK = "caption" - show SaveFormatK = "format" - show WithSourceK = "source" - show CaptionFormatK = "caption_format" - show PreambleK = "preamble" - show DpiK = "dpi" - show ExecutableK = "executable" - show DependenciesK = "dependencies" - show FileK = "file" - show MatplotlibTightBBoxK = "tight_bbox" - show MatplotlibTransparentK = "transparent" - + show DirectoryK = "directory" + show CaptionK = "caption" + show SaveFormatK = "format" + show WithSourceK = "source" + show CaptionFormatK = "caption_format" + show PreambleK = "preamble" + show DpiK = "dpi" + show SourceCodeLabelK = "source_label" + show ExecutableK = "executable" + show DependenciesK = "dependencies" + show FileK = "file" + show MatplotlibTightBBoxK = "tight_bbox" + show MatplotlibTransparentK = "transparent" -- | List of all keys related to pandoc-plot that -- can be specified in source material. inclusionKeys :: [InclusionKey] -inclusionKeys = enumFromTo (minBound::InclusionKey) maxBound - +inclusionKeys = enumFromTo (minBound :: InclusionKey) maxBound -- | Datatype containing all parameters required to run pandoc-plot. -- -- It is assumed that once a @FigureSpec@ has been created, no configuration -- can overload it; hence, a @FigureSpec@ completely encodes a particular figure. data FigureSpec = FigureSpec - { toolkit :: !Toolkit -- ^ Plotting toolkit to use for this figure. - , caption :: !Text -- ^ Figure caption. - , withSource :: !Bool -- ^ Append link to source code in caption. - , script :: !Script -- ^ Source code for the figure. - , saveFormat :: !SaveFormat -- ^ Save format of the figure. - , directory :: !FilePath -- ^ Directory where to save the file. - , dpi :: !Int -- ^ Dots-per-inch of figure. - , dependencies :: ![FilePath] -- ^ Files/directories on which this figure depends, e.g. data files. - , extraAttrs :: ![(Text, Text)] -- ^ Renderer-specific extra attributes. - , blockAttrs :: !Attr -- ^ Attributes not related to @pandoc-plot@ will be propagated. - } - + { -- | Plotting toolkit to use for this figure. + toolkit :: !Toolkit, + -- | Figure caption. + caption :: !Text, + -- | Append link to source code in caption. + withSource :: !Bool, + -- | Source code for the figure. + script :: !Script, + -- | Save format of the figure. + saveFormat :: !SaveFormat, + -- | Directory where to save the file. + directory :: !FilePath, + -- | Dots-per-inch of figure. + dpi :: !Int, + -- | Files/directories on which this figure depends, e.g. data files. + dependencies :: ![FilePath], + -- | Renderer-specific extra attributes. + extraAttrs :: ![(Text, Text)], + -- | Attributes not related to @pandoc-plot@ will be propagated. + blockAttrs :: !Attr + } -- | Generated figure file format supported by pandoc-plot. -- Note that not all formats are supported by all toolkits. data SaveFormat - = PNG -- ^ Portable network graphics - | PDF -- ^ Portable document format - | SVG -- ^ Scalable vector graphics - | JPG -- ^ JPEG/JPG compressed image - | EPS -- ^ Encapsulated postscript - | GIF -- ^ GIF format - | TIF -- ^ Tagged image format - | WEBP -- ^ WebP image format - | HTML -- ^ HTML for interactive plots. - deriving (Bounded, Enum, Eq, Show, Generic) + = -- | Portable network graphics + PNG + | -- | Portable document format + PDF + | -- | Scalable vector graphics + SVG + | -- | JPEG/JPG compressed image + JPG + | -- | Encapsulated postscript + EPS + | -- | GIF format + GIF + | -- | Tagged image format + TIF + | -- | WebP image format + WEBP + | -- | HTML for interactive plots. + HTML + deriving (Bounded, Enum, Eq, Show, Generic) instance IsString SaveFormat where - -- | An error is thrown if the save format cannot be parsed. That's OK - -- since pandoc-plot is a command-line tool and isn't expected to run - -- long. - fromString s - | s `elem` ["png", "PNG", ".png"] = PNG - | s `elem` ["pdf", "PDF", ".pdf"] = PDF - | s `elem` ["svg", "SVG", ".svg"] = SVG - | s `elem` ["eps", "EPS", ".eps"] = EPS - | s `elem` ["gif", "GIF", ".gif"] = GIF - | s `elem` ["jpg", "jpeg", "JPG", "JPEG", ".jpg", ".jpeg"] = JPG - | s `elem` ["tif", "tiff", "TIF", "TIFF", ".tif", ".tiff"] = TIF - | s `elem` ["webp", "WEBP", ".webp"] = WEBP - | s `elem` ["html", "HTML", ".html"] = HTML - | otherwise = errorWithoutStackTrace $ - mconcat [ s - , " is not one of valid save format : " - , mconcat $ intersperse ", " $ show <$> saveFormats - ] - where - saveFormats = (enumFromTo minBound maxBound) :: [SaveFormat] + fromString s + | s `elem` ["png", "PNG", ".png"] = PNG + | s `elem` ["pdf", "PDF", ".pdf"] = PDF + | s `elem` ["svg", "SVG", ".svg"] = SVG + | s `elem` ["eps", "EPS", ".eps"] = EPS + | s `elem` ["gif", "GIF", ".gif"] = GIF + | s `elem` ["jpg", "jpeg", "JPG", "JPEG", ".jpg", ".jpeg"] = JPG + | s `elem` ["tif", "tiff", "TIF", "TIFF", ".tif", ".tiff"] = TIF + | s `elem` ["webp", "WEBP", ".webp"] = WEBP + | s `elem` ["html", "HTML", ".html"] = HTML + | otherwise = + errorWithoutStackTrace $ + mconcat + [ s, + " is not one of valid save format : ", + mconcat $ intersperse ", " $ show <$> saveFormats + ] + where + saveFormats = (enumFromTo minBound maxBound) :: [SaveFormat] instance FromJSON SaveFormat -- TODO: test this parsing instance ToJSON SaveFormat where - toJSON = toJSON . extension - + toJSON = toJSON . extension -- | Save format file extension extension :: SaveFormat -> String extension fmt = mconcat [".", fmap toLower . show $ fmt] - isWindows :: Bool -isWindows = os `elem` ["mingw32", "win32", "cygwin32"] -- Aliases taken from cabal's Distribution.System module+isWindows = os `elem` ["mingw32", "win32", "cygwin32"] -- Aliases taken from cabal's Distribution.System module
src/Text/Pandoc/Filter/Plot/Parse.hs view
@@ -1,49 +1,47 @@-{-# LANGUAGE OverloadedStrings #-} -{-# 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 - -This module defines types and functions that help -with keeping track of figure specifications --} -module Text.Pandoc.Filter.Plot.Parse ( - plotToolkit - , parseFigureSpec - , captionReader -) where - -import Control.Monad (join, when) - -import Data.Char (isSpace) -import Data.Default (def) -import Data.List (intersperse) -import qualified Data.Map.Strict as Map -import Data.Maybe (fromMaybe, listToMaybe, isJust, fromJust) -import Data.String (fromString) -import Data.Text (Text, pack, unpack) -import qualified Data.Text as T -import qualified Data.Text.IO as TIO -import Data.Version (showVersion) - -import Paths_pandoc_plot (version) - -import System.FilePath (makeValid, normalise) - -import Text.Pandoc.Definition (Block (..), Inline, - Pandoc (..), Format(..)) +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} -import Text.Pandoc.Class (runPure) -import Text.Pandoc.Options (ReaderOptions (..)) -import Text.Pandoc.Readers (getReader, Reader(..)) +-- | +-- 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 +-- +-- This module defines types and functions that help +-- with keeping track of figure specifications +module Text.Pandoc.Filter.Plot.Parse + ( plotToolkit, + parseFigureSpec, + captionReader, + ) +where -import Text.Pandoc.Filter.Plot.Renderers -import Text.Pandoc.Filter.Plot.Monad +import Control.Monad (join, when) +import Data.Char (isSpace) +import Data.Default (def) +import Data.List (intersperse) +import qualified Data.Map.Strict as Map +import Data.Maybe (fromJust, fromMaybe, isJust, listToMaybe) +import Data.String (fromString) +import Data.Text (Text, pack, unpack) +import qualified Data.Text as T +import qualified Data.Text.IO as TIO +import Data.Version (showVersion) +import Paths_pandoc_plot (version) +import System.FilePath (makeValid, normalise) +import Text.Pandoc.Class (runPure) +import Text.Pandoc.Definition + ( Block (..), + Format (..), + Inline, + Pandoc (..), + ) +import Text.Pandoc.Filter.Plot.Monad +import Text.Pandoc.Filter.Plot.Renderers +import Text.Pandoc.Options (ReaderOptions (..)) +import Text.Pandoc.Readers (Reader (..), getReader) tshow :: Show a => a -> Text tshow = pack . show @@ -52,115 +50,117 @@ -- If an environment is detected, but the save format is incompatible, -- an error will be thrown. parseFigureSpec :: Block -> PlotM (Maybe FigureSpec) -parseFigureSpec block@(CodeBlock (id', classes, attrs) _) = - sequence $ fmap figureSpec - $ plotToolkit block >>= hasToolkit - where - hasToolkit = \tk -> if cls tk `elem` classes then return tk else Nothing - attrs' = Map.fromList attrs - preamblePath = unpack <$> Map.lookup (tshow PreambleK) attrs' +parseFigureSpec block@(CodeBlock (id', classes, attrs) _) = + sequence $ + fmap figureSpec $ + plotToolkit block >>= hasToolkit + where + hasToolkit = \tk -> if cls tk `elem` classes then return tk else Nothing + attrs' = Map.fromList attrs + preamblePath = unpack <$> Map.lookup (tshow PreambleK) attrs' - figureSpec :: Toolkit -> PlotM FigureSpec - figureSpec toolkit = do - conf <- asks envConfig - let extraAttrs' = parseExtraAttrs toolkit attrs' - header = comment toolkit $ "Generated by pandoc-plot " <> ((pack . showVersion) version) - defaultPreamble = preambleSelector toolkit conf + figureSpec :: Toolkit -> PlotM FigureSpec + figureSpec toolkit = do + conf <- asks envConfig + let extraAttrs' = parseExtraAttrs toolkit attrs' + header = comment toolkit $ "Generated by pandoc-plot " <> ((pack . showVersion) version) + defaultPreamble = preambleSelector toolkit conf - 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 toolkits - filteredAttrs = filter (\(k, _) -> k `notElem` (tshow <$> inclusionKeys)) attrs - defWithSource = defaultWithSource conf - defSaveFmt = defaultSaveFormat conf - defDPI = defaultDPI conf + 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 toolkits + filteredAttrs = filter (\(k, _) -> k `notElem` (tshow <$> inclusionKeys)) attrs + defWithSource = defaultWithSource conf + defSaveFmt = defaultSaveFormat conf + defDPI = defaultDPI conf - -- Decide between reading from file or using document content - content <- parseContent block + -- Decide between reading from file or using document content + content <- parseContent block - let caption = Map.findWithDefault mempty (tshow CaptionK) attrs' - withSource = fromMaybe defWithSource $ readBool <$> Map.lookup (tshow WithSourceK) attrs' - script = mconcat $ intersperse "\n" [header, includeScript, content] - saveFormat = fromMaybe defSaveFmt $ (fromString . unpack) <$> Map.lookup (tshow SaveFormatK) attrs' - directory = makeValid $ unpack $ Map.findWithDefault (pack $ defaultDirectory conf) (tshow DirectoryK) attrs' - dpi = fromMaybe defDPI $ (read . unpack) <$> Map.lookup (tshow DpiK) attrs' - extraAttrs = Map.toList extraAttrs' - blockAttrs = (id', filter (/= cls toolkit) classes, filteredAttrs) + let caption = Map.findWithDefault mempty (tshow CaptionK) attrs' + withSource = fromMaybe defWithSource $ readBool <$> Map.lookup (tshow WithSourceK) attrs' + script = mconcat $ intersperse "\n" [header, includeScript, content] + saveFormat = fromMaybe defSaveFmt $ (fromString . unpack) <$> Map.lookup (tshow SaveFormatK) attrs' + directory = makeValid $ unpack $ Map.findWithDefault (pack $ defaultDirectory conf) (tshow DirectoryK) attrs' + dpi = fromMaybe defDPI $ (read . unpack) <$> Map.lookup (tshow DpiK) attrs' + extraAttrs = Map.toList extraAttrs' + blockAttrs = (id', filter (/= cls toolkit) classes, filteredAttrs) - let blockDependencies = parseFileDependencies $ fromMaybe mempty $ Map.lookup (tshow DependenciesK) attrs' - dependencies = (defaultDependencies conf) <> blockDependencies - - -- This is the first opportunity to check save format compatibility - let saveFormatSupported = saveFormat `elem` (supportedSaveFormats toolkit) - when (not saveFormatSupported) $ do - let msg = pack $ mconcat ["Save format ", show saveFormat, " not supported by ", show toolkit ] - err msg - return FigureSpec{..} + let blockDependencies = parseFileDependencies $ fromMaybe mempty $ Map.lookup (tshow DependenciesK) attrs' + dependencies = (defaultDependencies conf) <> blockDependencies + -- This is the first opportunity to check save format compatibility + let saveFormatSupported = saveFormat `elem` (supportedSaveFormats toolkit) + when (not saveFormatSupported) $ do + let msg = pack $ mconcat ["Save format ", show saveFormat, " not supported by ", show toolkit] + err msg + return FigureSpec {..} parseFigureSpec _ = return Nothing - -- | Parse script content from a block, if possible. -- The script content can either come from a file -- or from the code block itself. If both are present, -- the file is preferred. parseContent :: Block -> PlotM Script parseContent (CodeBlock (_, _, attrs) content) = do - let attrs' = Map.fromList attrs - mfile = normalise . unpack <$> Map.lookup (tshow FileK) attrs' - when (content /= mempty && isJust mfile) $ do - err $ mconcat [ - "Figure refers to a file (", pack $ fromJust mfile - , ") but also has content in the document.\nThe file content will be preferred." - ] - let loadFromFile fp = do - info $ "Loading figure content from " <> pack fp - liftIO $ TIO.readFile fp - maybe (return content) loadFromFile mfile + let attrs' = Map.fromList attrs + mfile = normalise . unpack <$> Map.lookup (tshow FileK) attrs' + when (content /= mempty && isJust mfile) $ do + err $ + mconcat + [ "Figure refers to a file (", + pack $ fromJust mfile, + ") but also has content in the document.\nThe file content will be preferred." + ] + let loadFromFile fp = do + info $ "Loading figure content from " <> pack fp + liftIO $ TIO.readFile fp + maybe (return content) loadFromFile mfile parseContent _ = return mempty - -- | Determine which toolkit should be used to render the plot -- from a code block, if any. plotToolkit :: Block -> Maybe Toolkit plotToolkit (CodeBlock (_, classes, _) _) = - listToMaybe $ filter (\tk->cls tk `elem` classes) toolkits + listToMaybe $ filter (\tk -> cls tk `elem` classes) toolkits plotToolkit _ = Nothing - -- | Reader a caption, based on input document format captionReader :: Format -> Text -> Maybe [Inline] -captionReader (Format f) t = either (const Nothing) (Just . extractFromBlocks) $ runPure $ do +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 - extractFromBlocks (Pandoc _ blocks) = mconcat $ extractInlines <$> blocks - - extractInlines (Plain inlines) = inlines - extractInlines (Para inlines) = inlines - extractInlines (LineBlock multiinlines) = join multiinlines - extractInlines _ = [] + TextReader fct -> fct readerOpts t + _ -> return mempty + where + extractFromBlocks (Pandoc _ blocks) = mconcat $ extractInlines <$> blocks + extractInlines (Plain inlines) = inlines + extractInlines (Para inlines) = inlines + extractInlines (LineBlock multiinlines) = join multiinlines + extractInlines _ = [] -- | Flexible boolean parsing readBool :: Text -> Bool -readBool s | s `elem` ["True", "true", "'True'", "'true'", "1"] = True - | s `elem` ["False", "false", "'False'", "'false'", "0"] = False - | otherwise = errorWithoutStackTrace $ unpack $ mconcat ["Could not parse '", s, "' into a boolean. Please use 'True' or 'False'"] - +readBool s + | s `elem` ["True", "true", "'True'", "'true'", "1"] = True + | s `elem` ["False", "false", "'False'", "'false'", "0"] = False + | otherwise = errorWithoutStackTrace $ unpack $ mconcat ["Could not parse '", s, "' into a boolean. Please use 'True' or 'False'"] -- | Parse a list of file dependencies such as /[foo.bar, hello.txt]/. parseFileDependencies :: Text -> [FilePath] parseFileDependencies t - | t == mempty = mempty - | otherwise = fmap normalise - . fmap unpack - . fmap (T.dropAround isSpace) -- Remove leading/trailing whitespace on filenames - . T.splitOn "," - . T.dropAround (\c -> c `elem` ['[', ']']) $ t+ | t == mempty = mempty + | otherwise = + fmap normalise + . fmap unpack + . fmap (T.dropAround isSpace) -- Remove leading/trailing whitespace on filenames + . T.splitOn "," + . T.dropAround (\c -> c `elem` ['[', ']']) + $ t
src/Text/Pandoc/Filter/Plot/Renderers.hs view
@@ -1,208 +1,209 @@ {-# LANGUAGE OverloadedStrings #-} -{-# 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 - -Specification of renderers. --} - -module Text.Pandoc.Filter.Plot.Renderers ( - scriptExtension - , comment - , preambleSelector - , supportedSaveFormats - , scriptChecks - , parseExtraAttrs - , command - , capture - , executable - , toolkitAvailable - , availableToolkits - , availableToolkitsM - , unavailableToolkits - , unavailableToolkitsM - , OutputSpec(..) - , Executable(..) -) where - -import Control.Concurrent.Async.Lifted (forConcurrently) +{-# LANGUAGE RecordWildCards #-} -import Data.List ((\\)) -import Data.Map.Strict (Map) -import Data.Maybe (catMaybes) -import Data.Text (Text) +-- | +-- 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 +-- +-- Specification of renderers. +module Text.Pandoc.Filter.Plot.Renderers + ( scriptExtension, + comment, + language, + preambleSelector, + supportedSaveFormats, + scriptChecks, + parseExtraAttrs, + command, + capture, + executable, + toolkitAvailable, + availableToolkits, + availableToolkitsM, + unavailableToolkits, + unavailableToolkitsM, + OutputSpec (..), + Executable (..), + ) +where +import Control.Concurrent.Async.Lifted (forConcurrently) +import Data.List ((\\)) +import Data.Map.Strict (Map) +import Data.Maybe (catMaybes) +import Data.Text (Text) +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.GGPlot2 -import Text.Pandoc.Filter.Plot.Renderers.GNUPlot -import Text.Pandoc.Filter.Plot.Renderers.Graphviz -import Text.Pandoc.Filter.Plot.Renderers.Bokeh import Text.Pandoc.Filter.Plot.Renderers.Plotsjl -import Text.Pandoc.Filter.Plot.Renderers.Prelude (OutputSpec(..)) - -import Text.Pandoc.Filter.Plot.Monad - +import Text.Pandoc.Filter.Plot.Renderers.Prelude (OutputSpec (..)) -- Extension for script files, e.g. ".py", or ".m". scriptExtension :: Toolkit -> String -scriptExtension Matplotlib = ".py" +scriptExtension Matplotlib = ".py" scriptExtension PlotlyPython = ".py" -scriptExtension PlotlyR = ".r" -scriptExtension Matlab = ".m" -scriptExtension Mathematica = ".m" -scriptExtension Octave = ".m" -scriptExtension GGPlot2 = ".r" -scriptExtension GNUPlot = ".gp" -scriptExtension Graphviz = ".dot" -scriptExtension Bokeh = ".py" -scriptExtension Plotsjl = ".jl" +scriptExtension PlotlyR = ".r" +scriptExtension Matlab = ".m" +scriptExtension Mathematica = ".m" +scriptExtension Octave = ".m" +scriptExtension GGPlot2 = ".r" +scriptExtension GNUPlot = ".gp" +scriptExtension Graphviz = ".dot" +scriptExtension Bokeh = ".py" +scriptExtension Plotsjl = ".jl" +-- | Language that is used by a toolkit. Specifically used +-- to highlight the appropriate language in the external source code. +language :: Toolkit -> Text +language Matplotlib = "python" +language PlotlyPython = "python" +language PlotlyR = "r" +language Matlab = "matlab" +language Mathematica = "mathematica" +language Octave = "matlab" +language GGPlot2 = "r" +language GNUPlot = "gnuplot" +language Graphviz = "dot" +language Bokeh = "python" +language Plotsjl = "julia" -- Make a string into a comment comment :: Toolkit -> (Text -> Text) -comment Matplotlib = mappend "# " +comment Matplotlib = mappend "# " comment PlotlyPython = mappend "# " -comment PlotlyR = mappend "# " -comment Matlab = mappend "% " -comment Mathematica = \t -> mconcat ["(*", t, "*)"] -comment Octave = mappend "% " -comment GGPlot2 = mappend "# " -comment GNUPlot = mappend "# " -comment Graphviz = mappend "// " -comment Bokeh = mappend "# " -comment Plotsjl = mappend "# " - +comment PlotlyR = mappend "# " +comment Matlab = mappend "% " +comment Mathematica = \t -> mconcat ["(*", t, "*)"] +comment Octave = mappend "% " +comment GGPlot2 = mappend "# " +comment GNUPlot = mappend "# " +comment Graphviz = mappend "// " +comment Bokeh = mappend "# " +comment Plotsjl = mappend "# " -- | The function that maps from configuration to the preamble. preambleSelector :: Toolkit -> (Configuration -> Script) -preambleSelector Matplotlib = matplotlibPreamble +preambleSelector Matplotlib = matplotlibPreamble preambleSelector PlotlyPython = plotlyPythonPreamble -preambleSelector PlotlyR = plotlyRPreamble -preambleSelector Matlab = matlabPreamble -preambleSelector Mathematica = mathematicaPreamble -preambleSelector Octave = octavePreamble -preambleSelector GGPlot2 = ggplot2Preamble -preambleSelector GNUPlot = gnuplotPreamble -preambleSelector Graphviz = graphvizPreamble -preambleSelector Bokeh = bokehPreamble -preambleSelector Plotsjl = plotsjlPreamble - +preambleSelector PlotlyR = plotlyRPreamble +preambleSelector Matlab = matlabPreamble +preambleSelector Mathematica = mathematicaPreamble +preambleSelector Octave = octavePreamble +preambleSelector GGPlot2 = ggplot2Preamble +preambleSelector GNUPlot = gnuplotPreamble +preambleSelector Graphviz = graphvizPreamble +preambleSelector Bokeh = bokehPreamble +preambleSelector Plotsjl = plotsjlPreamble -- | Save formats supported by this renderer. supportedSaveFormats :: Toolkit -> [SaveFormat] -supportedSaveFormats Matplotlib = matplotlibSupportedSaveFormats +supportedSaveFormats Matplotlib = matplotlibSupportedSaveFormats supportedSaveFormats PlotlyPython = plotlyPythonSupportedSaveFormats -supportedSaveFormats PlotlyR = plotlyRSupportedSaveFormats -supportedSaveFormats Matlab = matlabSupportedSaveFormats -supportedSaveFormats Mathematica = mathematicaSupportedSaveFormats -supportedSaveFormats Octave = octaveSupportedSaveFormats -supportedSaveFormats GGPlot2 = ggplot2SupportedSaveFormats -supportedSaveFormats GNUPlot = gnuplotSupportedSaveFormats -supportedSaveFormats Graphviz = graphvizSupportedSaveFormats -supportedSaveFormats Bokeh = bokehSupportedSaveFormats -supportedSaveFormats Plotsjl = plotsjlSupportedSaveFormats - +supportedSaveFormats PlotlyR = plotlyRSupportedSaveFormats +supportedSaveFormats Matlab = matlabSupportedSaveFormats +supportedSaveFormats Mathematica = mathematicaSupportedSaveFormats +supportedSaveFormats Octave = octaveSupportedSaveFormats +supportedSaveFormats GGPlot2 = ggplot2SupportedSaveFormats +supportedSaveFormats GNUPlot = gnuplotSupportedSaveFormats +supportedSaveFormats Graphviz = graphvizSupportedSaveFormats +supportedSaveFormats Bokeh = bokehSupportedSaveFormats +supportedSaveFormats Plotsjl = plotsjlSupportedSaveFormats -- Checks to perform before running a script. If ANY check fails, -- the figure is not rendered. This is to prevent, for example, -- blocking operations to occur. scriptChecks :: Toolkit -> [Script -> CheckResult] scriptChecks Matplotlib = [matplotlibCheckIfShow] -scriptChecks Bokeh = [bokehCheckIfShow] +scriptChecks Bokeh = [bokehCheckIfShow] scriptChecks _ = mempty - -- | 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 Matplotlib = matplotlibExtraAttrs -parseExtraAttrs _ = return mempty - +parseExtraAttrs _ = return mempty -- | Generate the appropriate command-line command to generate a figure. -- The executable will need to be found first, hence the IO monad. -command :: Toolkit - -> OutputSpec - -> Text -- Executable name (e.g. "python3") - -> Text -command Matplotlib = matplotlibCommand +command :: + Toolkit -> + OutputSpec -> + Text -> -- Executable name (e.g. "python3") + Text +command Matplotlib = matplotlibCommand command PlotlyPython = plotlyPythonCommand -command PlotlyR = plotlyRCommand -command Matlab = matlabCommand -command Mathematica = mathematicaCommand -command Octave = octaveCommand -command GGPlot2 = ggplot2Command -command GNUPlot = gnuplotCommand -command Graphviz = graphvizCommand -command Bokeh = bokehCommand -command Plotsjl = plotsjlCommand - +command PlotlyR = plotlyRCommand +command Matlab = matlabCommand +command Mathematica = mathematicaCommand +command Octave = octaveCommand +command GGPlot2 = ggplot2Command +command GNUPlot = gnuplotCommand +command Graphviz = graphvizCommand +command Bokeh = bokehCommand +command Plotsjl = plotsjlCommand -- | Script fragment required to capture a figure. capture :: Toolkit -> (FigureSpec -> FilePath -> Script) -capture Matplotlib = matplotlibCapture +capture Matplotlib = matplotlibCapture capture PlotlyPython = plotlyPythonCapture -capture PlotlyR = plotlyRCapture -capture Matlab = matlabCapture -capture Mathematica = mathematicaCapture -capture Octave = octaveCapture -capture GGPlot2 = ggplot2Capture -capture GNUPlot = gnuplotCapture -capture Graphviz = graphvizCapture -capture Bokeh = bokehCapture -capture Plotsjl = plotsjlCapture - +capture PlotlyR = plotlyRCapture +capture Matlab = matlabCapture +capture Mathematica = mathematicaCapture +capture Octave = octaveCapture +capture GGPlot2 = ggplot2Capture +capture GNUPlot = gnuplotCapture +capture Graphviz = graphvizCapture +capture Bokeh = bokehCapture +capture Plotsjl = plotsjlCapture -- | Check if a toolkit is available, based on the current configuration toolkitAvailable :: Toolkit -> PlotM Bool -toolkitAvailable Matplotlib = matplotlibAvailable +toolkitAvailable Matplotlib = matplotlibAvailable toolkitAvailable PlotlyPython = plotlyPythonAvailable -toolkitAvailable PlotlyR = plotlyRAvailable -toolkitAvailable Matlab = matlabAvailable -toolkitAvailable Mathematica = mathematicaAvailable -toolkitAvailable Octave = octaveAvailable -toolkitAvailable GGPlot2 = ggplot2Available -toolkitAvailable GNUPlot = gnuplotAvailable -toolkitAvailable Graphviz = graphvizAvailable -toolkitAvailable Bokeh = bokehAvailable -toolkitAvailable Plotsjl = plotsjlAvailable - +toolkitAvailable PlotlyR = plotlyRAvailable +toolkitAvailable Matlab = matlabAvailable +toolkitAvailable Mathematica = mathematicaAvailable +toolkitAvailable Octave = octaveAvailable +toolkitAvailable GGPlot2 = ggplot2Available +toolkitAvailable GNUPlot = gnuplotAvailable +toolkitAvailable Graphviz = graphvizAvailable +toolkitAvailable Bokeh = bokehAvailable +toolkitAvailable Plotsjl = plotsjlAvailable -- | List of toolkits available on this machine. -- The executables to look for are taken from the configuration. availableToolkits :: Configuration -> IO [Toolkit] availableToolkits conf = runPlotM conf availableToolkitsM - -- | List of toolkits not available on this machine. -- The executables to look for are taken from the configur unavailableToolkits :: Configuration -> IO [Toolkit] unavailableToolkits conf = runPlotM conf unavailableToolkitsM - -- | Monadic version of @availableToolkits@. -- -- Note that logging is disabled availableToolkitsM :: PlotM [Toolkit] availableToolkitsM = silence $ do - mtks <- forConcurrently toolkits $ \tk -> do - available <- toolkitAvailable tk - if available - then return $ Just tk - else return Nothing - return $ catMaybes mtks + mtks <- forConcurrently toolkits $ \tk -> do + available <- toolkitAvailable tk + if available + then return $ Just tk + else return Nothing + return $ catMaybes mtks - -- | Monadic version of @unavailableToolkits@ unavailableToolkitsM :: PlotM [Toolkit] unavailableToolkitsM = (\\) toolkits <$> availableToolkitsM
src/Text/Pandoc/Filter/Plot/Renderers/Bokeh.hs view
@@ -1,67 +1,63 @@-{-# 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 Bokeh code blocks --} - -module Text.Pandoc.Filter.Plot.Renderers.Bokeh ( - bokehSupportedSaveFormats - , bokehCommand - , bokehCapture - , bokehAvailable - , bokehCheckIfShow -) where +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NoImplicitPrelude #-} -import Data.Monoid (Any(..)) -import qualified Data.Text as T -import Text.Pandoc.Filter.Plot.Renderers.Prelude +-- | +-- 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 Bokeh code blocks +module Text.Pandoc.Filter.Plot.Renderers.Bokeh + ( bokehSupportedSaveFormats, + bokehCommand, + bokehCapture, + bokehAvailable, + bokehCheckIfShow, + ) +where +import Data.Monoid (Any (..)) +import qualified Data.Text as T +import Text.Pandoc.Filter.Plot.Renderers.Prelude bokehSupportedSaveFormats :: [SaveFormat] bokehSupportedSaveFormats = [PNG, SVG, HTML] - bokehCommand :: OutputSpec -> Text -> Text -bokehCommand OutputSpec{..} exe = [st|#{exe} "#{oScriptPath}"|] - +bokehCommand OutputSpec {..} exe = [st|#{exe} "#{oScriptPath}"|] bokehAvailable :: PlotM Bool bokehAvailable = do - mexe <- executable Bokeh - case mexe of - Nothing -> return False - Just (Executable dir exe) -> commandSuccess dir [st|#{exe} -c "import bokeh; import selenium"|] - + mexe <- executable Bokeh + case mexe of + Nothing -> return False + Just (Executable dir exe) -> commandSuccess dir [st|#{exe} -c "import bokeh; import selenium"|] -- | Check if `matplotlib.pyplot.show()` calls are present in the script, -- which would halt pandoc-plot bokehCheckIfShow :: Script -> CheckResult -bokehCheckIfShow s = - if getAny $ mconcat showPresent - then CheckFailed "encountered a call to `bokeh.io.show`." - else CheckPassed - where - showPresent = (\n -> Any (T.isInfixOf n s)) <$> [ - "bokeh.io.show(" - , "show(" +bokehCheckIfShow s = + if getAny $ mconcat showPresent + then CheckFailed "encountered a call to `bokeh.io.show`." + else CheckPassed + where + showPresent = + (\n -> Any (T.isInfixOf n s)) + <$> [ "bokeh.io.show(", + "show(" ] - bokehCapture :: FigureSpec -> FilePath -> Script bokehCapture = appendCapture bokehCaptureFragment - bokehCaptureFragment :: FigureSpec -> FilePath -> Script -bokehCaptureFragment FigureSpec{..} fname = [st| +bokehCaptureFragment FigureSpec {..} fname = + [st| from bokeh.io import export_png, export_svgs, save from bokeh.models import Model from bokeh.resources import CDN @@ -73,9 +69,9 @@ __current_model = [obj for obj in globals().values() if isinstance(obj, Model)][-1] #{write} |] - where - write = case saveFormat of - HTML -> [st|save(__current_model, filename=r"#{fname}", resources=CDN)|] - SVG -> [st|__current_model.output_backend="svg"; export_svgs(__current_model, filename=r"#{fname}")|] - PNG -> [st|export_png(obj = __current_model, filename=r"#{fname}")|] - fmt -> errorWithoutStackTrace $ "Save format not supported: " <> show fmt + where + write = case saveFormat of + HTML -> [st|save(__current_model, filename=r"#{fname}", resources=CDN)|] + SVG -> [st|__current_model.output_backend="svg"; export_svgs(__current_model, filename=r"#{fname}")|] + PNG -> [st|export_png(obj = __current_model, filename=r"#{fname}")|] + fmt -> errorWithoutStackTrace $ "Save format not supported: " <> show fmt
src/Text/Pandoc/Filter/Plot/Renderers/GGPlot2.hs view
@@ -1,56 +1,53 @@-{-# 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 GGPlot2 plots code blocks --} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NoImplicitPrelude #-} -module Text.Pandoc.Filter.Plot.Renderers.GGPlot2 ( - ggplot2SupportedSaveFormats - , ggplot2Command - , ggplot2Capture - , ggplot2Available -) where +-- | +-- 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 GGPlot2 plots code blocks +module Text.Pandoc.Filter.Plot.Renderers.GGPlot2 + ( ggplot2SupportedSaveFormats, + ggplot2Command, + ggplot2Capture, + ggplot2Available, + ) +where import qualified Data.Text as T -import Text.Pandoc.Filter.Plot.Renderers.Prelude - +import Text.Pandoc.Filter.Plot.Renderers.Prelude ggplot2SupportedSaveFormats :: [SaveFormat] ggplot2SupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, TIF] - ggplot2Command :: OutputSpec -> Text -> Text -ggplot2Command OutputSpec{..} exe = [st|#{exe} "#{oScriptPath}"|] - +ggplot2Command OutputSpec {..} exe = [st|#{exe} "#{oScriptPath}"|] ggplot2Available :: PlotM Bool ggplot2Available = do - mexe <- executable GGPlot2 - case mexe of - Nothing -> return False - Just (Executable dir exe) -> - commandSuccess dir [st|#{exe} -e 'if(!require("ggplot2")) {quit(status=1)}'|] - + mexe <- executable GGPlot2 + case mexe of + Nothing -> return False + Just (Executable dir exe) -> + commandSuccess dir [st|#{exe} -e 'if(!require("ggplot2")) {quit(status=1)}'|] ggplot2Capture :: FigureSpec -> FilePath -> Script -ggplot2Capture fs fp = - T.unlines [ "pdf(NULL)" -- Prevent the creation of empty Rplots.pdf - , script fs - , ggplot2CaptureFragment fs fp - ] - +ggplot2Capture fs fp = + T.unlines + [ "pdf(NULL)", -- Prevent the creation of empty Rplots.pdf + script fs, + ggplot2CaptureFragment fs fp + ] ggplot2CaptureFragment :: FigureSpec -> FilePath -> Script -ggplot2CaptureFragment FigureSpec{..} fname = [st| +ggplot2CaptureFragment FigureSpec {..} fname = + [st| library(ggplot2) # just in case ggsave("#{toRPath fname}", plot = last_plot(), dpi = #{dpi}) |]
src/Text/Pandoc/Filter/Plot/Renderers/GNUPlot.hs view
@@ -1,56 +1,52 @@-{-# 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 --} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NoImplicitPrelude #-} -module Text.Pandoc.Filter.Plot.Renderers.GNUPlot ( - gnuplotSupportedSaveFormats - , gnuplotCommand - , gnuplotCapture - , gnuplotAvailable -) where +-- | +-- 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 +import Text.Pandoc.Filter.Plot.Renderers.Prelude gnuplotSupportedSaveFormats :: [SaveFormat] gnuplotSupportedSaveFormats = [PNG, SVG, EPS, GIF, JPG, PDF] - gnuplotCommand :: OutputSpec -> Text -> Text -gnuplotCommand OutputSpec{..} exe = [st|#{exe} -c "#{oScriptPath}"|] - +gnuplotCommand OutputSpec {..} exe = [st|#{exe} -c "#{oScriptPath}"|] gnuplotAvailable :: PlotM Bool gnuplotAvailable = do - mexe <- executable GNUPlot - case mexe of - Nothing -> return False - Just (Executable dir exe) -> - commandSuccess dir [st|"#{exe}" -h|] - + mexe <- executable GNUPlot + case mexe of + Nothing -> return False + Just (Executable dir exe) -> + commandSuccess dir [st|"#{exe}" -h|] gnuplotCapture :: FigureSpec -> FilePath -> Script gnuplotCapture = prependCapture gnuplotCaptureFragment - where - prependCapture f s fp = mconcat [f s fp, "\n", script s] - + where + prependCapture f s fp = mconcat [f s fp, "\n", script s] gnuplotCaptureFragment :: FigureSpec -> FilePath -> Script -gnuplotCaptureFragment FigureSpec{..} fname = [st| +gnuplotCaptureFragment FigureSpec {..} fname = + [st| set terminal #{terminalString saveFormat} set output '#{fname}' |] - -- | Terminal name for supported save formats terminalString :: SaveFormat -> Text
src/Text/Pandoc/Filter/Plot/Renderers/Graphviz.hs view
@@ -1,50 +1,46 @@-{-# 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 Graphviz plots code blocks --} - -module Text.Pandoc.Filter.Plot.Renderers.Graphviz ( - graphvizSupportedSaveFormats - , graphvizCommand - , graphvizCapture - , graphvizAvailable -) where +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NoImplicitPrelude #-} -import Data.Char -import Text.Pandoc.Filter.Plot.Renderers.Prelude +-- | +-- 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 Graphviz plots code blocks +module Text.Pandoc.Filter.Plot.Renderers.Graphviz + ( graphvizSupportedSaveFormats, + graphvizCommand, + graphvizCapture, + graphvizAvailable, + ) +where +import Data.Char +import Text.Pandoc.Filter.Plot.Renderers.Prelude graphvizSupportedSaveFormats :: [SaveFormat] graphvizSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, WEBP, GIF] - graphvizCommand :: OutputSpec -> Text -> Text -graphvizCommand OutputSpec{..} exe = - let fmt = fmap toLower . show . saveFormat $ oFigureSpec - dpi' = dpi oFigureSpec - in [st|#{exe} -T#{fmt} -Gdpi=#{dpi'} -o "#{oFigurePath}" "#{oScriptPath}"|] - +graphvizCommand OutputSpec {..} exe = + let fmt = fmap toLower . show . saveFormat $ oFigureSpec + dpi' = dpi oFigureSpec + in [st|#{exe} -T#{fmt} -Gdpi=#{dpi'} -o "#{oFigurePath}" "#{oScriptPath}"|] graphvizAvailable :: PlotM Bool graphvizAvailable = do - mexe <- executable Graphviz - case mexe of - Nothing -> return False - Just (Executable dir exe) -> - commandSuccess dir [st|#{exe} -?|] - + mexe <- executable Graphviz + case mexe of + Nothing -> return False + Just (Executable dir exe) -> + commandSuccess dir [st|#{exe} -?|] -- Graphviz export is entirely based on command-line arguments -- so there is no need to modify the script itself. graphvizCapture :: FigureSpec -> FilePath -> Script -graphvizCapture FigureSpec{..} _ = script +graphvizCapture FigureSpec {..} _ = script
src/Text/Pandoc/Filter/Plot/Renderers/Mathematica.hs view
@@ -1,49 +1,46 @@-{-# 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 Mathematica plots code blocks --} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NoImplicitPrelude #-} -module Text.Pandoc.Filter.Plot.Renderers.Mathematica ( - mathematicaSupportedSaveFormats - , mathematicaCommand - , mathematicaCapture - , mathematicaAvailable -) where +-- | +-- 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 Mathematica plots code blocks +module Text.Pandoc.Filter.Plot.Renderers.Mathematica + ( mathematicaSupportedSaveFormats, + mathematicaCommand, + mathematicaCapture, + mathematicaAvailable, + ) +where -import Text.Pandoc.Filter.Plot.Renderers.Prelude +import Text.Pandoc.Filter.Plot.Renderers.Prelude mathematicaSupportedSaveFormats :: [SaveFormat] mathematicaSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, GIF, TIF] - mathematicaCommand :: OutputSpec -> Text -> Text -mathematicaCommand OutputSpec{..} exe = [st|#{exe} -script "#{oScriptPath}"|] - +mathematicaCommand OutputSpec {..} exe = [st|#{exe} -script "#{oScriptPath}"|] mathematicaAvailable :: PlotM Bool mathematicaAvailable = do - mexe <- executable Mathematica - case mexe of - Nothing -> return False - Just (Executable dir exe) -> - commandSuccess dir [st|#{exe} -h|] -- TODO: test this - + mexe <- executable Mathematica + case mexe of + Nothing -> return False + Just (Executable dir exe) -> + commandSuccess dir [st|#{exe} -h|] -- TODO: test this mathematicaCapture :: FigureSpec -> FilePath -> Script mathematicaCapture = appendCapture mathematicaCaptureFragment - mathematicaCaptureFragment :: FigureSpec -> FilePath -> Script -mathematicaCaptureFragment FigureSpec{..} fname = [st| +mathematicaCaptureFragment FigureSpec {..} fname = + [st| Export["#{fname}", %, #{show saveFormat}, ImageResolution -> #{dpi}] |]
src/Text/Pandoc/Filter/Plot/Renderers/Matlab.hs view
@@ -1,52 +1,47 @@-{-# 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 Matlab code blocks --} - -module Text.Pandoc.Filter.Plot.Renderers.Matlab ( - matlabSupportedSaveFormats - , matlabCommand - , matlabCapture - , matlabAvailable -) where - -import System.Directory (exeExtension) +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NoImplicitPrelude #-} -import Text.Pandoc.Filter.Plot.Renderers.Prelude +-- | +-- 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 Matlab code blocks +module Text.Pandoc.Filter.Plot.Renderers.Matlab + ( matlabSupportedSaveFormats, + matlabCommand, + matlabCapture, + matlabAvailable, + ) +where +import System.Directory (exeExtension) +import Text.Pandoc.Filter.Plot.Renderers.Prelude matlabSupportedSaveFormats :: [SaveFormat] matlabSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, GIF, TIF] - matlabCommand :: OutputSpec -> Text -> Text -matlabCommand OutputSpec{..} exe = [st|#{exe} -batch "run('#{oScriptPath}')"|] - +matlabCommand OutputSpec {..} exe = [st|#{exe} -batch "run('#{oScriptPath}')"|] -- 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, +-- Therefore, we cannot rely on this behavior to know if matlab is present, -- like other toolkits. matlabAvailable :: PlotM Bool matlabAvailable = asksConfig matlabExe >>= (\exe -> liftIO $ existsOnPath (exe <> exeExtension)) - matlabCapture :: FigureSpec -> FilePath -> Script matlabCapture = appendCapture matlabCaptureFragment - matlabCaptureFragment :: FigureSpec -> FilePath -> Script -matlabCaptureFragment FigureSpec{..} fname = [st| +matlabCaptureFragment FigureSpec {..} fname = + [st| if exist("exportgraphics")>0 exportgraphics(gcf, '#{fname}', 'Resolution', #{dpi}); else
src/Text/Pandoc/Filter/Plot/Renderers/Matplotlib.hs view
@@ -1,92 +1,87 @@-{-# 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 Matplotlib code blocks. - -Note that the MatplotlibM renderer supports two extra arguments: - * @tight_bbox=True|False@ : Make plot bounding box tight. Default is False - * @transparent=True|False@ : Make plot background transparent (perfect for web pages). Default is False. --} - -module Text.Pandoc.Filter.Plot.Renderers.Matplotlib ( - matplotlibSupportedSaveFormats - , matplotlibCommand - , matplotlibCapture - , matplotlibExtraAttrs - , matplotlibAvailable - , matplotlibCheckIfShow -) where +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NoImplicitPrelude #-} -import Text.Pandoc.Filter.Plot.Renderers.Prelude +-- | +-- 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 Matplotlib code blocks. +-- +-- Note that the MatplotlibM renderer supports two extra arguments: +-- * @tight_bbox=True|False@ : Make plot bounding box tight. Default is False +-- * @transparent=True|False@ : Make plot background transparent (perfect for web pages). Default is False. +module Text.Pandoc.Filter.Plot.Renderers.Matplotlib + ( matplotlibSupportedSaveFormats, + matplotlibCommand, + matplotlibCapture, + matplotlibExtraAttrs, + matplotlibAvailable, + matplotlibCheckIfShow, + ) +where -import qualified Data.Map.Strict as M -import Data.Monoid (Any(..)) -import qualified Data.Text as T +import qualified Data.Map.Strict as M +import Data.Monoid (Any (..)) +import qualified Data.Text as T +import Text.Pandoc.Filter.Plot.Renderers.Prelude matplotlibSupportedSaveFormats :: [SaveFormat] matplotlibSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, GIF, TIF] - matplotlibCommand :: OutputSpec -> Text -> Text -matplotlibCommand OutputSpec{..} exe = [st|#{exe} "#{oScriptPath}"|] - +matplotlibCommand OutputSpec {..} exe = [st|#{exe} "#{oScriptPath}"|] matplotlibCapture :: FigureSpec -> FilePath -> Script matplotlibCapture = appendCapture matplotlibCaptureFragment - matplotlibCaptureFragment :: FigureSpec -> FilePath -> Script -matplotlibCaptureFragment FigureSpec{..} fname = [st| +matplotlibCaptureFragment FigureSpec {..} fname = + [st| import matplotlib.pyplot as plt plt.savefig(r"#{fname}", dpi=#{dpi}, transparent=#{transparent}, bbox_inches=#{tightBox}) |] - where attrs = M.fromList extraAttrs - tight_ = readBool $ M.findWithDefault "False" "tight" attrs - transparent_ = readBool $ M.findWithDefault "False" "transparent" attrs - tightBox = if tight_ then ("'tight'"::Text) else ("None"::Text) - transparent = if transparent_ then ("True"::Text) else ("False"::Text) - + where + attrs = M.fromList extraAttrs + tight_ = readBool $ M.findWithDefault "False" "tight" attrs + transparent_ = readBool $ M.findWithDefault "False" "transparent" attrs + tightBox = if tight_ then ("'tight'" :: Text) else ("None" :: Text) + transparent = if transparent_ then ("True" :: Text) else ("False" :: Text) matplotlibExtraAttrs :: M.Map Text Text -> (M.Map Text Text) matplotlibExtraAttrs kv = M.filterWithKey (\k _ -> k `elem` ["tight_bbox", "transparent"]) kv - matplotlibAvailable :: PlotM Bool matplotlibAvailable = do - mexe <- executable Matplotlib - case mexe of - Nothing -> return False - Just (Executable dir exe) -> - commandSuccess dir [st|#{exe} -c "import matplotlib"|] - + mexe <- executable Matplotlib + case mexe of + Nothing -> return False + Just (Executable dir exe) -> + commandSuccess dir [st|#{exe} -c "import matplotlib"|] -- | Check if `matplotlib.pyplot.show()` calls are present in the script, -- which would halt pandoc-plot matplotlibCheckIfShow :: Script -> CheckResult -matplotlibCheckIfShow s = - if getAny $ mconcat showPresent - then CheckFailed "encountered a call to `matplotlib.pyplot.show`." - else CheckPassed - where - showPresent = (\n -> Any (T.isInfixOf n s)) <$> [ - "matplotlib.pyplot.show()" - , "pyplot.show()" - , "plt.show()" +matplotlibCheckIfShow s = + if getAny $ mconcat showPresent + then CheckFailed "encountered a call to `matplotlib.pyplot.show`." + else CheckPassed + where + showPresent = + (\n -> Any (T.isInfixOf n s)) + <$> [ "matplotlib.pyplot.show()", + "pyplot.show()", + "plt.show()" ] - - -- | Flexible boolean parsing readBool :: Text -> Bool -readBool s | s `elem` ["True", "true", "'True'", "'true'", "1"] = True - | s `elem` ["False", "false", "'False'", "'false'", "0"] = False - | otherwise = errorWithoutStackTrace $ unpack $ mconcat ["Could not parse '", s, "' into a boolean. Please use 'True' or 'False'"] +readBool s + | s `elem` ["True", "true", "'True'", "'true'", "1"] = True + | s `elem` ["False", "false", "'False'", "'false'", "0"] = False + | otherwise = errorWithoutStackTrace $ unpack $ mconcat ["Could not parse '", s, "' into a boolean. Please use 'True' or 'False'"]
src/Text/Pandoc/Filter/Plot/Renderers/Octave.hs view
@@ -1,50 +1,46 @@-{-# 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 Octave plots code blocks --} - -module Text.Pandoc.Filter.Plot.Renderers.Octave ( - octaveSupportedSaveFormats - , octaveCommand - , octaveCapture - , octaveAvailable -) where +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NoImplicitPrelude #-} -import Text.Pandoc.Filter.Plot.Renderers.Prelude +-- | +-- 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 Octave plots code blocks +module Text.Pandoc.Filter.Plot.Renderers.Octave + ( octaveSupportedSaveFormats, + octaveCommand, + octaveCapture, + octaveAvailable, + ) +where +import Text.Pandoc.Filter.Plot.Renderers.Prelude octaveSupportedSaveFormats :: [SaveFormat] octaveSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, GIF, TIF] - octaveCommand :: OutputSpec -> Text -> Text -octaveCommand OutputSpec{..} exe = [st|#{exe} --no-gui --no-window-system "#{oScriptPath}"|] - +octaveCommand OutputSpec {..} exe = [st|#{exe} --no-gui --no-window-system "#{oScriptPath}"|] octaveAvailable :: PlotM Bool octaveAvailable = do - mexe <- executable Octave - case mexe of - Nothing -> return False - Just (Executable dir exe) -> - commandSuccess dir [st|#{exe} -h|] - + mexe <- executable Octave + case mexe of + Nothing -> return False + Just (Executable dir exe) -> + commandSuccess dir [st|#{exe} -h|] octaveCapture :: FigureSpec -> FilePath -> Script octaveCapture = appendCapture octaveCaptureFragment - octaveCaptureFragment :: FigureSpec -> FilePath -> Script -octaveCaptureFragment _ fname = [st| +octaveCaptureFragment _ fname = + [st| saveas(gcf, '#{fname}') |]
src/Text/Pandoc/Filter/Plot/Renderers/PlotlyPython.hs view
@@ -1,62 +1,58 @@-{-# 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 Plotly-python code blocks --} - -module Text.Pandoc.Filter.Plot.Renderers.PlotlyPython ( - plotlyPythonSupportedSaveFormats - , plotlyPythonCommand - , plotlyPythonCapture - , plotlyPythonAvailable -) where +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NoImplicitPrelude #-} -import Text.Pandoc.Filter.Plot.Renderers.Prelude +-- | +-- 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 Plotly-python code blocks +module Text.Pandoc.Filter.Plot.Renderers.PlotlyPython + ( plotlyPythonSupportedSaveFormats, + plotlyPythonCommand, + plotlyPythonCapture, + plotlyPythonAvailable, + ) +where +import Text.Pandoc.Filter.Plot.Renderers.Prelude plotlyPythonSupportedSaveFormats :: [SaveFormat] plotlyPythonSupportedSaveFormats = [PNG, JPG, WEBP, PDF, SVG, EPS, HTML] - plotlyPythonCommand :: OutputSpec -> Text -> Text -plotlyPythonCommand OutputSpec{..} exe = [st|#{exe} "#{oScriptPath}"|] - +plotlyPythonCommand OutputSpec {..} exe = [st|#{exe} "#{oScriptPath}"|] plotlyPythonAvailable :: PlotM Bool plotlyPythonAvailable = do - mexe <- executable Matplotlib - case mexe of - Nothing -> return False - Just (Executable dir exe) -> - commandSuccess dir [st|#{exe} -c "import plotly.graph_objects"|] - + mexe <- executable Matplotlib + case mexe of + Nothing -> return False + Just (Executable dir exe) -> + commandSuccess dir [st|#{exe} -c "import plotly.graph_objects"|] plotlyPythonCapture :: FigureSpec -> FilePath -> Script plotlyPythonCapture = appendCapture plotlyPythonCaptureFragment - plotlyPythonCaptureFragment :: FigureSpec -> FilePath -> Script -plotlyPythonCaptureFragment FigureSpec{..} fname = [st| +plotlyPythonCaptureFragment FigureSpec {..} fname = + [st| import plotly.graph_objects as go __current_plotly_figure = next(obj for obj in globals().values() if type(obj) == go.Figure) __current_plotly_figure.#{write_method}(r"#{fname}"#{extra_args}) |] - where - -- Note: the default behaviour for HTML export is - -- to embed the entire Plotly.js content. This means - -- that the resulting file can be used completely offline - write_method = case saveFormat of - HTML -> "write_html"::Text - _ -> "write_image" - extra_args = case saveFormat of - HTML -> ", include_plotlyjs='cdn'"::Text - _ -> mempty + where + -- Note: the default behaviour for HTML export is + -- to embed the entire Plotly.js content. This means + -- that the resulting file can be used completely offline + write_method = case saveFormat of + HTML -> "write_html" :: Text + _ -> "write_image" + extra_args = case saveFormat of + HTML -> ", include_plotlyjs='cdn'" :: Text + _ -> mempty
src/Text/Pandoc/Filter/Plot/Renderers/PlotlyR.hs view
@@ -1,75 +1,71 @@-{-# 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 Plotly/R plots code blocks --} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NoImplicitPrelude #-} -module Text.Pandoc.Filter.Plot.Renderers.PlotlyR ( - plotlyRSupportedSaveFormats - , plotlyRCommand - , plotlyRCapture - , plotlyRAvailable -) where +-- | +-- 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 Plotly/R plots code blocks +module Text.Pandoc.Filter.Plot.Renderers.PlotlyR + ( plotlyRSupportedSaveFormats, + plotlyRCommand, + plotlyRCapture, + plotlyRAvailable, + ) +where import qualified Data.Text as T -import Text.Pandoc.Filter.Plot.Renderers.Prelude - +import Text.Pandoc.Filter.Plot.Renderers.Prelude plotlyRSupportedSaveFormats :: [SaveFormat] plotlyRSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, HTML] - plotlyRCommand :: OutputSpec -> Text -> Text -plotlyRCommand OutputSpec{..} exe = [st|#{exe} "#{oScriptPath}"|] - +plotlyRCommand OutputSpec {..} exe = [st|#{exe} "#{oScriptPath}"|] plotlyRAvailable :: PlotM Bool plotlyRAvailable = do - mexe <- executable PlotlyR - case mexe of - Nothing -> return False - Just (Executable dir exe) -> - commandSuccess dir [st|#{exe} -e 'if(!require("plotly")) {quit(status=1)}'|] - + mexe <- executable PlotlyR + case mexe of + Nothing -> return False + Just (Executable dir exe) -> + commandSuccess dir [st|#{exe} -e 'if(!require("plotly")) {quit(status=1)}'|] plotlyRCapture :: FigureSpec -> FilePath -> Script -plotlyRCapture fs fp = - T.unlines [ "pdf(NULL)" -- Prevent the creation of empty Rplots.pdf - , script fs - , plotlyRCaptureFragment fs fp - ] - +plotlyRCapture fs fp = + T.unlines + [ "pdf(NULL)", -- Prevent the creation of empty Rplots.pdf + script fs, + plotlyRCaptureFragment fs fp + ] plotlyRCaptureFragment :: FigureSpec -> FilePath -> Script -plotlyRCaptureFragment spec@FigureSpec{..} fname = case saveFormat of - HTML -> plotlyRCaptureHtml spec fname - _ -> plotlyRCaptureStatic spec fname - +plotlyRCaptureFragment spec@FigureSpec {..} fname = case saveFormat of + HTML -> plotlyRCaptureHtml spec fname + _ -> plotlyRCaptureStatic spec fname -- Based on the following discussion: -- https://stackoverflow.com/q/34580095 plotlyRCaptureHtml :: FigureSpec -> FilePath -> Script -plotlyRCaptureHtml _ fname = [st| +plotlyRCaptureHtml _ fname = + [st| library(plotly) # just in case library(htmlwidgets) p <- last_plot() htmlwidgets::saveWidget(as_widget(p), "#{toRPath fname}") |] - -- Based on the following documentation: -- https://plotly.com/r/static-image-export/ plotlyRCaptureStatic :: FigureSpec -> FilePath -> Script -plotlyRCaptureStatic _ fname = [st| +plotlyRCaptureStatic _ fname = + [st| library(plotly) # just in case if (!require("processx")) install.packages("processx") pdf(NULL)
src/Text/Pandoc/Filter/Plot/Renderers/Plotsjl.hs view
@@ -1,52 +1,48 @@-{-# 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 Julia/Plots.jl code blocks --} - -module Text.Pandoc.Filter.Plot.Renderers.Plotsjl ( - plotsjlSupportedSaveFormats - , plotsjlCommand - , plotsjlCapture - , plotsjlAvailable -) where +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE NoImplicitPrelude #-} -import Text.Pandoc.Filter.Plot.Renderers.Prelude +-- | +-- 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 Julia/Plots.jl code blocks +module Text.Pandoc.Filter.Plot.Renderers.Plotsjl + ( plotsjlSupportedSaveFormats, + plotsjlCommand, + plotsjlCapture, + plotsjlAvailable, + ) +where +import Text.Pandoc.Filter.Plot.Renderers.Prelude -- Save formats support by most backends -- https://docs.plotsjl.org/latest/output/#Supported-output-file-formats-1 plotsjlSupportedSaveFormats :: [SaveFormat] plotsjlSupportedSaveFormats = [PNG, SVG, PDF] - plotsjlCommand :: OutputSpec -> Text -> Text -plotsjlCommand OutputSpec{..} exe = [st|#{exe} "#{oScriptPath}"|] - +plotsjlCommand OutputSpec {..} exe = [st|#{exe} "#{oScriptPath}"|] plotsjlAvailable :: PlotM Bool plotsjlAvailable = do - mexe <- executable Plotsjl - case mexe of - Nothing -> return False - Just (Executable dir exe) -> - commandSuccess dir [st|#{exe} -e "using Plots"|] - + mexe <- executable Plotsjl + case mexe of + Nothing -> return False + Just (Executable dir exe) -> + commandSuccess dir [st|#{exe} -e "using Plots"|] plotsjlCapture :: FigureSpec -> FilePath -> Script plotsjlCapture = appendCapture plotsjlCaptureFragment - plotsjlCaptureFragment :: FigureSpec -> FilePath -> Script -plotsjlCaptureFragment _ fname = [st| +plotsjlCaptureFragment _ fname = + [st| savefig(raw"#{fname}") |]
src/Text/Pandoc/Filter/Plot/Renderers/Prelude.hs view
@@ -1,70 +1,69 @@ {-# LANGUAGE OverloadedStrings #-} -{-| -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 -Prelude for renderers, containing some helpful utilities. --} -module Text.Pandoc.Filter.Plot.Renderers.Prelude ( - - module Prelude - , module Text.Pandoc.Filter.Plot.Monad - , Text - , st - , unpack - , commandSuccess - , existsOnPath - , OutputSpec(..) - , appendCapture - , toRPath -) where - -import Data.Maybe (isJust) -import Data.Text (Text, unpack) - -import System.Directory (findExecutable) -import System.FilePath (isPathSeparator) -import System.Exit (ExitCode(..)) - -import Text.Shakespeare.Text (st) - -import Text.Pandoc.Filter.Plot.Monad +-- | +-- 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 +-- +-- Prelude for renderers, containing some helpful utilities. +module Text.Pandoc.Filter.Plot.Renderers.Prelude + ( module Prelude, + module Text.Pandoc.Filter.Plot.Monad, + Text, + st, + unpack, + commandSuccess, + existsOnPath, + OutputSpec (..), + appendCapture, + toRPath, + ) +where +import Data.Maybe (isJust) +import Data.Text (Text, unpack) +import System.Directory (findExecutable) +import System.Exit (ExitCode (..)) +import System.FilePath (isPathSeparator) +import Text.Pandoc.Filter.Plot.Monad +import Text.Shakespeare.Text (st) -- | Check that the supplied command results in -- an exit code of 0 (i.e. no errors) -commandSuccess :: FilePath -- Directory from which to run the command - -> Text -- Command to run, including the executable - -> PlotM Bool +commandSuccess :: + FilePath -> -- Directory from which to run the command + Text -> -- Command to run, including the executable + PlotM Bool commandSuccess fp s = do - (ec, _) <- runCommand fp s - return $ ec == ExitSuccess - + (ec, _) <- runCommand fp s + return $ ec == ExitSuccess -- | Checks that an executable is available on path, at all. existsOnPath :: FilePath -> IO Bool existsOnPath fp = findExecutable fp >>= fmap isJust . return - -- | A shortcut to append capture script fragments to scripts -appendCapture :: (FigureSpec -> FilePath -> Script) - -> FigureSpec -> FilePath -> Script +appendCapture :: + (FigureSpec -> FilePath -> Script) -> + FigureSpec -> + FilePath -> + Script appendCapture f s fp = mconcat [script s, "\n", f s fp] - --- | Internal description of all information +-- | Internal description of all information -- needed to output a figure. -data OutputSpec = OutputSpec - { oFigureSpec :: FigureSpec -- ^ Figure spec - , oScriptPath :: FilePath -- ^ Path to the script to render - , oFigurePath :: FilePath -- ^ Figure output path - } - +data OutputSpec = OutputSpec + { -- | Figure spec + oFigureSpec :: FigureSpec, + -- | Path to the script to render + oScriptPath :: FilePath, + -- | Figure output path + oFigurePath :: FilePath + } -- | R paths use the '/' path separator toRPath :: FilePath -> FilePath -toRPath = fmap (\c -> if isPathSeparator c then '/' else c)+toRPath = fmap (\c -> if isPathSeparator c then '/' else c)
src/Text/Pandoc/Filter/Plot/Scripting.hs view
@@ -1,151 +1,160 @@-{-# LANGUAGE OverloadedStrings #-} -{-# 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 - -Scripting --} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE TemplateHaskell #-} +-- | +-- 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 +-- +-- Scripting module Text.Pandoc.Filter.Plot.Scripting - ( ScriptResult(..) - , runTempScript - , runScriptIfNecessary - , figurePath - ) where - -import Control.Exception.Lifted (bracket) -import Control.Monad.Reader - -import Data.Hashable (hash) -import Data.Text (Text, pack, unpack) -import qualified Data.Text.IO as T - -import Paths_pandoc_plot (version) - -import System.Directory (createDirectoryIfMissing, - doesFileExist, getTemporaryDirectory) -import System.Environment (getEnv, setEnv) -import System.Exit (ExitCode (..)) -import System.FilePath (addExtension, - normalise, replaceExtension, - takeDirectory, (</>)) - -import Text.Pandoc.Filter.Plot.Renderers -import Text.Pandoc.Filter.Plot.Monad + ( ScriptResult (..), + runTempScript, + runScriptIfNecessary, + figurePath, + sourceCodePath, + ) +where +import Control.Exception.Lifted (bracket) +import Control.Monad.Reader +import Data.Default (def) +import Data.Functor.Identity (Identity (..)) +import Data.Hashable (hash) +import Data.Text (Text, pack, unpack) +import qualified Data.Text.IO as T +import Paths_pandoc_plot (version) +import System.Directory + ( createDirectoryIfMissing, + doesFileExist, + getTemporaryDirectory, + ) +import System.Environment (getEnv, setEnv) +import System.Exit (ExitCode (..)) +import System.FilePath + ( addExtension, + normalise, + replaceExtension, + takeDirectory, + (</>), + ) +import Text.Pandoc.Class (runPure) +import Text.Pandoc.Definition +import Text.Pandoc.Filter.Plot.Monad +import Text.Pandoc.Filter.Plot.Renderers +import Text.Pandoc.Filter.Plot.Scripting.Template +import Text.Pandoc.Options (WriterOptions (..)) +import Text.Pandoc.SelfContained (makeSelfContained) +import Text.Pandoc.Templates +import Text.Pandoc.Writers (writeHtml5String) -- Run script as described by the spec, only if necessary runScriptIfNecessary :: FigureSpec -> PlotM ScriptResult runScriptIfNecessary spec = do - target <- figurePath spec - liftIO $ createDirectoryIfMissing True . takeDirectory $ target - - fileAlreadyExists <- liftIO . doesFileExist $ target - result <- if fileAlreadyExists - then return ScriptSuccess - else runTempScript spec - - logScriptResult result + target <- figurePath spec + liftIO $ createDirectoryIfMissing True . takeDirectory $ target - case result of - ScriptSuccess -> do - scp <- sourceCodePath spec - liftIO $ T.writeFile scp (script spec) >> return ScriptSuccess - other -> return other + fileAlreadyExists <- liftIO . doesFileExist $ target + result <- + if fileAlreadyExists + then return ScriptSuccess + else runTempScript spec - where - logScriptResult ScriptSuccess = return () - logScriptResult r = err . pack . show $ r + logScriptResult result + case result of + ScriptSuccess -> writeSource spec >> return ScriptSuccess + other -> return other + where + logScriptResult ScriptSuccess = return () + logScriptResult r = err . pack . show $ r -- | Possible result of running a script data ScriptResult - = ScriptSuccess - | ScriptChecksFailed Text -- Message - | ScriptFailure Text Int -- Command and exit code - | ToolkitNotInstalled Toolkit -- Script failed because toolkit is not installed + = ScriptSuccess + | ScriptChecksFailed Text -- Message + | ScriptFailure Text Int -- Command and exit code + | ToolkitNotInstalled Toolkit -- Script failed because toolkit is not installed instance Show ScriptResult where - show ScriptSuccess = "Script success." - show (ScriptChecksFailed msg) = unpack $ "Script checks failed: " <> msg - show (ScriptFailure msg ec) = mconcat ["Script failed with exit code ", show ec, " and the following message: ", unpack msg] - show (ToolkitNotInstalled tk) = (show tk) <> " toolkit not installed." - + show ScriptSuccess = "Script success." + show (ScriptChecksFailed msg) = unpack $ "Script checks failed: " <> msg + show (ScriptFailure msg ec) = mconcat ["Script failed with exit code ", show ec, " and the following message: ", unpack msg] + show (ToolkitNotInstalled tk) = (show tk) <> " toolkit not installed." -- Run script as described by the spec -- Checks are performed, according to the renderer -- Note that stdout from the script is suppressed, but not -- stderr. runTempScript :: FigureSpec -> PlotM ScriptResult -runTempScript spec@FigureSpec{..} = do - let checks = scriptChecks toolkit - checkResult = mconcat $ checks <*> [script] - case checkResult of - CheckFailed msg -> return $ ScriptChecksFailed msg - CheckPassed -> do - scriptPath <- tempScriptPath spec - target <- figurePath spec - - -- Check if executable is present - -- Note that checking if the toolkit if fully configured is much more involved, - -- and so we only check if the toolkit is appropriately installed if there is - -- an error. - exe <- executable toolkit - case exe of - Nothing -> error $ "Toolkit " <> show toolkit <> " is not installed." - Just (Executable exedir exename) -> - -- Change the PATH environment variable so the appropriate executable is - -- found first - withPrependedPath exedir $ do - let scriptWithCapture = (capture toolkit) spec target +runTempScript spec@FigureSpec {..} = do + let checks = scriptChecks toolkit + checkResult = mconcat $ checks <*> [script] + case checkResult of + CheckFailed msg -> return $ ScriptChecksFailed msg + CheckPassed -> do + scriptPath <- tempScriptPath spec + target <- figurePath spec - liftIO $ T.writeFile scriptPath scriptWithCapture - let outputSpec = OutputSpec { oFigureSpec = spec - , oScriptPath = scriptPath - , oFigurePath = target - } + -- Check if executable is present + -- Note that checking if the toolkit if fully configured is much more involved, + -- and so we only check if the toolkit is appropriately installed if there is + -- an error. + exe <- executable toolkit + case exe of + Nothing -> error $ "Toolkit " <> show toolkit <> " is not installed." + Just (Executable exedir exename) -> + -- Change the PATH environment variable so the appropriate executable is + -- found first + withPrependedPath exedir $ do + let scriptWithCapture = (capture toolkit) spec target - let command_ = command toolkit outputSpec exename - -- It is important that the CWD be inherited from the - -- parent process. See #2. - cwd <- asks envCWD - (ec, _) <- runCommand cwd command_ - case ec of - ExitSuccess -> return ScriptSuccess - ExitFailure code -> do - -- Two possible types of failures: either the script - -- failed because the toolkit was not available, or - -- because of a genuine error - toolkitInstalled <- toolkitAvailable toolkit - if toolkitInstalled - then return $ ScriptFailure command_ code - else return $ ToolkitNotInstalled toolkit + liftIO $ T.writeFile scriptPath scriptWithCapture + let outputSpec = + OutputSpec + { oFigureSpec = spec, + oScriptPath = scriptPath, + oFigurePath = target + } + let command_ = command toolkit outputSpec exename + -- It is important that the CWD be inherited from the + -- parent process. See #2. + cwd <- asks envCWD + (ec, _) <- runCommand cwd command_ + case ec of + ExitSuccess -> return ScriptSuccess + ExitFailure code -> do + -- Two possible types of failures: either the script + -- failed because the toolkit was not available, or + -- because of a genuine error + toolkitInstalled <- toolkitAvailable toolkit + if toolkitInstalled + then return $ ScriptFailure command_ code + else return $ ToolkitNotInstalled toolkit -- | Determine the temp script path from Figure specifications -- Note that for certain renderers, the appropriate file extension -- is important. tempScriptPath :: FigureSpec -> PlotM FilePath -tempScriptPath FigureSpec{..} = do - let ext = scriptExtension toolkit - -- MATLAB will refuse to process files that don't start with - -- a letter - -- Note that this hash is only so that we are running scripts from unique - -- file names; it does NOT determine whether this figure should - -- be rendered or not. - let hashedPath = "pandocplot" <> (show . abs . hash $ script) <> ext - liftIO $ (</> hashedPath) <$> getTemporaryDirectory - +tempScriptPath FigureSpec {..} = do + let ext = scriptExtension toolkit + -- MATLAB will refuse to process files that don't start with + -- a letter + -- Note that this hash is only so that we are running scripts from unique + -- file names; it does NOT determine whether this figure should + -- be rendered or not. + let hashedPath = "pandocplot" <> (show . abs . hash $ script) <> ext + liftIO $ (</> hashedPath) <$> getTemporaryDirectory -- | Determine the path to the source code that generated the figure. +-- To ensure that the source code path is distinguished from HTML figures, we use the extension .src.html. sourceCodePath :: FigureSpec -> PlotM FilePath -sourceCodePath = fmap normalise . fmap (flip replaceExtension ".txt") . figurePath - +sourceCodePath = fmap normalise . fmap (flip replaceExtension ".src.html") . figurePath -- | Hash of the content of a @FigureSpec@. Note that unlike usual hashes, -- two @FigureSpec@ with the same @figureContentHash@ does not mean that they are equal! @@ -153,21 +162,23 @@ -- Not all parts of a FigureSpec are related to running code. -- For example, changing the caption should not require running the figure again. figureContentHash :: FigureSpec -> PlotM Word -figureContentHash FigureSpec{..} = do - dependenciesHash <- sequence $ fileHash <$> dependencies - -- hash looks strange because instances only exist for 7-tuples or less - return $ fromIntegral - $ hash ( (fromEnum toolkit - , script - , fromEnum saveFormat - , directory) - , ( dpi - , dependenciesHash - , extraAttrs - , show version -- Included version because capture - ) -- scripts may change between releases - ) - +figureContentHash FigureSpec {..} = do + dependenciesHash <- sequence $ fileHash <$> dependencies + -- hash looks strange because instances only exist for 7-tuples or less + return $ + fromIntegral $ + hash + ( ( fromEnum toolkit, + script, + fromEnum saveFormat, + directory + ), + ( dpi, + dependenciesHash, + extraAttrs, + show version -- Included version because capture + ) -- scripts may change between releases + ) -- | Determine the path a figure should have. -- The path for this file is unique to the content of the figure, @@ -175,11 +186,10 @@ -- be rendered again or not. figurePath :: FigureSpec -> PlotM FilePath figurePath spec = do - fh <- figureContentHash spec - let ext = extension . saveFormat $ spec - stem = flip addExtension ext . show $ fh - return $ normalise $ directory spec </> stem - + fh <- figureContentHash spec + let ext = extension . saveFormat $ spec + stem = flip addExtension ext . show $ fh + return $ normalise $ directory spec </> stem -- | Prepend a directory to the PATH environment variable for the duration -- of a computation. @@ -188,12 +198,27 @@ -- computation, the PATH environment variable will be reverted back to -- its initial value. withPrependedPath :: FilePath -> PlotM a -> PlotM a -withPrependedPath dir f = do - pathVar <- liftIO $ getEnv "PATH" - let pathVarPrepended = mconcat [dir, ";", pathVar] - bracket - ( liftIO $ setEnv "PATH" pathVarPrepended) - (\_ -> liftIO $ setEnv "PATH" pathVar) - (\_ -> f) +withPrependedPath dir f = do + pathVar <- liftIO $ getEnv "PATH" + let pathVarPrepended = mconcat [dir, ";", pathVar] + bracket + (liftIO $ setEnv "PATH" pathVarPrepended) + (\_ -> liftIO $ setEnv "PATH" pathVar) + (\_ -> f) +-- | Write the source code of a figure to an HTML file with appropriate syntax highlighting. +writeSource :: FigureSpec -> PlotM () +writeSource spec = do + scp <- sourceCodePath spec + let doc = Pandoc mempty [CodeBlock (mempty, [language (toolkit spec)], mempty) (script spec)] + renderSource = \template -> do + let opts = def {writerTemplate = Just template} + -- Note that making the document self-contained is absolutely required so that the CSS for + -- syntax highlighting is included directly in the document. + t = either (const mempty) id $ runPure $ (writeHtml5String opts doc >>= makeSelfContained) + liftIO $ T.writeFile scp t + either (err . pack) (renderSource) $ runIdentity $ compileTemplate mempty sourceTemplate + +sourceTemplate :: Text +sourceTemplate = pack $(sourceTemplate_)
+ src/Text/Pandoc/Filter/Plot/Scripting/Template.hs view
@@ -0,0 +1,17 @@+{-# LANGUAGE TemplateHaskell #-} + +module Text.Pandoc.Filter.Plot.Scripting.Template (sourceTemplate_) where + +import Data.String (fromString) +import Language.Haskell.TH.Syntax +import System.FilePath ((</>)) + +sourceTemplate_ :: Q Exp +sourceTemplate_ = do + let fp = ("data" </> "srctemplate.html") + qAddDependentFile fp + d <- runIO $ readFile fp + strToExp d + where + strToExp :: String -> Q Exp + strToExp s = return $ VarE 'fromString `AppE` LitE (StringL s)
stack.yaml view
@@ -1,4 +1,4 @@-resolver: nightly-2020-10-11 # GHC 8.10.2 +resolver: nightly-2020-12-14 # GHC 8.10.2 packages: - . @@ -8,15 +8,18 @@ compiler: ghc-8.10.1 extra-deps: -- pandoc-2.11 -- citeproc-0.1@sha256:0c82d6cf5be3d82d4f755e6a2075ce224b9f2bdb891f1417ca49f5e78ac4e26a,5355 -- commonmark-0.1.0.2@sha256:fbff7a2ade0ce7d699964a87f765e503a3a9e22542c05f0f02ba7aad64e38af4,3278 -- commonmark-extensions-0.2.0.1@sha256:647aa8dba5fd46984ddedc15c3693c9c4d9655503d42006576bd8f0dadf8cd39,3176 +- pandoc-2.11.3.1 +- citeproc-0.3.0.1@sha256:1860a6757d5bc3ec149a434ed4011edb30155cb6ae92a1d83ccf3bd163bca0f1,5590 +- commonmark-0.1.1.2@sha256:c06ab05f0f224ab7982502a96e17952823a9b6dae8505fb35194b0baa9e2a975,3278 +- commonmark-extensions-0.2.0.4@sha256:6a437bcfa3c757af4262b71336513619990eafb5cfdc33e57a499c93ad225608,3184 - commonmark-pandoc-0.2.0.1@sha256:529c6e2c6cabf61558b66a28123eafc1d90d3324be29819f59f024e430312c1f,1105 +- doctemplates-0.9@sha256:eb0bf957ba8571746f36f07ceca43d311a0e390a9fdcfde6909f9f9be85b3b28,3125 +- skylighting-0.10.2@sha256:9c395703ca61752f4491044443e87d5b87ed94bca78c0a00b5ebf4e4b8e59904,10031 +- skylighting-core-0.10.2@sha256:5070d9f08135cdb7c935c5936a4a76a9e1922ec846cd40d6e2837f42e7a9f29f,8159 # For development - git: https://github.com/owickstrom/pandoc-include-code.git - commit: d5cbdc86eb1abcd9561c0965ac423d7011378ddb + commit: 89c8465549960872257da993a399d978a269d6a6 - ghc-check-0.3.0.1@sha256:651d9b2b75a5cbdb3c942117801bfa566079bcaf20360e4d0959f9e575c30b39,1070 # Faster compilation.
tests/Common.hs view
@@ -1,355 +1,384 @@-{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} module Common where -import Control.Monad (unless, when) - -import Data.List (isInfixOf, isSuffixOf) -import Data.Monoid ((<>)) -import Data.String (fromString) -import Data.Text (Text, pack, unpack) -import qualified Data.Text as T - -import Test.Tasty -import Test.Tasty.HUnit - -import Text.Pandoc.Filter.Plot -import Text.Pandoc.Filter.Plot.Internal - -import qualified Text.Pandoc.Builder as B -import qualified Text.Pandoc.Definition as B -import Text.Pandoc.JSON - -import System.Directory (createDirectory, - createDirectoryIfMissing, - doesDirectoryExist, - doesFileExist, listDirectory, - removeDirectoryRecursive, - removePathForcibly, - getTemporaryDirectory) -import System.FilePath (takeExtensions, (</>)) - +import Control.Monad (unless, when) +import Data.List (isInfixOf, isSuffixOf, (!!)) +import Data.Monoid ((<>)) +import Data.String (fromString) +import Data.Text (Text, pack, unpack) +import qualified Data.Text as T +import System.Directory + ( createDirectory, + createDirectoryIfMissing, + doesDirectoryExist, + doesFileExist, + getTemporaryDirectory, + listDirectory, + removeDirectoryRecursive, + removePathForcibly, + ) +import System.FilePath (takeExtensions, (</>)) +import Test.Tasty +import Test.Tasty.HUnit +import qualified Text.Pandoc.Builder as B +import qualified Text.Pandoc.Definition as B +import Text.Pandoc.Filter.Plot +import Text.Pandoc.Filter.Plot.Internal +import Text.Pandoc.JSON defaultTestConfig :: Configuration -defaultTestConfig = defaultConfiguration { logVerbosity = Silent - , logSink = StdErr - } +defaultTestConfig = + defaultConfiguration + { logVerbosity = Silent, + logSink = StdErr + } ------------------------------------------------------------------------------- -- 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 - let postfix = unpack . cls $ tk - tempDir <- (</> "test-file-creation-" <> postfix) <$> getTemporaryDirectory - ensureDirectoryExistsAndEmpty tempDir - - let cb = (addDirectory tempDir $ codeBlock tk (trivialContent tk)) - _ <- runPlotM defaultTestConfig $ make cb - filesCreated <- length <$> listDirectory tempDir - assertEqual "" 2 filesCreated + testCase "writes output files in appropriate directory" $ do + let postfix = unpack . cls $ tk + tempDir <- (</> "test-file-creation-" <> postfix) <$> getTemporaryDirectory + ensureDirectoryExistsAndEmpty tempDir + let cb = (addDirectory tempDir $ codeBlock tk (trivialContent tk)) + _ <- runPlotM defaultTestConfig $ make cb + filesCreated <- length <$> listDirectory tempDir + assertEqual "" 2 filesCreated ------------------------------------------------------------------------------- -- Test that plot files and source files are created when the filter is run -- and the path of the files involves spaces. See Issue #2 testFileCreationPathWithSpaces :: Toolkit -> TestTree testFileCreationPathWithSpaces tk = - testCase "writes output files in appropriate directory (with spaces)" $ do - let postfix = unpack . cls $ tk - tempDir <- (</> "test-file-creation-with-spaces- -" <> postfix) <$> getTemporaryDirectory - ensureDirectoryExistsAndEmpty tempDir - - let cb = (addDirectory tempDir $ codeBlock tk (trivialContent tk)) - _ <- runPlotM defaultTestConfig $ make cb - filesCreated <- length <$> listDirectory tempDir - assertEqual "" 2 filesCreated + testCase "writes output files in appropriate directory (with spaces)" $ do + let postfix = unpack . cls $ tk + tempDir <- (</> "test-file-creation-with-spaces- -" <> postfix) <$> getTemporaryDirectory + ensureDirectoryExistsAndEmpty tempDir + let cb = (addDirectory tempDir $ codeBlock tk (trivialContent tk)) + _ <- runPlotM defaultTestConfig $ make cb + filesCreated <- length <$> listDirectory tempDir + assertEqual "" 2 filesCreated ------------------------------------------------------------------------------- -- Test that included files are found within the source testFileInclusion :: Toolkit -> TestTree testFileInclusion tk = - testCase "includes plot inclusions" $ do - let postfix = unpack . cls $ tk - tempDir <- (</> "test-file-inclusion-" <> postfix) <$> getTemporaryDirectory - ensureDirectoryExistsAndEmpty tempDir + testCase "includes plot inclusions" $ do + let postfix = unpack . cls $ tk + tempDir <- (</> "test-file-inclusion-" <> postfix) <$> getTemporaryDirectory + ensureDirectoryExistsAndEmpty tempDir - let cb = (addPreamble (include tk) $ - addDirectory tempDir $ codeBlock tk (trivialContent tk)) - _ <- runPlotM defaultTestConfig $ make cb - inclusion <- readFile (include tk) - sourcePath <- head . filter (isExtensionOf "txt") <$> listDirectory tempDir - src <- readFile (tempDir </> sourcePath) - assertIsInfix inclusion src - where - include Matplotlib = "tests/includes/matplotlib.py" - include PlotlyPython = "tests/includes/plotly-python.py" - include PlotlyR = "tests/includes/plotly-r.r" - include Matlab = "tests/includes/matlabplot.m" - include Mathematica = "tests/includes/mathplot.m" - include Octave = "tests/includes/octave.m" - include GGPlot2 = "tests/includes/ggplot2.r" - include GNUPlot = "tests/includes/gnuplot.gp" - include Graphviz = "tests/includes/graphviz.dot" - include Bokeh = "tests/includes/bokeh.py" - include Plotsjl = "tests/includes/plotsjl.jl" + let cb = + ( addPreamble (include tk) $ + addDirectory tempDir $ codeBlock tk (trivialContent tk) + ) + _ <- runPlotM defaultTestConfig $ make cb + inclusion <- readFile (include tk) + sourcePath <- head . filter (isExtensionOf ".src.html") <$> listDirectory tempDir + src <- readFile (tempDir </> sourcePath) + assertIsInfix inclusion src + where + include Matplotlib = "tests/includes/matplotlib.py" + include PlotlyPython = "tests/includes/plotly-python.py" + include PlotlyR = "tests/includes/plotly-r.r" + include Matlab = "tests/includes/matlabplot.m" + include Mathematica = "tests/includes/mathplot.m" + include Octave = "tests/includes/octave.m" + include GGPlot2 = "tests/includes/ggplot2.r" + include GNUPlot = "tests/includes/gnuplot.gp" + include Graphviz = "tests/includes/graphviz.dot" + include Bokeh = "tests/includes/bokeh.py" + include Plotsjl = "tests/includes/plotsjl.jl" ------------------------------------------------------------------------------- -- Test that the files are saved in the appropriate format testSaveFormat :: Toolkit -> TestTree testSaveFormat tk = - testCase "saves in the appropriate format" $ do - let postfix = unpack . cls $ tk - tempDir <- (</> "test-safe-format-" <> postfix) <$> getTemporaryDirectory - ensureDirectoryExistsAndEmpty tempDir - let fmt = head (supportedSaveFormats tk) - cb = (addSaveFormat fmt $ - addDirectory tempDir $ codeBlock tk (trivialContent tk)) - _ <- runPlotM defaultTestConfig $ make cb - numberjpgFiles <- - length <$> filter (isExtensionOf (extension fmt)) <$> - listDirectory tempDir - assertEqual "" numberjpgFiles 1 + testCase "saves in the appropriate format" $ do + let postfix = unpack . cls $ tk + tempDir <- (</> "test-safe-format-" <> postfix) <$> getTemporaryDirectory + ensureDirectoryExistsAndEmpty tempDir + let fmt = head (supportedSaveFormats tk) + cb = + ( addSaveFormat fmt $ + addDirectory tempDir $ codeBlock tk (trivialContent tk) + ) + _ <- runPlotM defaultTestConfig $ make 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) <$> getTemporaryDirectory - ensureDirectoryExistsAndEmpty tempDir + testCase "appropriately omits links to source code" $ do + let postfix = unpack . cls $ tk + tempDir <- (</> "test-caption-links-" <> postfix) <$> getTemporaryDirectory + 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 <- runPlotM defaultTestConfig $ make noSource - blockWithSource <- runPlotM defaultTestConfig $ make withSource + 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 <- runPlotM defaultTestConfig $ make noSource + blockWithSource <- runPlotM defaultTestConfig $ make 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) + -- 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 - where - extractCaption (B.Para blocks) = extractImageCaption . head $ blocks - extractCaption _ = mempty + extractImageCaption (Image _ c _) = c + extractImageCaption _ = mempty - extractImageCaption (Image _ c _) = c - extractImageCaption _ = mempty +------------------------------------------------------------------------------- +-- Test that it is possible to change the source code label in captions +testSourceLabel :: Toolkit -> TestTree +testSourceLabel tk = + testCase "appropriately changes the source code label" $ do + let postfix = unpack . cls $ tk + tempDir <- (</> "test-source-label-" <> postfix) <$> getTemporaryDirectory + ensureDirectoryExistsAndEmpty tempDir + -- Note that this test requires that the actual caption be empty + -- so that the caption is only the source code label + let withSource = + addWithSource True $ + addDirectory tempDir $ + addCaption mempty $ -- This test requires that the actual caption be empty + codeBlock tk (trivialContent tk) + blockWithSource <- runPlotM defaultTestConfig {sourceCodeLabel = "Test label"} $ make withSource + + -- The caption will look like [Space, Str "(", Link ... ]. Hence, we skip the first elements with (!!) + let resultCaption = linkLabel $ (!! 2) . B.toList $ extractCaption blockWithSource + assertEqual "" (B.str "Test label") resultCaption + where + extractCaption (B.Para blocks) = extractImageCaption . head $ blocks + extractCaption _ = mempty + + extractImageCaption (Image _ c _) = B.fromList c + extractImageCaption _ = mempty + + linkLabel (B.Link _ ils _) = B.fromList ils + linkLabel _ = 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) <$> getTemporaryDirectory - ensureDirectoryExistsAndEmpty tempDir - - let config = defaultTestConfig { defaultDirectory = tempDir - , defaultSaveFormat = JPG} +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) <$> getTemporaryDirectory + ensureDirectoryExistsAndEmpty tempDir - -- Not all toolkits support both save formats - when ( JPG `elem` supportedSaveFormats tk - && PNG `elem` supportedSaveFormats tk - ) $ do + let config = + defaultTestConfig + { defaultDirectory = tempDir, + defaultSaveFormat = JPG + } - let cb = addDirectory tempDir - $ addSaveFormat PNG - $ codeBlock tk (trivialContent tk) - _ <- runPlotM config $ make cb + -- 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) + _ <- runPlotM config $ make 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 + 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 bold formatting in captions is correctly rendered testMarkdownFormattingCaption1 :: Toolkit -> TestTree testMarkdownFormattingCaption1 tk = - testCase "appropriately parses captions 1" $ do - let postfix = unpack . cls $ tk - tempDir <- (</> "test-caption-parsing1-" <> postfix) <$> getTemporaryDirectory - ensureDirectoryExistsAndEmpty tempDir + testCase "appropriately parses captions 1" $ do + let postfix = unpack . cls $ tk + tempDir <- (</> "test-caption-parsing1-" <> postfix) <$> getTemporaryDirectory + 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" - result <- runPlotM (defaultTestConfig {captionFormat=fmt}) $ make cb - assertIsInfix expected (extractCaption result) - where - extractCaption (B.Para blocks) = extractImageCaption . head $ blocks - extractCaption _ = mempty + -- 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" + result <- runPlotM (defaultTestConfig {captionFormat = fmt}) $ make cb + assertIsInfix expected (extractCaption result) + where + extractCaption (B.Para blocks) = extractImageCaption . head $ blocks + extractCaption _ = mempty - extractImageCaption (Image _ c _) = c - extractImageCaption _ = mempty + extractImageCaption (Image _ c _) = c + extractImageCaption _ = mempty ------------------------------------------------------------------------------- -- Test that Markdown bold formatting in captions is correctly rendered testMarkdownFormattingCaption2 :: Toolkit -> TestTree testMarkdownFormattingCaption2 tk = - testCase "appropriately parses captions 2" $ do - let postfix = unpack . cls $ tk - tempDir <- (</> "test-caption-parsing2-" <> postfix) <$> getTemporaryDirectory - ensureDirectoryExistsAndEmpty tempDir - - -- Note that this test is fragile, in the sense that the expected result must be carefully - -- constructed - let expected = [Link ("",[],[]) [Str "title"] ("https://google.com","")] - cb = addDirectory tempDir - $ addCaption "[title](https://google.com)" - $ codeBlock tk (trivialContent tk) - fmt = B.Format "markdown" - result <- runPlotM (defaultTestConfig {captionFormat=fmt}) $ make cb - assertIsInfix expected (extractCaption result) - where - extractCaption (B.Para blocks) = extractImageCaption . head $ blocks - extractCaption _ = mempty + testCase "appropriately parses captions 2" $ do + let postfix = unpack . cls $ tk + tempDir <- (</> "test-caption-parsing2-" <> postfix) <$> getTemporaryDirectory + ensureDirectoryExistsAndEmpty tempDir - extractImageCaption (Image _ c _) = c - extractImageCaption _ = mempty + -- Note that this test is fragile, in the sense that the expected result must be carefully + -- constructed + let expected = [Link ("", [], []) [Str "title"] ("https://google.com", "")] + cb = + addDirectory tempDir $ + addCaption "[title](https://google.com)" $ + codeBlock tk (trivialContent tk) + fmt = B.Format "markdown" + result <- runPlotM (defaultTestConfig {captionFormat = fmt}) $ make cb + assertIsInfix expected (extractCaption result) + where + extractCaption (B.Para blocks) = extractImageCaption . head $ blocks + extractCaption _ = mempty + extractImageCaption (Image _ c _) = c + extractImageCaption _ = mempty ------------------------------------------------------------------------------- -- Test that cleanOutpuDirs correctly cleans the output directory specified in a block. testCleanOutputDirs :: Toolkit -> TestTree -testCleanOutputDirs tk = - testCase "correctly cleans output directories" $ do - let postfix = unpack . cls $ tk - tempDir <- (</> "test-clean-output-dir" <> postfix) <$> getTemporaryDirectory - ensureDirectoryExistsAndEmpty tempDir +testCleanOutputDirs tk = + testCase "correctly cleans output directories" $ do + let postfix = unpack . cls $ tk + tempDir <- (</> "test-clean-output-dir" <> postfix) <$> getTemporaryDirectory + ensureDirectoryExistsAndEmpty tempDir - let cb = addDirectory tempDir - $ codeBlock tk (trivialContent tk) - - result <- runPlotM defaultTestConfig $ make cb - cleanedDirs <- cleanOutputDirs defaultTestConfig cb + let cb = + addDirectory tempDir $ + codeBlock tk (trivialContent tk) - assertEqual "" [tempDir] cleanedDirs + result <- runPlotM defaultTestConfig $ make cb + cleanedDirs <- cleanOutputDirs defaultTestConfig cb - outputDirExists <- doesDirectoryExist tempDir - assertEqual "" outputDirExists False + assertEqual "" [tempDir] cleanedDirs + outputDirExists <- doesDirectoryExist tempDir + assertEqual "" outputDirExists False ------------------------------------------------------------------------------- -- Test that toolkit checks failed when appropriate. testChecksFail :: Toolkit -> TestTree -testChecksFail tk = - testCase "script checks fail when appropriate" $ do - assertChecksFail tk - where - assertChecksFail Matplotlib = do - let postfix = unpack . cls $ tk - tempDir <- (</> "test-checks" <> postfix) <$> getTemporaryDirectory - ensureDirectoryExistsAndEmpty tempDir - - let cb = addDirectory tempDir $ codeBlock Matplotlib "plt.show()" - result <- runPlotM defaultTestConfig $ makeEither cb - let expectedCheck :: Either PandocPlotError a -> Bool - expectedCheck (Left (ScriptChecksFailedError _)) = True - expectedCheck _ = False - assertBool "" (expectedCheck result) - - assertChecksFail _ = assertEqual "Test skipped" True True +testChecksFail tk = + testCase "script checks fail when appropriate" $ do + assertChecksFail tk + where + assertChecksFail Matplotlib = do + let postfix = unpack . cls $ tk + tempDir <- (</> "test-checks" <> postfix) <$> getTemporaryDirectory + ensureDirectoryExistsAndEmpty tempDir + let cb = addDirectory tempDir $ codeBlock Matplotlib "plt.show()" + result <- runPlotM defaultTestConfig $ makeEither cb + let expectedCheck :: Either PandocPlotError a -> Bool + expectedCheck (Left (ScriptChecksFailedError _)) = True + expectedCheck _ = False + assertBool "" (expectedCheck result) + assertChecksFail _ = assertEqual "Test skipped" True True codeBlock :: Toolkit -> Script -> Block codeBlock tk script = CodeBlock (mempty, [cls tk], mempty) script - trivialContent :: Toolkit -> Script -trivialContent Matplotlib = "import matplotlib.pyplot as plt\n" +trivialContent Matplotlib = "import matplotlib.pyplot as plt\n" trivialContent PlotlyPython = "import plotly.graph_objects as go; fit = go.Figure()\n" -trivialContent PlotlyR = "library(plotly)\nfig <- plot_ly(midwest, x = ~percollege, color = ~state, type = \"box\")" -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)" -trivialContent Graphviz = "digraph {A -> B [label=\"test\"];}" -trivialContent Bokeh = T.unlines [ "from bokeh.plotting import figure" - , "p = figure(title='simple line example')" - , "p.line([1,2,3,4], [5,6,7,8])" - ] -trivialContent Plotsjl = "using Plots; x = 1:10; y = rand(10); plot(x, y);" - +trivialContent PlotlyR = "library(plotly)\nfig <- plot_ly(midwest, x = ~percollege, color = ~state, type = \"box\")" +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)" +trivialContent Graphviz = "digraph {A -> B [label=\"test\"];}" +trivialContent Bokeh = + T.unlines + [ "from bokeh.plotting import figure", + "p = figure(title='simple line example')", + "p.line([1,2,3,4], [5,6,7,8])" + ] +trivialContent Plotsjl = "using Plots; x = 1:10; y = rand(10); plot(x, y);" addCaption :: String -> Block -> Block addCaption caption (CodeBlock (id', cls, attrs) script) = - CodeBlock (id', cls, attrs ++ [(tshow CaptionK, pack caption)]) script - + CodeBlock (id', cls, attrs ++ [(tshow CaptionK, pack caption)]) script addDirectory :: FilePath -> Block -> Block addDirectory dir (CodeBlock (id', cls, attrs) script) = - CodeBlock (id', cls, attrs ++ [(tshow DirectoryK, pack dir)]) script - + CodeBlock (id', cls, attrs ++ [(tshow DirectoryK, pack dir)]) script addPreamble :: FilePath -> Block -> Block addPreamble inclusionPath (CodeBlock (id', cls, attrs) script) = - CodeBlock (id', cls, attrs ++ [(tshow PreambleK, pack inclusionPath)]) script - + CodeBlock (id', cls, attrs ++ [(tshow PreambleK, pack inclusionPath)]) script addSaveFormat :: SaveFormat -> Block -> Block addSaveFormat saveFormat (CodeBlock (id', cls, attrs) script) = - CodeBlock (id', cls, attrs ++ [(tshow SaveFormatK, pack . extension $ saveFormat)]) script - + CodeBlock (id', cls, attrs ++ [(tshow SaveFormatK, pack . extension $ saveFormat)]) script addDPI :: Int -> Block -> Block addDPI dpi (CodeBlock (id', cls, attrs) script) = - CodeBlock (id', cls, attrs ++ [(tshow DpiK, pack . show $ dpi)]) script - + CodeBlock (id', cls, attrs ++ [(tshow DpiK, pack . show $ dpi)]) script addWithSource :: Bool -> Block -> Block addWithSource yn (CodeBlock (id', cls, attrs) script) = - CodeBlock (id', cls, attrs ++ [(tshow WithSourceK, pack . show $ yn)]) script - + CodeBlock (id', cls, attrs ++ [(tshow WithSourceK, pack . show $ yn)]) script -- | Assert that a file exists assertFileExists :: HasCallStack => FilePath -> Assertion assertFileExists filepath = do - fileExists <- doesFileExist filepath - unless fileExists (assertFailure msg) + fileExists <- doesFileExist filepath + unless fileExists (assertFailure msg) where 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]) - +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 isExtensionOf :: String -> FilePath -> Bool -isExtensionOf ext@('.':_) = isSuffixOf ext . takeExtensions -isExtensionOf ext = isSuffixOf ('.':ext) . takeExtensions - +isExtensionOf ext@('.' : _) = isSuffixOf ext . takeExtensions +isExtensionOf ext = isSuffixOf ('.' : ext) . takeExtensions -- | Assert that the first list is contained, -- wholly and intact, anywhere within the second. @@ -358,16 +387,14 @@ where msg = mconcat ["Expected ", show xs, " to be an infix of ", show ys] - -- Ensure a directory is empty but exists. ensureDirectoryExistsAndEmpty :: FilePath -> IO () ensureDirectoryExistsAndEmpty dir = do - exists <- doesDirectoryExist dir - if exists - then removePathForcibly dir - else return () - createDirectory dir - + exists <- doesDirectoryExist dir + if exists + then removePathForcibly dir + else return () + createDirectory dir tshow :: Show a => a -> Text tshow = pack . show
tests/Main.hs view
@@ -1,100 +1,98 @@-{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} -import Control.Monad (forM_) - -import qualified Data.Map.Strict as Map -import Data.Text (Text, unpack) -import qualified Data.Text as T - -import Test.Tasty -import Test.Tasty.HUnit - -import qualified Text.Pandoc.Builder as B -import qualified Text.Pandoc.Definition as B -import Text.Pandoc.JSON - -import Common -import Text.Pandoc.Filter.Plot -import Text.Pandoc.Filter.Plot.Internal +import Common +import Control.Monad (forM_) +import qualified Data.Map.Strict as Map +import Data.Text (Text, unpack) +import qualified Data.Text as T +import Test.Tasty +import Test.Tasty.HUnit +import qualified Text.Pandoc.Builder as B +import qualified Text.Pandoc.Definition as B +import Text.Pandoc.Filter.Plot +import Text.Pandoc.Filter.Plot.Internal +import Text.Pandoc.JSON main :: IO () main = do - available <- availableToolkits defaultTestConfig - unavailable <- unavailableToolkits defaultTestConfig - forM_ unavailable $ \tk -> do - putStrLn $ show tk <> " is not available. Its tests will be skipped." - - defaultMain $ testGroup "All tests" - [ testGroup - "Configuration tests" - [ testEmptyConfiguration - , testExampleConfiguration - , testConfigurationPathMeta - ] - , testGroup - "Parsing tests" - [ testCaptionReader ] - , testGroup - "HTML embedding tests" - [ testHtmlBodyEmbedding - , testHtmlEmbedding - ] - , testGroup - "Toolkit tests" - (toolkitSuite <$> available) - ] + available <- availableToolkits defaultTestConfig + unavailable <- unavailableToolkits defaultTestConfig + forM_ unavailable $ \tk -> do + putStrLn $ show tk <> " is not available. Its tests will be skipped." + defaultMain $ + testGroup + "All tests" + [ testGroup + "Configuration tests" + [ testEmptyConfiguration, + testExampleConfiguration, + testConfigurationPathMeta + ], + testGroup + "Parsing tests" + [testCaptionReader], + testGroup + "HTML embedding tests" + [ testHtmlBodyEmbedding, + testHtmlEmbedding + ], + testGroup + "Toolkit tests" + (toolkitSuite <$> available) + ] -- | Suite of tests that every renderer should pass toolkitSuite :: Toolkit -> TestTree toolkitSuite tk = - testGroup (show tk) $ - [ testFileCreation - , testFileCreationPathWithSpaces - , testFileInclusion - , testSaveFormat - , testWithSource - , testOverrideConfiguration - , testMarkdownFormattingCaption1 - , testMarkdownFormattingCaption2 - , testCleanOutputDirs - , testChecksFail - ] <*> [tk] - + testGroup (show tk) $ + [ testFileCreation, + testFileCreationPathWithSpaces, + testFileInclusion, + testSaveFormat, + testWithSource, + testSourceLabel, + testOverrideConfiguration, + testMarkdownFormattingCaption1, + testMarkdownFormattingCaption2, + testCleanOutputDirs, + testChecksFail + ] + <*> [tk] testEmptyConfiguration :: TestTree -testEmptyConfiguration = - testCase "empty configuration is correctly parsed to default values" $ do - let config = defaultConfiguration - - parsedConfig <- configuration "tests/fixtures/.empty-config.yml" - assertEqual "" config parsedConfig +testEmptyConfiguration = + testCase "empty configuration is correctly parsed to default values" $ do + let config = defaultConfiguration + parsedConfig <- configuration "tests/fixtures/.empty-config.yml" + assertEqual "" config parsedConfig -- The example configuration is build by hand (to add comments) --- and it is embedded into the executable. Therefore, we must make sure it +-- and it is embedded into the executable. Therefore, we must make sure it -- is correctly parsed (and is therefore valid.) testExampleConfiguration :: TestTree -testExampleConfiguration = - testCase "example configuration is correctly parsed" $ do - -- The example config reflects the Windows default - -- Therefore, we need to test against the Windows default, - -- even on other OSes - let config = defaultConfiguration { matplotlibExe = "python" - , plotlyPythonExe = "python" - , bokehExe = "python" - , defaultDependencies = ["file1.txt", "file2.txt"] - } - - parsedConfig <- configuration "example-config.yml" - assertEqual "" config parsedConfig +testExampleConfiguration = + testCase "example configuration is correctly parsed" $ do + -- The example config reflects the Windows default + -- Therefore, we need to test against the Windows default, + -- even on other OSes + let config = + defaultConfiguration + { matplotlibExe = "python", + plotlyPythonExe = "python", + bokehExe = "python", + defaultDependencies = ["file1.txt", "file2.txt"] + } + parsedConfig <- configuration "example-config.yml" + assertEqual "" config parsedConfig -- Test that the path to configuration in metadata is found correctly testConfigurationPathMeta :: TestTree -testConfigurationPathMeta = - testCase "Configuration path stored in metadata is correctly parsed" $ do +testConfigurationPathMeta = + testCase "Configuration path stored in metadata is correctly parsed" $ do let configPath = "tests/fixtures/.config-meta.yml" meta = B.Meta $ Map.fromList [("plot-configuration", B.MetaString configPath)] @@ -102,58 +100,59 @@ expected <- configuration (unpack configPath) assertEqual "" expected parsedConfig - testCaptionReader :: TestTree -testCaptionReader = - testCase "caption is parsed in the same way as input document format" $ do - -- Note that this test is fragile, in the sense that the expected result must be carefully - -- constructed - let caption="Here is a [link](https://www.google.com) in a caption." - expected = Just $ [Str "Here",Space,Str "is",Space,Str "a",Space,Link ("",[],[]) [Str "link"] ("https://www.google.com",""),Space,Str "in",Space,Str "a",Space,Str "caption."] - fmt = B.Format "markdown+tex_math_dollars" - parsed = captionReader fmt caption - - assertEqual "" expected parsed +testCaptionReader = + testCase "caption is parsed in the same way as input document format" $ do + -- Note that this test is fragile, in the sense that the expected result must be carefully + -- constructed + let caption = "Here is a [link](https://www.google.com) in a caption." + expected = Just $ [Str "Here", Space, Str "is", Space, Str "a", Space, Link ("", [], []) [Str "link"] ("https://www.google.com", ""), Space, Str "in", Space, Str "a", Space, Str "caption."] + fmt = B.Format "markdown+tex_math_dollars" + parsed = captionReader fmt caption + assertEqual "" expected parsed testHtmlBodyEmbedding :: TestTree -testHtmlBodyEmbedding = - testCase "HTML body can be extracted from file" $ do - let html=T.unlines ["<!DOCTYPE html>" - ,"<html lang=\"en\">" - ," <head>" - ," <meta charset=\"utf-8\">" - ," <title>title</title>" - ," <link rel=\"stylesheet\" href=\"style.css\">" - ," </head>" - ," <body>" - ," <p>Hello</p>" - ," </body>" - ,"</html>" - ] - extracted = extractPlot html - expected = "\n <p>Hello</p>" - - assertEqual "" expected extracted +testHtmlBodyEmbedding = + testCase "HTML body can be extracted from file" $ do + let html = + T.unlines + [ "<!DOCTYPE html>", + "<html lang=\"en\">", + " <head>", + " <meta charset=\"utf-8\">", + " <title>title</title>", + " <link rel=\"stylesheet\" href=\"style.css\">", + " </head>", + " <body>", + " <p>Hello</p>", + " </body>", + "</html>" + ] + extracted = extractPlot html + expected = "\n <p>Hello</p>" + assertEqual "" expected extracted testHtmlEmbedding :: TestTree -testHtmlEmbedding = - testCase "HTML body and head scripts can be extracted from file" $ do - let html=T.unlines ["<!DOCTYPE html>" - ,"<html lang=\"en\">" - ," <head>" - ," <meta charset=\"utf-8\">" - ," <title>title</title>" - ," <link rel=\"stylesheet\" href=\"style.css\">" - ," <script src=\"script.js\"></script>" - ," </head>" - ," <body>" - ," <p>Hello</p>" - ," </body>" - ,"</html>" - ] - extracted = extractPlot html - expected = "<script src=\"script.js\" defer></script>\n <p>Hello</p>" +testHtmlEmbedding = + testCase "HTML body and head scripts can be extracted from file" $ do + let html = + T.unlines + [ "<!DOCTYPE html>", + "<html lang=\"en\">", + " <head>", + " <meta charset=\"utf-8\">", + " <title>title</title>", + " <link rel=\"stylesheet\" href=\"style.css\">", + " <script src=\"script.js\"></script>", + " </head>", + " <body>", + " <p>Hello</p>", + " </body>", + "</html>" + ] + extracted = extractPlot html + expected = "<script src=\"script.js\" defer></script>\n <p>Hello</p>" - assertEqual "" expected extracted+ assertEqual "" expected extracted