diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,11 @@
 
 pandoc-plot uses [Semantic Versioning](http://semver.org/spec/v2.0.0.html)
 
+## Release 1.7.0
+
+* Added support for the declarative diagram language [D2](https://d2lang.com/), thanks to a contribution by Sanchayan Maity (#60).
+* Added support for `optparse-applicative` v0.18.
+
 ## Release 1.6.2
 
 * Fixed some imports which were removed in `mtl-2.3`.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -71,6 +71,7 @@
   - `plotsjl`: plots using the [Julia `Plots.jl`](https://docs.juliaplots.org/latest/) package;
   - `plantuml`: diagrams using the [PlantUML](https://plantuml.com/) software suite;
   - `sageplot`: plots using the [Sage](https://www.sagemath.org/) software system.
+  - `d2`: plots using [D2](https://d2lang.com/). 
 
 To know which toolkits are useable on *your machine* (and which ones are
 not available), you can check with the `toolkits` command:
diff --git a/example-config.yml b/example-config.yml
--- a/example-config.yml
+++ b/example-config.yml
@@ -142,3 +142,7 @@
   # preamble: sageplot.sage
   executable: sage
   command_line_arguments:
+
+d2:
+  executable: d2
+  command_line_arguments:
diff --git a/executable/Main.hs b/executable/Main.hs
--- a/executable/Main.hs
+++ b/executable/Main.hs
@@ -61,11 +61,11 @@
     cls,
     configurationPathMeta,
     executable,
+    pathToExe,
     readDoc,
     runPlotM,
     supportedSaveFormats,
-    toolkits, 
-    pathToExe
+    toolkits,
   )
 import Text.Pandoc.JSON (toJSONFilter)
 import Text.ParserCombinators.ReadP (readP_to_S)
@@ -105,7 +105,13 @@
                   ]
               )
             <> header (mconcat ["pandoc-plot ", V.showVersion pandocPlotVersion, " - generate figures directly in documents"])
-            <> footerDoc (Just footer')
+            <> footerDoc
+              ( Just $
+                  P.vsep
+                    [ "More information can be found via the manual (pandoc-plot --manual) or the",
+                      "repository README, located at https://github.com/LaurentRDC/pandoc-plot"
+                    ]
+              )
         )
 
     optparse = do
@@ -286,7 +292,7 @@
       putStrLn $ "Toolkit: " <> show tk
       when avail $ do
         exe <- runPlotM Nothing conf $ executable tk
-        putStrLn $ "    Executable: " <> (pathToExe exe)
+        putStrLn $ "    Executable: " <> pathToExe exe
       putStrLn $ "    Code block trigger: " <> (unpack . cls $ tk)
       putStrLn $ "    Supported save formats: " <> (mconcat . intersperse ", " . fmap show $ supportedSaveFormats tk)
       putStrLn mempty
@@ -318,12 +324,3 @@
   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"
-    ]
diff --git a/pandoc-plot.cabal b/pandoc-plot.cabal
--- a/pandoc-plot.cabal
+++ b/pandoc-plot.cabal
@@ -1,6 +1,6 @@
 cabal-version:  2.2
 name:           pandoc-plot
-version:        1.6.2
+version:        1.7.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 
@@ -48,6 +48,8 @@
     tests/includes/plotly-r.r
     tests/includes/plotsjl.jl
     tests/includes/plantuml.txt
+    tests/includes/sagemath.sage
+    tests/includes/d2-dd.d2
 
 source-repository head
     type: git
@@ -79,6 +81,7 @@
         Text.Pandoc.Filter.Plot.Renderers.Plotsjl
         Text.Pandoc.Filter.Plot.Renderers.PlantUML
         Text.Pandoc.Filter.Plot.Renderers.SageMath
+        Text.Pandoc.Filter.Plot.Renderers.D2
         Text.Pandoc.Filter.Plot.Monad
         Text.Pandoc.Filter.Plot.Monad.Logging
         Text.Pandoc.Filter.Plot.Monad.Types
@@ -98,8 +101,8 @@
         -Wmissing-export-lists
         -Wpartial-fields
     build-depends:
-          aeson              >= 2     && <3
-        , base               >= 4.11  && <5
+          aeson              >= 2     && < 3
+        , base               >= 4.11  && < 5
         , bytestring
         , containers
         , data-default  
@@ -137,11 +140,11 @@
         , directory
         , filepath
         , gitrev                >= 1    && < 2
-        , optparse-applicative  >= 0.14 && < 1
+        , optparse-applicative  >= 0.14 && < 0.19
         , pandoc
         , pandoc-plot
-        , pandoc-types          >= 1.21 && <2
-        , template-haskell      >  2.7 && < 3
+        , pandoc-types          >= 1.21 && < 2
+        , template-haskell      >  2.7  && < 3
         , typed-process
         , text
     default-language: Haskell2010
@@ -151,12 +154,12 @@
     hs-source-dirs:  tests
     main-is:         Main.hs
     other-modules:   Common
-    build-depends:   base                 >= 4.11 && < 5
+    build-depends:   base
                    , containers
                    , directory
                    , filepath
                    , hspec-expectations
-                   , pandoc-types         >= 1.20 && <= 2
+                   , pandoc-types
                    , pandoc-plot
                    , tasty
                    , tasty-hunit
diff --git a/src/Text/Pandoc/Filter/Plot.hs b/src/Text/Pandoc/Filter/Plot.hs
--- a/src/Text/Pandoc/Filter/Plot.hs
+++ b/src/Text/Pandoc/Filter/Plot.hs
@@ -22,7 +22,7 @@
 --
 -- @
 -- This is a paragraph.
--- 
+--
 -- ```{.matlabplot}
 -- figure()
 -- plot([1,2,3,4,5], [1,2,3,4,5], '-k')
@@ -34,7 +34,7 @@
 -- @
 -- ```{.gnuplot format=png caption="Sinusoidal function" source=true}
 -- sin(x)
--- 
+--
 -- set xlabel "x"
 -- set ylabel "y"
 -- ```
@@ -63,8 +63,7 @@
 --       code block will be ignored. This path should be specified with respect to the current working
 --       directory, and not with respect to the document.
 --
--- All attributes are described in the online documentation, linked on the home page. 
-
+-- All attributes are described in the online documentation, linked on the home page.
 module Text.Pandoc.Filter.Plot
   ( -- * Operating on whole Pandoc documents
     plotFilter,
@@ -106,7 +105,7 @@
 import Data.Text (Text, pack, unpack)
 import Data.Version (Version)
 import Paths_pandoc_plot (version)
-import Text.Pandoc.Definition (Block, Meta (..), Format, MetaValue (..), Pandoc (..))
+import Text.Pandoc.Definition (Block, Format, Meta (..), MetaValue (..), Pandoc (..))
 import Text.Pandoc.Filter.Plot.Internal
   ( Configuration (..),
     FigureSpec,
@@ -175,7 +174,7 @@
 -- on documents without having all necessary toolkits installed. In this case, error
 -- messages are printed to stderr, and blocks are left unchanged.
 --
--- __Note that this function is DEPRECATED in favour of @plotFilter@. It will be 
+-- __Note that this function is DEPRECATED in favour of @plotFilter@. It will be
 -- removed in the next major update (v2+).__
 plotTransform ::
   -- | Configuration for default values
@@ -183,11 +182,12 @@
   -- | Input document
   Pandoc ->
   IO Pandoc
-{-# DEPRECATED plotTransform
-  [ "plotTransform has been deprecated in favour of plotFilter, which is aware of conversion format."
-  , "plotTransform will be removed in an upcoming major update."
-  ] 
-#-}
+{-# DEPRECATED
+  plotTransform
+  [ "plotTransform has been deprecated in favour of plotFilter, which is aware of conversion format.",
+    "plotTransform will be removed in an upcoming major update."
+  ]
+  #-}
 plotTransform conf = plotFilter conf Nothing
 
 -- | The version of the pandoc-plot package.
@@ -197,7 +197,7 @@
 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 unless running in strict mode. 
+-- was not meant to become a figure, return the block as-is unless running in strict mode.
 -- In strict mode, any failure (for example, due to a missing plotting toolkit) will halt execution.
 --
 -- New in version 1.2.0: this function will detect nested code blocks, for example in @Div@ blocks.
@@ -208,7 +208,7 @@
     onError b e = do
       whenStrict $ throwStrictError (pack . show $ e)
       return b
-    
+
     whenStrict f = asksConfig strictMode >>= \s -> when s f
 
 -- | Try to process the block with `pandoc-plot`, documenting the error.
@@ -239,4 +239,4 @@
   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 (IncompatibleSaveFormatError tk sv) = "ERROR (pandoc-plot) Save format " <> show sv <> " not supported by the " <> show tk <> " toolkit." 
+  show (IncompatibleSaveFormatError tk sv) = "ERROR (pandoc-plot) Save format " <> show sv <> " not supported by the " <> show tk <> " toolkit."
diff --git a/src/Text/Pandoc/Filter/Plot/Clean.hs b/src/Text/Pandoc/Filter/Plot/Clean.hs
--- a/src/Text/Pandoc/Filter/Plot/Clean.hs
+++ b/src/Text/Pandoc/Filter/Plot/Clean.hs
@@ -31,9 +31,9 @@
 import Text.Pandoc.Class (runIO)
 import Text.Pandoc.Definition (Block, Pandoc)
 import Text.Pandoc.Error (handleError)
-import Text.Pandoc.Format (FlavoredFormat(..))
 import Text.Pandoc.Filter.Plot.Monad
 import Text.Pandoc.Filter.Plot.Parse
+import Text.Pandoc.Format (FlavoredFormat (..))
 import qualified Text.Pandoc.Options as P
 import qualified Text.Pandoc.Readers as P
 import Text.Pandoc.Walk (Walkable, query)
@@ -44,7 +44,7 @@
 --
 -- The cleaned directories are returned.
 cleanOutputDirs ::
-  Walkable Block b =>
+  (Walkable Block b) =>
   Configuration ->
   b ->
   IO [FilePath]
@@ -59,7 +59,7 @@
 
 -- | Analyze a document to determine where would the pandoc-plot output directories be.
 outputDirs ::
-  Walkable Block b =>
+  (Walkable Block b) =>
   b ->
   PlotM [FilePath]
 outputDirs = fmap (nub . catMaybes) . sequence . query (\b -> [hasDirectory <$> parseFigureSpec b])
@@ -70,7 +70,7 @@
 
 -- | PlotM version of @cleanOutputDirs@
 cleanOutputDirsM ::
-  Walkable Block b =>
+  (Walkable Block b) =>
   b ->
   PlotM [FilePath]
 cleanOutputDirsM doc = do
diff --git a/src/Text/Pandoc/Filter/Plot/Configuration.hs b/src/Text/Pandoc/Filter/Plot/Configuration.hs
--- a/src/Text/Pandoc/Filter/Plot/Configuration.hs
+++ b/src/Text/Pandoc/Filter/Plot/Configuration.hs
@@ -17,8 +17,8 @@
   )
 where
 
-import Data.Aeson ( (.:?), (.!=), Key, Value(Object, Null), FromJSON(parseJSON) )
-import Data.String (IsString(..))
+import Data.Aeson (FromJSON (parseJSON), Key, Value (Null, Object), (.!=), (.:?))
+import Data.String (IsString (..))
 import Data.Text (Text, unpack)
 import qualified Data.Text.IO as TIO
 import Data.Yaml.Config (ignoreEnv, loadYamlSettings)
@@ -64,6 +64,7 @@
       plotsjlPreamble = mempty,
       plantumlPreamble = mempty,
       sagemathPreamble = mempty,
+      d2Preamble = mempty,
       -- Executables
       matplotlibExe = python,
       matlabExe = "matlab",
@@ -78,6 +79,7 @@
       plotsjlExe = "julia",
       plantumlExe = "java",
       sagemathExe = "sage",
+      d2Exe = "d2",
       -- Command line arguments
       matplotlibCmdArgs = mempty,
       matlabCmdArgs = mempty,
@@ -92,6 +94,7 @@
       plotsjlCmdArgs = mempty,
       plantumlCmdArgs = "-jar plantuml.jar",
       sagemathCmdArgs = mempty,
+      d2CmdArgs = mempty,
       -- Extras
       matplotlibTightBBox = False,
       matplotlibTransparent = False
@@ -151,7 +154,8 @@
     _bokehPrec :: !BokehPrecursor,
     _plotsjlPrec :: !PlotsjlPrecursor,
     _plantumlPrec :: !PlantUMLPrecursor,
-    _sagemathPrec :: !SageMathPrecursor
+    _sagemathPrec :: !SageMathPrecursor,
+    _d2Prec :: !D2Precursor
   }
 
 defaultConfigPrecursor :: ConfigPrecursor
@@ -178,7 +182,8 @@
       _bokehPrec = BokehPrecursor Nothing (bokehExe defaultConfiguration) (bokehCmdArgs defaultConfiguration),
       _plotsjlPrec = PlotsjlPrecursor Nothing (plotsjlExe defaultConfiguration) (plotsjlCmdArgs defaultConfiguration),
       _plantumlPrec = PlantUMLPrecursor Nothing (plantumlExe defaultConfiguration) (plantumlCmdArgs defaultConfiguration),
-      _sagemathPrec = SageMathPrecursor Nothing (sagemathExe defaultConfiguration) (sagemathCmdArgs defaultConfiguration)
+      _sagemathPrec = SageMathPrecursor Nothing (sagemathExe defaultConfiguration) (sagemathCmdArgs defaultConfiguration),
+      _d2Prec = D2Precursor Nothing (d2Exe defaultConfiguration) (d2CmdArgs defaultConfiguration)
     }
 
 data LoggingPrecursor = LoggingPrecursor
@@ -219,13 +224,16 @@
 
 data SageMathPrecursor = SageMathPrecursor {_sagemathPreamble :: !(Maybe FilePath), _sagemathExe :: !FilePath, _sagemathCmdArgs :: !Text}
 
+data D2Precursor = D2Precursor {_d2Preamble :: !(Maybe FilePath), _d2Exe :: !FilePath, _d2CmdArgs :: !Text}
+
 instance FromJSON LoggingPrecursor where
   parseJSON (Object v) =
-    LoggingPrecursor <$> v .:? "verbosity" .!= logVerbosity defaultConfiguration
+    LoggingPrecursor
+      <$> v .:? "verbosity" .!= logVerbosity defaultConfiguration
       <*> v .:? "filepath"
   parseJSON _ = fail $ mconcat ["Could not parse logging configuration. "]
 
-asKey :: InclusionKey -> Key 
+asKey :: InclusionKey -> Key
 asKey = fromString . show
 
 instance FromJSON MatplotlibPrecursor where
@@ -286,7 +294,11 @@
   parseJSON (Object v) = SageMathPrecursor <$> v .:? asKey PreambleK <*> v .:? asKey ExecutableK .!= sagemathExe defaultConfiguration <*> v .:? asKey CommandLineArgsK .!= sagemathCmdArgs defaultConfiguration
   parseJSON _ = fail $ mconcat ["Could not parse ", show SageMath, " configuration."]
 
-toolkitAsKey :: Toolkit -> Key 
+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."]
+
+toolkitAsKey :: Toolkit -> Key
 toolkitAsKey = fromString . unpack . cls
 
 instance FromJSON ConfigPrecursor where
@@ -315,6 +327,7 @@
     _plotsjlPrec <- v .:? toolkitAsKey Plotsjl .!= _plotsjlPrec defaultConfigPrecursor
     _plantumlPrec <- v .:? toolkitAsKey PlantUML .!= _plantumlPrec defaultConfigPrecursor
     _sagemathPrec <- v .:? toolkitAsKey SageMath .!= _sagemathPrec defaultConfigPrecursor
+    _d2Prec <- v .:? toolkitAsKey D2 .!= _d2Prec defaultConfigPrecursor
 
     return $ ConfigPrecursor {..}
   parseJSON _ = fail "Could not parse configuration."
@@ -349,6 +362,7 @@
       plotsjlExe = _plotsjlExe _plotsjlPrec
       plantumlExe = _plantumlExe _plantumlPrec
       sagemathExe = _sagemathExe _sagemathPrec
+      d2Exe = _d2Exe _d2Prec
 
       matplotlibCmdArgs = _matplotlibCmdArgs _matplotlibPrec
       matlabCmdArgs = _matlabCmdArgs _matlabPrec
@@ -363,6 +377,7 @@
       plotsjlCmdArgs = _plotsjlCmdArgs _plotsjlPrec
       plantumlCmdArgs = _plantumlCmdArgs _plantumlPrec
       sagemathCmdArgs = _sagemathCmdArgs _sagemathPrec
+      d2CmdArgs = _d2CmdArgs _d2Prec
 
   matplotlibPreamble <- readPreamble (_matplotlibPreamble _matplotlibPrec)
   matlabPreamble <- readPreamble (_matlabPreamble _matlabPrec)
@@ -377,8 +392,8 @@
   plotsjlPreamble <- readPreamble (_plotsjlPreamble _plotsjlPrec)
   plantumlPreamble <- readPreamble (_plantumlPreamble _plantumlPrec)
   sagemathPreamble <- readPreamble (_sagemathPreamble _sagemathPrec)
+  d2Preamble <- readPreamble (_d2Preamble _d2Prec)
 
   return Configuration {..}
   where
     readPreamble = maybe mempty TIO.readFile
-
diff --git a/src/Text/Pandoc/Filter/Plot/Embed.hs b/src/Text/Pandoc/Filter/Plot/Embed.hs
--- a/src/Text/Pandoc/Filter/Plot/Embed.hs
+++ b/src/Text/Pandoc/Filter/Plot/Embed.hs
@@ -32,15 +32,15 @@
   )
 import Text.Pandoc.Builder as Builder
   ( Inlines,
-    fromList,
     figureWith,
+    fromList,
     imageWith,
-    plain,
     link,
-    str,
+    plain,
     simpleCaption,
+    str,
     toList,
-  ) 
+  )
 import Text.Pandoc.Class (runPure)
 import Text.Pandoc.Definition (Attr, Block (..), Format, Pandoc (..))
 import Text.Pandoc.Error (handleError)
@@ -84,7 +84,9 @@
   return . head . toList $
     -- We want the attributes both on the Figure element and the contained Image element
     -- so that pandoc-plot plays nice with pandoc-crossref and other filters
-    figureWith as (simpleCaption (plain caption')) $ plain $ imageWith mempty (pack fp) mempty caption'
+    figureWith as (simpleCaption (plain caption')) $
+      plain $
+        imageWith mempty (pack fp) mempty caption'
 
 -- TODO: also add the case where SVG plots can be
 --       embedded in HTML output
@@ -172,11 +174,11 @@
 extractPlot t =
   let tags = canonicalizeTags $ parseTagsOptions parseOptionsFast t
       extracted = headScripts tags <> [inside "body" tags]
-      -- In the past (e.g. commit 8417b011ccb20263427822c7447840ab4a30a41e), we used to
-      -- make all JS scripts 'deferred'. This turned out to be problematic for plotly 
+   in -- In the past (e.g. commit 8417b011ccb20263427822c7447840ab4a30a41e), we used to
+      -- make all JS scripts 'deferred'. This turned out to be problematic for plotly
       -- specifically (see issue #39). In the future, we may want to defer scripts for
       -- certain toolkits, but that's a testing nightmare...
-   in mconcat $ renderTags <$> extracted
+      mconcat $ renderTags <$> extracted
   where
     headScripts = partitions (~== ("<script>" :: String)) . inside "head"
 
diff --git a/src/Text/Pandoc/Filter/Plot/Monad.hs b/src/Text/Pandoc/Filter/Plot/Monad.hs
--- a/src/Text/Pandoc/Filter/Plot/Monad.hs
+++ b/src/Text/Pandoc/Filter/Plot/Monad.hs
@@ -149,7 +149,7 @@
     \logger -> runReaderT (evalStateT v st) (RuntimeEnv fmt conf logger cwd sem)
 
 -- | maps a function, performing at most @N@ actions concurrently.
-mapConcurrentlyN :: Traversable t => Int -> (a -> PlotM b) -> t a -> PlotM (t b)
+mapConcurrentlyN :: (Traversable t) => Int -> (a -> PlotM b) -> t a -> PlotM (t b)
 mapConcurrentlyN n f xs = do
   -- Emulating a pool of processes with locked access
   sem <- liftIO $ newQSemN n
@@ -282,6 +282,7 @@
     exeSelector Plotsjl = asksConfig plotsjlExe
     exeSelector PlantUML = asksConfig plantumlExe
     exeSelector SageMath = asksConfig sagemathExe
+    exeSelector D2 = asksConfig d2Exe
 
 -- | The @Configuration@ type holds the default values to use
 -- when running pandoc-plot. These values can be overridden in code blocks.
@@ -350,6 +351,8 @@
     plantumlPreamble :: !Script,
     -- | The default preamble script for the SageMath toolkit.
     sagemathPreamble :: !Script,
+    -- | The default preamble script for the d2 toolkit.
+    d2Preamble :: !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.
@@ -376,6 +379,8 @@
     plantumlExe :: !FilePath,
     -- | The executable to use to generate figures using SageMath.
     sagemathExe :: !FilePath,
+    -- | The executable to use to generate figures using d2.
+    d2Exe :: !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.
@@ -402,6 +407,8 @@
     plantumlCmdArgs :: !Text,
     -- | Command-line arguments to pass to the interpreter for the SageMath toolkit.
     sagemathCmdArgs :: !Text,
+    -- | Command-line arguments to pass to the interpreter for the d2 toolkit.
+    d2CmdArgs :: !Text,
     -- | Whether or not to make Matplotlib figures tight by default.
     matplotlibTightBBox :: !Bool,
     -- | Whether or not to make Matplotlib figures transparent by default.
diff --git a/src/Text/Pandoc/Filter/Plot/Monad/Logging.hs b/src/Text/Pandoc/Filter/Plot/Monad/Logging.hs
--- a/src/Text/Pandoc/Filter/Plot/Monad/Logging.hs
+++ b/src/Text/Pandoc/Filter/Plot/Monad/Logging.hs
@@ -76,7 +76,7 @@
   = LogMessage Text
   | EndLogging
 
-class Monad m => MonadLogger m where
+class (Monad m) => MonadLogger m where
   askLogger :: m Logger
 
 -- | Ensure that all log messages are flushed, and stop logging
diff --git a/src/Text/Pandoc/Filter/Plot/Monad/Types.hs b/src/Text/Pandoc/Filter/Plot/Monad/Types.hs
--- a/src/Text/Pandoc/Filter/Plot/Monad/Types.hs
+++ b/src/Text/Pandoc/Filter/Plot/Monad/Types.hs
@@ -13,7 +13,7 @@
 module Text.Pandoc.Filter.Plot.Monad.Types
   ( Toolkit (..),
     Renderer (..),
-    AvailabilityCheck(..),
+    AvailabilityCheck (..),
     Script,
     CheckResult (..),
     InclusionKey (..),
@@ -36,9 +36,9 @@
 import Data.List (intersperse)
 import Data.String (IsString (..))
 import Data.Text (Text, pack, unpack)
-import Data.Yaml (FromJSON(..), ToJSON (toJSON), withText)
+import Data.Yaml (FromJSON (..), ToJSON (toJSON), withText)
 import GHC.Generics (Generic)
-import System.FilePath (splitFileName, (</>), isAbsolute)
+import System.FilePath (isAbsolute, splitFileName, (</>))
 import System.Info (os)
 import Text.Pandoc.Definition (Attr)
 
@@ -61,6 +61,7 @@
   | Plotsjl
   | PlantUML
   | SageMath
+  | D2
   deriving (Bounded, Eq, Enum, Generic, Ord)
 
 -- | This instance should only be used to display toolkit names
@@ -78,6 +79,7 @@
   show Plotsjl = "Julia/Plots.jl"
   show PlantUML = "PlantUML"
   show SageMath = "SageMath"
+  show D2 = "D2"
 
 -- | Class name which will trigger the filter
 cls :: Toolkit -> Text
@@ -94,21 +96,23 @@
 cls Plotsjl = "plotsjl"
 cls PlantUML = "plantuml"
 cls SageMath = "sageplot"
+cls D2 = "d2"
 
 -- | Executable program, and sometimes the directory where it can be found.
-data Executable 
+data Executable
   = AbsExe FilePath Text
   | RelExe Text
 
 exeFromPath :: FilePath -> Executable
 exeFromPath fp
-  | isAbsolute fp = let (dir, name) = splitFileName fp
-                     in AbsExe dir (pack name)
-  | otherwise     = RelExe (pack 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
+pathToExe (AbsExe dir name) = dir </> unpack name
+pathToExe (RelExe name) = unpack name
 
 -- | Source context for plotting scripts
 type Script = Text
@@ -238,13 +242,13 @@
     | s `elem` ["html", "HTML", ".html"] = HTML
     | s `elem` ["latex", "LaTeX", ".tex"] = LaTeX
     | otherwise =
-      errorWithoutStackTrace $
-        mconcat
-          [ s,
-            " is not one of the valid save formats : ",
-            mconcat $ intersperse ", " $ show <$> saveFormats,
-            " (and lowercase variations). "
-          ]
+        errorWithoutStackTrace $
+          mconcat
+            [ s,
+              " is not one of the valid save formats : ",
+              mconcat $ intersperse ", " $ show <$> saveFormats,
+              " (and lowercase variations). "
+            ]
     where
       saveFormats = enumFromTo minBound maxBound :: [SaveFormat]
 
diff --git a/src/Text/Pandoc/Filter/Plot/Parse.hs b/src/Text/Pandoc/Filter/Plot/Parse.hs
--- a/src/Text/Pandoc/Filter/Plot/Parse.hs
+++ b/src/Text/Pandoc/Filter/Plot/Parse.hs
@@ -39,25 +39,25 @@
     Inline,
     Pandoc (..),
   )
-import Text.Pandoc.Format (parseFlavoredFormat)
 import Text.Pandoc.Filter.Plot.Monad
 import Text.Pandoc.Filter.Plot.Renderers
+import Text.Pandoc.Format (parseFlavoredFormat)
 import Text.Pandoc.Options (ReaderOptions (..))
 import Text.Pandoc.Readers (Reader (..), getReader)
 
-tshow :: Show a => a -> Text
+tshow :: (Show a) => a -> Text
 tshow = pack . show
 
 data ParseFigureResult
-  -- | The block is not meant to become a figure
-  = NotAFigure
-  -- | The block is meant to become a figure
-  | PFigure FigureSpec
-  -- | The block is meant to become a figure, but the plotting toolkit is missing
-  | MissingToolkit Toolkit
-  -- | The block is meant to become a figure, but the figure format is incompatible 
-  --   with the plotting toolkit
-  | UnsupportedSaveFormat Toolkit SaveFormat
+  = -- | The block is not meant to become a figure
+    NotAFigure
+  | -- | The block is meant to become a figure
+    PFigure FigureSpec
+  | -- | The block is meant to become a figure, but the plotting toolkit is missing
+    MissingToolkit Toolkit
+  | -- | The block is meant to become a figure, but the figure format is incompatible
+    --   with the plotting toolkit
+    UnsupportedSaveFormat Toolkit SaveFormat
 
 -- | Determine inclusion specifications from @Block@ attributes.
 -- If an environment is detected, but the save format is incompatible,
@@ -105,7 +105,7 @@
 
           -- Decide between reading from file or using document content
           content <- parseContent block
-          
+
           defaultExe <- executable rendererToolkit
 
           let caption = Map.findWithDefault mempty (tshow CaptionK) attrs'
@@ -124,8 +124,8 @@
           _' <-
             unless (saveFormat `elem` rendererSupportedSaveFormats) $
               let msg = pack $ mconcat ["Save format ", show saveFormat, " not supported by ", show toolkit]
-              in err msg
-          
+               in err msg
+
           -- Ensure that the save format makes sense given the final conversion format, if known
           return $ PFigure (FigureSpec {..})
 -- Base case: block is not a CodeBlock
@@ -190,7 +190,7 @@
 parseFileDependencies t
   | t == mempty = mempty
   | otherwise =
-    fmap (normalise . unpack . T.dropAround isSpace)
-      . T.splitOn ","
-      . T.dropAround (\c -> c `elem` ['[', ']'])
-      $ t
+      fmap (normalise . unpack . T.dropAround isSpace)
+        . T.splitOn ","
+        . T.dropAround (\c -> c `elem` ['[', ']'])
+        $ t
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers.hs b/src/Text/Pandoc/Filter/Plot/Renderers.hs
--- a/src/Text/Pandoc/Filter/Plot/Renderers.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers.hs
@@ -33,38 +33,68 @@
 import qualified Data.Map.Strict as M
 import Data.Maybe (catMaybes, isJust)
 import Data.Text (Text, pack)
+import System.Directory (findExecutable)
 import System.Exit (ExitCode (..))
 import Text.Pandoc.Filter.Plot.Monad
 import Text.Pandoc.Filter.Plot.Monad.Logging
   ( Logger (lVerbosity),
   )
 import Text.Pandoc.Filter.Plot.Renderers.Bokeh
-    ( bokeh, bokehSupportedSaveFormats )
+  ( bokeh,
+    bokehSupportedSaveFormats,
+  )
+import Text.Pandoc.Filter.Plot.Renderers.D2
+  ( d2,
+    d2SupportedSaveFormats,
+  )
 import Text.Pandoc.Filter.Plot.Renderers.GGPlot2
-    ( ggplot2, ggplot2SupportedSaveFormats )
+  ( ggplot2,
+    ggplot2SupportedSaveFormats,
+  )
 import Text.Pandoc.Filter.Plot.Renderers.GNUPlot
-    ( gnuplot, gnuplotSupportedSaveFormats )
+  ( gnuplot,
+    gnuplotSupportedSaveFormats,
+  )
 import Text.Pandoc.Filter.Plot.Renderers.Graphviz
-    ( graphviz, graphvizSupportedSaveFormats )
+  ( graphviz,
+    graphvizSupportedSaveFormats,
+  )
 import Text.Pandoc.Filter.Plot.Renderers.Mathematica
-    ( mathematica, mathematicaSupportedSaveFormats )
+  ( mathematica,
+    mathematicaSupportedSaveFormats,
+  )
 import Text.Pandoc.Filter.Plot.Renderers.Matlab
-    ( matlab, matlabSupportedSaveFormats )
+  ( matlab,
+    matlabSupportedSaveFormats,
+  )
 import Text.Pandoc.Filter.Plot.Renderers.Matplotlib
-    ( matplotlib, matplotlibSupportedSaveFormats )
+  ( matplotlib,
+    matplotlibSupportedSaveFormats,
+  )
 import Text.Pandoc.Filter.Plot.Renderers.Octave
-    ( octave, octaveSupportedSaveFormats )
+  ( octave,
+    octaveSupportedSaveFormats,
+  )
 import Text.Pandoc.Filter.Plot.Renderers.PlantUML
-    ( plantuml, plantumlSupportedSaveFormats )
+  ( plantuml,
+    plantumlSupportedSaveFormats,
+  )
 import Text.Pandoc.Filter.Plot.Renderers.PlotlyPython
-    ( plotlyPython, plotlyPythonSupportedSaveFormats )
+  ( plotlyPython,
+    plotlyPythonSupportedSaveFormats,
+  )
 import Text.Pandoc.Filter.Plot.Renderers.PlotlyR
-    ( plotlyR, plotlyRSupportedSaveFormats )
+  ( plotlyR,
+    plotlyRSupportedSaveFormats,
+  )
 import Text.Pandoc.Filter.Plot.Renderers.Plotsjl
-    ( plotsjl, plotsjlSupportedSaveFormats )
+  ( plotsjl,
+    plotsjlSupportedSaveFormats,
+  )
 import Text.Pandoc.Filter.Plot.Renderers.SageMath
-    ( sagemath, sagemathSupportedSaveFormats )
-import System.Directory (findExecutable)
+  ( sagemath,
+    sagemathSupportedSaveFormats,
+  )
 
 -- | Get the renderer associated with a toolkit.
 -- If the renderer has not been used before,
@@ -83,6 +113,7 @@
 renderer Plotsjl = plotsjl
 renderer PlantUML = plantuml
 renderer SageMath = sagemath
+renderer D2 = d2
 
 -- | Save formats supported by this renderer.
 supportedSaveFormats :: Toolkit -> [SaveFormat]
@@ -99,6 +130,7 @@
 supportedSaveFormats Plotsjl = plotsjlSupportedSaveFormats
 supportedSaveFormats PlantUML = plantumlSupportedSaveFormats
 supportedSaveFormats SageMath = sagemathSupportedSaveFormats
+supportedSaveFormats D2 = d2SupportedSaveFormats
 
 -- | The function that maps from configuration to the preamble.
 preambleSelector :: Toolkit -> (Configuration -> Script)
@@ -115,13 +147,19 @@
 preambleSelector Plotsjl = plotsjlPreamble
 preambleSelector PlantUML = plantumlPreamble
 preambleSelector SageMath = sagemathPreamble
+preambleSelector D2 = d2Preamble
 
 -- | 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 = M.filterWithKey (\k _ -> k `elem` [ pack $ show MatplotlibTightBBoxK
-                                                               , pack $ show MatplotlibTransparentK
-                                                               ])
+parseExtraAttrs Matplotlib =
+  M.filterWithKey
+    ( \k _ ->
+        k
+          `elem` [ pack $ show MatplotlibTightBBoxK,
+                   pack $ show MatplotlibTransparentK
+                 ]
+    )
 parseExtraAttrs _ = return mempty
 
 -- | List of toolkits available on this machine.
@@ -139,22 +177,22 @@
 -- Note that logging is disabled
 availableToolkitsM :: PlotM [Toolkit]
 availableToolkitsM = asNonStrictAndSilent $ do
-    mtks <- forConcurrently toolkits $ \tk -> do
-      r <- renderer tk
-      exe <- executable tk
-      a <- isAvailable exe (rendererAvailability r)
-      if a
-        then return $ Just tk
-        else return Nothing
-    return $ catMaybes mtks
+  mtks <- forConcurrently toolkits $ \tk -> do
+    r <- renderer tk
+    exe <- executable tk
+    a <- isAvailable exe (rendererAvailability r)
+    if a
+      then return $ Just tk
+      else return Nothing
+  return $ catMaybes mtks
   where
-    asNonStrictAndSilent = local (\(RuntimeEnv f c l d s) -> RuntimeEnv f (c{strictMode = False}) (l{lVerbosity = Silent}) d s)
+    asNonStrictAndSilent = local (\(RuntimeEnv f c l d s) -> RuntimeEnv f (c {strictMode = False}) (l {lVerbosity = Silent}) d s)
 
-    -- | Check that the supplied command results in
+    -- \| Check that the supplied command results in
     -- an exit code of 0 (i.e. no errors)
     commandSuccess :: Text -> PlotM Bool
     commandSuccess s = do
-      cwd <- asks envCWD 
+      cwd <- asks envCWD
       (ec, _) <- runCommand cwd s
       debug $ mconcat ["Command ", s, " resulted in ", pack $ show ec]
       return $ ec == ExitSuccess
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/Bokeh.hs b/src/Text/Pandoc/Filter/Plot/Renderers/Bokeh.hs
--- a/src/Text/Pandoc/Filter/Plot/Renderers/Bokeh.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/Bokeh.hs
@@ -24,19 +24,19 @@
 
 bokeh :: PlotM Renderer
 bokeh = do
-      cmdargs <- asksConfig bokehCmdArgs
-      return $
-        Renderer
-          { rendererToolkit = Bokeh,
-            rendererCapture = appendCapture bokehCaptureFragment,
-            rendererCommand = bokehCommand cmdargs,
-            rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -c "import bokeh; import selenium"|],
-            rendererSupportedSaveFormats = bokehSupportedSaveFormats,
-            rendererChecks = [bokehCheckIfShow],
-            rendererLanguage = "python",
-            rendererComment = mappend "# ",
-            rendererScriptExtension = ".py"
-          }
+  cmdargs <- asksConfig bokehCmdArgs
+  return
+    $ Renderer
+      { rendererToolkit = Bokeh,
+        rendererCapture = appendCapture bokehCaptureFragment,
+        rendererCommand = bokehCommand cmdargs,
+        rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -c "import bokeh; import selenium"|],
+        rendererSupportedSaveFormats = bokehSupportedSaveFormats,
+        rendererChecks = [bokehCheckIfShow],
+        rendererLanguage = "python",
+        rendererComment = mappend "# ",
+        rendererScriptExtension = ".py"
+      }
 
 bokehSupportedSaveFormats :: [SaveFormat]
 bokehSupportedSaveFormats = [PNG, SVG, HTML]
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/D2.hs b/src/Text/Pandoc/Filter/Plot/Renderers/D2.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/D2.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+-- |
+-- Module      : $header$
+-- Copyright   : (c) Sanchayan Maity, 2023 - present
+-- License     : GNU GPL, version 2 or above
+-- Maintainer  : sanchayan@sanchayanmaity.net
+-- Stability   : internal
+-- Portability : portable
+--
+-- Rendering D2 plots code blocks
+module Text.Pandoc.Filter.Plot.Renderers.D2
+  ( d2,
+    d2SupportedSaveFormats,
+  )
+where
+
+import Text.Pandoc.Filter.Plot.Renderers.Prelude
+
+d2 :: PlotM Renderer
+d2 = do
+  cmdargs <- asksConfig d2CmdArgs
+  return
+    $ Renderer
+      { rendererToolkit = D2,
+        rendererCapture = d2Capture,
+        rendererCommand = d2Command cmdargs,
+        rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -v|],
+        rendererSupportedSaveFormats = d2SupportedSaveFormats,
+        rendererChecks = mempty,
+        rendererLanguage = "d2",
+        rendererComment = mappend "# ",
+        rendererScriptExtension = ".d2"
+      }
+
+d2SupportedSaveFormats :: [SaveFormat]
+d2SupportedSaveFormats = [PNG, PDF, SVG]
+
+d2Command :: Text -> OutputSpec -> Text
+d2Command cmdargs OutputSpec {..} = [st|#{pathToExe oExecutable} #{cmdargs} "#{oScriptPath}" "#{oFigurePath}"|]
+
+-- d2 export is entirely based on command-line arguments
+-- so there is no need to modify the script itself.
+d2Capture :: FigureSpec -> FilePath -> Script
+d2Capture FigureSpec {..} _ = script
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/GGPlot2.hs b/src/Text/Pandoc/Filter/Plot/Renderers/GGPlot2.hs
--- a/src/Text/Pandoc/Filter/Plot/Renderers/GGPlot2.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/GGPlot2.hs
@@ -23,19 +23,19 @@
 
 ggplot2 :: PlotM Renderer
 ggplot2 = do
-      cmdargs <- asksConfig ggplot2CmdArgs
-      return $
-        Renderer
-          { rendererToolkit = GGPlot2,
-            rendererCapture = ggplot2Capture,
-            rendererCommand = ggplot2Command cmdargs,
-            rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -e "if(!require('ggplot2')) {quit(status=1)}"|],
-            rendererSupportedSaveFormats = ggplot2SupportedSaveFormats,
-            rendererChecks = mempty,
-            rendererLanguage = "r",
-            rendererComment = mappend "# ",
-            rendererScriptExtension = ".r"
-          }
+  cmdargs <- asksConfig ggplot2CmdArgs
+  return
+    $ Renderer
+      { rendererToolkit = GGPlot2,
+        rendererCapture = ggplot2Capture,
+        rendererCommand = ggplot2Command cmdargs,
+        rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -e "if(!require('ggplot2')) {quit(status=1)}"|],
+        rendererSupportedSaveFormats = ggplot2SupportedSaveFormats,
+        rendererChecks = mempty,
+        rendererLanguage = "r",
+        rendererComment = mappend "# ",
+        rendererScriptExtension = ".r"
+      }
 
 ggplot2SupportedSaveFormats :: [SaveFormat]
 ggplot2SupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, TIF]
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/GNUPlot.hs b/src/Text/Pandoc/Filter/Plot/Renderers/GNUPlot.hs
--- a/src/Text/Pandoc/Filter/Plot/Renderers/GNUPlot.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/GNUPlot.hs
@@ -22,19 +22,19 @@
 
 gnuplot :: PlotM Renderer
 gnuplot = do
-      cmdargs <- asksConfig gnuplotCmdArgs
-      return $
-        Renderer
-          { rendererToolkit = GNUPlot,
-            rendererCapture = gnuplotCapture,
-            rendererCommand = gnuplotCommand cmdargs,
-            rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -h|],
-            rendererSupportedSaveFormats = gnuplotSupportedSaveFormats,
-            rendererChecks = mempty,
-            rendererLanguage = "gnuplot",
-            rendererComment = mappend "# ",
-            rendererScriptExtension = ".gp"
-          }
+  cmdargs <- asksConfig gnuplotCmdArgs
+  return
+    $ Renderer
+      { rendererToolkit = GNUPlot,
+        rendererCapture = gnuplotCapture,
+        rendererCommand = gnuplotCommand cmdargs,
+        rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -h|],
+        rendererSupportedSaveFormats = gnuplotSupportedSaveFormats,
+        rendererChecks = mempty,
+        rendererLanguage = "gnuplot",
+        rendererComment = mappend "# ",
+        rendererScriptExtension = ".gp"
+      }
 
 gnuplotSupportedSaveFormats :: [SaveFormat]
 gnuplotSupportedSaveFormats = [LaTeX, PNG, SVG, EPS, GIF, JPG, PDF]
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/Graphviz.hs b/src/Text/Pandoc/Filter/Plot/Renderers/Graphviz.hs
--- a/src/Text/Pandoc/Filter/Plot/Renderers/Graphviz.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/Graphviz.hs
@@ -23,19 +23,19 @@
 
 graphviz :: PlotM Renderer
 graphviz = do
-      cmdargs <- asksConfig graphvizCmdArgs
-      return $
-        Renderer
-          { rendererToolkit = Graphviz,
-            rendererCapture = graphvizCapture,
-            rendererCommand = graphvizCommand cmdargs,
-            rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -?|],
-            rendererSupportedSaveFormats = graphvizSupportedSaveFormats,
-            rendererChecks = mempty,
-            rendererLanguage = "dot",
-            rendererComment = mappend "// ",
-            rendererScriptExtension = ".dot"
-          }
+  cmdargs <- asksConfig graphvizCmdArgs
+  return
+    $ Renderer
+      { rendererToolkit = Graphviz,
+        rendererCapture = graphvizCapture,
+        rendererCommand = graphvizCommand cmdargs,
+        rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -?|],
+        rendererSupportedSaveFormats = graphvizSupportedSaveFormats,
+        rendererChecks = mempty,
+        rendererLanguage = "dot",
+        rendererComment = mappend "// ",
+        rendererScriptExtension = ".dot"
+      }
 
 graphvizSupportedSaveFormats :: [SaveFormat]
 graphvizSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, WEBP, GIF]
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/Mathematica.hs b/src/Text/Pandoc/Filter/Plot/Renderers/Mathematica.hs
--- a/src/Text/Pandoc/Filter/Plot/Renderers/Mathematica.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/Mathematica.hs
@@ -22,19 +22,20 @@
 
 mathematica :: PlotM Renderer
 mathematica = do
-      cmdargs <- asksConfig mathematicaCmdArgs
-      return $
-        Renderer
-          { rendererToolkit = Mathematica,
-            rendererCapture = mathematicaCapture,
-            rendererCommand = mathematicaCommand cmdargs,
-            rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -h|], -- TODO: test this
-            rendererSupportedSaveFormats = mathematicaSupportedSaveFormats,
-            rendererChecks = mempty,
-            rendererLanguage = "mathematica",
-            rendererComment = \t -> mconcat ["(*", t, "*)"],
-            rendererScriptExtension = ".m"
-          }
+  cmdargs <- asksConfig mathematicaCmdArgs
+  return
+    $ Renderer
+      { rendererToolkit = Mathematica,
+        rendererCapture = mathematicaCapture,
+        rendererCommand = mathematicaCommand cmdargs,
+        rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -h|],
+        -- TODO: test this
+        rendererSupportedSaveFormats = mathematicaSupportedSaveFormats,
+        rendererChecks = mempty,
+        rendererLanguage = "mathematica",
+        rendererComment = \t -> mconcat ["(*", t, "*)"],
+        rendererScriptExtension = ".m"
+      }
 
 mathematicaSupportedSaveFormats :: [SaveFormat]
 mathematicaSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, GIF, TIF]
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/Matlab.hs b/src/Text/Pandoc/Filter/Plot/Renderers/Matlab.hs
--- a/src/Text/Pandoc/Filter/Plot/Renderers/Matlab.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/Matlab.hs
@@ -22,32 +22,32 @@
 
 matlab :: PlotM Renderer
 matlab = do
-      cmdargs <- asksConfig matlabCmdArgs
-      return $
-        Renderer
-          { rendererToolkit = Matlab,
-            rendererCapture = matlabCapture,
-            rendererCommand = matlabCommand cmdargs,
-            -- On Windows at least, "matlab -help"  actually returns -1, even though the
-            -- help text is shown successfully!
-            -- Therefore, we cannot rely on this behavior to know if matlab is present,
-            -- like other toolkits.
-            rendererAvailability = ExecutableExists,
-            rendererSupportedSaveFormats = matlabSupportedSaveFormats,
-            rendererChecks = mempty,
-            rendererLanguage = "matlab",
-            rendererComment = mappend "% ",
-            rendererScriptExtension = ".m"
-          }
+  cmdargs <- asksConfig matlabCmdArgs
+  return
+    $ Renderer
+      { rendererToolkit = Matlab,
+        rendererCapture = matlabCapture,
+        rendererCommand = matlabCommand cmdargs,
+        -- On Windows at least, "matlab -help"  actually returns -1, even though the
+        -- help text is shown successfully!
+        -- Therefore, we cannot rely on this behavior to know if matlab is present,
+        -- like other toolkits.
+        rendererAvailability = ExecutableExists,
+        rendererSupportedSaveFormats = matlabSupportedSaveFormats,
+        rendererChecks = mempty,
+        rendererLanguage = "matlab",
+        rendererComment = mappend "% ",
+        rendererScriptExtension = ".m"
+      }
 
 matlabSupportedSaveFormats :: [SaveFormat]
 matlabSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, GIF, TIF]
 
-matlabCommand :: Text  -> OutputSpec -> Text
-matlabCommand cmdargs OutputSpec {..} = 
+matlabCommand :: Text -> OutputSpec -> Text
+matlabCommand cmdargs OutputSpec {..} =
   -- The MATLAB 'run' function will switch to the directory where the script
   -- is located before executing the script. Therefore, we first save the current
-  -- working directory in the variable 'pandoc_plot_cwd' so that we can use it 
+  -- working directory in the variable 'pandoc_plot_cwd' so that we can use it
   -- when exporting the figure
   [st|#{pathToExe oExecutable} #{cmdargs} -sd '#{oCWD}' -noFigureWindows -batch "pandoc_plot_cwd=pwd; run('#{oScriptPath}')"|]
 
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/Matplotlib.hs b/src/Text/Pandoc/Filter/Plot/Renderers/Matplotlib.hs
--- a/src/Text/Pandoc/Filter/Plot/Renderers/Matplotlib.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/Matplotlib.hs
@@ -29,19 +29,19 @@
 
 matplotlib :: PlotM Renderer
 matplotlib = do
-      cmdargs <- asksConfig matplotlibCmdArgs
-      return $
-        Renderer
-          { rendererToolkit = Matplotlib,
-            rendererCapture = matplotlibCapture,
-            rendererCommand = matplotlibCommand cmdargs,
-            rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -c "import matplotlib"|],
-            rendererSupportedSaveFormats = matplotlibSupportedSaveFormats,
-            rendererChecks = [matplotlibCheckIfShow],
-            rendererLanguage = "python",
-            rendererComment = mappend "# ",
-            rendererScriptExtension = ".py"
-          }
+  cmdargs <- asksConfig matplotlibCmdArgs
+  return
+    $ Renderer
+      { rendererToolkit = Matplotlib,
+        rendererCapture = matplotlibCapture,
+        rendererCommand = matplotlibCommand cmdargs,
+        rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -c "import matplotlib"|],
+        rendererSupportedSaveFormats = matplotlibSupportedSaveFormats,
+        rendererChecks = [matplotlibCheckIfShow],
+        rendererLanguage = "python",
+        rendererComment = mappend "# ",
+        rendererScriptExtension = ".py"
+      }
 
 matplotlibSupportedSaveFormats :: [SaveFormat]
 matplotlibSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, GIF, TIF]
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/Octave.hs b/src/Text/Pandoc/Filter/Plot/Renderers/Octave.hs
--- a/src/Text/Pandoc/Filter/Plot/Renderers/Octave.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/Octave.hs
@@ -22,19 +22,19 @@
 
 octave :: PlotM Renderer
 octave = do
-      cmdargs <- asksConfig octaveCmdArgs
-      return $
-        Renderer
-          { rendererToolkit = Octave,
-            rendererCapture = octaveCapture,
-            rendererCommand = octaveCommand cmdargs,
-            rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -h|],
-            rendererSupportedSaveFormats = octaveSupportedSaveFormats,
-            rendererChecks = mempty,
-            rendererLanguage = "matlab",
-            rendererComment = mappend "% ",
-            rendererScriptExtension = ".m"
-          }
+  cmdargs <- asksConfig octaveCmdArgs
+  return
+    $ Renderer
+      { rendererToolkit = Octave,
+        rendererCapture = octaveCapture,
+        rendererCommand = octaveCommand cmdargs,
+        rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -h|],
+        rendererSupportedSaveFormats = octaveSupportedSaveFormats,
+        rendererChecks = mempty,
+        rendererLanguage = "matlab",
+        rendererComment = mappend "% ",
+        rendererScriptExtension = ".m"
+      }
 
 octaveSupportedSaveFormats :: [SaveFormat]
 octaveSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, GIF, TIF]
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/PlantUML.hs b/src/Text/Pandoc/Filter/Plot/Renderers/PlantUML.hs
--- a/src/Text/Pandoc/Filter/Plot/Renderers/PlantUML.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/PlantUML.hs
@@ -24,19 +24,19 @@
 
 plantuml :: PlotM Renderer
 plantuml = do
-      cmdargs <- asksConfig plantumlCmdArgs
-      return $
-        Renderer
-          { rendererToolkit = PlantUML,
-            rendererCapture = plantumlCapture,
-            rendererCommand = plantumlCommand cmdargs,
-            rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} #{cmdargs} -h|],
-            rendererSupportedSaveFormats = plantumlSupportedSaveFormats,
-            rendererChecks = mempty,
-            rendererLanguage = "plantuml",
-            rendererComment = mappend "' ",
-            rendererScriptExtension = ".txt"
-          }
+  cmdargs <- asksConfig plantumlCmdArgs
+  return
+    $ Renderer
+      { rendererToolkit = PlantUML,
+        rendererCapture = plantumlCapture,
+        rendererCommand = plantumlCommand cmdargs,
+        rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} #{cmdargs} -h|],
+        rendererSupportedSaveFormats = plantumlSupportedSaveFormats,
+        rendererChecks = mempty,
+        rendererLanguage = "plantuml",
+        rendererComment = mappend "' ",
+        rendererScriptExtension = ".txt"
+      }
 
 plantumlSupportedSaveFormats :: [SaveFormat]
 plantumlSupportedSaveFormats = [PNG, PDF, SVG]
@@ -45,10 +45,10 @@
 plantumlCommand cmdargs OutputSpec {..} =
   let fmt = fmap toLower . show . saveFormat $ oFigureSpec
       dir = takeDirectory oFigurePath
-    -- the command below works as long as the script name is the same basename
-    -- as the target figure path. E.g.: script basename of pandocplot123456789.txt
-    -- will result in pandocplot123456789.(extension)
-   in [st|#{pathToExe oExecutable} #{cmdargs} -t#{fmt} -output "#{oCWD </> dir}" "#{normalizePath oScriptPath}"|]
+   in -- the command below works as long as the script name is the same basename
+      -- as the target figure path. E.g.: script basename of pandocplot123456789.txt
+      -- will result in pandocplot123456789.(extension)
+      [st|#{pathToExe oExecutable} #{cmdargs} -t#{fmt} -output "#{oCWD </> dir}" "#{normalizePath oScriptPath}"|]
 
 normalizePath :: String -> String
 normalizePath = map f
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/PlotlyPython.hs b/src/Text/Pandoc/Filter/Plot/Renderers/PlotlyPython.hs
--- a/src/Text/Pandoc/Filter/Plot/Renderers/PlotlyPython.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/PlotlyPython.hs
@@ -22,19 +22,19 @@
 
 plotlyPython :: PlotM Renderer
 plotlyPython = do
-      cmdargs <- asksConfig plotlyPythonCmdArgs
-      return $
-            Renderer
-              { rendererToolkit = PlotlyPython,
-                rendererCapture = plotlyPythonCapture,
-                rendererCommand = plotlyPythonCommand cmdargs,
-                rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -c "import plotly.graph_objects"|],
-                rendererSupportedSaveFormats = plotlyPythonSupportedSaveFormats,
-                rendererChecks = mempty,
-                rendererLanguage = "python",
-                rendererComment = mappend "# ",
-                rendererScriptExtension = ".py"
-              }
+  cmdargs <- asksConfig plotlyPythonCmdArgs
+  return
+    $ Renderer
+      { rendererToolkit = PlotlyPython,
+        rendererCapture = plotlyPythonCapture,
+        rendererCommand = plotlyPythonCommand cmdargs,
+        rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -c "import plotly.graph_objects"|],
+        rendererSupportedSaveFormats = plotlyPythonSupportedSaveFormats,
+        rendererChecks = mempty,
+        rendererLanguage = "python",
+        rendererComment = mappend "# ",
+        rendererScriptExtension = ".py"
+      }
 
 plotlyPythonSupportedSaveFormats :: [SaveFormat]
 plotlyPythonSupportedSaveFormats = [PNG, JPG, WEBP, PDF, SVG, EPS, HTML]
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/PlotlyR.hs b/src/Text/Pandoc/Filter/Plot/Renderers/PlotlyR.hs
--- a/src/Text/Pandoc/Filter/Plot/Renderers/PlotlyR.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/PlotlyR.hs
@@ -23,19 +23,19 @@
 
 plotlyR :: PlotM Renderer
 plotlyR = do
-      cmdargs <- asksConfig plotlyRCmdArgs
-      return $
-        Renderer
-          { rendererToolkit = PlotlyR,
-            rendererCapture = plotlyRCapture,
-            rendererCommand = plotlyRCommand cmdargs,
-            rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -e "if(!require('plotly')) {quit(status=1)}"|],
-            rendererSupportedSaveFormats = plotlyRSupportedSaveFormats,
-            rendererChecks = mempty,
-            rendererLanguage = "r",
-            rendererComment = mappend "# ",
-            rendererScriptExtension = ".r"
-          }
+  cmdargs <- asksConfig plotlyRCmdArgs
+  return
+    $ Renderer
+      { rendererToolkit = PlotlyR,
+        rendererCapture = plotlyRCapture,
+        rendererCommand = plotlyRCommand cmdargs,
+        rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -e "if(!require('plotly')) {quit(status=1)}"|],
+        rendererSupportedSaveFormats = plotlyRSupportedSaveFormats,
+        rendererChecks = mempty,
+        rendererLanguage = "r",
+        rendererComment = mappend "# ",
+        rendererScriptExtension = ".r"
+      }
 
 plotlyRSupportedSaveFormats :: [SaveFormat]
 plotlyRSupportedSaveFormats = [PNG, PDF, SVG, JPG, EPS, HTML]
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/Plotsjl.hs b/src/Text/Pandoc/Filter/Plot/Renderers/Plotsjl.hs
--- a/src/Text/Pandoc/Filter/Plot/Renderers/Plotsjl.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/Plotsjl.hs
@@ -22,26 +22,26 @@
 
 plotsjl :: PlotM Renderer
 plotsjl = do
-      cmdargs <- asksConfig plotsjlCmdArgs
-      return $
-        Renderer
-          { rendererToolkit = Plotsjl,
-            rendererCapture = plotsjlCapture,
-            rendererCommand = plotsjlCommand cmdargs,
-            rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -e "using Plots"|],
-            rendererSupportedSaveFormats = plotsjlSupportedSaveFormats,
-            rendererChecks = mempty,
-            rendererLanguage = "julia",
-            rendererComment = mappend "# ",
-            rendererScriptExtension = ".jl"
-          }
+  cmdargs <- asksConfig plotsjlCmdArgs
+  return
+    $ Renderer
+      { rendererToolkit = Plotsjl,
+        rendererCapture = plotsjlCapture,
+        rendererCommand = plotsjlCommand cmdargs,
+        rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -e "using Plots"|],
+        rendererSupportedSaveFormats = plotsjlSupportedSaveFormats,
+        rendererChecks = mempty,
+        rendererLanguage = "julia",
+        rendererComment = mappend "# ",
+        rendererScriptExtension = ".jl"
+      }
 
 -- Save formats support by most backends
 -- https://docs.plotsjl.org/latest/output/#Supported-output-file-formats-1
 plotsjlSupportedSaveFormats :: [SaveFormat]
 plotsjlSupportedSaveFormats = [PNG, SVG, PDF]
 
-plotsjlCommand :: Text ->  OutputSpec -> Text
+plotsjlCommand :: Text -> OutputSpec -> Text
 plotsjlCommand cmdargs OutputSpec {..} = [st|#{pathToExe oExecutable} #{cmdargs} -- "#{oScriptPath}"|]
 
 plotsjlCapture :: FigureSpec -> FilePath -> Script
diff --git a/src/Text/Pandoc/Filter/Plot/Renderers/SageMath.hs b/src/Text/Pandoc/Filter/Plot/Renderers/SageMath.hs
--- a/src/Text/Pandoc/Filter/Plot/Renderers/SageMath.hs
+++ b/src/Text/Pandoc/Filter/Plot/Renderers/SageMath.hs
@@ -22,19 +22,19 @@
 
 sagemath :: PlotM Renderer
 sagemath = do
-      cmdargs <- asksConfig sagemathCmdArgs
-      return $
-        Renderer
-          { rendererToolkit = SageMath,
-            rendererCapture = sagemathCapture,
-            rendererCommand = sagemathCommand cmdargs,
-            rendererAvailability = CommandSuccess  $ \exe -> [st|#{pathToExe exe} -v|],
-            rendererSupportedSaveFormats = sagemathSupportedSaveFormats,
-            rendererChecks = mempty,
-            rendererLanguage = "sagemath",
-            rendererComment = mappend "# ",
-            rendererScriptExtension = ".sage"
-          }
+  cmdargs <- asksConfig sagemathCmdArgs
+  return
+    $ Renderer
+      { rendererToolkit = SageMath,
+        rendererCapture = sagemathCapture,
+        rendererCommand = sagemathCommand cmdargs,
+        rendererAvailability = CommandSuccess $ \exe -> [st|#{pathToExe exe} -v|],
+        rendererSupportedSaveFormats = sagemathSupportedSaveFormats,
+        rendererChecks = mempty,
+        rendererLanguage = "sagemath",
+        rendererComment = mappend "# ",
+        rendererScriptExtension = ".sage"
+      }
 
 -- See here:
 -- https://doc.sagemath.org/html/en/reference/plotting/sage/plot/graphics.html#sage.plot.graphics.Graphics.save
diff --git a/src/Text/Pandoc/Filter/Plot/Scripting.hs b/src/Text/Pandoc/Filter/Plot/Scripting.hs
--- a/src/Text/Pandoc/Filter/Plot/Scripting.hs
+++ b/src/Text/Pandoc/Filter/Plot/Scripting.hs
@@ -38,9 +38,9 @@
   ( addExtension,
     normalise,
     replaceExtension,
-    takeDirectory,
-    (</>), 
     takeBaseName,
+    takeDirectory,
+    (</>),
   )
 import Text.Pandoc.Class (runPure)
 import Text.Pandoc.Definition (Block (CodeBlock), Pandoc (Pandoc))
@@ -116,7 +116,7 @@
       let scriptWithCapture = rendererCapture renderer_ spec target
 
       -- Note the use of a lock. This is a crude solution for issue #53, where
-      -- multiple identical figures can cause a race condition to write to the 
+      -- multiple identical figures can cause a race condition to write to the
       -- same output file.
       sem <- asks envIOLock
       liftIO $ withMVar sem $ \_ -> T.writeFile scriptPath scriptWithCapture
@@ -188,7 +188,7 @@
   fh <- figureContentHash spec
   let ext = extension . saveFormat $ spec
       -- MATLAB will refuse to process files that don't start with
-      -- a letter so it is simplest to use filenames that start 
+      -- a letter so it is simplest to use filenames that start
       -- with "pandocplot" throughout
       stem = flip addExtension ext . mappend "pandocplot" . show $ fh
   return $ normalise $ directory spec </> stem
@@ -205,9 +205,9 @@
             -- 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)
-        
+
         -- Note the use of a lock. This is a crude solution for issue #53, where
-        -- multiple identical figures can cause a race condition to write to the 
+        -- multiple identical figures can cause a race condition to write to the
         -- same output file.
         sem <- asks envIOLock
         liftIO $ withMVar sem $ \_ -> T.writeFile scp t
diff --git a/tests/Common.hs b/tests/Common.hs
--- a/tests/Common.hs
+++ b/tests/Common.hs
@@ -98,7 +98,8 @@
 
     let cb =
           ( addPreamble (include tk) $
-              addDirectory tempDir $ codeBlock tk (trivialContent tk)
+              addDirectory tempDir $
+                codeBlock tk (trivialContent tk)
           )
     _ <- runPlotM Nothing defaultTestConfig $ make cb
     inclusion <- readFile (include tk)
@@ -119,6 +120,7 @@
     include Plotsjl = "tests/includes/plotsjl.jl"
     include PlantUML = "tests/includes/plantuml.txt"
     include SageMath = "tests/includes/sagemath.sage"
+    include D2 = "tests/includes/d2-dd.d2"
 
 -------------------------------------------------------------------------------
 -- Test that the files are saved in the appropriate format
@@ -131,11 +133,13 @@
     let fmt = head (supportedSaveFormats tk)
         cb =
           ( addSaveFormat fmt $
-              addDirectory tempDir $ codeBlock tk (trivialContent tk)
+              addDirectory tempDir $
+                codeBlock tk (trivialContent tk)
           )
     _ <- runPlotM Nothing defaultTestConfig $ make cb
     numberjpgFiles <-
-      length <$> filter (isExtensionOf (extension fmt))
+      length
+        <$> filter (isExtensionOf (extension fmt))
         <$> listDirectory tempDir
     assertEqual "" numberjpgFiles 1
 
@@ -143,7 +147,7 @@
 -- Test that the appropriate error is raised when trying to save figures
 -- in an incompatible format
 testSaveFormatIncompatibility :: Toolkit -> TestTree
-testSaveFormatIncompatibility tk = 
+testSaveFormatIncompatibility tk =
   testCase "raises the appropriate error on save format incompatibility" $ do
     let allSaveFormats = enumFromTo minBound maxBound :: [SaveFormat]
         incompatibleFormats = S.toList $ S.difference (S.fromList allSaveFormats) (S.fromList $ supportedSaveFormats tk)
@@ -179,16 +183,16 @@
             addDirectory tempDir $
               addCaption expected $
                 codeBlock tk (trivialContent tk)
-    blockNoSource   <- runPlotM Nothing defaultTestConfig $ make noSource
+    blockNoSource <- runPlotM Nothing defaultTestConfig $ make noSource
     blockWithSource <- runPlotM Nothing 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.Plain $ B.toList (fromString expected)] (extractCaption blockNoSource)
+    assertEqual "" [B.Plain $ B.toList (fromString expected)] (extractCaption blockNoSource)
     assertNotEqual "" [B.Plain $ B.toList (fromString expected)] (extractCaption blockWithSource)
   where
-    extractCaption (B.Figure _ (Caption _ caption) _) = caption 
+    extractCaption (B.Figure _ (Caption _ caption) _) = caption
 
 -------------------------------------------------------------------------------
 -- Test that it is possible to change the source code label in captions
@@ -211,7 +215,7 @@
     let [Plain [Space, _, B.Link _ ils _, _]] = extractCaption blockWithSource
     assertEqual "" (B.toList $ B.str "Test label") ils
   where
-    extractCaption (B.Figure _ (Caption _ caption) _) = caption 
+    extractCaption (B.Figure _ (Caption _ caption) _) = caption
 
     linkLabel (B.Plain [B.Link _ ils _]) = B.fromList ils
     linkLabel _ = mempty
@@ -248,10 +252,12 @@
         _ <- runPlotM Nothing config $ make cb
 
         numberPngFiles <-
-          length <$> filter (isExtensionOf (extension PNG))
+          length
+            <$> filter (isExtensionOf (extension PNG))
             <$> listDirectory (defaultDirectory config)
         numberJpgFiles <-
-          length <$> filter (isExtensionOf (extension JPG))
+          length
+            <$> filter (isExtensionOf (extension JPG))
             <$> listDirectory (defaultDirectory config)
         assertEqual "" numberPngFiles 1
         assertEqual "" numberJpgFiles 0
@@ -276,7 +282,7 @@
     result <- runPlotM Nothing (defaultTestConfig {captionFormat = fmt}) $ make cb
     assertIsInfix expected (extractCaption result)
   where
-    extractCaption (B.Figure _ (Caption _ caption) _) = caption 
+    extractCaption (B.Figure _ (Caption _ caption) _) = caption
 
 -------------------------------------------------------------------------------
 -- Test that Markdown bold formatting in captions is correctly rendered
@@ -298,7 +304,7 @@
     result <- runPlotM Nothing (defaultTestConfig {captionFormat = fmt}) $ make cb
     assertIsInfix expected (extractCaption result)
   where
-    extractCaption (B.Figure _ (Caption _ caption) _) = caption 
+    extractCaption (B.Figure _ (Caption _ caption) _) = caption
 
 -------------------------------------------------------------------------------
 -- Test that cleanOutpuDirs correctly cleans the output directory specified in a block.
@@ -353,19 +359,26 @@
     -- Note that this test is fragile, in the sense that the expected result must be carefully
     -- constructed
     let expectedAttrs = ("hello", [cls tk], [("key1", "val1"), ("key2", "val2")])
-        cb = setAttrs expectedAttrs $
-              addDirectory tempDir $
-                addCaption "[title](https://google.com)" $
-                  codeBlock tk (trivialContent tk)
+        cb =
+          setAttrs expectedAttrs $
+            addDirectory tempDir $
+              addCaption "[title](https://google.com)" $
+                codeBlock tk (trivialContent tk)
         fmt = B.Format "markdown"
-    Figure (id', _, keyvals) _ _ <- runPlotM Nothing (defaultTestConfig { captionFormat = fmt
-                                                                        , defaultDirectory = tempDir
-                                                                        }) $ make cb
+    Figure (id', _, keyvals) _ _ <-
+      runPlotM
+        Nothing
+        ( defaultTestConfig
+            { captionFormat = fmt,
+              defaultDirectory = tempDir
+            }
+        )
+        $ make cb
     let (expectedId, _, expectedKeyVals) = expectedAttrs
     assertEqual "identifier" expectedId id'
     assertEqual "key-value pairs" expectedKeyVals keyvals
   where
-    extractCaption (B.Figure _ (Caption _ caption) _) = caption 
+    extractCaption (B.Figure _ (Caption _ caption) _) = caption
 
 codeBlock :: Toolkit -> Script -> Block
 codeBlock tk script = CodeBlock (mempty, [cls tk], mempty) script
@@ -389,6 +402,7 @@
 trivialContent Plotsjl = "using Plots; x = 1:10; y = rand(10); plot(x, y);"
 trivialContent PlantUML = "@startuml\nAlice -> Bob: test\n@enduml"
 trivialContent SageMath = "G = plot(sin, 1, 10)"
+trivialContent D2 = "x -> y -> z"
 
 addCaption :: String -> Block -> Block
 addCaption caption (CodeBlock (id', cls, attrs) script) =
@@ -418,7 +432,7 @@
 setAttrs attrs (CodeBlock _ script) = CodeBlock attrs script
 
 -- | Assert that a file exists
-assertFileExists :: HasCallStack => FilePath -> Assertion
+assertFileExists :: (HasCallStack) => FilePath -> Assertion
 assertFileExists filepath = do
   fileExists <- doesFileExist filepath
   unless fileExists (assertFailure msg)
@@ -455,5 +469,5 @@
     else return ()
   createDirectory dir
 
-tshow :: Show a => a -> Text
+tshow :: (Show a) => a -> Text
 tshow = pack . show
diff --git a/tests/includes/d2-dd.d2 b/tests/includes/d2-dd.d2
new file mode 100644
--- /dev/null
+++ b/tests/includes/d2-dd.d2
diff --git a/tests/includes/sagemath.sage b/tests/includes/sagemath.sage
new file mode 100644
--- /dev/null
+++ b/tests/includes/sagemath.sage
