89 | $endif$
90 | $body$
91 | $for(include-after)$
92 | $include-after$
93 | $endfor$
94 |
95 |
96 |
--------------------------------------------------------------------------------
/src/Knit/Utilities/Streamly.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE CPP #-}
2 | {-# LANGUAGE DataKinds #-}
3 | {-# LANGUAGE DerivingVia #-}
4 | {-# LANGUAGE FlexibleContexts #-}
5 | {-# LANGUAGE GADTs #-}
6 | {-# LANGUAGE GeneralizedNewtypeDeriving #-}
7 | {-# LANGUAGE OverloadedStrings #-}
8 | {-# LANGUAGE RankNTypes #-}
9 | {-# LANGUAGE ScopedTypeVariables #-}
10 | {-# LANGUAGE TypeApplications #-}
11 | {-# LANGUAGE UnboxedTuples #-} -- This is required for the PrimMonad instance
12 | {-# LANGUAGE UndecidableInstances #-}
13 |
14 | module Knit.Utilities.Streamly
15 | (
16 | StreamlyM
17 | , StreamlyEffects(..)
18 | , streamlyToKnit
19 | , logStreamly
20 | , errStreamly
21 | )
22 | where
23 |
24 | import Prelude hiding (error)
25 | import qualified Knit.Effect.Logger as Knit.Logger
26 |
27 | import qualified Polysemy
28 |
29 | import Control.Monad.Catch (MonadThrow, MonadCatch)
30 | import qualified Control.Monad.Primitive as Prim
31 | import Control.Exception (throwIO)
32 | import qualified Text.Pandoc as PA
33 | import Control.Monad.Base (MonadBase)
34 | import Control.Monad.Trans.Control (MonadBaseControl)
35 | import qualified Data.Text as Text
36 |
37 | -- | record-of-functions to hold access to effects we want to have available in this
38 | -- ReaderT over IO wrapper for Streamly
39 | data StreamlyEffects = StreamlyEffects { logIO :: Knit.Logger.LogSeverity -> Text.Text -> IO (), error :: (forall a . Text -> IO a) }
40 |
41 | -- | Use the logging function in the Reader to log in a StreamlyM context.
42 | logStreamly :: Knit.Logger.LogSeverity -> Text.Text -> StreamlyM ()
43 | logStreamly ls t = do
44 | logFunction <- asks logIO
45 | liftIO $ logFunction ls t
46 | {-# INLINEABLE logStreamly #-}
47 |
48 | errStreamly :: Text -> StreamlyM a
49 | errStreamly msg = do
50 | errFunction <- asks error
51 | liftIO $ errFunction msg
52 | {-# INLINEABLE errStreamly #-}
53 |
54 | -- | IO with a ReaderT layer we can use to expose effects we need. For now just logging.
55 | #if MIN_VERSION_streamly(0,9,0)
56 | newtype StreamlyM a = StreamlyM { unStreamlyM :: ReaderT StreamlyEffects IO a }
57 | deriving newtype (Functor, Applicative, Monad, MonadReader StreamlyEffects)
58 | deriving (MonadThrow, MonadCatch, MonadIO, Prim.PrimMonad, MonadBase IO, MonadBaseControl IO) via (ReaderT StreamlyEffects IO)
59 | #else
60 | newtype StreamlyM a = StreamlyM { unStreamlyM :: ReaderT StreamlyEffects IO a }
61 | deriving newtype (Functor, Applicative, Monad, MonadReader StreamlyEffects)
62 | deriving (MonadThrow, MonadCatch, MonadIO, Prim.PrimMonad, MonadBase IO, MonadBaseControl IO) via (ReaderT StreamlyEffects IO)
63 | #endif
64 |
65 | -- | lift a 'StreamlyM' computation into a 'Knit.Sem' computation
66 | streamlyToKnit :: (Polysemy.Member (Polysemy.Embed IO) r
67 | , Knit.Logger.LogWithPrefixesLE r
68 | )
69 | => StreamlyM a -> Polysemy.Sem r a
70 | streamlyToKnit sa = do
71 | curPrefix <- Knit.Logger.getPrefix
72 | let logFunction = Knit.Logger.logWithPrefixToIO
73 | errFunction msg = throwIO $ PA.PandocSomeError $ "(from streamlyToKnit) " <> toText msg
74 | se = StreamlyEffects (\ls lmsg -> logFunction curPrefix (Knit.Logger.LogEntry ls lmsg)) errFunction
75 | Polysemy.embed $ runReaderT (unStreamlyM sa) se
76 | {-# INLINEABLE streamlyToKnit #-}
77 |
--------------------------------------------------------------------------------
/src/Knit/Report/Input/Visualization/Hvega.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE ExtendedDefaultRules #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | {-# LANGUAGE ScopedTypeVariables #-}
4 | {-# LANGUAGE FlexibleContexts #-}
5 | {-# LANGUAGE TypeOperators #-}
6 | {-# LANGUAGE DataKinds #-}
7 | {-# LANGUAGE GADTs #-}
8 | {-|
9 | Module : Knit.Report.Input.Visualization.Hvega
10 | Description : Support functions for simple reports using Pandoc
11 | Copyright : (c) Adam Conner-Sax 2019
12 | License : BSD-3-Clause
13 | Maintainer : adam_conner_sax@yahoo.com
14 | Stability : experimental
15 |
16 | Functions to add hvega charts (using Blaze Html) to the current Pandoc document.
17 | -}
18 | module Knit.Report.Input.Visualization.Hvega
19 | (
20 | -- * Add hvega Inputs
21 | addHvega
22 | , addHvega'
23 | )
24 | where
25 |
26 | import Knit.Report.Input.Html.Blaze ( addBlaze )
27 |
28 | import qualified Data.Aeson.Encode.Pretty as A
29 | import qualified Data.ByteString.Lazy.Char8 as BS
30 | import qualified Data.Text as T
31 | import qualified Graphics.Vega.VegaLite as GV
32 | import qualified Text.Blaze.Html5 as BH
33 | import qualified Text.Blaze.Html5.Attributes as BHA
34 |
35 | import qualified Polysemy as P
36 | import qualified Knit.Effect.Pandoc as PE
37 | import qualified Knit.Effect.PandocMonad as PM
38 | import qualified Knit.Effect.UnusedId as KUI
39 |
40 |
41 | -- TODO: Add some autogenerated unique id support
42 |
43 | -- | Add hvega (via html). Requires html since vega-lite renders using javascript.
44 | addHvega'
45 | :: ( PM.PandocEffects effs
46 | , P.Member PE.ToPandoc effs
47 | , P.Member KUI.UnusedId effs
48 | )
49 | => Maybe T.Text -- ^ figure id, will get next unused with prefix "figure" if Nothing
50 | -> Maybe T.Text -- ^ figure caption, none if Nothing
51 | -> Bool
52 | -> GV.VegaLite
53 | -> P.Sem effs T.Text
54 | addHvega' idTextM captionTextM svg vl = do
55 | PE.require PE.VegaSupport
56 | idText <- maybe (KUI.getNextUnusedId "figure") return idTextM
57 | addBlaze $ placeVisualization idText captionTextM svg vl
58 | return idText
59 |
60 | addHvega
61 | :: ( PM.PandocEffects effs
62 | , P.Member PE.ToPandoc effs
63 | , P.Member KUI.UnusedId effs
64 | )
65 | => Maybe T.Text -- ^ figure id, will get next unused with prefix "figure" if Nothing
66 | -> Maybe T.Text -- ^ figure caption, none if Nothing
67 | -> GV.VegaLite
68 | -> P.Sem effs T.Text
69 | addHvega idTextM captionTextM vl = addHvega' idTextM captionTextM False vl
70 |
71 | -- | Build (Blaze) Html for hvega visualization with the given id
72 | placeVisualization :: T.Text -> Maybe T.Text -> Bool -> GV.VegaLite -> BH.Html
73 | placeVisualization idText captionTextM svg vl =
74 | let vegaScript :: T.Text =
75 | decodeUtf8 $ BS.toStrict $ A.encodePretty $ GV.fromVL vl
76 | script =
77 | "var vlSpec=\n"
78 | <> vegaScript
79 | <> ";\n"
80 | <> "vegaEmbed(\'#"
81 | <> idText
82 | <> "\',vlSpec"
83 | <> (if svg then ",{\"renderer\": \"svg\"}" else ",{\"renderer\": \"canvas\"}")
84 | <> ");"
85 | in BH.figure BH.! BHA.id (BH.toValue idText) $ do
86 | BH.script BH.! BHA.type_ "text/javascript" $ BH.preEscapedToHtml script
87 | whenJust captionTextM (BH.figcaption . BH.toHtml)
88 |
--------------------------------------------------------------------------------
/src/Knit/Report/Input/Table/Colonnade.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE FlexibleContexts #-}
2 | {-# LANGUAGE GADTs #-}
3 | {-# LANGUAGE OverloadedStrings #-}
4 | {-|
5 | Module : Knit.Report.Input.Latex
6 | Description : Support functions for adding LaTeX fragments into a report
7 | Copyright : (c) Adam Conner-Sax 2019
8 | License : BSD-3-Clause
9 | Maintainer : adam_conner_sax@yahoo.com
10 | Stability : experimental
11 |
12 | Functions to add table fragments, using , to the current Pandoc.
13 |
14 | Notes:
15 |
16 | 1. The way these tables render will depend entirely on how styling is set in the pandoc template. For Html, this will be determined by css in the template.
17 | Styling put in here does not make it through to Html output.
18 | If you need specific styling, you can use the same colonnade functions these do and embed raw html. Somehow.
19 | -}
20 | module Knit.Report.Input.Table.Colonnade
21 | (
22 | -- * Add Colonnade table fragments
23 | addColonnadeTextTable
24 | , addColonnadeHtmlTable
25 | , addColonnadeCellTable
26 | -- * Re-exports
27 | , module Colonnade
28 | , module Text.Blaze.Colonnade
29 | )
30 | where
31 |
32 | import qualified Colonnade as C
33 | import Colonnade
34 | import qualified Text.Blaze.Colonnade as BC
35 | import Text.Blaze.Colonnade
36 | import qualified Text.Blaze.Html as BH
37 | import qualified Text.Blaze.Html5.Attributes as BHA
38 | import Knit.Report.Input.Html.Blaze ( addBlaze )
39 |
40 | import qualified Polysemy as P
41 | import qualified Knit.Effect.Pandoc as PE
42 | import qualified Knit.Effect.PandocMonad as PM
43 |
44 | -- | Add a table given a Colonnade representation producing text
45 | addColonnadeTextTable
46 | :: (PM.PandocEffects effs, P.Member PE.ToPandoc effs, Foldable f)
47 | => C.Colonnade C.Headed a Text -- ^ How to encode data as columns
48 | -> f a -- ^ collection of data
49 | -> P.Sem effs ()
50 | addColonnadeTextTable colonnade rows = do
51 | let toCell t = BC.Cell (BHA.style "border: 1px solid black") (BH.toHtml t) -- styling here gets lost. But maybe I can fix?
52 | addBlaze $ BC.encodeCellTable
53 | (BHA.style "border: 1px solid black; border-collapse: collapse") -- this gets lost. Leaving it here in case I fix that!
54 | (fmap toCell colonnade)
55 | rows
56 |
57 | -- | Add a Html Table
58 | addColonnadeHtmlTable
59 | :: (PM.PandocEffects effs, P.Member PE.ToPandoc effs, Foldable f)
60 | => BH.Attribute -- ^ Attributes of
Html element, currently unused by knit-haskell
61 | -> C.Colonnade C.Headed a BH.Html -- ^ How to encode data as columns
62 | -> f a -- ^ collection of data
63 | -> P.Sem effs ()
64 | addColonnadeHtmlTable attr colonnade rows =
65 | addBlaze $ BC.encodeHtmlTable attr colonnade rows
66 |
67 | -- | Add a Cell Table
68 | addColonnadeCellTable
69 | :: (PM.PandocEffects effs, P.Member PE.ToPandoc effs, Foldable f)
70 | => BH.Attribute -- ^ Attributes of
Html element, currently unused by knit-haskell
71 | -> C.Colonnade C.Headed a BC.Cell -- ^ How to encode data as columns
72 | -> f a -- ^ collection of data
73 | -> P.Sem effs ()
74 | addColonnadeCellTable attr colonnade rows =
75 | addBlaze $ BC.encodeCellTable attr colonnade rows
76 |
--------------------------------------------------------------------------------
/src/Knit/Report/Error.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE AllowAmbiguousTypes #-}
2 | {-# LANGUAGE ConstraintKinds #-}
3 | {-# LANGUAGE DataKinds #-}
4 | {-# LANGUAGE ExtendedDefaultRules #-}
5 | {-# LANGUAGE FlexibleContexts #-}
6 | {-# LANGUAGE GADTs #-}
7 | {-# LANGUAGE OverloadedStrings #-}
8 | {-# LANGUAGE PolyKinds #-}
9 | {-# LANGUAGE Rank2Types #-}
10 | {-# LANGUAGE ScopedTypeVariables #-}
11 | {-# LANGUAGE TypeOperators #-}
12 | {-# LANGUAGE TypeApplications #-}
13 | {-# LANGUAGE TypeFamilies #-}
14 | {-# LANGUAGE UndecidableInstances #-}
15 | {-|
16 | Module : Knit.Report.Error
17 | Description : knit-haskell functions to handle and raise errors in knit-haskell reports.
18 | Copyright : (c) Adam Conner-Sax 2019
19 | License : BSD-3-Clause
20 | Maintainer : adam_conner_sax@yahoo.com
21 | Stability : experimental
22 |
23 | This module has various combinators for simplifying the throwing of Errors in knit-haskell.
24 |
25 | are available, and might be useful for seeing how all this works.
26 |
27 | -}
28 | module Knit.Report.Error
29 | (
30 | -- * Error combinators
31 | knitError
32 | , knitMaybe
33 | , knitMaybeM
34 | , knitEither
35 | , knitEitherM
36 | , knitMapError
37 | )
38 | where
39 |
40 | import qualified Knit.Report.EffectStack as K
41 | import qualified Text.Pandoc.Error as PA
42 | import Knit.Effect.PandocMonad ( textToPandocText )
43 |
44 | import qualified Data.Text as T
45 |
46 | import qualified Polysemy as P
47 | import qualified Polysemy.Error as PE
48 |
49 |
50 |
51 | -- | Throw an error with a specific message. This will emerge as a 'PandocSomeError' in order
52 | -- to avoid complicating the error type.
53 | -- NB: The Member constraint is satisfied by KnitEffectStack m.
54 | knitError :: P.Member (PE.Error PA.PandocError) r => T.Text -> P.Sem r a
55 | knitError msg =
56 | PE.throw (PA.PandocSomeError $ textToPandocText $ "Knit User Error: " <> msg)
57 |
58 | -- | Throw on 'Nothing' with given message. This will emerge as a 'PandocSomeError' in order
59 | -- to avoid complicating the error type.
60 | knitMaybe
61 | :: P.Member (PE.Error PA.PandocError) r => T.Text -> Maybe a -> P.Sem r a
62 | knitMaybe msg = maybe (knitError msg) pure
63 |
64 | -- | Throw on 'Nothing' with given message. This will emerge as a 'PandocSomeError' in order
65 | -- to avoid complicating the error type.
66 | knitMaybeM
67 | :: P.Member (PE.Error PA.PandocError) r => T.Text -> Maybe (P.Sem r a) -> P.Sem r a
68 | knitMaybeM msg = fromMaybe (knitError msg)
69 |
70 | -- | Throw on 'Left' with message. This will emerge as a 'PandocSomeError' in order
71 | -- to avoid complicating the error type.
72 | knitEither
73 | :: P.Member (PE.Error PA.PandocError) r => Either T.Text a -> P.Sem r a
74 | knitEither = either knitError return
75 |
76 | -- | Throw on 'Left' with message. This will emerge as a 'PandocSomeError' in order
77 | -- to avoid complicating the error type.
78 | knitEitherM
79 | :: P.Member (PE.Error PA.PandocError) r => Either T.Text (P.Sem r a) -> P.Sem r a
80 | knitEitherM = either knitError id
81 |
82 | -- | Map an error type, @e, into a 'PandocError' so it will be handled in this stack
83 | knitMapError
84 | :: forall e r a
85 | . K.KnitEffects r
86 | => (e -> T.Text)
87 | -> P.Sem (PE.Error e ': r) a
88 | -> P.Sem r a
89 | knitMapError f = PE.mapError $ PA.PandocSomeError . textToPandocText . f
90 |
--------------------------------------------------------------------------------
/docs/mtl_example.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | knit-haskell mtl example
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
Some example markdown
24 |
25 |
Markdown
27 | is a nice way to write formatted notes with a minimum of
28 | code.
29 |
It supports links and tables and some styling
30 | information.
31 |
32 |
33 |
34 |
Some example latex
35 |
Overused favorite equation: \(e^{i\pi} + 1 = 0\)
37 |
38 |
39 |
An example hvega visualization
40 |
41 |
65 |
66 |
67 |
70 |
An example of getting env from a base monad, and time
71 | from the Pandoc Effects.
72 |
This is from the MyApp environment.
73 |
2022-03-08 00:33:27.304602 UTC
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/src/Knit/Report/Input/Visualization/Diagrams.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE ExtendedDefaultRules #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | {-# LANGUAGE ScopedTypeVariables #-}
4 | {-# LANGUAGE FlexibleContexts #-}
5 | {-# LANGUAGE TypeOperators #-}
6 | {-# LANGUAGE DataKinds #-}
7 | {-# LANGUAGE GADTs #-}
8 | {-|
9 | Module : Knit.Report.Input.Visualization.Diagrams
10 | Description : Support addition of Diagrams to knitted reports.
11 | Copyright : (c) Adam Conner-Sax 2019
12 | License : BSD-3-Clause
13 | Maintainer : adam_conner_sax@yahoo.com
14 | Stability : experimental
15 |
16 | Functions to Diagrams (from the Diagrams library) to the current Pandoc document.
17 | -}
18 | module Knit.Report.Input.Visualization.Diagrams
19 | (
20 | -- * Add Diagrams Inputs
21 | addDiagramAsSVG
22 | -- * Diagrams Re-Exports
23 | , module Diagrams.Prelude
24 | , module Diagrams.Backend.SVG
25 | )
26 | where
27 |
28 | import Knit.Report.Input.Html.Blaze ( addBlaze )
29 | import Text.Blaze.Html ( preEscapedLazyText
30 | , toValue
31 | )
32 | import qualified Text.Blaze.Html5 as BH
33 | import qualified Text.Blaze.Html5.Attributes as BHA
34 |
35 | import qualified Data.Text as T
36 | --import Data.Maybe ( fromMaybe )
37 |
38 | import qualified Diagrams.Prelude as D
39 | import Diagrams.Prelude hiding ( trace ) -- this conflicts with Pandoc trace. TO get it, you'll need to import it directly
40 | --import qualified Diagrams.TwoD.Size as D
41 | import qualified Diagrams.Backend.SVG as DSVG
42 | import Diagrams.Backend.SVG
43 | import qualified Graphics.Svg as SVG
44 |
45 | import qualified Polysemy as P
46 | import qualified Knit.Effect.Pandoc as PE
47 | import qualified Knit.Effect.PandocMonad as PM
48 | import qualified Knit.Effect.UnusedId as KUI
49 |
50 | -- | Add diagram (via SVG inserted as HTML) with user supplied width and height.
51 | addDiagramAsSVG
52 | :: ( PM.PandocEffects effs
53 | , P.Member PE.ToPandoc effs
54 | , P.Member KUI.UnusedId effs
55 | )
56 | => Maybe T.Text -- ^ id attribute for figure. Will use next unused "figure" id if Nothing
57 | -> Maybe T.Text -- ^ caption for figure
58 | -> Double -- ^ width in pixels (?)
59 | -> Double -- ^ height in pixels (?)
60 | -> D.QDiagram DSVG.SVG D.V2 Double D.Any-- ^ diagram
61 | -> P.Sem effs T.Text
62 | addDiagramAsSVG idTextM captionTextM wPixels hPixels diagram = do
63 | idText <- maybe (KUI.getNextUnusedId "figure") return idTextM
64 | let svgOptions =
65 | DSVG.SVGOptions (D.dims2D wPixels hPixels) Nothing idText [] False
66 | addDiagramAsSVGWithOptions (Just idText) captionTextM svgOptions diagram
67 |
68 | -- | Add diagram (via SVG inserted as HTML) with user-supplied options.
69 | addDiagramAsSVGWithOptions
70 | :: ( PM.PandocEffects effs
71 | , P.Member PE.ToPandoc effs
72 | , P.Member KUI.UnusedId effs
73 | )
74 | => Maybe T.Text -- ^ id attribute for figure, will use next unsed "figure" id if nothing
75 | -> Maybe T.Text -- ^ caption for figure
76 | -> DSVG.Options DSVG.SVG D.V2 Double
77 | -> D.QDiagram DSVG.SVG D.V2 Double D.Any-- ^ diagram
78 | -> P.Sem effs T.Text
79 | addDiagramAsSVGWithOptions idTextM captionTextM svgOptions diagram = do
80 | idText <- maybe (KUI.getNextUnusedId "figure") return idTextM
81 | addBlaze $ BH.figure BH.! BHA.id (toValue idText) $ do
82 | preEscapedLazyText $ SVG.renderText $ D.renderDia DSVG.SVG
83 | svgOptions
84 | diagram
85 | whenJust captionTextM (BH.figcaption . BH.toHtml)
86 | return idText
87 |
--------------------------------------------------------------------------------
/src/Knit/Effect/Docs.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE DataKinds #-}
2 | {-# LANGUAGE DeriveFunctor #-}
3 | {-# LANGUAGE DeriveTraversable #-}
4 | {-# LANGUAGE FlexibleContexts #-}
5 | {-# LANGUAGE GADTs #-}
6 | {-# LANGUAGE RankNTypes #-}
7 | {-# LANGUAGE PolyKinds #-}
8 | {-# LANGUAGE ScopedTypeVariables #-}
9 | {-# LANGUAGE StandaloneDeriving #-}
10 | {-# LANGUAGE TypeOperators #-}
11 | {-# OPTIONS_GHC -fwarn-incomplete-patterns #-}
12 | {-|
13 | Module : Knit.Effect.Docs
14 | Description : Polysemy effect for creating a list of named documents
15 | Copyright : (c) Adam Conner-Sax 2019
16 | License : BSD-3-Clause
17 | Maintainer : adam_conner_sax@yahoo.com
18 | Stability : experimental
19 |
20 | effect used by knit-haskell when one code-base is used to create multiple docs.
21 | Each can be created and then stored in the
22 | list maintained by this effect. Then, when the effects are "run", this list can be processed to produce the required
23 | output.
24 | -}
25 | module Knit.Effect.Docs
26 | (
27 | -- * Effect
28 | Docs
29 |
30 | -- * Actions
31 | , newDoc
32 |
33 | -- * Interpretations
34 | , toDocList
35 | , toDocListWith
36 | , toDocListWithM
37 |
38 | -- * Helper Types
39 | , DocWithInfo(..)
40 |
41 | -- * Helper Functions
42 | , mapDocs
43 | , mapDocsM
44 | )
45 | where
46 |
47 | import qualified Polysemy as P
48 | import Polysemy.Internal ( send )
49 | import qualified Polysemy.Writer as P
50 | import qualified Control.Monad
51 |
52 | -- | GADT to represent storing a document and some info for processing it.
53 | data Docs i a m r where
54 | NewDoc ::i -> a -> Docs i a m ()
55 |
56 | -- | Action of the 'Docs' Effect. Store a document.
57 | newDoc :: P.Member (Docs i a) effs => i -> a -> P.Sem effs ()
58 | newDoc info doc = send $ NewDoc info doc
59 |
60 | -- | Data type to hold one document with info of type @i@ and doc of type @a@.
61 | data DocWithInfo i a = DocWithInfo { dwiInfo :: i, dwiDoc :: a }
62 | deriving instance Functor (DocWithInfo i)
63 | deriving instance Foldable (DocWithInfo i)
64 | deriving instance Traversable (DocWithInfo i)
65 |
66 | -- | Intepret 'Docs' in @Polysemy.Writer [DocWithInfo i a]'
67 | toWriter
68 | :: P.Sem (Docs i a ': effs) ()
69 | -> P.Sem (P.Writer [DocWithInfo i a] ': effs) ()
70 | toWriter = P.reinterpret f
71 | where
72 | f :: Docs i a m x -> P.Sem (P.Writer [DocWithInfo i a] ': effs) x
73 | f (NewDoc i d) = P.tell [DocWithInfo i d]
74 |
75 | -- | Interpret 'Docs' (via 'Polysemy.Writer'), producing a list of @DocWithInfo i a@
76 | toDocList :: P.Sem (Docs i a ': effs) () -> P.Sem effs [DocWithInfo i a]
77 | toDocList = fmap fst . P.runWriter . toWriter
78 |
79 | -- | Map over the doc part of @Functor m => m [DocWithInfo i a]@ with an @a->b@ resulting in @m [DocWithInfo i b]@
80 | mapDocs
81 | :: Monad m => (i -> a -> b) -> m [DocWithInfo i a] -> m [DocWithInfo i b]
82 | mapDocs f = fmap (fmap (\(DocWithInfo i a) -> DocWithInfo i (f i a)))
83 |
84 | -- | Map over the doc part of @Monad m => m [DocWithInfo i a]@ with @a -> m b@ resulting in @m [DocWithInfo i b]@
85 | mapDocsM
86 | :: Monad m => (i -> a -> m b) -> m [DocWithInfo i a] -> m [DocWithInfo i b]
87 | mapDocsM f = mapM sequenceA Control.Monad.<=< mapDocs f --(traverse (traverse f) =<<)
88 |
89 | -- | Combine the interpretation and mapping step.
90 | -- Commonly used to "run" the effect and map the results to your desired output format.
91 | toDocListWith
92 | :: (i -> a -> b)
93 | -> P.Sem (Docs i a ': effs) ()
94 | -> P.Sem effs [DocWithInfo i b]
95 | toDocListWith f = mapDocs f . toDocList
96 |
97 | -- | Combine the interpretation and effectful mapping step.
98 | -- Commonly used to "run" the effect and map the results to your deisred output format.
99 | toDocListWithM
100 | :: (i -> a -> P.Sem effs b)
101 | -> P.Sem (Docs i a ': effs) ()
102 | -> P.Sem effs [DocWithInfo i b]
103 | toDocListWithM f = mapDocsM f . toDocList
104 |
105 |
--------------------------------------------------------------------------------
/examples/MultiDocExample.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE QuasiQuotes #-}
2 | {-# LANGUAGE FlexibleContexts #-}
3 | {-# LANGUAGE OverloadedStrings #-}
4 | {-# LANGUAGE TypeApplications #-}
5 | {-# LANGUAGE GADTs #-}
6 | module Main where
7 |
8 | import qualified Knit.Report as K
9 |
10 | import qualified Data.Map as M
11 | import qualified Data.Text.IO as T
12 | import qualified Data.Text.Lazy as TL
13 | import qualified Data.Text as T
14 | import Data.String.Here ( here )
15 | import qualified Graphics.Vega.VegaLite as V
16 |
17 | templateVars :: M.Map String String
18 | templateVars = M.fromList
19 | [ ("lang" , "English")
20 | , ("author" , "Adam Conner-Sax")
21 | , ("pagetitle", "knit-haskell simple multi-doc example")
22 | -- , ("tufte","True")
23 | ]
24 |
25 | main :: IO ()
26 | main = do
27 | let template = K.FromIncludedTemplateDir "pandoc-adaptive-bootstrap-KH.html"
28 | tvWithCss <- K.addCss (K.FullySpecifiedCssPath "css/pandoc-bootstrap.css")
29 | templateVars
30 | pandocWriterConfig <- K.mkPandocWriterConfig template
31 | tvWithCss
32 | K.mindocOptionsF
33 | let knitConfig = (K.defaultKnitConfig Nothing)
34 | { K.outerLogPrefix = Just "MultiDocExample.Main"
35 | , K.logIf = K.logAll
36 | , K.pandocWriterConfig = pandocWriterConfig
37 | }
38 |
39 | resE <- K.knitHtmls knitConfig $ do
40 | K.newPandoc (K.PandocInfo "multi_doc1" M.empty) makeDoc1
41 | K.newPandoc (K.PandocInfo "multi_doc2" M.empty) makeDoc2
42 | case resE of
43 | Right namedDocs ->
44 | K.writeAllPandocResultsWithInfoAsHtml "docs" namedDocs
45 | Left err -> putStrLn $ "pandoc error: " ++ show err
46 |
47 | md1 :: T.Text
48 | md1 = [here|
49 | ## Some example markdown
50 | * [Markdown][MarkdownLink] is a nice way to write formatted notes with a minimum of code.
51 | * It supports links and tables and some *styling* information.
52 |
53 | [MarkDownLink]:
54 | |]
55 |
56 | makeDoc1 :: K.KnitOne effs => K.Sem effs ()
57 | makeDoc1 = K.wrapPrefix "makeDoc1" $ do
58 | K.logLE K.Info "adding some markdown."
59 | K.addMarkDown md1
60 | K.logLE K.Info "adding some latex."
61 | K.addMarkDown "## Some example latex (Doc 1)"
62 | K.addLatex "Overused favorite equation: $e^{i\\pi} + 1 = 0$"
63 | K.logLE K.Info "adding a visualization."
64 | K.addMarkDown "## An example hvega visualization (Doc 1)"
65 | _ <- K.addHvega Nothing Nothing exampleVis
66 | return ()
67 |
68 | md2 :: T.Text
69 | md2 = [here|
70 | ## Some example markdown
71 | * This is some more markdown! Now for document 2. It's still a nice way to write formatted notes with a minimum of code.
72 | * It supports links and tables and some *styling* information.
73 |
74 | [MarkDownLink]:
75 | |]
76 |
77 | makeDoc2 :: K.KnitOne effs => K.Sem effs ()
78 | makeDoc2 = K.wrapPrefix "makeDoc2" $ do
79 | K.logLE K.Info "adding some markdown."
80 | K.addMarkDown md2
81 | K.logLE K.Info "adding some latex."
82 | K.addMarkDown "## Some example latex (Doc 2)"
83 | K.addLatex "A different equation: $a^2 + b^2 = c^2$"
84 | K.logLE K.Info "adding a visualization."
85 | K.addMarkDown "## An example hvega visualization (Doc 2)"
86 | _ <- K.addHvega Nothing Nothing exampleVis
87 | return ()
88 |
89 | exampleVis :: V.VegaLite
90 | exampleVis =
91 | let cars =
92 | V.dataFromUrl "https://vega.github.io/vega-datasets/data/cars.json" []
93 | enc =
94 | V.encoding
95 | . V.position V.X [V.PName "Horsepower", V.PmType V.Quantitative]
96 | . V.position V.Y [V.PName "Miles_per_Gallon", V.PmType V.Quantitative]
97 | . V.color [V.MName "Origin", V.MmType V.Nominal]
98 | bkg = V.background "rgba(0, 0, 0, 0.05)"
99 | in V.toVegaLite [bkg, cars, V.mark V.Circle [], enc []]
100 |
101 |
--------------------------------------------------------------------------------
/src/Knit/Report/Other/Blaze.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE ExtendedDefaultRules #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | {-# LANGUAGE ScopedTypeVariables #-}
4 | {-# LANGUAGE TypeApplications #-}
5 | {-|
6 | Module : Knit.Report.Other.Blaze
7 | Description : Support functions for simple reports in Blaze
8 | Copyright : (c) Adam Conner-Sax 2019
9 | License : BSD-3-Clause
10 | Maintainer : adam_conner_sax@yahoo.com
11 | Stability : experimental
12 |
13 | Functions to support some simple reports using only Blaze.
14 |
15 | Using the Pandoc framework instead is recommended.
16 | -}
17 | module Knit.Report.Other.Blaze
18 | (
19 | -- * Add relevant headers, scripts
20 | makeReportHtml
21 | -- * add report pieces
22 | , placeVisualization
23 | , placeTextSection
24 | , latexToHtml
25 | , latex_
26 | )
27 | where
28 |
29 | import qualified Data.Aeson.Encode.Pretty as A
30 | import qualified Data.ByteString.Lazy.Char8 as BS
31 | import qualified Graphics.Vega.VegaLite as GV
32 | import qualified Text.Blaze.Html5 as H
33 | import Text.Blaze.Html5 ( (!) )
34 | import qualified Text.Blaze.Html5.Attributes as HA
35 | import qualified Text.Pandoc as P
36 |
37 | -- | Convert Latex to Blaze Html
38 | latexToHtml :: Text -> H.Html
39 | latexToHtml lText = do
40 | let
41 | latexReadOptions = P.def
42 | htmlWriteOptions = P.def { P.writerHTMLMathMethod = P.MathJax "" }
43 | asHtml =
44 | P.readLaTeX latexReadOptions lText >>= P.writeHtml5String htmlWriteOptions
45 | case P.runPure asHtml of
46 | Left err -> H.span (H.toHtml $ show @String err)
47 | Right htmlText -> H.span (H.preEscapedToHtml htmlText)
48 |
49 | latex_ :: Text -> H.Html
50 | latex_ = latexToHtml
51 |
52 | mathJaxScript :: H.Html
53 | mathJaxScript =
54 | H.script
55 | ! HA.src
56 | "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML"
57 | ! HA.async ""
58 | $ ""
59 |
60 |
61 |
62 | vegaScripts2 :: H.Html
63 | vegaScripts2 = do
64 | H.script ! HA.src "https://cdn.jsdelivr.net/npm/vega@4.4.0" $ ""
65 | H.script ! HA.src "https://cdn.jsdelivr.net/npm/vega-lite@3.0.0-rc11" $ ""
66 | H.script ! HA.src "https://cdn.jsdelivr.net/npm/vega-embed@3.28.0" $ ""
67 |
68 | vegaScripts3 :: H.Html
69 | vegaScripts3 = do
70 | H.script ! HA.src "https://cdn.jsdelivr.net/npm/vega@4.4.0/build/vega.js" $ ""
71 | H.script
72 | ! HA.src
73 | "https://cdn.jsdelivr.net/npm/vega-lite@3.0.0-rc12/build/vega-lite.js"
74 | $ ""
75 | H.script
76 | ! HA.src
77 | "https://cdn.jsdelivr.net/npm/vega-embed@3.29.1/build/vega-embed.js"
78 | $ ""
79 |
80 | -- | Add headers for using Tufte css
81 | tufteSetup :: H.Html
82 | tufteSetup = do
83 | H.link ! HA.rel "stylesheet" ! HA.href
84 | "https://cdnjs.cloudflare.com/ajax/libs/tufte-css/1.4/tufte.min.css"
85 | H.meta ! HA.name "viewport" ! HA.content "width=device-width, initial-scale=1"
86 |
87 | -- | Wrap given html in appropriate headers for the hvega and latex functions to work
88 | makeReportHtml :: Text -> H.Html -> H.Html
89 | makeReportHtml title reportHtml = H.html $ H.docTypeHtml $ do
90 | H.head $ do
91 | H.title (H.toHtml title)
92 | tufteSetup
93 | mathJaxScript
94 | vegaScripts2
95 | H.body $ H.article reportHtml
96 |
97 | -- | Add an hvega visualization with the given id
98 | placeVisualization :: Text -> GV.VegaLite -> H.Html
99 | placeVisualization idText vl =
100 | let vegaScript :: Text =
101 | decodeUtf8 $ BS.toStrict $ A.encodePretty $ GV.fromVL vl
102 | script =
103 | "var vlSpec=\n"
104 | <> vegaScript
105 | <> ";\n"
106 | <> "vegaEmbed(\'#"
107 | <> idText
108 | <> "\',vlSpec);"
109 | in H.figure
110 | ! HA.id (H.toValue idText)
111 | $ H.script
112 | ! HA.type_ "text/javascript"
113 | $ H.preEscapedToHtml script
114 |
115 | -- | Add the given Html as a new section
116 | placeTextSection :: H.Html -> H.Html
117 | placeTextSection = H.section
118 |
119 |
--------------------------------------------------------------------------------
/src/Knit/Report/Other/Lucid.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE ExtendedDefaultRules #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | {-# LANGUAGE ScopedTypeVariables #-}
4 | {-# LANGUAGE TypeApplications #-}
5 | {-|
6 | Module : Knit.Report.Other.Lucid
7 | Description : freer-simple random effect
8 | Copyright : (c) Adam Conner-Sax 2019
9 | License : BSD-3-Clause
10 | Maintainer : adam_conner_sax@yahoo.com
11 | Stability : experimental
12 |
13 | Functions to support some simple reports using Lucid. Particularly to support adding latex and hvega charts.
14 | -}
15 | module Knit.Report.Other.Lucid
16 | (
17 | -- * Setup, headers, scripts, etc.
18 | makeReportHtml
19 | -- * add specific report bits
20 | , placeVisualization
21 | , placeTextSection
22 | , latexToHtml
23 | -- * helpers
24 | , latex_
25 | )
26 | where
27 |
28 | import qualified Data.Aeson.Encode.Pretty as A
29 | import qualified Data.ByteString.Lazy.Char8 as BS
30 | import qualified Data.Text.Encoding as T
31 | import qualified Graphics.Vega.VegaLite as GV
32 | import qualified Lucid as H
33 | import qualified Text.Pandoc as P
34 |
35 | -- | Convert Latex to Lucid Html
36 | latexToHtml :: Text -> H.Html ()
37 | latexToHtml lText = do
38 | let latexReadOptions = P.def
39 | htmlWriteOptions = P.def { P.writerHTMLMathMethod = P.MathJax "" }
40 | asHtml = P.readLaTeX latexReadOptions lText >>= P.writeHtml5String htmlWriteOptions
41 | case P.runPure asHtml of
42 | Left err -> H.span_ (H.toHtml $ show @String err)
43 | Right htmlText -> H.span_ (H.toHtmlRaw htmlText)
44 |
45 | -- | Convert Latex to Lucid Html
46 | latex_ :: Text -> H.Html ()
47 | latex_ = latexToHtml
48 |
49 | -- | Add headers for MathJax
50 | mathJaxScript :: H.Html ()
51 | mathJaxScript = H.script_ [H.src_ "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML", H.async_ ""] ("" :: String)
52 |
53 | -- | Add headers to use vega-lite (v2)
54 | vegaScripts2 :: H.Html ()
55 | vegaScripts2 = do
56 | H.script_ [H.src_ "https://cdn.jsdelivr.net/npm/vega@4.4.0"] ("" :: String)
57 | H.script_ [H.src_ "https://cdn.jsdelivr.net/npm/vega-lite@3.0.0-rc11"] ("" :: String)
58 | H.script_ [H.src_ "https://cdn.jsdelivr.net/npm/vega-embed@3.28.0"] ("" :: String)
59 |
60 | -- | Add headers to use vega-lite (v3)
61 | vegaScripts3 :: H.Html ()
62 | vegaScripts3 = do
63 | H.script_ [H.src_ "https://cdn.jsdelivr.net/npm/vega@4.4.0/build/vega.js"] ("" :: String)
64 | H.script_ [H.src_ "https://cdn.jsdelivr.net/npm/vega-lite@3.0.0-rc12/build/vega-lite.js"] ("" :: String)
65 | H.script_ [H.src_ "https://cdn.jsdelivr.net/npm/vega-embed@3.29.1/build/vega-embed.js"] ("" :: String)
66 |
67 | -- | Add headers to use Tufte css
68 | tufteSetup :: H.Html ()
69 | tufteSetup = do
70 | H.link_ [H.rel_ "stylesheet", H.href_ "https://cdnjs.cloudflare.com/ajax/libs/tufte-css/1.4/tufte.min.css"]
71 | H.meta_ [H.name_ "viewport", H.content_"width=device-width, initial-scale=1"]
72 |
73 | -- | -- | wrap given html in appropriate headers for the hvega and latex functions to work
74 | makeReportHtml :: Text -> H.Html a -> H.Html a
75 | makeReportHtml title reportHtml = H.html_ $ htmlHead >> H.body_ (H.article_ reportHtml) where
76 | htmlHead :: H.Html () = H.head_ (do
77 | H.title_ (H.toHtmlRaw title)
78 | tufteSetup
79 | mathJaxScript
80 | vegaScripts2
81 | return ()
82 | )
83 |
84 | -- | add an hvega visualization with the given id
85 | placeVisualization :: Text -> GV.VegaLite -> H.Html ()
86 | placeVisualization idText vl =
87 | let vegaScript :: Text = T.decodeUtf8 $ BS.toStrict $ A.encodePretty $ GV.fromVL vl
88 | script = "var vlSpec=\n" <> vegaScript <> ";\n" <> "vegaEmbed(\'#" <> idText <> "\',vlSpec);"
89 | in H.figure_ [H.id_ idText] (H.script_ [H.type_ "text/javascript"] (H.toHtmlRaw script))
90 |
91 | -- | add the given Html as a new section
92 | placeTextSection :: H.Html () -> H.Html ()
93 | placeTextSection = H.section_ [{- attributes/styles here -}]
94 |
95 |
--------------------------------------------------------------------------------
/docs/multi_doc2.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | knit-haskell simple multi-doc example
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
Some example markdown
48 |
49 |
This is some more markdown! Now for document 2. It's
50 | still a nice way to write formatted notes with a minimum of
51 | code.
52 |
It supports links and tables and some styling
53 | information.