Skip to content

Commit

Permalink
Add initial support for Mermaid (#74)
Browse files Browse the repository at this point in the history
* Fix copy paste typo for D2

* Add initial support for Mermaid

Requires mermaid-cli to be installed and `mmdc` command
to be available.
  • Loading branch information
SanchayanMaity authored Sep 18, 2024
1 parent 5753e8f commit 8f69379
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 3 deletions.
4 changes: 4 additions & 0 deletions example-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,7 @@ sageplot:
d2:
executable: d2
command_line_arguments:

mermaid:
executable: mmdc
command_line_arguments:
1 change: 1 addition & 0 deletions pandoc-plot.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ library
Text.Pandoc.Filter.Plot.Renderers.SageMath
Text.Pandoc.Filter.Plot.Renderers.D2
Text.Pandoc.Filter.Plot.Renderers.Asymptote
Text.Pandoc.Filter.Plot.Renderers.Mermaid
Text.Pandoc.Filter.Plot.Monad
Text.Pandoc.Filter.Plot.Monad.Logging
Text.Pandoc.Filter.Plot.Monad.Types
Expand Down
21 changes: 18 additions & 3 deletions src/Text/Pandoc/Filter/Plot/Configuration.hs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ defaultConfiguration =
sagemathPreamble = mempty,
d2Preamble = mempty,
asyPreamble = mempty,
mermaidPreamble = mempty,
-- Executables
matplotlibExe = python,
matlabExe = "matlab",
Expand All @@ -82,6 +83,7 @@ defaultConfiguration =
sagemathExe = "sage",
d2Exe = "d2",
asyExe = "asy",
mermaidExe = "mmdc",
-- Command line arguments
matplotlibCmdArgs = mempty,
matlabCmdArgs = mempty,
Expand All @@ -98,6 +100,7 @@ defaultConfiguration =
sagemathCmdArgs = mempty,
d2CmdArgs = mempty,
asyCmdArgs = mempty,
mermaidCmdArgs = mempty,
-- Extras
matplotlibTightBBox = False,
matplotlibTransparent = False
Expand Down Expand Up @@ -159,7 +162,8 @@ data ConfigPrecursor = ConfigPrecursor
_plantumlPrec :: !PlantUMLPrecursor,
_sagemathPrec :: !SageMathPrecursor,
_d2Prec :: !D2Precursor,
_asyPrec :: !AsyPrecursor
_asyPrec :: !AsyPrecursor,
_mermaidPrec :: !MermaidPrecursor
}

defaultConfigPrecursor :: ConfigPrecursor
Expand Down Expand Up @@ -188,7 +192,8 @@ defaultConfigPrecursor =
_plantumlPrec = PlantUMLPrecursor Nothing (plantumlExe defaultConfiguration) (plantumlCmdArgs defaultConfiguration),
_sagemathPrec = SageMathPrecursor Nothing (sagemathExe defaultConfiguration) (sagemathCmdArgs defaultConfiguration),
_d2Prec = D2Precursor Nothing (d2Exe defaultConfiguration) (d2CmdArgs defaultConfiguration),
_asyPrec = AsyPrecursor Nothing (asyExe defaultConfiguration) (asyCmdArgs defaultConfiguration)
_asyPrec = AsyPrecursor Nothing (asyExe defaultConfiguration) (asyCmdArgs defaultConfiguration),
_mermaidPrec = MermaidPrecursor Nothing (mermaidExe defaultConfiguration) (mermaidCmdArgs defaultConfiguration)
}

data LoggingPrecursor = LoggingPrecursor
Expand Down Expand Up @@ -233,6 +238,8 @@ data D2Precursor = D2Precursor {_d2Preamble :: !(Maybe FilePath), _d2Exe :: !Fil

data AsyPrecursor = AsyPrecursor {_asyPreamble :: !(Maybe FilePath), _asyExe :: !FilePath, _asyCmdArgs :: !Text}

data MermaidPrecursor = MermaidPrecursor {_mermaidPreamble :: !(Maybe FilePath), _mermaidExe :: !FilePath, _mermaidCmdArgs :: !Text}

instance FromJSON LoggingPrecursor where
parseJSON (Object v) =
LoggingPrecursor
Expand Down Expand Up @@ -303,12 +310,16 @@ instance FromJSON SageMathPrecursor where

instance FromJSON D2Precursor where
parseJSON (Object v) = D2Precursor <$> v .:? asKey PreambleK <*> v .:? asKey ExecutableK .!= d2Exe defaultConfiguration <*> v .:? asKey CommandLineArgsK .!= d2CmdArgs defaultConfiguration
parseJSON _ = fail $ mconcat ["Could not parse ", show SageMath, " configuration."]
parseJSON _ = fail $ mconcat ["Could not parse ", show D2, " configuration."]

instance FromJSON AsyPrecursor where
parseJSON (Object v) = AsyPrecursor <$> v .:? asKey PreambleK <*> v .:? asKey ExecutableK .!= asyExe defaultConfiguration <*> v .:? asKey CommandLineArgsK .!= asyCmdArgs defaultConfiguration
parseJSON _ = fail $ mconcat ["Could not parse ", show Asymptote, " configuration."]

instance FromJSON MermaidPrecursor where
parseJSON (Object v) = MermaidPrecursor <$> v .:? asKey PreambleK <*> v .:? asKey ExecutableK .!= mermaidExe defaultConfiguration <*> v .:? asKey CommandLineArgsK .!= mermaidCmdArgs defaultConfiguration
parseJSON _ = fail $ mconcat ["Could not parse ", show Mermaid, " configuration."]

toolkitAsKey :: Toolkit -> Key
toolkitAsKey = fromString . unpack . cls

Expand Down Expand Up @@ -340,6 +351,7 @@ instance FromJSON ConfigPrecursor where
_sagemathPrec <- v .:? toolkitAsKey SageMath .!= _sagemathPrec defaultConfigPrecursor
_d2Prec <- v .:? toolkitAsKey D2 .!= _d2Prec defaultConfigPrecursor
_asyPrec <- v .:? toolkitAsKey Asymptote .!= _asyPrec defaultConfigPrecursor
_mermaidPrec <- v .:? toolkitAsKey Mermaid .!= _mermaidPrec defaultConfigPrecursor

return $ ConfigPrecursor {..}
parseJSON _ = fail "Could not parse configuration."
Expand Down Expand Up @@ -376,6 +388,7 @@ renderConfig ConfigPrecursor {..} = do
sagemathExe = _sagemathExe _sagemathPrec
d2Exe = _d2Exe _d2Prec
asyExe = _asyExe _asyPrec
mermaidExe = _mermaidExe _mermaidPrec

matplotlibCmdArgs = _matplotlibCmdArgs _matplotlibPrec
matlabCmdArgs = _matlabCmdArgs _matlabPrec
Expand All @@ -392,6 +405,7 @@ renderConfig ConfigPrecursor {..} = do
sagemathCmdArgs = _sagemathCmdArgs _sagemathPrec
d2CmdArgs = _d2CmdArgs _d2Prec
asyCmdArgs = _asyCmdArgs _asyPrec
mermaidCmdArgs = _mermaidCmdArgs _mermaidPrec

matplotlibPreamble <- readPreamble (_matplotlibPreamble _matplotlibPrec)
matlabPreamble <- readPreamble (_matlabPreamble _matlabPrec)
Expand All @@ -408,6 +422,7 @@ renderConfig ConfigPrecursor {..} = do
sagemathPreamble <- readPreamble (_sagemathPreamble _sagemathPrec)
d2Preamble <- readPreamble (_d2Preamble _d2Prec)
asyPreamble <- readPreamble (_asyPreamble _asyPrec)
mermaidPreamble <- readPreamble (_mermaidPreamble _mermaidPrec)

return Configuration {..}
where
Expand Down
7 changes: 7 additions & 0 deletions src/Text/Pandoc/Filter/Plot/Monad.hs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ executable tk = exeSelector tk <&> exeFromPath
exeSelector SageMath = asksConfig sagemathExe
exeSelector D2 = asksConfig d2Exe
exeSelector Asymptote = asksConfig asyExe
exeSelector Mermaid = asksConfig mermaidExe

-- | The @Configuration@ type holds the default values to use
-- when running pandoc-plot. These values can be overridden in code blocks.
Expand Down Expand Up @@ -356,6 +357,8 @@ data Configuration = Configuration
d2Preamble :: !Script,
-- | The default preamble script for the Asymptote toolkit.
asyPreamble :: !Script,
-- | The default preamble script for the Mermaid toolkit.
mermaidPreamble :: !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.
Expand Down Expand Up @@ -386,6 +389,8 @@ data Configuration = Configuration
d2Exe :: !FilePath,
-- | The executable to use to generate figures using Asymptote
asyExe :: !FilePath,
-- | The executable to use to generate figures using Mermaid
mermaidExe :: !FilePath,
-- | Command-line arguments to pass to the Python interpreter for the Matplotlib toolkit
matplotlibCmdArgs :: !Text,
-- | Command-line arguments to pass to the interpreter for the MATLAB toolkit.
Expand Down Expand Up @@ -416,6 +421,8 @@ data Configuration = Configuration
d2CmdArgs :: !Text,
-- | Command-line arguments to pass to the interpreter for the Asymptote toolkit.
asyCmdArgs :: !Text,
-- | Command-line arguments to pass to the interpreter for the Mermaid toolkit.
mermaidCmdArgs :: !Text,
-- | Whether or not to make Matplotlib figures tight by default.
matplotlibTightBBox :: !Bool,
-- | Whether or not to make Matplotlib figures transparent by default.
Expand Down
3 changes: 3 additions & 0 deletions src/Text/Pandoc/Filter/Plot/Monad/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ data Toolkit
| SageMath
| D2
| Asymptote
| Mermaid
deriving (Bounded, Eq, Enum, Generic, Ord)

-- | This instance should only be used to display toolkit names
Expand All @@ -82,6 +83,7 @@ instance Show Toolkit where
show SageMath = "SageMath"
show D2 = "D2"
show Asymptote = "Asymptote"
show Mermaid = "Mermaid"

-- | Class name which will trigger the filter
cls :: Toolkit -> Text
Expand All @@ -100,6 +102,7 @@ cls PlantUML = "plantuml"
cls SageMath = "sageplot"
cls D2 = "d2"
cls Asymptote = "asy"
cls Mermaid = "mermaid"

-- | Executable program, and sometimes the directory where it can be found.
data Executable
Expand Down
7 changes: 7 additions & 0 deletions src/Text/Pandoc/Filter/Plot/Renderers.hs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ import Text.Pandoc.Filter.Plot.Renderers.Asymptote
( asymptote,
asymptoteSupportedSaveFormats,
)
import Text.Pandoc.Filter.Plot.Renderers.Mermaid
( mermaid,
mermaidSupportedSaveFormats,
)
-- | Get the renderer associated with a toolkit.
-- If the renderer has not been used before,
-- initialize it and store where it is. It will be re-used.
Expand All @@ -118,6 +122,7 @@ renderer PlantUML = plantuml
renderer SageMath = sagemath
renderer D2 = d2
renderer Asymptote = asymptote
renderer Mermaid = mermaid

-- | Save formats supported by this renderer.
supportedSaveFormats :: Toolkit -> [SaveFormat]
Expand All @@ -136,6 +141,7 @@ supportedSaveFormats PlantUML = plantumlSupportedSaveFormats
supportedSaveFormats SageMath = sagemathSupportedSaveFormats
supportedSaveFormats D2 = d2SupportedSaveFormats
supportedSaveFormats Asymptote = asymptoteSupportedSaveFormats
supportedSaveFormats Mermaid = mermaidSupportedSaveFormats

-- | The function that maps from configuration to the preamble.
preambleSelector :: Toolkit -> (Configuration -> Script)
Expand All @@ -154,6 +160,7 @@ preambleSelector PlantUML = plantumlPreamble
preambleSelector SageMath = sagemathPreamble
preambleSelector D2 = d2Preamble
preambleSelector Asymptote = asyPreamble
preambleSelector Mermaid = mermaidPreamble

-- | Parse code block headers for extra attributes that are specific
-- to this renderer. By default, no extra attributes are parsed.
Expand Down
51 changes: 51 additions & 0 deletions src/Text/Pandoc/Filter/Plot/Renderers/Mermaid.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE NoImplicitPrelude #-}

-- |
-- Module : $header$
-- Copyright : (c) Sanchayan Maity, 2024 - present
-- License : GNU GPL, version 2 or above
-- Maintainer : [email protected]
-- Stability : internal
-- Portability : portable
--
-- Rendering Mermaid plots code blocks
module Text.Pandoc.Filter.Plot.Renderers.Mermaid
( mermaid,
mermaidSupportedSaveFormats,
)
where

import Data.Char (toLower)
import Text.Pandoc.Filter.Plot.Renderers.Prelude

mermaid :: PlotM Renderer
mermaid = do
cmdargs <- asksConfig mermaidCmdArgs
return
$ Renderer
{ rendererToolkit = Mermaid,
rendererCapture = mermaidCapture,
rendererCommand = mermaidCommand cmdargs,
rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -V|],
rendererSupportedSaveFormats = mermaidSupportedSaveFormats,
rendererChecks = mempty,
rendererLanguage = "mermaid",
rendererComment = mempty,
rendererScriptExtension = ".mermaid"
}

mermaidSupportedSaveFormats :: [SaveFormat]
mermaidSupportedSaveFormats = [PDF, PNG, SVG]

mermaidCommand :: Text -> OutputSpec -> Text
mermaidCommand cmdargs OutputSpec {..} =
let fmt = fmap toLower . show . saveFormat $ oFigureSpec
in [st|#{pathToExe oExecutable} #{cmdargs} -q -e #{fmt} -i "#{oScriptPath}" -o "#{oFigurePath}"|]

-- Mermaid export is entirely based on command-line arguments
-- so there is no need to modify the script itself.
mermaidCapture :: FigureSpec -> FilePath -> Script
mermaidCapture FigureSpec {..} _ = script
2 changes: 2 additions & 0 deletions tests/Common.hs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ testFileInclusion tk =
include SageMath = "tests/includes/sagemath.sage"
include D2 = "tests/includes/d2-dd.d2"
include Asymptote = "tests/includes/asymptote.asy"
include Mermaid = "tests/includes/mermaid.mermaid"

-------------------------------------------------------------------------------
-- Tests that the files are saved in all the advertised formats
Expand Down Expand Up @@ -423,6 +424,7 @@ trivialContent PlantUML = "@startuml\nAlice -> Bob: test\n@enduml"
trivialContent SageMath = "G = plot(sin, 1, 10)"
trivialContent D2 = "x -> y -> z"
trivialContent Asymptote = "draw((0,0)--(1,0));"
trivialContent Mermaid = "graph LR\n\tA --> B"

addCaption :: String -> Block -> Block
addCaption caption (CodeBlock (id', cls, attrs) script) =
Expand Down
Empty file added tests/includes/mermaid.mermaid
Empty file.

0 comments on commit 8f69379

Please sign in to comment.