Skip to content

Commit

Permalink
Merge pull request #47 from LaurentRDC/specifying-executable
Browse files Browse the repository at this point in the history
Overhauled executable handling
  • Loading branch information
LaurentRDC authored May 12, 2022
2 parents f24a05d + 4d7e487 commit f0af92a
Show file tree
Hide file tree
Showing 25 changed files with 292 additions and 483 deletions.
11 changes: 11 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,17 @@ jobs:
exit 1
fi
pandoc-plot clean tests/issue30.md
# The idea here is to install some random package (npstreams) to
# check whether the plots will be rendered in the appropriate
# environment
python -m venv ./issue46
./issue46/bin/python -m pip install npstreams matplotlib
pandoc --filter pandoc-plot -i tests/issue46.md -t native
if [ $(ls "plots" | wc -l) != 2 ]; then
exit 1
fi
pandoc-plot clean tests/issue46.md
- name: Build documentation
run: source tools/mkmanual.sh
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

pandoc-plot uses [Semantic Versioning](http://semver.org/spec/v2.0.0.html)

## Release 1.5.2

* Overhauled the way executables are handled. This fixes an issue where executables specified in documents (rather than configuration) were ignored (#46).

## Release 1.5.1

* Figures with no captions (and no link to the source script), will now be shown as an image, without figure numbering (#37).
Expand Down
3 changes: 1 addition & 2 deletions cabal.project
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
packages: pandoc-plot.cabal
allow-newer: all
packages: pandoc-plot.cabal
11 changes: 5 additions & 6 deletions executable/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ module Main where

import Control.Monad (join, msum, void, 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)
Expand Down Expand Up @@ -58,15 +57,15 @@ import Text.Pandoc.Filter.Plot
plotFilter,
)
import Text.Pandoc.Filter.Plot.Internal
( Executable (..),
cleanOutputDirs,
( cleanOutputDirs,
cls,
configurationPathMeta,
executable,
readDoc,
runPlotM,
supportedSaveFormats,
toolkits,
toolkits,
pathToExe
)
import Text.Pandoc.JSON (toJSONFilter)
import Text.ParserCombinators.ReadP (readP_to_S)
Expand Down Expand Up @@ -286,8 +285,8 @@ showAvailableToolkits mfp = do
toolkitInfo avail conf tk = do
putStrLn $ "Toolkit: " <> show tk
when avail $ do
Executable dir exe <- fmap fromJust $ runPlotM Nothing conf $ executable tk
putStrLn $ " Executable: " <> (dir </> unpack exe)
exe <- runPlotM Nothing conf $ executable tk
putStrLn $ " Executable: " <> (pathToExe exe)
putStrLn $ " Code block trigger: " <> (unpack . cls $ tk)
putStrLn $ " Supported save formats: " <> (mconcat . intersperse ", " . fmap show $ supportedSaveFormats tk)
putStrLn mempty
Expand Down
11 changes: 7 additions & 4 deletions pandoc-plot.cabal
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 2.2
name: pandoc-plot
version: 1.5.1
version: 1.5.2
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
Expand All @@ -14,7 +14,11 @@ maintainer: Laurent P. René de Cotret
license: GPL-2.0-or-later
license-file: LICENSE
build-type: Simple
tested-with: GHC == 8.10.4, GHC == 9.0.1
tested-with: GHC == 8.10.4,
GHC == 9.0.1,
GHC == 9.0.1,
GHC == 9.2.1,
GHC == 9.2.2
extra-source-files:
CHANGELOG.md
LICENSE
Expand Down Expand Up @@ -94,7 +98,7 @@ library
, directory >= 1.2.7 && < 2
, filepath >= 1.4 && < 2
, hashable >= 1 && < 2
, pandoc >= 2.10 && < 3
, pandoc >= 2.11 && < 3
, pandoc-types >= 1.22 && < 1.23
, lifted-async >= 0.10 && < 1
, lifted-base >= 0.2 && < 1
Expand Down Expand Up @@ -144,7 +148,6 @@ test-suite tests
, containers
, directory
, filepath
, hspec
, hspec-expectations
, pandoc-types >= 1.20 && <= 2
, pandoc-plot
Expand Down
21 changes: 5 additions & 16 deletions src/Text/Pandoc/Filter/Plot/Monad.hs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ import Control.Monad.State.Strict
evalStateT,
)
import Data.ByteString.Lazy (toStrict)
import Data.Functor ((<&>))
import Data.Hashable (hash)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
Expand All @@ -84,7 +83,6 @@ import Data.Text.Encoding (decodeUtf8With)
import Data.Text.Encoding.Error (lenientDecode)
import System.Directory
( doesFileExist,
findExecutable,
getCurrentDirectory,
getModificationTime,
)
Expand Down Expand Up @@ -141,7 +139,6 @@ runPlotM fmt conf v = do
cwd <- getCurrentDirectory
st <-
PlotState <$> newMVar mempty
<*> newMVar mempty
let verbosity = logVerbosity conf
sink = logSink conf
withLogger verbosity sink $
Expand Down Expand Up @@ -224,27 +221,23 @@ throwStrictError msg = do
logger <- askLogger
liftIO $ terminateLogging logger >> exitFailure

-- Plot state is used for caching.
-- One part consists of a map of filepaths to hashes
-- Plot state is used for caching 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
-- the state must be synchronized; otherwise, each thread might compute its own
-- hashes.
-- The other part is comprised of a map of toolkits to renderers (possibly missing)
-- This means that checking if renderers are available will only be done once.
type FileHash = Word

data PlotState
= PlotState
(MVar (Map FilePath FileHash))
(MVar (Map Toolkit (Maybe Renderer)))

-- | 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
PlotState varHashes <- get
hashes <- liftIO $ takeMVar varHashes
(fh, hashes') <- case M.lookup path hashes of
Nothing -> do
Expand All @@ -256,7 +249,7 @@ fileHash path = do
debug $ mconcat ["Hash of dependency ", pack path, " already calculated."]
return (h, hashes)
liftIO $ putMVar varHashes hashes'
put $ PlotState varHashes varExes
put $ PlotState varHashes
return fh
where
-- As a proxy for the state of a file dependency, we use the modification time
Expand All @@ -269,12 +262,8 @@ fileHash path = do
else err (mconcat ["Dependency ", pack fp, " does not exist."]) >> return 0

-- | Find an executable.
executable :: Toolkit -> PlotM (Maybe Executable)
executable tk =
exeSelector tk
>>= \name ->
liftIO $
findExecutable name <&> fmap exeFromPath
executable :: Toolkit -> PlotM Executable
executable tk = exeSelector tk >>= return . exeFromPath
where
exeSelector Matplotlib = asksConfig matplotlibExe
exeSelector PlotlyPython = asksConfig plotlyPythonExe
Expand Down
31 changes: 24 additions & 7 deletions src/Text/Pandoc/Filter/Plot/Monad/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
module Text.Pandoc.Filter.Plot.Monad.Types
( Toolkit (..),
Renderer (..),
AvailabilityCheck(..),
Script,
CheckResult (..),
InclusionKey (..),
Expand All @@ -26,6 +27,7 @@ module Text.Pandoc.Filter.Plot.Monad.Types
inclusionKeys,
Executable (..),
exeFromPath,
pathToExe,
-- Utilities
isWindows,
)
Expand All @@ -37,7 +39,7 @@ import Data.String (IsString (..))
import Data.Text (Text, pack, unpack)
import Data.Yaml (FromJSON(..), ToJSON (toJSON), withText)
import GHC.Generics (Generic)
import System.FilePath (splitFileName)
import System.FilePath (splitFileName, (</>), isAbsolute)
import System.Info (os)
import Text.Pandoc.Definition (Attr)

Expand Down Expand Up @@ -94,13 +96,20 @@ cls Plotsjl = "plotsjl"
cls PlantUML = "plantuml"
cls SageMath = "sageplot"

-- | Executable program and directory where it can be found.
data Executable = Executable FilePath Text
-- | Executable program, and sometimes the directory where it can be found.
data Executable
= AbsExe FilePath Text
| RelExe Text

exeFromPath :: FilePath -> Executable
exeFromPath fp =
let (dir, name) = splitFileName fp
in Executable dir (pack name)
exeFromPath fp
| isAbsolute fp = let (dir, name) = splitFileName fp
in AbsExe dir (pack name)
| otherwise = RelExe (pack fp)

pathToExe :: Executable -> FilePath
pathToExe (AbsExe dir name) = dir </> unpack name
pathToExe (RelExe name) = unpack name

-- | Source context for plotting scripts
type Script = Text
Expand Down Expand Up @@ -170,6 +179,8 @@ inclusionKeys = enumFromTo (minBound :: InclusionKey) maxBound
data FigureSpec = FigureSpec
{ -- | Renderer to use for this figure.
renderer_ :: !Renderer,
-- | Executable to use in rendering this figure.
fsExecutable :: Executable,
-- | Figure caption.
caption :: !Text,
-- | Append link to source code in caption.
Expand Down Expand Up @@ -263,15 +274,21 @@ data OutputSpec = OutputSpec
oScriptPath :: FilePath,
-- | Figure output path
oFigurePath :: FilePath,
-- | Executable to use during rendering
oExecutable :: Executable,
-- | Current working directory
oCWD :: FilePath
}

data AvailabilityCheck
= CommandSuccess (Executable -> Text)
| ExecutableExists

data Renderer = Renderer
{ rendererToolkit :: Toolkit,
rendererExe :: Executable,
rendererCapture :: FigureSpec -> FilePath -> Script,
rendererCommand :: OutputSpec -> Text,
rendererAvailability :: AvailabilityCheck,
rendererSupportedSaveFormats :: [SaveFormat],
rendererChecks :: [Script -> CheckResult],
rendererLanguage :: Text,
Expand Down
9 changes: 4 additions & 5 deletions src/Text/Pandoc/Filter/Plot/Parse.hs
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,7 @@ parseFigureSpec block@(CodeBlock (id', classes, attrs) _) = do
Nothing -> return NotAFigure
Just tk -> do
r <- renderer tk
case r of
Nothing -> do
err $ mconcat ["Renderer for ", tshow tk, " needed but is not installed"]
return $ MissingToolkit tk
Just r' -> figureSpec r'
figureSpec r
where
attrs' = Map.fromList attrs
preamblePath = unpack <$> Map.lookup (tshow PreambleK) attrs'
Expand Down Expand Up @@ -108,8 +104,11 @@ parseFigureSpec block@(CodeBlock (id', classes, attrs) _) = do

-- Decide between reading from file or using document content
content <- parseContent block

defaultExe <- executable rendererToolkit

let caption = Map.findWithDefault mempty (tshow CaptionK) attrs'
fsExecutable = maybe defaultExe (exeFromPath . unpack) $ Map.lookup (tshow ExecutableK) attrs'
withSource = maybe defWithSource readBool (Map.lookup (tshow WithSourceK) attrs')
script = mconcat $ intersperse "\n" [header, includeScript, content]
directory = makeValid $ unpack $ Map.findWithDefault (pack $ defaultDirectory conf) (tshow DirectoryK) attrs'
Expand Down
Loading

0 comments on commit f0af92a

Please sign in to comment.