├── data ├── pandoc-data │ └── data │ │ └── templates │ │ ├── default.haddock │ │ ├── default.jira │ │ ├── default.textile │ │ ├── default.xwiki │ │ ├── default.dokuwiki │ │ ├── default.mediawiki │ │ ├── default.zimwiki │ │ ├── default.opml │ │ ├── default.plain │ │ ├── default.commonmark │ │ ├── default.markdown │ │ ├── default.org │ │ ├── default.asciidoc │ │ ├── default.man │ │ ├── default.texinfo │ │ ├── default.asciidoctor │ │ ├── default.rst │ │ ├── default.rtf │ │ ├── default.muse │ │ ├── default.docbook5 │ │ ├── default.docbook4 │ │ ├── default.tei │ │ ├── default.epub3 │ │ ├── default.epub2 │ │ ├── default.latex.rej │ │ ├── default.icml │ │ ├── default.html5 │ │ ├── default.html4 │ │ ├── default.slidy │ │ ├── default.ms │ │ ├── default.s5 │ │ ├── default.slideous │ │ ├── default.context │ │ ├── default.dzslides │ │ ├── default.jats │ │ └── default.opendocument └── knit-haskell-templates │ ├── pandoc-bootstrap-KH.html │ ├── pandoc-adaptive-bootstrap-KH.html │ └── mindoc-pandoc-KH.html ├── cabal.project ├── .gitignore ├── hie.yaml ├── src └── Knit │ ├── Report │ ├── Input │ │ ├── Html │ │ │ ├── Lucid.hs │ │ │ └── Blaze.hs │ │ ├── Latex.hs │ │ ├── Html.hs │ │ ├── MarkDown │ │ │ └── PandocMarkDown.hs │ │ ├── RST.hs │ │ ├── Visualization │ │ │ ├── Hvega.hs │ │ │ └── Diagrams.hs │ │ └── Table │ │ │ └── Colonnade.hs │ ├── Error.hs │ ├── Other │ │ ├── Blaze.hs │ │ └── Lucid.hs │ ├── Output.hs │ └── Output │ │ └── Html.hs │ ├── Effect │ ├── Internal │ │ └── Logger.hs │ ├── UnusedId.hs │ ├── Docs.hs │ ├── Timer.hs │ └── Html.hs │ ├── Utilities │ └── Streamly.hs │ └── Report.hs ├── .github └── workflows │ └── ci.yml ├── LICENSE ├── docs ├── cache_example.html ├── mtl_example.html ├── multi_doc2.html ├── multi_doc1.html └── index.html └── examples ├── ErrorExample.hs ├── MultiDocExample.hs ├── MtlExample.hs ├── SimpleExample.hs ├── Index.hs ├── CacheExample3.hs ├── AsyncExample.hs ├── RandomExample.hs └── CacheExample.hs /data/pandoc-data/data/templates/default.haddock: -------------------------------------------------------------------------------- 1 | $body$ 2 | -------------------------------------------------------------------------------- /cabal.project: -------------------------------------------------------------------------------- 1 | packages: ./ 2 | 3 | 4 | allow-newer: base, ghc-prim, polysemy-zoo:containers, polysemy-zoo:constraints 5 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.jira: -------------------------------------------------------------------------------- 1 | $for(include-before)$ 2 | $include-before$ 3 | 4 | $endfor$ 5 | $body$ 6 | $for(include-after)$ 7 | 8 | $include-after$ 9 | $endfor$ 10 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.textile: -------------------------------------------------------------------------------- 1 | $for(include-before)$ 2 | $include-before$ 3 | 4 | $endfor$ 5 | $body$ 6 | $for(include-after)$ 7 | 8 | $include-after$ 9 | $endfor$ 10 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.xwiki: -------------------------------------------------------------------------------- 1 | $for(include-before)$ 2 | $include-before$ 3 | 4 | $endfor$ 5 | $if(toc)$ 6 | {{toc /}} 7 | 8 | $endif$ 9 | $body$ 10 | $for(include-after)$ 11 | 12 | $include-after$ 13 | $endfor$ 14 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.dokuwiki: -------------------------------------------------------------------------------- 1 | $for(include-before)$ 2 | $include-before$ 3 | 4 | $endfor$ 5 | $if(toc)$ 6 | __TOC__ 7 | 8 | $endif$ 9 | $body$ 10 | $for(include-after)$ 11 | 12 | $include-after$ 13 | $endfor$ 14 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.mediawiki: -------------------------------------------------------------------------------- 1 | $for(include-before)$ 2 | $include-before$ 3 | 4 | $endfor$ 5 | $if(toc)$ 6 | __TOC__ 7 | 8 | $endif$ 9 | $body$ 10 | $for(include-after)$ 11 | 12 | $include-after$ 13 | $endfor$ 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .stack-work/* 2 | *~ 3 | *# 4 | /dist 5 | \#*# 6 | .#* 7 | /dist-newstyle 8 | cabal.project.local 9 | *.json 10 | *.png 11 | TAGS 12 | .ghc* 13 | bench/*.html 14 | report.html 15 | *.prof 16 | *.hp 17 | *.ps 18 | *.aux 19 | *.png 20 | .knit-haskell-cache 21 | .knit-haskell-cache2 22 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.zimwiki: -------------------------------------------------------------------------------- 1 | Content-Type: text/x-zim-wiki 2 | Wiki-Format: zim 0.4 3 | 4 | $for(include-before)$ 5 | $include-before$ 6 | 7 | $endfor$ 8 | $if(toc)$ 9 | __TOC__ 10 | 11 | $endif$ 12 | $body$ 13 | $for(include-after)$ 14 | 15 | $include-after$ 16 | $endfor$ 17 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.opml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $title$ 5 | $date$ 6 | $for(author)$$author$$sep$; $endfor$ 7 | 8 | 9 | $body$ 10 | 11 | 12 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.plain: -------------------------------------------------------------------------------- 1 | $if(titleblock)$ 2 | $titleblock$ 3 | 4 | $endif$ 5 | $for(header-includes)$ 6 | $header-includes$ 7 | 8 | $endfor$ 9 | $for(include-before)$ 10 | $include-before$ 11 | 12 | $endfor$ 13 | $if(toc)$ 14 | $table-of-contents$ 15 | 16 | $endif$ 17 | $body$ 18 | $for(include-after)$ 19 | 20 | $include-after$ 21 | $endfor$ 22 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.commonmark: -------------------------------------------------------------------------------- 1 | $if(titleblock)$ 2 | $titleblock$ 3 | 4 | $endif$ 5 | $for(header-includes)$ 6 | $header-includes$ 7 | 8 | $endfor$ 9 | $for(include-before)$ 10 | $include-before$ 11 | 12 | $endfor$ 13 | $if(toc)$ 14 | $table-of-contents$ 15 | 16 | $endif$ 17 | $body$ 18 | $for(include-after)$ 19 | 20 | $include-after$ 21 | $endfor$ 22 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.markdown: -------------------------------------------------------------------------------- 1 | $if(titleblock)$ 2 | $titleblock$ 3 | 4 | $endif$ 5 | $for(header-includes)$ 6 | $header-includes$ 7 | 8 | $endfor$ 9 | $for(include-before)$ 10 | $include-before$ 11 | 12 | $endfor$ 13 | $if(toc)$ 14 | $table-of-contents$ 15 | 16 | $endif$ 17 | $body$ 18 | $for(include-after)$ 19 | 20 | $include-after$ 21 | $endfor$ 22 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.org: -------------------------------------------------------------------------------- 1 | $if(title)$ 2 | #+TITLE: $title$ 3 | 4 | $endif$ 5 | $if(author)$ 6 | #+AUTHOR: $for(author)$$author$$sep$; $endfor$ 7 | $endif$ 8 | $if(date)$ 9 | #+DATE: $date$ 10 | 11 | $endif$ 12 | $for(header-includes)$ 13 | $header-includes$ 14 | 15 | $endfor$ 16 | $for(include-before)$ 17 | $include-before$ 18 | 19 | $endfor$ 20 | $body$ 21 | $for(include-after)$ 22 | 23 | $include-after$ 24 | $endfor$ 25 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.asciidoc: -------------------------------------------------------------------------------- 1 | $if(titleblock)$ 2 | = $title$ 3 | $if(author)$ 4 | $for(author)$$author$$sep$; $endfor$ 5 | $endif$ 6 | $if(date)$ 7 | $date$ 8 | $endif$ 9 | $if(keywords)$ 10 | :keywords: $for(keywords)$$keywords$$sep$, $endfor$ 11 | $endif$ 12 | $if(lang)$ 13 | :lang: $lang$ 14 | $endif$ 15 | $if(toc)$ 16 | :toc: 17 | $endif$ 18 | 19 | $endif$ 20 | $if(abstract)$ 21 | [abstract] 22 | == Abstract 23 | $abstract$ 24 | 25 | $endif$ 26 | $for(header-includes)$ 27 | $header-includes$ 28 | 29 | $endfor$ 30 | $for(include-before)$ 31 | $include-before$ 32 | 33 | $endfor$ 34 | $body$ 35 | $for(include-after)$ 36 | 37 | $include-after$ 38 | $endfor$ 39 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.man: -------------------------------------------------------------------------------- 1 | $if(has-tables)$ 2 | .\"t 3 | $endif$ 4 | $if(pandoc-version)$ 5 | .\" Automatically generated by Pandoc $pandoc-version$ 6 | .\" 7 | $endif$ 8 | $if(adjusting)$ 9 | .ad $adjusting$ 10 | $endif$ 11 | .TH "$title$" "$section$" "$date$" "$footer$" "$header$" 12 | $if(hyphenate)$ 13 | .hy 14 | $else$ 15 | .nh \" Turn off hyphenation by default. 16 | $endif$ 17 | $for(header-includes)$ 18 | $header-includes$ 19 | $endfor$ 20 | $for(include-before)$ 21 | $include-before$ 22 | $endfor$ 23 | $body$ 24 | $for(include-after)$ 25 | $include-after$ 26 | $endfor$ 27 | $if(author)$ 28 | .SH AUTHORS 29 | $for(author)$$author$$sep$; $endfor$. 30 | $endif$ 31 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.texinfo: -------------------------------------------------------------------------------- 1 | \input texinfo 2 | @documentencoding UTF-8 3 | $for(header-includes)$ 4 | $header-includes$ 5 | $endfor$ 6 | 7 | $if(strikeout)$ 8 | @macro textstrikeout{text} 9 | ~~\text\~~ 10 | @end macro 11 | 12 | $endif$ 13 | @ifnottex 14 | @paragraphindent 0 15 | @end ifnottex 16 | $if(titlepage)$ 17 | @titlepage 18 | @title $title$ 19 | $for(author)$ 20 | @author $author$ 21 | $endfor$ 22 | $if(date)$ 23 | $date$ 24 | $endif$ 25 | @end titlepage 26 | 27 | $endif$ 28 | $for(include-before)$ 29 | $include-before$ 30 | 31 | $endfor$ 32 | $if(toc)$ 33 | @contents 34 | 35 | $endif$ 36 | $body$ 37 | $for(include-after)$ 38 | 39 | $include-after$ 40 | $endfor$ 41 | 42 | @bye 43 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.asciidoctor: -------------------------------------------------------------------------------- 1 | $if(titleblock)$ 2 | = $title$ 3 | $if(author)$ 4 | $for(author)$$author$$sep$; $endfor$ 5 | $endif$ 6 | $if(date)$ 7 | $date$ 8 | $endif$ 9 | $if(keywords)$ 10 | :keywords: $for(keywords)$$keywords$$sep$, $endfor$ 11 | $endif$ 12 | $if(lang)$ 13 | :lang: $lang$ 14 | $endif$ 15 | $if(toc)$ 16 | :toc: 17 | $endif$ 18 | $if(math)$ 19 | :stem: latexmath 20 | $endif$ 21 | 22 | $endif$ 23 | $if(abstract)$ 24 | [abstract] 25 | == Abstract 26 | $abstract$ 27 | 28 | $endif$ 29 | $for(header-includes)$ 30 | $header-includes$ 31 | 32 | $endfor$ 33 | $for(include-before)$ 34 | $include-before$ 35 | 36 | $endfor$ 37 | $body$ 38 | $for(include-after)$ 39 | 40 | $include-after$ 41 | $endfor$ 42 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.rst: -------------------------------------------------------------------------------- 1 | $if(titleblock)$ 2 | $titleblock$ 3 | 4 | $endif$ 5 | $for(author)$ 6 | :Author: $author$ 7 | $endfor$ 8 | $if(date)$ 9 | :Date: $date$ 10 | $endif$ 11 | $if(author)$ 12 | 13 | $else$ 14 | $if(date)$ 15 | 16 | $endif$ 17 | $endif$ 18 | $if(rawtex)$ 19 | .. role:: raw-latex(raw) 20 | :format: latex 21 | .. 22 | 23 | $endif$ 24 | $for(include-before)$ 25 | $include-before$ 26 | 27 | $endfor$ 28 | $if(toc)$ 29 | .. contents:: 30 | :depth: $toc-depth$ 31 | .. 32 | 33 | $endif$ 34 | $if(number-sections)$ 35 | .. section-numbering:: 36 | 37 | $endif$ 38 | $for(header-includes)$ 39 | $header-includes$ 40 | 41 | $endfor$ 42 | $body$ 43 | $for(include-after)$ 44 | 45 | $include-after$ 46 | $endfor$ 47 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\deff0{\fonttbl{\f0 \fswiss Helvetica;}{\f1 Courier;}} 2 | {\colortbl;\red255\green0\blue0;\red0\green0\blue255;} 3 | \widowctrl\hyphauto 4 | $for(header-includes)$ 5 | $header-includes$ 6 | $endfor$ 7 | 8 | $if(title)$ 9 | {\pard \qc \f0 \sa180 \li0 \fi0 \b \fs36 $title$\par} 10 | $endif$ 11 | $for(author)$ 12 | {\pard \qc \f0 \sa180 \li0 \fi0 $author$\par} 13 | $endfor$ 14 | $if(date)$ 15 | {\pard \qc \f0 \sa180 \li0 \fi0 $date$\par} 16 | $endif$ 17 | $if(spacer)$ 18 | {\pard \ql \f0 \sa180 \li0 \fi0 \par} 19 | $endif$ 20 | $if(toc)$ 21 | $table-of-contents$ 22 | $endif$ 23 | $for(include-before)$ 24 | $include-before$ 25 | $endfor$ 26 | $body$ 27 | $for(include-after)$ 28 | $include-after$ 29 | $endfor$ 30 | } 31 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.muse: -------------------------------------------------------------------------------- 1 | $if(author)$ 2 | #author $author$ 3 | $endif$ 4 | $if(title)$ 5 | #title $title$ 6 | $endif$ 7 | $if(lang)$ 8 | #lang $lang$ 9 | $endif$ 10 | $if(LISTtitle)$ 11 | #LISTtitle $LISTtitle$ 12 | $endif$ 13 | $if(subtitle)$ 14 | #subtitle $subtitle$ 15 | $endif$ 16 | $if(SORTauthors)$ 17 | #SORTauthors $SORTauthors$ 18 | $endif$ 19 | $if(SORTtopics)$ 20 | #SORTtopics $SORTtopics$ 21 | $endif$ 22 | $if(date)$ 23 | #date $date$ 24 | $endif$ 25 | $if(notes)$ 26 | #notes $notes$ 27 | $endif$ 28 | $if(source)$ 29 | #source $source$ 30 | $endif$ 31 | 32 | $for(header-includes)$ 33 | $header-includes$ 34 | 35 | $endfor$ 36 | $for(include-before)$ 37 | $include-before$ 38 | 39 | $endfor$ 40 | $body$ 41 | $for(include-after)$ 42 | 43 | $include-after$ 44 | $endfor$ 45 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.docbook5: -------------------------------------------------------------------------------- 1 | 2 | 3 |
12 | 13 | $title$ 14 | $if(subtitle)$ 15 | $subtitle$ 16 | $endif$ 17 | $if(author)$ 18 | 19 | $for(author)$ 20 | 21 | $author$ 22 | 23 | $endfor$ 24 | 25 | $endif$ 26 | $if(date)$ 27 | $date$ 28 | $endif$ 29 | 30 | $for(include-before)$ 31 | $include-before$ 32 | $endfor$ 33 | $body$ 34 | $for(include-after)$ 35 | $include-after$ 36 | $endfor$ 37 |
38 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.docbook4: -------------------------------------------------------------------------------- 1 | 2 | $if(mathml)$ 3 | 5 | $else$ 6 | 8 | $endif$ 9 |
10 | 11 | $title$ 12 | $if(author)$ 13 | 14 | $for(author)$ 15 | 16 | $author$ 17 | 18 | $endfor$ 19 | 20 | $endif$ 21 | $if(date)$ 22 | $date$ 23 | $endif$ 24 | 25 | $for(include-before)$ 26 | $include-before$ 27 | $endfor$ 28 | $body$ 29 | $for(include-after)$ 30 | $include-after$ 31 | $endfor$ 32 |
33 | -------------------------------------------------------------------------------- /hie.yaml: -------------------------------------------------------------------------------- 1 | cradle: 2 | cabal: 3 | - path: "./src" 4 | component: "lib:knit-haskell" 5 | 6 | - path: "./examples" 7 | component: "knit-haskell:test:AsyncExample" 8 | 9 | - path: "./examples" 10 | component: "knit-haskell:test:CacheExample" 11 | 12 | - path: "./examples" 13 | component: "knit-haskell:test:CacheExample2" 14 | 15 | - path: "./examples" 16 | component: "knit-haskell:test:CacheExample3" 17 | 18 | - path: "./examples" 19 | component: "knit-haskell:test:ErrorExample" 20 | 21 | - path: "./examples" 22 | component: "knit-haskell:test:Index" 23 | 24 | - path: "./examples" 25 | component: "knit-haskell:test:MtlExample" 26 | 27 | - path: "./examples" 28 | component: "knit-haskell:test:MultiDocExample" 29 | 30 | - path: "./examples" 31 | component: "knit-haskell:test:RandomExample" 32 | 33 | - path: "./examples" 34 | component: "knit-haskell:test:SimpleExample" 35 | -------------------------------------------------------------------------------- /src/Knit/Report/Input/Html/Lucid.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleContexts #-} 2 | {-# LANGUAGE GADTs #-} 3 | {-| 4 | Module : Knit.Report.Input.Html.Lucid 5 | Description : Support functions for adding Lucid Html fragments into a report 6 | Copyright : (c) Adam Conner-Sax 2019 7 | License : BSD-3-Clause 8 | Maintainer : adam_conner_sax@yahoo.com 9 | Stability : experimental 10 | 11 | Functions to add Lucid fragments into a Pandoc generated report. 12 | -} 13 | module Knit.Report.Input.Html.Lucid 14 | ( 15 | -- * Add Lucid 16 | addLucid 17 | ) 18 | where 19 | 20 | import Knit.Report.Input.Html ( addLazyTextHtml ) 21 | import qualified Lucid as LH 22 | 23 | import qualified Polysemy as P 24 | import qualified Knit.Effect.Pandoc as PE 25 | import qualified Knit.Effect.PandocMonad as PM 26 | 27 | -- | Add Lucid Html 28 | addLucid 29 | :: (PM.PandocEffects effs, P.Member PE.ToPandoc effs) 30 | => LH.Html () 31 | -> P.Sem effs () 32 | addLucid = addLazyTextHtml . LH.renderText 33 | -------------------------------------------------------------------------------- /src/Knit/Report/Input/Latex.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleContexts #-} 2 | {-# LANGUAGE GADTs #-} 3 | {-| 4 | Module : Knit.Report.Input.Latex 5 | Description : Support functions for adding LaTeX fragments into a report 6 | Copyright : (c) Adam Conner-Sax 2019 7 | License : BSD-3-Clause 8 | Maintainer : adam_conner_sax@yahoo.com 9 | Stability : experimental 10 | 11 | Functions to add LaTeX fragments to the current Pandoc. 12 | -} 13 | module Knit.Report.Input.Latex 14 | ( 15 | -- * Add LaTeX fragments 16 | addLatex 17 | ) 18 | where 19 | 20 | import qualified Data.Text as T 21 | import qualified Text.Pandoc as PA 22 | 23 | import qualified Polysemy as P 24 | import qualified Knit.Effect.Pandoc as PE 25 | import qualified Knit.Effect.PandocMonad as PM 26 | 27 | -- | Add LaTeX 28 | addLatex 29 | :: (PM.PandocEffects effs, P.Member PE.ToPandoc effs) 30 | => T.Text 31 | -> P.Sem effs () 32 | addLatex lText = do 33 | PE.require PE.LatexSupport 34 | PE.addFrom PE.ReadLaTeX PA.def lText 35 | -------------------------------------------------------------------------------- /src/Knit/Effect/Internal/Logger.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ConstraintKinds #-} 2 | {-# LANGUAGE DerivingStrategies #-} 3 | {-# LANGUAGE OverloadedStrings #-} 4 | {-| 5 | Module : Knit.Effect.Internal.Logger 6 | Description : Types for logging within knit-haskell itself 7 | Copyright : (c) Adam Conner-Sax 2024 8 | License : BSD-3-Clause 9 | Maintainer : adam_conner_sax@yahoo.com 10 | Stability : experimental 11 | 12 | -} 13 | module Knit.Effect.Internal.Logger 14 | ( 15 | -- KHLogCategory(..) 16 | -- , khLogCategorySeverity 17 | -- , khLogCategoryText 18 | khDebugLogSeverity 19 | , khDebugLog 20 | ) 21 | where 22 | 23 | import Knit.Effect.Logger as KLog 24 | import Polysemy as P 25 | 26 | khDebugLogSeverity :: KLog.LogSeverity 27 | khDebugLogSeverity = (KLog.Debug 3) 28 | {-# INLINEABLE khDebugLogSeverity #-} 29 | 30 | -- | logging level for debugging messages from the library itself. 31 | khDebugLog :: LogWithPrefixesCat effs => Text -> P.Sem effs () 32 | khDebugLog = KLog.logCat "KH_Other" khDebugLogSeverity 33 | {-# INLINEABLE khDebugLog #-} 34 | -------------------------------------------------------------------------------- /src/Knit/Report/Input/Html/Blaze.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleContexts #-} 2 | {-# LANGUAGE GADTs #-} 3 | {-| 4 | Module : Knit.Report.Input.Html.Blaze 5 | Description : Support functions for adding Blaze Html fragments into a report 6 | Copyright : (c) Adam Conner-Sax 2019 7 | License : BSD-3-Clause 8 | Maintainer : adam_conner_sax@yahoo.com 9 | Stability : experimental 10 | 11 | Functions to add Blaze fragments into a Pandoc generated report. 12 | -} 13 | module Knit.Report.Input.Html.Blaze 14 | ( 15 | -- * Add Blaze 16 | addBlaze 17 | ) 18 | where 19 | 20 | import Knit.Report.Input.Html ( addLazyTextHtml ) 21 | import qualified Text.Blaze.Html as BH 22 | import qualified Text.Blaze.Html.Renderer.Text as BH 23 | 24 | import qualified Polysemy as P 25 | import qualified Knit.Effect.Pandoc as PE 26 | import qualified Knit.Effect.PandocMonad as PM 27 | 28 | 29 | -- | Add Blaze Html 30 | addBlaze 31 | :: (PM.PandocEffects effs, P.Member PE.ToPandoc effs) 32 | => BH.Html 33 | -> P.Sem effs () 34 | addBlaze = addLazyTextHtml . BH.renderHtml 35 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | types: 8 | - opened 9 | - synchronize 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | cabal: ["3.2"] 16 | ghc: ["8.8.3", "8.10.2"] 17 | env: 18 | CONFIG: "--enable-tests --enable-benchmarks" 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: actions/setup-haskell@v1.1.4 22 | id: setup-haskell-cabal 23 | with: 24 | ghc-version: ${{ matrix.ghc }} 25 | cabal-version: ${{ matrix.cabal }} 26 | - run: cabal v2-update 27 | - run: cabal v2-freeze $CONFIG 28 | - uses: actions/cache@v2 29 | with: 30 | path: | 31 | ${{ steps.setup-haskell-cabal.outputs.cabal-store }} 32 | dist-newstyle 33 | key: ${{ runner.os }}-${{ matrix.ghc }}-${{ hashFiles('cabal.project.freeze') }} 34 | restore-keys: | 35 | ${{ runner.os }}-${{ matrix.ghc }}- 36 | - run: cabal v2-build $CONFIG 37 | - run: cabal v2-test $CONFIG 38 | - run: cabal v2-haddock $CONFIG 39 | - run: cabal v2-sdist -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.tei: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | $title$ 7 | $for(author)$ 8 | $author$ 9 | $endfor$ 10 | 11 | 12 | $if(publicationStmt)$ 13 |

$if(publicationStmt)$$publicationStmt$$endif$

14 | $endif$ 15 | $if(license)$ 16 | $license$ 17 | $endif$ 18 | $if(publisher)$ 19 | $publisher$ 20 | $endif$ 21 | $if(pubPlace)$ 22 | $pubPlace$ 23 | $endif$ 24 | $if(address)$ 25 |
$address$
26 | $endif$ 27 | $if(date)$ 28 | $date$ 29 | $endif$ 30 |
31 | 32 | $if(sourceDesc)$ 33 | $sourceDesc$ 34 | $else$ 35 |

Produced by pandoc.

36 | $endif$ 37 |
38 |
39 |
40 | 41 | $for(include-before)$ 42 | $include-before$ 43 | $endfor$ 44 | 45 | $body$ 46 | 47 | $for(include-after)$ 48 | $include-after$ 49 | $endfor$ 50 | 51 |
52 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.epub3: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $pagetitle$ 8 | $if(highlighting-css)$ 9 | 12 | $endif$ 13 | $for(css)$ 14 | 15 | $endfor$ 16 | $for(header-includes)$ 17 | $header-includes$ 18 | $endfor$ 19 | 20 | 21 | $if(titlepage)$ 22 |
23 | $for(title)$ 24 | $if(title.type)$ 25 |

$title.text$

26 | $else$ 27 |

$title$

28 | $endif$ 29 | $endfor$ 30 | $if(subtitle)$ 31 |

$subtitle$

32 | $endif$ 33 | $for(author)$ 34 |

$author$

35 | $endfor$ 36 | $for(creator)$ 37 |

$creator.text$

38 | $endfor$ 39 | $if(publisher)$ 40 |

$publisher$

41 | $endif$ 42 | $if(date)$ 43 |

$date$

44 | $endif$ 45 | $if(rights)$ 46 |
$rights$
47 | $endif$ 48 |
49 | $else$ 50 | $for(include-before)$ 51 | $include-before$ 52 | $endfor$ 53 | $body$ 54 | $for(include-after)$ 55 | $include-after$ 56 | $endfor$ 57 | $endif$ 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.epub2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | $pagetitle$ 9 | $if(highlighting-css)$ 10 | 13 | $endif$ 14 | $for(css)$ 15 | 16 | $endfor$ 17 | $for(header-includes)$ 18 | $header-includes$ 19 | $endfor$ 20 | 21 | 22 | $if(titlepage)$ 23 | $for(title)$ 24 | $if(title.text)$ 25 |

$title.text$

26 | $else$ 27 |

$title$

28 | $endif$ 29 | $endfor$ 30 | $if(subtitle)$ 31 |

$subtitle$

32 | $endif$ 33 | $for(author)$ 34 |

$author$

35 | $endfor$ 36 | $for(creator)$ 37 |

$creator.text$

38 | $endfor$ 39 | $if(publisher)$ 40 |

$publisher$

41 | $endif$ 42 | $if(date)$ 43 |

$date$

44 | $endif$ 45 | $if(rights)$ 46 |
$rights$
47 | $endif$ 48 | $else$ 49 | $for(include-before)$ 50 | $include-before$ 51 | $endfor$ 52 | $body$ 53 | $for(include-after)$ 54 | $include-after$ 55 | $endfor$ 56 | $endif$ 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, Adam Conner-Sax 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Adam Conner-Sax nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.latex.rej: -------------------------------------------------------------------------------- 1 | *************** 2 | *** 11,16 **** 3 | \setbeamertemplate{caption label separator}{: } 4 | \setbeamercolor{caption name}{fg=normal text.fg} 5 | \beamertemplatenavigationsymbols$if(navigation)$$navigation$$else$empty$endif$ 6 | $endif$ 7 | $if(beamerarticle)$ 8 | \usepackage{beamerarticle} % needs to be loaded first 9 | --- 11,51 ---- 10 | \setbeamertemplate{caption label separator}{: } 11 | \setbeamercolor{caption name}{fg=normal text.fg} 12 | \beamertemplatenavigationsymbols$if(navigation)$$navigation$$else$empty$endif$ 13 | + % Prevent slide breaks in the middle of a paragraph: 14 | + \widowpenalties 1 10000 15 | + \raggedbottom 16 | + $if(section-titles)$ 17 | + \setbeamertemplate{part page}{ 18 | + \centering 19 | + \begin{beamercolorbox}[sep=16pt,center]{part title} 20 | + \usebeamerfont{part title}\insertpart\par 21 | + \end{beamercolorbox} 22 | + } 23 | + \setbeamertemplate{section page}{ 24 | + \centering 25 | + \begin{beamercolorbox}[sep=12pt,center]{part title} 26 | + \usebeamerfont{section title}\insertsection\par 27 | + \end{beamercolorbox} 28 | + } 29 | + \setbeamertemplate{subsection page}{ 30 | + \centering 31 | + \begin{beamercolorbox}[sep=8pt,center]{part title} 32 | + \usebeamerfont{subsection title}\insertsubsection\par 33 | + \end{beamercolorbox} 34 | + } 35 | + \AtBeginPart{ 36 | + \frame{\partpage} 37 | + } 38 | + \AtBeginSection{ 39 | + \ifbibliography 40 | + \else 41 | + \frame{\sectionpage} 42 | + \fi 43 | + } 44 | + \AtBeginSubsection{ 45 | + \frame{\subsectionpage} 46 | + } 47 | + $endif$ 48 | $endif$ 49 | $if(beamerarticle)$ 50 | \usepackage{beamerarticle} % needs to be loaded first 51 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.icml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $charStyles$ 8 | 9 | 10 | 12 | 13 | 14 | 15 | LeftAlign 16 | . 17 | 18 | 10 19 | 20 | 21 | 22 | 23 | $parStyles$ 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | 37 | 38 | 39 | $body$ 40 | 41 | 42 | $hyperlinks$ 43 | 44 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.html5: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $for(author-meta)$ 8 | 9 | $endfor$ 10 | $if(date-meta)$ 11 | 12 | $endif$ 13 | $if(keywords)$ 14 | 15 | $endif$ 16 | $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ 17 | 26 | $if(highlighting-css)$ 27 | 30 | $endif$ 31 | $for(css)$ 32 | 33 | $endfor$ 34 | $if(math)$ 35 | $math$ 36 | $endif$ 37 | 40 | $for(header-includes)$ 41 | $header-includes$ 42 | $endfor$ 43 | 44 | 45 | $for(include-before)$ 46 | $include-before$ 47 | $endfor$ 48 | $if(title)$ 49 |
50 |

$title$

51 | $if(subtitle)$ 52 |

$subtitle$

53 | $endif$ 54 | $for(author)$ 55 |

$author$

56 | $endfor$ 57 | $if(date)$ 58 |

$date$

59 | $endif$ 60 |
61 | $endif$ 62 | $if(toc)$ 63 | 66 | $endif$ 67 | $body$ 68 | $for(include-after)$ 69 | $include-after$ 70 | $endfor$ 71 | 72 | 73 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.html4: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $for(author-meta)$ 8 | 9 | $endfor$ 10 | $if(date-meta)$ 11 | 12 | $endif$ 13 | $if(keywords)$ 14 | 15 | $endif$ 16 | $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ 17 | 26 | $if(highlighting-css)$ 27 | 30 | $endif$ 31 | $for(css)$ 32 | 33 | $endfor$ 34 | $if(math)$ 35 | $math$ 36 | $endif$ 37 | $for(header-includes)$ 38 | $header-includes$ 39 | $endfor$ 40 | 41 | 42 | $for(include-before)$ 43 | $include-before$ 44 | $endfor$ 45 | $if(title)$ 46 |
47 |

$title$

48 | $if(subtitle)$ 49 |

$subtitle$

50 | $endif$ 51 | $for(author)$ 52 |

$author$

53 | $endfor$ 54 | $if(date)$ 55 |

$date$

56 | $endif$ 57 |
58 | $endif$ 59 | $if(toc)$ 60 |
61 | $table-of-contents$ 62 |
63 | $endif$ 64 | $body$ 65 | $for(include-after)$ 66 | $include-after$ 67 | $endfor$ 68 | 69 | 70 | -------------------------------------------------------------------------------- /docs/cache_example.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 cache example 36 | 37 | 38 | 39 | 40 | 41 |
42 |
43 |
44 | 45 |
46 |
47 |
48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/Knit/Report/Input/Html.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ExtendedDefaultRules #-} 2 | {-# LANGUAGE ScopedTypeVariables #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE TypeOperators #-} 5 | {-# LANGUAGE DataKinds #-} 6 | {-# LANGUAGE GADTs #-} 7 | {-| 8 | Module : Knit.Report.Input.Html 9 | Description : Support functions for adding Html fragments into a Pandoc document. 10 | Copyright : (c) Adam Conner-Sax 2019 11 | License : BSD-3-Clause 12 | Maintainer : adam_conner_sax@yahoo.com 13 | Stability : experimental 14 | 15 | Functions and Pandoc option sets for adding Html fragments into a Pandoc document. 16 | -} 17 | module Knit.Report.Input.Html 18 | ( 19 | -- * Add html formatted as Text 20 | addStrictTextHtml 21 | , addLazyTextHtml 22 | 23 | -- * Default Options 24 | , htmlReaderOptions 25 | , htmlReaderOptionsWithHeader 26 | ) 27 | where 28 | 29 | import qualified Data.Text as T 30 | import qualified Data.Text.Lazy as LT 31 | import qualified Text.Pandoc as P 32 | import qualified Text.Pandoc.Extensions as PA 33 | 34 | import qualified Polysemy as P 35 | import qualified Knit.Effect.Pandoc as PE 36 | import qualified Knit.Effect.PandocMonad as PM 37 | 38 | 39 | -- | Base Html reader options 40 | htmlReaderOptions :: P.ReaderOptions 41 | htmlReaderOptions = 42 | P.def { P.readerExtensions = PA.extensionsFromList [PA.Ext_raw_html] } 43 | 44 | -- | Html reader options for complete document 45 | htmlReaderOptionsWithHeader :: P.ReaderOptions 46 | htmlReaderOptionsWithHeader = htmlReaderOptions { P.readerStandalone = True } 47 | 48 | -- | Add Strict Text Html to current Pandoc 49 | addStrictTextHtml 50 | :: (PM.PandocEffects effs, P.Member PE.ToPandoc effs) 51 | => T.Text 52 | -> P.Sem effs () 53 | addStrictTextHtml = PE.addFrom PE.ReadHtml htmlReaderOptions 54 | 55 | -- | Add Lazy Text Html to current Pandoc 56 | addLazyTextHtml 57 | :: (PM.PandocEffects effs, P.Member PE.ToPandoc effs) 58 | => LT.Text 59 | -> P.Sem effs () 60 | addLazyTextHtml = addStrictTextHtml . toText 61 | -------------------------------------------------------------------------------- /src/Knit/Effect/UnusedId.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleContexts #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | {-# LANGUAGE DataKinds #-} 4 | {-# LANGUAGE PolyKinds #-} 5 | {-# LANGUAGE GADTs #-} 6 | {-# LANGUAGE TypeOperators #-} 7 | {-# LANGUAGE ScopedTypeVariables #-} 8 | {-# LANGUAGE TypeApplications #-} 9 | {-# OPTIONS_GHC -fwarn-incomplete-patterns #-} 10 | {-| 11 | Module : Knit.Effect.UnusedId 12 | Description : Wrapper around Polysemy.State for generating unused ids with a given prefix 13 | Copyright : (c) Adam Conner-Sax 2019 14 | License : BSD-3-Clause 15 | Maintainer : adam_conner_sax@yahoo.com 16 | Stability : experimental 17 | 18 | This module contains a state effect used to maintain a map of unused id numbers which can be used for HTML 19 | ids or figure numbering. 20 | 21 | -} 22 | module Knit.Effect.UnusedId 23 | ( 24 | -- * Effect 25 | UnusedId 26 | 27 | -- * actions 28 | , getNextUnusedId 29 | 30 | -- * interpretations 31 | , runUnusedId 32 | ) 33 | where 34 | 35 | import qualified Polysemy as P 36 | import qualified Polysemy.AtomicState as PS 37 | 38 | import qualified Data.Map as M 39 | import qualified Data.Text as T 40 | 41 | import qualified Data.IORef as IORef 42 | 43 | -- | Type alias for the dictionary ('M.Map') of current last used id at each prefix. 44 | type IdMap = M.Map T.Text Int 45 | 46 | -- | Type alias for 'Polysemy.AtomicState' using "IdMap". 47 | type UnusedId = PS.AtomicState IdMap 48 | 49 | -- | Get an unused id with prefix as specified. Useful for figures, etc. 50 | getNextUnusedId :: P.Member UnusedId r => T.Text -> P.Sem r T.Text 51 | getNextUnusedId prefixT = do 52 | idMap <- PS.atomicGet @IdMap 53 | let nextId = fromMaybe 1 $ M.lookup prefixT idMap 54 | PS.atomicPut $ M.insert prefixT (nextId + 1) idMap 55 | return $ prefixT <> "_" <> show nextId 56 | 57 | -- | Run the UnusedId effect and throw away the state. 58 | runUnusedId :: P.Member (P.Embed IO) r => P.Sem (UnusedId ': r) a -> P.Sem r a 59 | runUnusedId ma = do 60 | ioRef <- P.embed $ IORef.newIORef mempty 61 | PS.runAtomicStateIORef ioRef ma 62 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.slidy: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | $for(author-meta)$ 10 | 11 | $endfor$ 12 | $if(date-meta)$ 13 | 14 | $endif$ 15 | $if(keywords)$ 16 | 17 | $endif$ 18 | $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ 19 | 28 | $if(highlighting-css)$ 29 | 32 | $endif$ 33 | 35 | $for(css)$ 36 | 38 | $endfor$ 39 | $if(math)$ 40 | $math$ 41 | $endif$ 42 | $for(header-includes)$ 43 | $header-includes$ 44 | $endfor$ 45 | 47 | $if(duration)$ 48 | 49 | $endif$ 50 | 51 | 52 | $for(include-before)$ 53 | $include-before$ 54 | $endfor$ 55 | $if(title)$ 56 |
57 |

$title$

58 | $if(subtitle)$ 59 |

$subtitle$

60 | $endif$ 61 | $if(author)$ 62 |

63 | $for(author)$$author$$sep$
$endfor$ 64 |

65 | $endif$ 66 | $if(date)$ 67 |

$date$

68 | $endif$ 69 |
70 | $endif$ 71 | $if(toc)$ 72 |
73 | $table-of-contents$ 74 |
75 | $endif$ 76 | $body$ 77 | $for(include-after)$ 78 | $include-after$ 79 | $endfor$ 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/Knit/Report/Input/MarkDown/PandocMarkDown.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ExtendedDefaultRules #-} 2 | {-# LANGUAGE ScopedTypeVariables #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE TypeOperators #-} 5 | {-# LANGUAGE DataKinds #-} 6 | {-# LANGUAGE GADTs #-} 7 | {-| 8 | Module : Knit.Report.Input.Markdown.PandocMarkdown 9 | Description : Functions to add Pandoc MarkDown fragments to a Pandoc 10 | Copyright : (c) Adam Conner-Sax 2019 11 | License : BSD-3-Clause 12 | Maintainer : adam_conner_sax@yahoo.com 13 | Stability : experimental 14 | 15 | Functions to add Pandoc MarkDown fragments to the current Pandoc. 16 | -} 17 | module Knit.Report.Input.MarkDown.PandocMarkDown 18 | ( 19 | -- * Default Options 20 | markDownReaderOptions 21 | 22 | -- * functions to add various thing to the current Pandoc 23 | , addMarkDown 24 | , addMarkDownWithOptions 25 | ) 26 | where 27 | 28 | import qualified Data.Text as T 29 | import qualified Text.Pandoc as PA 30 | 31 | import qualified Polysemy as P 32 | import qualified Knit.Effect.Pandoc as PE 33 | import qualified Knit.Effect.PandocMonad as PM 34 | 35 | -- | Base Pandoc MarkDown reader options 36 | markDownReaderOptions :: PA.ReaderOptions 37 | markDownReaderOptions = PA.def 38 | { PA.readerStandalone = True 39 | , PA.readerExtensions = PA.extensionsFromList 40 | [ PA.Ext_auto_identifiers 41 | , PA.Ext_backtick_code_blocks 42 | , PA.Ext_fancy_lists 43 | , PA.Ext_footnotes 44 | , PA.Ext_simple_tables 45 | , PA.Ext_multiline_tables 46 | , PA.Ext_tex_math_dollars 47 | , PA.Ext_header_attributes 48 | , PA.Ext_implicit_header_references 49 | , PA.Ext_implicit_figures 50 | ] 51 | } 52 | 53 | -- | Add a Pandoc MarkDown fragment with the given options 54 | addMarkDownWithOptions 55 | :: (PM.PandocEffects effs, P.Member PE.ToPandoc effs) 56 | => PA.ReaderOptions 57 | -> T.Text 58 | -> P.Sem effs () 59 | addMarkDownWithOptions = PE.addFrom PE.ReadMarkDown 60 | 61 | -- | Add a Pandoc MarkDown fragment with default options 62 | addMarkDown 63 | :: (PM.PandocEffects effs, P.Member PE.ToPandoc effs) 64 | => T.Text 65 | -> P.Sem effs () 66 | addMarkDown = addMarkDownWithOptions markDownReaderOptions 67 | 68 | -------------------------------------------------------------------------------- /data/knit-haskell-templates/pandoc-bootstrap-KH.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $for(author-meta)$ 8 | 9 | $endfor$ 10 | $if(date-meta)$ 11 | 12 | $endif$ 13 | $if(title-prefix)$$title-prefix$ - $endif$$pagetitle$ 14 | 15 | $if(quotes)$ 16 | 17 | $endif$ 18 | $if(highlighting-css)$ 19 | 22 | $endif$ 23 | $for(css)$ 24 | 25 | $endfor$ 26 | $if(math)$ 27 | $math$ 28 | $endif$ 29 | $for(header-includes)$ 30 | $header-includes$ 31 | $endfor$ 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | $if(title)$ 41 | 56 | $endif$ 57 |
58 |
59 | $if(toc)$ 60 |
61 |
62 |
    63 | 64 |
65 | $toc$ 66 |
67 |
68 | $endif$ 69 |
70 | $for(include-before)$ 71 | $include-before$ 72 | $endfor$ 73 | $body$ 74 | $for(include-after)$ 75 | $include-after$ 76 | $endfor$ 77 |
78 |
79 |
80 | 81 | 82 | -------------------------------------------------------------------------------- /src/Knit/Report/Input/RST.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE ExtendedDefaultRules #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE GADTs #-} 5 | {-# LANGUAGE OverloadedStrings #-} 6 | {-# LANGUAGE ScopedTypeVariables #-} 7 | {-# LANGUAGE TypeOperators #-} 8 | 9 | {-| 10 | Module : Knit.Report.Input.RST 11 | Description : Functions to RST fragments to a Pandoc 12 | Copyright : (c) Adam Conner-Sax 2021 13 | License : BSD-3-Clause 14 | Maintainer : adam_conner_sax@yahoo.com 15 | Stability : experimental 16 | 17 | Functions to add RST fragments to the current Pandoc. 18 | -} 19 | module Knit.Report.Input.RST 20 | ( 21 | -- * Default Options 22 | rstReaderOptions 23 | 24 | -- * functions to add various thing to the current Pandoc 25 | , addRST 26 | , addRSTWithOptions 27 | , addRSTFromFile 28 | , addRSTFromFileWithOptions 29 | ) 30 | where 31 | 32 | import qualified Data.Text as T 33 | import qualified Text.Pandoc as PA 34 | 35 | import qualified Polysemy as P 36 | import qualified Knit.Effect.Pandoc as PE 37 | import qualified Knit.Effect.PandocMonad as PM 38 | 39 | -- | Base Pandoc MarkDown reader options 40 | rstReaderOptions :: PA.ReaderOptions 41 | rstReaderOptions = PA.def 42 | { PA.readerStandalone = True 43 | , PA.readerExtensions = PA.extensionsFromList 44 | [ PA.Ext_auto_identifiers 45 | , PA.Ext_backtick_code_blocks 46 | , PA.Ext_fancy_lists 47 | , PA.Ext_footnotes 48 | , PA.Ext_simple_tables 49 | , PA.Ext_multiline_tables 50 | , PA.Ext_tex_math_dollars 51 | , PA.Ext_header_attributes 52 | , PA.Ext_implicit_header_references 53 | , PA.Ext_implicit_figures 54 | ] 55 | } 56 | 57 | -- | Add a Pandoc RST fragment with the given options 58 | addRSTWithOptions 59 | :: (PM.PandocEffects effs, P.Member PE.ToPandoc effs) 60 | => PA.ReaderOptions 61 | -> T.Text 62 | -> P.Sem effs () 63 | addRSTWithOptions = PE.addFrom PE.ReadRST 64 | 65 | -- | Add a Pandoc RST fragment with default options 66 | addRST 67 | :: (PM.PandocEffects effs, P.Member PE.ToPandoc effs) 68 | => T.Text 69 | -> P.Sem effs () 70 | addRST = addRSTWithOptions rstReaderOptions 71 | 72 | addRSTFromFileWithOptions :: (PM.PandocEffects effs, P.Member PE.ToPandoc effs) 73 | => PA.ReaderOptions 74 | -> FilePath 75 | -> P.Sem effs () 76 | addRSTFromFileWithOptions opts fp = addRSTWithOptions opts (".. include:: " <> toText fp <> "\n") 77 | 78 | addRSTFromFile :: (PM.PandocEffects effs, P.Member PE.ToPandoc effs) 79 | => FilePath 80 | -> P.Sem effs () 81 | addRSTFromFile = addRSTFromFileWithOptions rstReaderOptions 82 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.ms: -------------------------------------------------------------------------------- 1 | .\" **** Custom macro definitions ********************************* 2 | .\" * Super/subscript 3 | .\" (https://lists.gnu.org/archive/html/groff/2012-07/msg00046.html) 4 | .ds { \v'-0.3m'\\s[\\n[.s]*9u/12u] 5 | .ds } \s0\v'0.3m' 6 | .ds < \v'0.3m'\s[\\n[.s]*9u/12u] 7 | .ds > \s0\v'-0.3m' 8 | .\" * Horizontal line 9 | .de HLINE 10 | .LP 11 | .ce 12 | \l'20' 13 | .. 14 | $if(highlighting-macros)$ 15 | .\" * Syntax highlighting macros 16 | $highlighting-macros$ 17 | $endif$ 18 | .\" **** Settings ************************************************* 19 | .\" text width 20 | .nr LL 5.5i 21 | .\" left margin 22 | .nr PO 1.25i 23 | .\" top margin 24 | .nr HM 1.25i 25 | .\" bottom margin 26 | .nr FM 1.25i 27 | .\" header/footer width 28 | .nr LT \n[LL] 29 | .\" point size 30 | .nr PS $if(pointsize)$$pointsize$$else$10p$endif$ 31 | .\" line height 32 | .nr VS $if(lineheight)$$lineheight$$else$12p$endif$ 33 | .\" font family: A, BM, H, HN, N, P, T, ZCM 34 | .fam $if(fontfamily)$$fontfamily$$else$T$endif$ 35 | .\" paragraph indent 36 | .nr PI $if(indent)$$indent$$else$2m$endif$ 37 | .\" interparagraph space 38 | .nr PD 0.33v 39 | .\" footnote width 40 | .nr FL \n[LL] 41 | .\" footnote point size 42 | .nr FPS (\n[PS] - 2000) 43 | $if(papersize)$ 44 | .\" paper size 45 | .ds paper $papersize$ 46 | $endif$ 47 | .\" color used for strikeout 48 | .defcolor strikecolor rgb 0.7 0.7 0.7 49 | .\" color for links (rgb) 50 | .ds PDFHREF.COLOUR 0.35 0.00 0.60 51 | .\" border for links (default none) 52 | .ds PDFHREF.BORDER 0 0 0 53 | .\" point size difference between heading levels 54 | .nr PSINCR 1p 55 | .\" heading level above which point size no longer changes 56 | .nr GROWPS 2 57 | .\" comment these out if you want a dot after section numbers: 58 | .als SN SN-NO-DOT 59 | .als SN-STYLE SN-NO-DOT 60 | .\" pdf outline fold level 61 | .nr PDFOUTLINE.FOLDLEVEL 3 62 | .\" start out in outline view 63 | .pdfview /PageMode /UseOutlines 64 | .\" *************************************************************** 65 | .\" PDF metadata 66 | .pdfinfo /Title "$title-meta$" 67 | .pdfinfo /Author "$author-meta$" 68 | $if(adjusting)$ 69 | .ad $adjusting$ 70 | $endif$ 71 | $if(hyphenate)$ 72 | .hy 73 | $else$ 74 | .nh \" Turn off hyphenation by default. 75 | $endif$ 76 | $if(has-inline-math)$ 77 | .EQ 78 | delim @@ 79 | .EN 80 | $endif$ 81 | $for(header-includes)$ 82 | $header-includes$ 83 | $endfor$ 84 | $if(title)$ 85 | .TL 86 | $title$ 87 | $endif$ 88 | $for(author)$ 89 | .AU 90 | $author$ 91 | $endfor$ 92 | $if(date)$ 93 | .AU 94 | .sp 0.5 95 | .ft R 96 | $date$ 97 | $endif$ 98 | $if(abstract)$ 99 | .AB 100 | $abstract$ 101 | .AE 102 | $endif$ 103 | .\" 1 column (use .2C for two column) 104 | .1C 105 | $for(include-before)$ 106 | $include-before$ 107 | $endfor$ 108 | $body$ 109 | $if(toc)$ 110 | .TC 111 | $endif$ 112 | $for(include-after)$ 113 | $include-after$ 114 | $endfor$ 115 | .pdfsync 116 | -------------------------------------------------------------------------------- /examples/ErrorExample.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 as T 12 | import Data.String.Here ( here ) 13 | import qualified Graphics.Vega.VegaLite as V 14 | 15 | templateVars :: M.Map String String 16 | templateVars = M.fromList 17 | [ ("lang" , "English") 18 | , ("author" , "Adam Conner-Sax") 19 | , ("pagetitle", "knit-haskell simple example") 20 | -- , ("tufte","True") 21 | ] 22 | 23 | main :: IO () 24 | main = do 25 | let template = K.FromIncludedTemplateDir "pandoc-bootstrap-KH.html" 26 | pandocWriterConfig <- K.mkPandocWriterConfig template 27 | templateVars 28 | K.mindocOptionsF 29 | let knitConfig = (K.defaultKnitConfig Nothing) 30 | { K.outerLogPrefix = Just "ErrorExample.Main" 31 | , K.logIf = K.logAll 32 | , K.pandocWriterConfig = pandocWriterConfig 33 | } 34 | resE <- K.knitHtml knitConfig makeDocWithKnitError 35 | case resE of 36 | Right htmlAsText -> 37 | K.writeAndMakePathLT "docs/error_example.html" htmlAsText 38 | Left err -> putStrLn $ "Pandoc Error: " ++ show err 39 | 40 | md1 :: T.Text 41 | md1 = [here| 42 | ## Some example markdown 43 | * [Markdown][MarkdownLink] is a nice way to write formatted notes with a minimum of code. 44 | * It supports links and tables and some *styling* information. 45 | 46 | [MarkDownLink]: 47 | |] 48 | 49 | makeDocWithKnitError :: K.KnitOne effs => K.Sem effs () 50 | makeDocWithKnitError = K.wrapPrefix "makeDocWithKnitError" $ do 51 | K.logLE K.Info "adding some markdown..." 52 | K.addMarkDown md1 53 | K.logLE K.Info "adding some latex..." 54 | K.addMarkDown "## Some example latex" 55 | K.addLatex "Overused favorite equation: $e^{i\\pi} + 1 = 0$" 56 | K.logLE K.Info "adding a visualization..." 57 | K.knitError 58 | "Uh oh! Something went wrong which I am explaining with this message." 59 | K.addMarkDown "## An example hvega visualization" 60 | _ <- K.addHvega Nothing Nothing exampleVis 61 | return () 62 | 63 | 64 | exampleVis :: V.VegaLite 65 | exampleVis = 66 | let cars = 67 | V.dataFromUrl "https://vega.github.io/vega-datasets/data/cars.json" [] 68 | enc = 69 | V.encoding 70 | . V.position V.X [V.PName "Horsepower", V.PmType V.Quantitative] 71 | . V.position V.Y [V.PName "Miles_per_Gallon", V.PmType V.Quantitative] 72 | . V.color [V.MName "Origin", V.MmType V.Nominal] 73 | bkg = V.background "rgba(0, 0, 0, 0.05)" 74 | in V.toVegaLite [bkg, cars, V.mark V.Circle [], enc []] 75 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.s5: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $for(author-meta)$ 8 | 9 | 10 | $endfor$ 11 | $if(date-meta)$ 12 | 13 | $endif$ 14 | $if(keywords)$ 15 | 16 | $endif$ 17 | $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ 18 | 27 | 28 | 29 | 30 | $if(highlighting-css)$ 31 | 34 | $endif$ 35 | $for(css)$ 36 | 37 | $endfor$ 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | $if(math)$ 46 | $math$ 47 | $endif$ 48 | $for(header-includes)$ 49 | $header-includes$ 50 | $endfor$ 51 | 52 | 53 | $for(include-before)$ 54 | $include-before$ 55 | $endfor$ 56 |
57 |
58 |
59 | 60 | 64 |
65 |
66 | $if(title)$ 67 |
68 |

$title$

69 | $if(subtitle)$ 70 |

$subtitle$

71 | $endif$ 72 | $if(author)$ 73 |

$for(author)$$author$$sep$
$endfor$

74 | $endif$ 75 | $if(date)$ 76 |

$date$

77 | $endif$ 78 |
79 | $endif$ 80 | $if(toc)$ 81 |
82 | $table-of-contents$ 83 |
84 | $endif$ 85 | $body$ 86 | $for(include-after)$ 87 | $include-after$ 88 | $endfor$ 89 |
90 | 91 | 92 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.slideous: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | $for(author-meta)$ 10 | 11 | $endfor$ 12 | $if(date-meta)$ 13 | 14 | $endif$ 15 | $if(keywords)$ 16 | 17 | $endif$ 18 | $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ 19 | 28 | $if(highlighting-css)$ 29 | 32 | $endif$ 33 | 35 | $for(css)$ 36 | 38 | $endfor$ 39 | $if(math)$ 40 | $math$ 41 | $endif$ 42 | $for(header-includes)$ 43 | $header-includes$ 44 | $endfor$ 45 | 47 | $if(duration)$ 48 | 49 | $endif$ 50 | 51 | 52 | $for(include-before)$ 53 | $include-before$ 54 | $endfor$ 55 |
56 | 57 | of {$$slidecount} 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | ½ 69 | {$$title}, {$$author} 70 |
71 | $if(title)$ 72 |
73 |

$title$

74 | $if(subtitle)$ 75 |

$subtitle$

76 | $endif$ 77 |

78 | $for(author)$$author$$sep$
$endfor$ 79 |

80 | $if(date)$ 81 |

$date$

82 | $endif$ 83 |
84 | $endif$ 85 | $if(toc)$ 86 |
87 | $table-of-contents$ 88 |
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.
  • 54 |
55 |
56 |
57 |

Some example latex (Doc 2)

58 |

A different equation: \(a^2 + 59 | b^2 = c^2\)

60 |
61 |
63 |

An example hvega visualization (Doc 2)

64 |
65 | 89 |
90 |
91 |
92 |
93 |
94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /data/knit-haskell-templates/pandoc-adaptive-bootstrap-KH.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 | $for(author-meta)$ 36 | 37 | $endfor$ 38 | $if(date-meta)$ 39 | 40 | $endif$ 41 | $if(title-prefix)$$title-prefix$ - $endif$$pagetitle$ 42 | 43 | $if(quotes)$ 44 | 45 | $endif$ 46 | $if(highlighting-css)$ 47 | 50 | $endif$ 51 | $for(css)$ 52 | 53 | $endfor$ 54 | $if(math)$ 55 | $math$ 56 | $endif$ 57 | $for(header-includes)$ 58 | $header-includes$ 59 | $endfor$ 60 | 61 | 62 | 63 | 64 | $if(title)$ 65 | 80 | $endif$ 81 |
82 |
83 | $if(toc)$ 84 |
85 |
86 | 87 | $toc$ 88 | 89 |
90 |
91 | $endif$ 92 |
93 | $for(include-before)$ 94 | $include-before$ 95 | $endfor$ 96 | $body$ 97 | $for(include-after)$ 98 | $include-after$ 99 | $endfor$ 100 |
101 |
102 |
103 | $if(analytics)$ 104 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /docs/multi_doc1.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 |
  • Markdown 51 | is a nice way to write formatted notes with a minimum of 52 | code.
  • 53 |
  • It supports links and tables and some styling 54 | information.
  • 55 |
56 |
57 |
58 |

Some example latex (Doc 1)

59 |

Overused favorite equation: \(e^{i\pi} + 1 = 0\)

61 |
62 |
64 |

An example hvega visualization (Doc 1)

65 |
66 | 90 |
91 |
92 |
93 |
94 |
95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /examples/MtlExample.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE OverloadedStrings #-} 4 | {-# LANGUAGE TypeApplications #-} 5 | {-# LANGUAGE GADTs #-} 6 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} 7 | module Main where 8 | 9 | import qualified Knit.Report as K 10 | 11 | import Control.Monad.IO.Class ( MonadIO ) 12 | import qualified Data.Map as M 13 | import qualified Data.Text.IO as T 14 | import qualified Data.Text.Lazy as TL 15 | import qualified Data.Text as T 16 | import Data.String.Here ( here ) 17 | import qualified Graphics.Vega.VegaLite as V 18 | 19 | import Control.Monad.Reader ( ReaderT 20 | , ask 21 | , runReaderT 22 | ) 23 | 24 | templateVars :: M.Map String String 25 | templateVars = M.fromList 26 | [ ("lang" , "English") 27 | , ("author" , "Adam Conner-Sax") 28 | , ("pagetitle", "knit-haskell mtl example") 29 | -- , ("tufte","True") 30 | ] 31 | 32 | -- A demo application stack 33 | newtype MyApp env a = MyStack { unMyApp :: ReaderT env IO a } deriving (Functor, Applicative, Monad, MonadIO) 34 | 35 | type ExampleApp = MyApp T.Text 36 | 37 | runExampleApp :: T.Text -> ExampleApp a -> IO a 38 | runExampleApp t = flip runReaderT t . unMyApp 39 | 40 | getEnv :: MyApp env env 41 | getEnv = MyStack $ ask 42 | 43 | main :: IO () 44 | main = do 45 | let template = K.FromIncludedTemplateDir "pandoc-bootstrap-KH.html" 46 | templateVarsWithCss <- K.addCss 47 | (K.FullySpecifiedCssPath "css/pandoc-bootstrap.css") 48 | templateVars 49 | pandocWriterConfig <- K.mkPandocWriterConfig template 50 | templateVarsWithCss 51 | K.mindocOptionsF 52 | 53 | let knitConfig = (K.defaultKnitConfig Nothing) 54 | { K.outerLogPrefix = Just "MtlExample.Main" 55 | , K.logIf = K.logAll 56 | , K.pandocWriterConfig = pandocWriterConfig 57 | } 58 | resE <- runExampleApp "This is from the MyApp environment." 59 | $ K.knitHtml knitConfig makeDoc 60 | case resE of 61 | Right htmlAsText -> 62 | K.writeAndMakePathLT "docs/mtl_example.html" htmlAsText 63 | Left err -> putStrLn $ "Pandoc error: " ++ show err 64 | 65 | md1 :: T.Text 66 | md1 = [here| 67 | ## Some example markdown 68 | * [Markdown][MarkdownLink] is a nice way to write formatted notes with a minimum of code. 69 | * It supports links and tables and some *styling* information. 70 | 71 | [MarkDownLink]: 72 | |] 73 | 74 | makeDoc :: (K.KnitOne effs, K.KnitBase ExampleApp effs) => K.Sem effs () 75 | makeDoc = K.wrapPrefix "makeDoc" $ do 76 | K.logLE K.Info "adding some markdown..." 77 | K.addMarkDown md1 78 | 79 | K.logLE K.Info "adding some latex..." 80 | K.addMarkDown "## Some example latex" 81 | K.addLatex "Overused favorite equation: $e^{i\\pi} + 1 = 0$" 82 | 83 | K.logLE K.Info "adding a visualization..." 84 | K.addMarkDown "## An example hvega visualization" 85 | _ <- K.addHvega Nothing Nothing exampleVis 86 | 87 | K.logLE K.Info 88 | "Retrieving some text from the base monad and current date-time." 89 | envText <- K.liftKnit @ExampleApp getEnv 90 | curTime <- K.getCurrentTime 91 | K.addMarkDown 92 | "## An example of getting env from a base monad, and time from the Pandoc Effects." 93 | K.addMarkDown $ envText <> "\n\n" <> (T.pack $ show curTime) 94 | 95 | exampleVis :: V.VegaLite 96 | exampleVis = 97 | let cars = 98 | V.dataFromUrl "https://vega.github.io/vega-datasets/data/cars.json" [] 99 | enc = 100 | V.encoding 101 | . V.position V.X [V.PName "Horsepower", V.PmType V.Quantitative] 102 | . V.position V.Y [V.PName "Miles_per_Gallon", V.PmType V.Quantitative] 103 | . V.color [V.MName "Origin", V.MmType V.Nominal] 104 | bkg = V.background "rgba(0, 0, 0, 0.05)" 105 | in V.toVegaLite [bkg, cars, V.mark V.Circle [], enc []] 106 | -------------------------------------------------------------------------------- /examples/SimpleExample.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 as T 12 | import Data.String.Here ( here ) 13 | import qualified Graphics.Vega.VegaLite as V 14 | 15 | import qualified Plots as P 16 | 17 | templateVars :: M.Map String String 18 | templateVars = M.fromList 19 | [ ("lang" , "English") 20 | , ("author" , "Adam Conner-Sax") 21 | , ("pagetitle", "knit-haskell simple example") 22 | -- , ("tufte","True") 23 | ] 24 | 25 | main :: IO () 26 | main = do 27 | let template = K.FromIncludedTemplateDir "pandoc-adaptive-bootstrap-KH.html" 28 | pandocWriterConfig <- K.mkPandocWriterConfig template 29 | templateVars 30 | K.mindocOptionsF 31 | 32 | let knitConfig = (K.defaultKnitConfig Nothing) 33 | { K.outerLogPrefix = Just "SimpleExample.Main" 34 | , K.logIf = K.logAll 35 | , K.pandocWriterConfig = pandocWriterConfig 36 | } 37 | resE <- K.knitHtml knitConfig makeDoc 38 | case resE of 39 | Right htmlAsText -> 40 | K.writeAndMakePathLT "docs/simple_example.html" htmlAsText 41 | Left err -> putStrLn $ "Pandoc Error: " ++ show err 42 | 43 | md1 :: T.Text 44 | md1 = [here| 45 | ## Some example markdown 46 | * [Markdown][MarkdownLink] is a nice way to write formatted notes with a minimum of code. 47 | * It supports links and tables and some *styling* information. 48 | 49 | [MarkDownLink]: 50 | |] 51 | 52 | makeDoc :: K.KnitOne effs => K.Sem effs () 53 | makeDoc = K.wrapPrefix "makeDoc" $ do 54 | K.logLE K.Info "adding some markdown..." 55 | K.addMarkDown md1 56 | K.logLE K.Info "adding some latex..." 57 | K.addMarkDown "## Some example latex" 58 | K.addLatex "Overused favorite equation: $e^{i\\pi} + 1 = 0$" 59 | K.logLE K.Info "adding a visualization..." 60 | K.addMarkDown "## An example hvega visualization" 61 | _ <- K.addHvega Nothing (Just "From the cars data-set") exampleVis 62 | K.addMarkDown 63 | "## Example Diagrams visualizations, the second using the plots library." 64 | K.logLE K.Info "adding a Diagrams example and plots example..." 65 | _ <- K.addDiagramAsSVG Nothing (Just "Example diagram") 300 300 exampleDiagram 66 | _ <- K.addDiagramAsSVG 67 | Nothing 68 | (Just "Example diagrams visualization using the Plots library") 69 | 300 70 | 300 71 | samplePlot 72 | return () 73 | 74 | -- example using HVega 75 | exampleVis :: V.VegaLite 76 | exampleVis = 77 | let cars = 78 | V.dataFromUrl "https://vega.github.io/vega-datasets/data/cars.json" [] 79 | enc = 80 | V.encoding 81 | . V.position V.X [V.PName "Horsepower", V.PmType V.Quantitative] 82 | . V.position V.Y [V.PName "Miles_per_Gallon", V.PmType V.Quantitative] 83 | . V.color [V.MName "Origin", V.MmType V.Nominal] 84 | bkg = V.background "rgba(0, 0, 0, 0.05)" 85 | in V.toVegaLite [bkg, cars, V.mark V.Circle [], enc []] 86 | 87 | 88 | -- sampleDiagram 89 | -- from 90 | 91 | hilbert 0 = mempty 92 | hilbert n = 93 | hilbert' (n - 1) 94 | K.# K.reflectY 95 | <> K.vrule 1 96 | <> hilbert (n - 1) 97 | <> K.hrule 1 98 | <> hilbert (n - 1) 99 | <> K.vrule (-1) 100 | <> hilbert' (n - 1) 101 | K.# K.reflectX 102 | where hilbert' m = hilbert m K.# K.rotateBy (1 / 4) 103 | 104 | exampleDiagram :: K.Diagram K.SVG 105 | exampleDiagram = 106 | K.frame 1 . K.lw K.medium . K.lc K.darkred . K.strokeT $ hilbert 5 107 | 108 | 109 | -- example using Plots (as an example of using Diagrams) 110 | samplePlot :: K.Diagram K.SVG 111 | samplePlot = P.renderAxis logAxis 112 | 113 | logData = [K.V2 1 10, K.V2 2 100, K.V2 2.5 316, K.V2 3 1000] 114 | 115 | logAxis :: P.Axis K.SVG K.V2 Double 116 | logAxis = P.r2Axis K.&~ do 117 | P.scatterPlot' logData 118 | 119 | P.yAxis P.&= do 120 | P.logScale K..= P.LogAxis 121 | P.majorTicksFunction K..= P.logMajorTicks 5 -- <> pure [1] 122 | 123 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.context: -------------------------------------------------------------------------------- 1 | $if(context-lang)$ 2 | \mainlanguage[$context-lang$] 3 | $endif$ 4 | $if(context-dir)$ 5 | \setupalign[$context-dir$] 6 | \setupdirections[bidi=on,method=two] 7 | $endif$ 8 | % Enable hyperlinks 9 | \setupinteraction 10 | [state=start, 11 | $if(title)$ 12 | title={$title$}, 13 | $endif$ 14 | $if(subtitle)$ 15 | subtitle={$subtitle$}, 16 | $endif$ 17 | $if(author)$ 18 | author={$for(author)$$author$$sep$; $endfor$}, 19 | $endif$ 20 | $if(keywords)$ 21 | keyword={$for(keywords)$$keywords$$sep$; $endfor$}, 22 | $endif$ 23 | style=$linkstyle$, 24 | color=$linkcolor$, 25 | contrastcolor=$linkcontrastcolor$] 26 | 27 | % make chapter, section bookmarks visible when opening document 28 | \placebookmarks[chapter, section, subsection, subsubsection, subsubsubsection, subsubsubsubsection][chapter, section] 29 | \setupinteractionscreen[option=bookmark] 30 | \setuptagging[state=start] 31 | 32 | $if(papersize)$ 33 | \setuppapersize[$for(papersize)$$papersize$$sep$,$endfor$] 34 | $endif$ 35 | $if(layout)$ 36 | \setuplayout[$for(layout)$$layout$$sep$,$endfor$] 37 | $endif$ 38 | $if(pagenumbering)$ 39 | \setuppagenumbering[$for(pagenumbering)$$pagenumbering$$sep$,$endfor$] 40 | $endif$ 41 | $if(pdfa)$ 42 | % attempt to generate PDF/A 43 | \setupbackend 44 | [format=PDF/A-1b:2005, 45 | intent=sRGB IEC61966-2.1, 46 | profile=sRGB.icc] 47 | $endif$ 48 | 49 | % use microtypography 50 | \definefontfeature[default][default][script=latn, protrusion=quality, expansion=quality, itlc=yes, textitalics=yes, onum=yes, pnum=yes] 51 | \definefontfeature[smallcaps][script=latn, protrusion=quality, expansion=quality, smcp=yes, onum=yes, pnum=yes] 52 | \setupalign[hz,hanging] 53 | \setupitaliccorrection[global, always] 54 | 55 | \setupbodyfontenvironment[default][em=italic] % use italic as em, not slanted 56 | 57 | \definefallbackfamily[mainface][rm][CMU Serif][preset=range:greek, force=yes] 58 | \definefontfamily[mainface][rm][$if(mainfont)$$mainfont$$else$Latin Modern Roman$endif$] 59 | \definefontfamily[mainface][mm][$if(mathfont)$$mathfont$$else$Latin Modern Math$endif$] 60 | \definefontfamily[mainface][ss][$if(sansfont)$$sansfont$$else$Latin Modern Sans$endif$] 61 | \definefontfamily[mainface][tt][$if(monofont)$$monofont$$else$Latin Modern Typewriter$endif$][features=none] 62 | \setupbodyfont[mainface$if(fontsize)$,$fontsize$$endif$] 63 | 64 | \setupwhitespace[$if(whitespace)$$whitespace$$else$medium$endif$] 65 | $if(indenting)$ 66 | \setupindenting[$for(indenting)$$indenting$$sep$,$endfor$] 67 | $endif$ 68 | $if(interlinespace)$ 69 | \setupinterlinespace[$for(interlinespace)$$interlinespace$$sep$,$endfor$] 70 | $endif$ 71 | 72 | \setuphead[chapter] [style=\tfd,header=empty] 73 | \setuphead[section] [style=\tfc] 74 | \setuphead[subsection] [style=\tfb] 75 | \setuphead[subsubsection] [style=\bf] 76 | \setuphead[subsubsubsection] [style=\sc] 77 | \setuphead[subsubsubsubsection][style=\it] 78 | 79 | $if(headertext)$ 80 | \setupheadertexts$for(headertext)$[$headertext$]$endfor$ 81 | $endif$ 82 | $if(footertext)$ 83 | \setupfootertexts$for(footertext)$[$footertext$]$endfor$ 84 | $endif$ 85 | $if(number-sections)$ 86 | $else$ 87 | \setuphead[chapter, section, subsection, subsubsection, subsubsubsection, subsubsubsubsection][number=no] 88 | $endif$ 89 | 90 | \definedescription 91 | [description] 92 | [headstyle=bold, style=normal, location=hanging, width=broad, margin=1cm, alternative=hanging] 93 | 94 | \setupitemize[autointro] % prevent orphan list intro 95 | \setupitemize[indentnext=no] 96 | 97 | \setupfloat[figure][default={here,nonumber}] 98 | \setupfloat[table][default={here,nonumber}] 99 | 100 | \setupthinrules[width=15em] % width of horizontal rules 101 | 102 | \setupxtable[frame=off] 103 | \setupxtable[head][topframe=on,bottomframe=on] 104 | \setupxtable[body][] 105 | \setupxtable[foot][bottomframe=on] 106 | 107 | $for(header-includes)$ 108 | $header-includes$ 109 | $endfor$ 110 | 111 | \starttext 112 | $if(title)$ 113 | \startalignment[middle] 114 | {\tfd $title$} 115 | $if(subtitle)$ 116 | \smallskip 117 | {\tfa $subtitle$} 118 | $endif$ 119 | $if(author)$ 120 | \smallskip 121 | {\tfa $for(author)$$author$$sep$\crlf $endfor$} 122 | $endif$ 123 | $if(date)$ 124 | \smallskip 125 | {\tfa $date$} 126 | $endif$ 127 | \bigskip 128 | \stopalignment 129 | $endif$ 130 | $if(abstract)$ 131 | \midaligned{\it Abstract} 132 | \startnarrower[2*middle] 133 | $abstract$ 134 | \stopnarrower 135 | \blank[big] 136 | $endif$ 137 | $for(include-before)$ 138 | $include-before$ 139 | $endfor$ 140 | $if(toc)$ 141 | \completecontent 142 | $endif$ 143 | $if(lot)$ 144 | \completelistoftables 145 | $endif$ 146 | $if(lof)$ 147 | \completelistoffigures 148 | $endif$ 149 | 150 | $body$ 151 | 152 | $for(include-after)$ 153 | $include-after$ 154 | $endfor$ 155 | \stoptext 156 | -------------------------------------------------------------------------------- /examples/Index.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 as T 12 | import Data.String.Here ( here ) 13 | 14 | templateVars :: M.Map String String 15 | templateVars = M.fromList 16 | [ ("lang" , "English") 17 | , ("author" , "Adam Conner-Sax") 18 | , ("pagetitle", "knit-haskell example index") 19 | -- , ("tufte","True") 20 | ] 21 | 22 | main :: IO () 23 | main = do 24 | let template = K.FromIncludedTemplateDir "pandoc-adaptive-bootstrap-KH.html" 25 | pandocWriterConfig <- K.mkPandocWriterConfig template 26 | templateVars 27 | K.mindocOptionsF 28 | 29 | let knitConfig = (K.defaultKnitConfig Nothing) 30 | { K.outerLogPrefix = Just "Index.Main" 31 | , K.logIf = K.logAll 32 | , K.pandocWriterConfig = pandocWriterConfig 33 | } 34 | resE <- K.knitHtml knitConfig makeDoc 35 | case resE of 36 | Right htmlAsText -> 37 | K.writeAndMakePathLT "docs/index.html" htmlAsText 38 | Left err -> putStrLn $ "Pandoc Error: " ++ show err 39 | 40 | 41 | mdIndex :: T.Text 42 | mdIndex = [here| 43 | ## Examples 44 | - [Simple][SimpleSource] demonstrates the bare bones features of the library. 45 | Creating a document from a few fragments and then "knitting" it 46 | into HTML text and writing that to a file. This includes hvega, diagrams and plots examples. 47 | Result [here][SimpleExample] 48 | 49 | - [MultiDoc][MultiDocSource] demonstrates how to build multiple documents. 50 | Results [here][MultiDocExample1] and [here][MultiDocExample2]. 51 | 52 | - [Mtl][MtlSource] demonstrates the same simple features as above, 53 | but runs them atop an example mtl stack, 54 | allowing access to the mtl stack's functionality during document assembly. 55 | Result [here][MtlExample]. 56 | 57 | - [Random][RandomSource] builds on the mtl example to show how you can also add 58 | an additional polysemy effect (in this case, 59 | Polysemy.RandomFu from polysemy-RandomFu) to your document-building. 60 | This one also demonstrates a use of colonnade for adding a formatted table to the document. 61 | Result [here][RandomExample]. 62 | 63 | - [Error][ErrorSource]: Similar to "Simple" but throws a user error during document assembly. 64 | Result [here][ErrorExample]. 65 | 66 | - [Async][AsyncSource]: Similar to "SimpleExample" but uses Polysemy's 67 | sequenceConcurrently to run some example computations 68 | concurrently (as long as you compile with "-threaded"). 69 | Result [here][AsyncExample] 70 | 71 | - [Cache][CacheSource]: Similar to "SimpleExample" but uses the "AtomicCache" 72 | effect to store the result of a computation. 73 | Demonstrates the behavior of the cache when multiple threads 74 | attempt to access the same item--the first thread loads/creates the 75 | data while the other blocks until the data is in-memory. 76 | Also demonstrates use of time-stamps to force rebuilding when tracked inputs change. 77 | Result [here][CacheExample]. 78 | 79 | - [CustomCache][CacheSource2]: Similar to "CacheExample" but implements and uses 80 | a different serializer and persistence layer than the default. 81 | Result [here][CacheExample2]. 82 | 83 | [SimpleExample]: 84 | [SimpleSource]: 85 | [MultiDocExample1]: 86 | [MultiDocExample2]: 87 | [MultiDocSource]: 88 | [MtlExample]: 89 | [MtlSource]: 90 | [RandomExample]: 91 | [RandomSource]: 92 | [ErrorSource]: 93 | [AsyncExample]: 94 | [AsyncSource]: 95 | [CacheExample]: 96 | [CacheSource]: 97 | [CacheExample2]: 98 | [CacheSource2]: 99 | |] 100 | 101 | makeDoc :: K.KnitOne effs => K.Sem effs () 102 | makeDoc = K.wrapPrefix "makeDoc" $ do 103 | K.logLE K.Info "Building example index" 104 | K.addMarkDown mdIndex 105 | return () 106 | 107 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.dzslides: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $for(author-meta)$ 6 | 7 | $endfor$ 8 | $if(date-meta)$ 9 | 10 | $endif$ 11 | $if(keywords)$ 12 | 13 | $endif$ 14 | $if(title-prefix)$$title-prefix$ – $endif$$pagetitle$ 15 | 24 | $if(highlighting-css)$ 25 | 28 | $endif$ 29 | $if(css)$ 30 | $for(css)$ 31 | 32 | $endfor$ 33 | $else$ 34 | 35 | 36 | 169 | $endif$ 170 | $if(math)$ 171 | $math$ 172 | $endif$ 173 | $for(header-includes)$ 174 | $header-includes$ 175 | $endfor$ 176 | 177 | 178 | $if(title)$ 179 |
180 |

$title$

181 | $if(subtitle)$ 182 |

$subtitle$

183 | $endif$ 184 |
185 | $if(author)$$for(author)$$author$$sep$, $endfor$$endif$ · $if(date)$$date$$endif$ 186 |
187 |
188 | $endif$ 189 | $if(toc)$ 190 |
191 | $table-of-contents$ 192 |
193 | $endif$ 194 | $for(include-before)$ 195 | $include-before$ 196 | $endfor$ 197 | $body$ 198 | $for(include-after)$ 199 | $include-after$ 200 | $endfor$ 201 | $dzslides-core$ 202 | 203 | 204 | -------------------------------------------------------------------------------- /src/Knit/Report/Output.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedStrings #-} 2 | module Knit.Report.Output 3 | ( 4 | -- * Pandoc Writer Configuration 5 | PandocWriterConfig(..) 6 | , TemplateVariables 7 | , WriterOptionsF 8 | , mkPandocWriterConfig 9 | 10 | -- * Pandoc Template Types 11 | , TemplatePath(..) 12 | , CssPath(..) 13 | , addCss 14 | 15 | -- * file writing helpers 16 | , writeAllPandocResultsWithInfo 17 | , writePandocResultWithInfo 18 | , writeAndMakePathLT 19 | , writeAndMakePath 20 | ) 21 | where 22 | 23 | import qualified Paths_knit_haskell as Paths 24 | import qualified Data.Map as M 25 | import qualified Text.Pandoc as PA 26 | import qualified Data.Text as T 27 | import qualified Data.Text.Lazy as TL 28 | import qualified System.Directory as SD 29 | 30 | import qualified Knit.Effect.Docs as KD 31 | import qualified Knit.Effect.Pandoc as KP 32 | 33 | type TemplateVariables = M.Map String String 34 | type WriterOptionsF = PA.WriterOptions -> PA.WriterOptions 35 | 36 | data PandocWriterConfig = 37 | PandocWriterConfig 38 | { 39 | templateFP :: Maybe FilePath 40 | -- ^ optional path to pandoc 41 | , templateVars :: TemplateVariables 42 | -- ^ variable substitutions for the 43 | , optionsF :: WriterOptionsF 44 | -- ^ change default 45 | } 46 | 47 | -- | Make a 'PandocWriterConfig' from a PandocTemplate specification 48 | mkPandocWriterConfig 49 | :: TemplatePath 50 | -> TemplateVariables 51 | -> WriterOptionsF 52 | -> IO PandocWriterConfig 53 | mkPandocWriterConfig tp tv wf = do 54 | templateFPM <- pandocTemplatePath tp 55 | return $ PandocWriterConfig templateFPM tv wf 56 | 57 | -- | Type to specify path to template, 58 | -- which may be in a directory installed with knit-haskell. 59 | data TemplatePath = DefaultTemplate 60 | | FromIncludedTemplateDir T.Text 61 | | FullySpecifiedTemplatePath T.Text 62 | 63 | -- | get correct path to give Pandoc, depending on how things are installed 64 | pandocTemplatePath :: TemplatePath -> IO (Maybe String) 65 | pandocTemplatePath DefaultTemplate = return Nothing 66 | pandocTemplatePath (FullySpecifiedTemplatePath x) = return $ Just (toString x) 67 | pandocTemplatePath (FromIncludedTemplateDir x) = 68 | fmap (Just . (<> "/knit-haskell-templates/" <> toString x)) Paths.getDataDir 69 | 70 | -- | Type to specify path to Css, 71 | -- which may be in a directory installed with knit-haskell or not. 72 | data CssPath = FromIncludedCssDir T.Text 73 | | FullySpecifiedCssPath T.Text 74 | 75 | -- | Add a CssPath to an existing TemplateVariables 76 | -- which may already have Css paths specified 77 | addCss :: CssPath -> TemplateVariables -> IO TemplateVariables 78 | addCss (FullySpecifiedCssPath x) pt = return $ appendCss x pt 79 | addCss (FromIncludedCssDir x) pt = do 80 | dir <- Paths.getDataDir 81 | let fp = toText dir <> "/knit-haskell-css/" <> x 82 | return $ appendCss fp pt 83 | 84 | 85 | -- | Append a filepath (given as Text) to the existing Css paths in TemplateVariables 86 | appendCss :: T.Text -> TemplateVariables -> TemplateVariables 87 | appendCss x tv = 88 | let curValM = M.lookup "css" tv 89 | newVal = maybe (toString x) (\y -> y <> "," <> toString x) curValM 90 | in M.insert "css" newVal tv 91 | 92 | -- utilities for file output 93 | 94 | -- | Write each lazy text from a list of 'KD.DocWithInfo' 95 | -- to disk. File names come from the 'KP.PandocInfo' 96 | -- Directory and file extension are function arguments. 97 | writeAllPandocResultsWithInfo 98 | :: T.Text -> T.Text -> [KP.DocWithInfo KP.PandocInfo TL.Text] -> IO () 99 | writeAllPandocResultsWithInfo dir extension = 100 | void . traverse (writePandocResultWithInfo dir extension) -- fmap (const ()) :: IO [()] -> IO () 101 | 102 | -- | Write the Lazy Text in a 'KD.DocWithInfo' to disk 103 | -- Name comes from the 'KP.PandocInfo' 104 | -- Directory and file extection are arguments to the function 105 | -- Create the parent directory or directories, if necessary. 106 | writePandocResultWithInfo 107 | :: T.Text -- ^ directory 108 | -> T.Text -- ^ extension 109 | -> KD.DocWithInfo KP.PandocInfo TL.Text 110 | -> IO () 111 | writePandocResultWithInfo dir extension (KD.DocWithInfo (KP.PandocInfo n _) x) 112 | = do 113 | let fPath = dir <> "/" <> n <> "." <> extension 114 | writeAndMakePathLT fPath x 115 | 116 | -- | Write Lazy Text (Pandoc's Html result) to disk. 117 | -- Create the parent directory or directories, if necessary. 118 | writeAndMakePathLT :: T.Text -> TL.Text -> IO () 119 | writeAndMakePathLT fPath = writeAndMakePath fPath toText 120 | 121 | -- | Write (to disk) something which can be converted to text. 122 | -- Create the parent directory or directories, if necessary. 123 | writeAndMakePath :: T.Text -> (a -> T.Text) -> a -> IO () 124 | writeAndMakePath fPath toStrictText x = do 125 | let (dirPath, fName) = T.breakOnEnd "/" fPath 126 | putStrLn 127 | $ toString 128 | $ "If necessary, creating " 129 | <> dirPath 130 | <> " (and parents), and writing " 131 | <> fName 132 | SD.createDirectoryIfMissing True (toString dirPath) 133 | writeFileText (toString fPath) $ toStrictText x 134 | 135 | -------------------------------------------------------------------------------- /src/Knit/Effect/Timer.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ConstraintKinds #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE GADTs #-} 5 | {-# LANGUAGE LambdaCase #-} 6 | {-# LANGUAGE OverloadedStrings #-} 7 | {-# LANGUAGE ScopedTypeVariables #-} 8 | {-# LANGUAGE StrictData #-} 9 | {-# LANGUAGE TypeApplications #-} 10 | {-# LANGUAGE TypeOperators #-} 11 | {-# LANGUAGE BangPatterns #-} 12 | {-# LANGUAGE PolyKinds #-} 13 | 14 | module Knit.Effect.Timer 15 | ( 16 | Timer 17 | , WithTimer 18 | , start 19 | , snapshot 20 | , finish 21 | , timed 22 | , withTiming 23 | , logWithTiming 24 | , logTiming 25 | , interpretTimerInIO 26 | ) 27 | where 28 | 29 | import Prelude hiding (error) 30 | 31 | import Knit.Effect.UnusedId as KU 32 | 33 | import qualified Polysemy as P 34 | import qualified Polysemy.AtomicState as P 35 | import Polysemy.Internal ( send ) 36 | import qualified Data.IORef as IORef 37 | import qualified Data.Map.Strict as Map 38 | 39 | import Text.Printf (printf) 40 | 41 | --import qualified Data.Text as Text 42 | import System.CPUTime (getCPUTime) 43 | import qualified Data.Time.Clock as T 44 | 45 | data Timings a b = Timings { wall :: a, cpu :: b } 46 | 47 | type Timed = Timings Double Double 48 | 49 | type Snapshot = Timings T.UTCTime Integer 50 | 51 | snapsTimed :: Snapshot -> Snapshot -> Timed 52 | snapsTimed (Timings utcStart cpuStart) (Timings utcEnd cpuEnd) = Timings (utcDiffSecs utcEnd utcStart) (cpuDiffSecs cpuEnd cpuStart) 53 | where 54 | cpuDiffSecs s e = fromIntegral (e - s) / 10 ^ (12 :: Int) 55 | utcDiffSecs s e = realToFrac $ T.nominalDiffTimeToSeconds $ T.diffUTCTime e s 56 | 57 | data Timer k m r where 58 | Start :: k -> Timer k m () 59 | Snapshot :: k -> Timer k m (Maybe Timed) 60 | Finish :: k -> Timer k m (Maybe Timed) 61 | 62 | start :: P.Member (Timer k) effs => k -> P.Sem effs () 63 | start = send . Start 64 | {-# INLINEABLE start #-} 65 | 66 | snapshot :: P.Member (Timer k) effs => k -> P.Sem effs (Maybe Timed) 67 | snapshot = send . Snapshot 68 | {-# INLINEABLE snapshot #-} 69 | 70 | finish :: P.Member (Timer k) effs => k -> P.Sem effs (Maybe Timed) 71 | finish = send . Finish 72 | {-# INLINEABLE finish #-} 73 | 74 | type TimerStartMap k = P.AtomicState (Map.Map k Snapshot) 75 | 76 | getSnap :: P.Member (P.Embed IO) effs => P.Sem effs Snapshot 77 | getSnap = Timings <$> P.embed T.getCurrentTime <*> P.embed getCPUTime 78 | {-# INLINEABLE getSnap #-} 79 | 80 | interpretTimerInIO :: forall k effs . (Ord k, P.Member (P.Embed IO) effs) => P.InterpreterFor (Timer k) effs 81 | interpretTimerInIO mx = do 82 | ioRef <- P.embed $ IORef.newIORef mempty 83 | let nat :: (forall ri x . (Timer k) (P.Sem ri) x -> P.Sem (TimerStartMap k ': effs) x) 84 | nat = \case 85 | Start k -> do 86 | snap <- getSnap 87 | P.atomicModify $ Map.insert k snap 88 | Snapshot k -> do 89 | startTimeM <- Map.lookup k <$> P.atomicGet 90 | case startTimeM of 91 | Nothing -> pure Nothing 92 | Just startTime -> do 93 | snap <- getSnap 94 | pure $ Just $ snapsTimed snap startTime 95 | Finish k -> do 96 | startTimeM <- Map.lookup k <$> P.atomicGet 97 | case startTimeM of 98 | Nothing -> pure Nothing 99 | Just startTime -> do 100 | snap <- getSnap 101 | P.atomicModify @(Map k Snapshot) $ Map.delete k 102 | pure $ Just $ snapsTimed snap startTime 103 | P.runAtomicStateIORef ioRef 104 | $ P.reinterpret nat mx 105 | 106 | type WithTimer r = (P.Members [Timer Text, KU.UnusedId] r) 107 | 108 | -- | Wrap an action with a timer and produce the time with the result 109 | timed :: WithTimer r => P.Sem r a -> P.Sem r (a, Maybe Timed) 110 | timed ma = do 111 | timerId <- KU.getNextUnusedId "AnonTimer" 112 | start timerId 113 | a <- ma 114 | tM <- finish timerId 115 | pure (a, tM) 116 | {-# INLINEABLE timed #-} 117 | 118 | -- | Build a new action from en existing one and its timing 119 | withTiming :: WithTimer r => (a -> Maybe Timed -> P.Sem r b) -> P.Sem r a -> P.Sem r b 120 | withTiming withTime ma = timed ma >>= uncurry withTime 121 | {-# INLINEABLE withTiming #-} 122 | 123 | -- | Given a logging function and a way to produce a message from the action result and the time, 124 | -- produce an action which runs that function with that message after the initial action. 125 | logWithTiming :: WithTimer r => (Text -> P.Sem r ()) -> (a -> Maybe Timed -> Text) -> P.Sem r a -> P.Sem r a 126 | logWithTiming logF logTimeMsg = withTiming f where 127 | f a s = logF (logTimeMsg a s) >> pure a 128 | {-# INLINEABLE logWithTiming #-} 129 | 130 | -- | Given a logging function and a way to produce a message from the action result and the time, 131 | -- produce an action which runs that function with that message after the initial action. 132 | logTiming :: WithTimer r => (Text -> P.Sem r ()) -> Text -> P.Sem r a -> P.Sem r a 133 | logTiming logF t ma = logF (t <> "...") >> logWithTiming logF msg ma where 134 | msg _ tM = case tM of 135 | Just (Timings wallS cpuS) -> t <> " [wall: " <> toText @String (printf "%0.3f" wallS) <> "s; cpu: " <> toText @String (printf "%0.3f" cpuS) <> "]" 136 | Nothing -> "(TIMING ERROR)" 137 | {-# INLINEABLE logTiming #-} 138 | -------------------------------------------------------------------------------- /src/Knit/Effect/Html.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleContexts #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE PolyKinds #-} 4 | {-# LANGUAGE GADTs #-} 5 | {-# LANGUAGE TypeOperators #-} 6 | {-# LANGUAGE ScopedTypeVariables #-} 7 | {-# OPTIONS_GHC -fwarn-incomplete-patterns #-} 8 | {-| 9 | Module : Knit.Effect.Html 10 | Description : Polysemy writer effects for creating Lucid/Blaze documents 11 | Copyright : (c) Adam Conner-Sax 2019 12 | License : BSD-3-Clause 13 | Maintainer : adam_conner_sax@yahoo.com 14 | Stability : experimental 15 | 16 | Create a Lucid or Blaze html document (using a Writer to intersperse html and other code) and then use the 'Knit.Haskell.Docs' effect 17 | to store that document for processing/output later. 18 | -} 19 | module Knit.Effect.Html 20 | ( 21 | -- * Lucid 22 | 23 | -- ** Effects 24 | Lucid 25 | , LucidDocs 26 | 27 | -- ** Actions 28 | , lucid 29 | 30 | -- ** Intepretations 31 | , lucidToNamedText 32 | , lucidHtml 33 | , lucidToText 34 | , newLucidDoc 35 | 36 | -- * Blaze 37 | 38 | -- ** Effects 39 | , Blaze 40 | , BlazeDocs 41 | 42 | -- ** Actions 43 | , blaze 44 | 45 | -- ** Interpretations 46 | , blazeToNamedText 47 | , blazeHtml 48 | , blazeToText 49 | , newBlazeDoc 50 | 51 | -- * Re-exports 52 | , DocWithInfo(..) 53 | ) 54 | where 55 | 56 | import qualified Polysemy as P 57 | import qualified Polysemy.Writer as P 58 | 59 | import qualified Lucid as LH 60 | import qualified Text.Blaze.Html as BH 61 | import qualified Text.Blaze.Html.Renderer.Text as BH 62 | import qualified Data.Text.Lazy as TL 63 | import qualified Data.Text as T 64 | 65 | import Knit.Effect.Docs ( Docs 66 | , DocWithInfo(..) 67 | , newDoc 68 | , toDocList 69 | ) 70 | 71 | -- For now, just handle the Html () case since then it's monoidal and we can interpret via writer 72 | --newtype FreerHtml = FreerHtml { unFreer :: H.Html () } 73 | 74 | -- | Type-Alias for a single Lucid document writer. 75 | type Lucid = P.Writer (LH.Html ()) 76 | 77 | -- | Type-Alias for a single Blaze document writer. 78 | type Blaze = P.Writer BH.Html 79 | 80 | -- | Add a Lucid html fragment to the current Lucid doc. 81 | lucid :: P.Member Lucid effs => LH.Html () -> P.Sem effs () 82 | lucid = P.tell 83 | 84 | -- | Add a Blaze html fragment to the current Blaze doc. 85 | blaze :: P.Member Blaze effs => BH.Html -> P.Sem effs () 86 | blaze = P.tell 87 | 88 | -- | Type-Alias for the 'Knit.Effects.Docs' effect (multi-document Writer), specialized to Lucid docs. 89 | -- To be used in an app that produces multiple html outputs, built up from Lucid bits. 90 | type LucidDocs = Docs T.Text (LH.Html ()) 91 | 92 | -- | Type-Alias for the 'Knit.Effects.Docs' effect (multi-document Writer) specialized to Blaze docs. 93 | -- To be used in an app that produces multiple html outputs, built up from Blaze bits. 94 | type BlazeDocs = Docs T.Text BH.Html 95 | 96 | -- | Take the current Lucid HTML in the writer and add it to the set of named docs with the given name. 97 | -- NB: Only use this function for making sets of documents built exclusively from Lucid. Otherwise use the more general Pandoc infrastructure in 98 | -- 'Knit.Effects.Pandoc'. 99 | newLucidDoc 100 | :: P.Member LucidDocs effs 101 | => T.Text 102 | -> P.Sem (Lucid ': effs) () 103 | -> P.Sem effs () 104 | newLucidDoc n l = (fst <$> P.runWriter l) >>= newDoc n 105 | 106 | -- | take the current Blaze HTML in the writer and add it to the set of named docs with the given name 107 | -- NB: Only use this function for making sets of documents built exclusively from Blaze. Otherwise use the more general Pandoc infrastructure in 108 | -- 'Knit.Effects.Pandoc'. 109 | newBlazeDoc 110 | :: P.Member BlazeDocs effs 111 | => T.Text 112 | -> P.Sem (Blaze ': effs) () 113 | -> P.Sem effs () 114 | newBlazeDoc n l = (fst <$> P.runWriter l) >>= newDoc n 115 | 116 | -- | Interpret the LucidDocs effect (via Writer), producing a list of named Lucid docs, suitable for writing to disk. 117 | lucidToNamedText 118 | :: P.Sem (LucidDocs ': effs) () -> P.Sem effs [DocWithInfo T.Text TL.Text] 119 | lucidToNamedText = fmap (fmap (fmap LH.renderText)) . toDocList -- monad, list, NamedDoc itself 120 | 121 | -- | Interpret the BlazeDocs effect (via Writer), producing a list of named Blaze docs. 122 | blazeToNamedText 123 | :: P.Sem (BlazeDocs ': effs) () -> P.Sem effs [DocWithInfo T.Text TL.Text] 124 | blazeToNamedText = fmap (fmap (fmap BH.renderHtml)) . toDocList -- monad, list, NamedDoc itself 125 | 126 | -- | Interprest the Lucid effect (via Writer), producing a Lucid @Html ()@ from the currently written doc 127 | lucidHtml :: P.Sem (Lucid ': effs) () -> P.Sem effs (LH.Html ()) 128 | lucidHtml = fmap fst . P.runWriter 129 | 130 | -- | Interpret the Lucid effect (via Writer), producing @Text@ from the currently written doc 131 | lucidToText :: P.Sem (Lucid ': effs) () -> P.Sem effs TL.Text 132 | lucidToText = fmap LH.renderText . lucidHtml 133 | 134 | -- | Interpret the Blaze effect (via Writer), producing a Blaze @Html@ from the currently written doc. 135 | blazeHtml :: P.Sem (Blaze ': effs) () -> P.Sem effs BH.Html 136 | blazeHtml = fmap fst . P.runWriter 137 | 138 | -- | Interpret the Blaze effect (via Writer), producing @Text@ from the currently written doc. 139 | blazeToText :: P.Sem (Blaze ': effs) () -> P.Sem effs TL.Text 140 | blazeToText = fmap BH.renderHtml . blazeHtml 141 | 142 | -------------------------------------------------------------------------------- /examples/CacheExample3.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ConstraintKinds #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE GADTs #-} 5 | {-# LANGUAGE OverloadedStrings #-} 6 | {-# LANGUAGE QuasiQuotes #-} 7 | {-# LANGUAGE ScopedTypeVariables #-} 8 | {-# LANGUAGE TypeApplications #-} 9 | 10 | module Main where 11 | 12 | import qualified Knit.Report as Knit 13 | --import qualified Knit.Utilities.Streamly as Knit.Streamly 14 | 15 | import qualified Streamly.Prelude as Streamly 16 | 17 | import qualified Control.Concurrent as CC 18 | import qualified Control.Monad.IO.Class as MonadIO 19 | import qualified Data.Map as M 20 | import qualified Data.Text as T 21 | import Data.String.Here ( here ) 22 | import qualified Graphics.Vega.VegaLite as V 23 | import qualified Plots as P 24 | 25 | 26 | templateVars :: M.Map String String 27 | templateVars = M.fromList 28 | [ ("lang" , "English") 29 | , ("author" , "Adam Conner-Sax") 30 | , ("pagetitle", "knit-haskell cache example") 31 | -- , ("tufte","True") 32 | ] 33 | 34 | main :: IO () 35 | main = do 36 | let template = Knit.FromIncludedTemplateDir "pandoc-adaptive-bootstrap-KH.html" 37 | pandocWriterConfig <- Knit.mkPandocWriterConfig template 38 | templateVars 39 | Knit.mindocOptionsF 40 | 41 | let knitConfig = (Knit.defaultKnitConfig Nothing) 42 | { Knit.outerLogPrefix = Just "CacheExample3.Main" 43 | , Knit.logIf = Knit.logAll 44 | , Knit.pandocWriterConfig = pandocWriterConfig 45 | } 46 | resE <- Knit.knitHtml knitConfig makeDoc 47 | 48 | case resE of 49 | Right htmlAsText -> 50 | Knit.writeAndMakePathLT "docs/cache_example.html" htmlAsText 51 | Left err -> putStrLn $ "Pandoc Error: " ++ show err 52 | 53 | md1 :: T.Text 54 | md1 = [here| 55 | ## Some example markdown 56 | * [Markdown][MarkdownLink] is a nice way to write formatted notes with a minimum of code. 57 | * It supports links and tables and some *styling* information. 58 | 59 | [MarkDownLink]: 60 | |] 61 | 62 | makeDoc :: forall r.(Knit.KnitOne r, Knit.CacheEffectsD r) => Knit.Sem r () 63 | makeDoc = Knit.wrapPrefix "makeDoc" $ do 64 | 65 | Knit.logLE Knit.Info "Clearing \"cacheExample/test4.bin\" from cache..." 66 | Knit.clearIfPresent "cacheExample/test4.bin" 67 | Knit.logLE Knit.Info $ "retrieveOrMake list of data the first time. Should make. But not deserialize upon use since the actual data is returned in the cache object.." 68 | testList1_C <- listLoaderWC 69 | Knit.logLE Knit.Info $ "retrieveOrMake list of data the second time. Should load from disk. But not deserialize until use." 70 | testList2_C <- listLoaderWC 71 | Knit.logLE Knit.Info "Using first list via show" 72 | Knit.ignoreCacheTime testList1_C >>= MonadIO.liftIO . putStrLn . show 73 | Knit.logLE Knit.Info "Using second list via show" 74 | Knit.ignoreCacheTime testList2_C >>= MonadIO.liftIO . putStrLn . show 75 | 76 | return () 77 | 78 | listLoader :: (Knit.KnitEffects q, Knit.CacheEffectsD q) => Knit.Sem q [Int] 79 | listLoader = Knit.ignoreCacheTimeM listLoaderWC 80 | 81 | listLoaderWC :: (Knit.KnitEffects q, Knit.CacheEffectsD q) 82 | => Knit.Sem q (Knit.ActionWithCacheTime q [Int]) 83 | listLoaderWC = Knit.wrapPrefix "listLoaderWC" $ do 84 | Knit.logLE Knit.Diagnostic $ "listLoaderWC called" 85 | Knit.retrieveOrMake "cacheExample/test.bin" (pure ()) $ const $ do 86 | Knit.logLE Knit.Diagnostic "Waiting to make..." 87 | Knit.liftKnit $ CC.threadDelay 1000000 88 | Knit.logLE Knit.Diagnostic "Making test data" 89 | return [1,10,100] 90 | 91 | 92 | listLoader2 ::(Knit.KnitEffects q, Knit.CacheEffectsD q) 93 | => Knit.Sem q [Int] 94 | listLoader2 = Knit.ignoreCacheTimeM $ do 95 | cachedList <- listLoaderWC 96 | Knit.retrieveOrMake "cacheExample/test2.bin" cachedList $ \lInt -> do 97 | return $ fmap (*2) lInt 98 | 99 | -- example using HVega 100 | exampleVis :: V.VegaLite 101 | exampleVis = 102 | let cars = 103 | V.dataFromUrl "https://vega.github.io/vega-datasets/data/cars.json" [] 104 | enc = 105 | V.encoding 106 | . V.position V.X [V.PName "Horsepower", V.PmType V.Quantitative] 107 | . V.position V.Y [V.PName "Miles_per_Gallon", V.PmType V.Quantitative] 108 | . V.color [V.MName "Origin", V.MmType V.Nominal] 109 | bkg = V.background "rgba(0, 0, 0, 0.05)" 110 | in V.toVegaLite [bkg, cars, V.mark V.Circle [], enc []] 111 | 112 | 113 | -- sampleDiagram 114 | -- from 115 | 116 | hilbert 0 = mempty 117 | hilbert n = 118 | hilbert' (n - 1) 119 | Knit.# Knit.reflectY 120 | <> Knit.vrule 1 121 | <> hilbert (n - 1) 122 | <> Knit.hrule 1 123 | <> hilbert (n - 1) 124 | <> Knit.vrule (-1) 125 | <> hilbert' (n - 1) 126 | Knit.# Knit.reflectX 127 | where hilbert' m = hilbert m Knit.# Knit.rotateBy (1 / 4) 128 | 129 | exampleDiagram :: Knit.Diagram Knit.SVG 130 | exampleDiagram = 131 | Knit.frame 1 . Knit.lw Knit.medium . Knit.lc Knit.darkred . Knit.strokeT $ hilbert 5 132 | 133 | 134 | -- example using Plots (as an example of using Diagrams) 135 | samplePlot :: Knit.Diagram Knit.SVG 136 | samplePlot = P.renderAxis logAxis 137 | 138 | logData = [Knit.V2 1 10, Knit.V2 2 100, Knit.V2 2.5 316, Knit.V2 3 1000] 139 | 140 | logAxis :: P.Axis Knit.SVG Knit.V2 Double 141 | logAxis = P.r2Axis Knit.&~ do 142 | P.scatterPlot' logData 143 | P.yAxis P.&= do 144 | P.logScale Knit..= P.LogAxis 145 | P.majorTicksFunction Knit..= P.logMajorTicks 5 -- <> pure [1] 146 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.jats: -------------------------------------------------------------------------------- 1 | 2 | $if(xml-stylesheet)$ 3 | 4 | $endif$ 5 | 7 | $if(article.type)$ 8 |
9 | $else$ 10 |
11 | $endif$ 12 | 13 | 14 | $if(journal.publisher-id)$ 15 | $journal.publisher-id$ 16 | $endif$ 17 | $if(journal.nlm-ta)$ 18 | $journal.nlm-ta$ 19 | $endif$ 20 | $if(journal.pmc)$ 21 | $journal.pmc$ 22 | $endif$ 23 | 24 | $if(journal.title)$ 25 | $journal.title$ 26 | $endif$ 27 | $if(journal.abbrev-title)$ 28 | $journal.abbrev-title$ 29 | $endif$ 30 | 31 | $if(journal.pissn)$ 32 | $journal.pissn$ 33 | $endif$ 34 | $if(journal.eissn)$ 35 | $journal.eissn$ 36 | $endif$ 37 | 38 | $journal.publisher-name$ 39 | $if(journal.publisher-loc)$ 40 | $journal.publisher-loc$ 41 | $endif$ 42 | 43 | 44 | 45 | $if(article.publisher-id)$ 46 | $article.publisher-id$ 47 | $endif$ 48 | $if(article.doi)$ 49 | $article.doi$ 50 | $endif$ 51 | $if(article.pmid)$ 52 | $article.pmid$ 53 | $endif$ 54 | $if(article.pmcid)$ 55 | $article.pmcid$ 56 | $endif$ 57 | $if(article.art-access-id)$ 58 | $article.art-access-id$ 59 | $endif$ 60 | $if(article.heading)$ 61 | 62 | 63 | $article.heading$ 64 | 65 | $if(article.categories)$ 66 | 67 | $for(article.categories)$ 68 | $article.categories$ 69 | $endfor$ 70 | 71 | $endif$ 72 | 73 | $endif$ 74 | $if(title)$ 75 | 76 | $title$ 77 | 78 | $endif$ 79 | $if(author)$ 80 | 81 | $for(author)$ 82 | 83 | $if(author.orcid)$ 84 | $author.orcid$ 85 | $endif$ 86 | $if(author.surname)$ 87 | 88 | $author.surname$ 89 | $author.given-names$ 90 | 91 | $else$ 92 | $author$ 93 | $endif$ 94 | $if(author.email)$ 95 | $author.email$ 96 | $endif$ 97 | $if(author.aff-id)$ 98 | 99 | $endif$ 100 | $if(author.cor-id)$ 101 | * 102 | $endif$ 103 | 104 | $endfor$ 105 | 106 | $endif$ 107 | $if(article.author-notes)$ 108 | 109 | $if(article.author-notes.corresp)$ 110 | $for(article.author-notes.corresp)$ 111 | * E-mail: $article.author-notes.corresp.email$ 112 | $endfor$ 113 | $endif$ 114 | $if(article.author-notes.conflict)$ 115 |

$article.author-notes.conflict$

116 | $endif$ 117 | $if(article.author-notes.con)$ 118 |

$article.author-notes.con$

119 | $endif$ 120 |
121 | $endif$ 122 | $if(date)$ 123 | 124 | $if(date.day)$ 125 | $date.day$ 126 | $endif$ 127 | $if(date.month)$ 128 | $date.month$ 129 | $endif$ 130 | $date.year$ 131 | 132 | $endif$ 133 | $if(article.volume)$ 134 | $article.volume$ 135 | $endif$ 136 | $if(article.issue)$ 137 | $article.issue$ 138 | $endif$ 139 | $if(article.fpage)$ 140 | $article.fpage$ 141 | $endif$ 142 | $if(article.lpage)$ 143 | $article.lpage$ 144 | $endif$ 145 | $if(article.elocation-id)$ 146 | $article.elocation-id$ 147 | $endif$ 148 | $if(history)$ 149 | 150 | 151 | $endif$ 152 | $if(copyright)$ 153 | 154 | $if(copyright.statement)$ 155 | $copyright.statement$ 156 | $endif$ 157 | $if(copyright.year)$ 158 | $copyright.year$ 159 | $endif$ 160 | $if(copyright.holder)$ 161 | $copyright.holder$ 162 | $endif$ 163 | $if(copyright.text)$ 164 | 165 | $copyright.text$ 166 | 167 | 168 | $endif$ 169 | $endif$ 170 | $if(tags)$ 171 | 172 | $for(tags)$ 173 | $tags$ 174 | $endfor$ 175 | 176 | $endif$ 177 | $if(article.funding-statement)$ 178 | 179 | $article.funding-statement$ 180 | 181 | $endif$ 182 |
183 | $if(notes)$ 184 | $notes$ 185 | $endif$ 186 |
187 | 188 | $body$ 189 | 190 | 191 | $if(back)$ 192 | $back$ 193 | $endif$ 194 | 195 |
196 | -------------------------------------------------------------------------------- /examples/AsyncExample.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 as T 12 | import Data.String.Here ( here ) 13 | import qualified Graphics.Vega.VegaLite as V 14 | import qualified Plots as P 15 | 16 | import Control.Concurrent (threadDelay) 17 | 18 | templateVars :: M.Map String String 19 | templateVars = M.fromList 20 | [ ("lang" , "English") 21 | , ("author" , "Adam Conner-Sax") 22 | , ("pagetitle", "knit-haskell async example") 23 | -- , ("tufte","True") 24 | ] 25 | 26 | main :: IO () 27 | main = do 28 | let template = K.FromIncludedTemplateDir "pandoc-adaptive-bootstrap-KH.html" 29 | pandocWriterConfig <- K.mkPandocWriterConfig template 30 | templateVars 31 | K.mindocOptionsF 32 | let knitConfig = (K.defaultKnitConfig Nothing) 33 | { K.outerLogPrefix = Just "AsyncExample.Main" 34 | , K.logIf = K.logAll 35 | , K.pandocWriterConfig = pandocWriterConfig 36 | } 37 | resE <- K.knitHtml knitConfig makeDoc 38 | 39 | case resE of 40 | Right htmlAsText -> 41 | K.writeAndMakePathLT "docs/async_example.html" htmlAsText 42 | Left err -> putStrLn $ "Pandoc Error: " ++ show err 43 | 44 | md1 :: T.Text 45 | md1 = [here| 46 | ## Some example markdown 47 | * [Markdown][MarkdownLink] is a nice way to write formatted notes with a minimum of code. 48 | * It supports links and tables and some *styling* information. 49 | 50 | [MarkDownLink]: 51 | |] 52 | 53 | makeDoc :: K.KnitOne effs => K.Sem effs () 54 | makeDoc = K.wrapPrefix "makeDoc" $ do 55 | K.logLE K.Info "adding some markdown..." 56 | K.addMarkDown md1 57 | K.logLE K.Info "adding some latex..." 58 | K.addMarkDown "## Some example latex" 59 | K.addLatex "Overused favorite equation: $e^{i\\pi} + 1 = 0$" 60 | K.logLE K.Info "Launching some concurrent long-running computations..." 61 | asyncResultsM <- sequence <$> K.sequenceConcurrently [delay 2000 1, delay 2000 2, delay 2000 3] 62 | case asyncResultsM of 63 | Nothing -> K.logLE K.Error "One or more concurrent calculations failed." 64 | Just results -> do 65 | K.logLE K.Info "Concurrent calculations succeeded." 66 | K.addMarkDown $ "## Some concurrent calculation results: " <> (T.pack $ show results) 67 | K.logLE K.Info "adding a visualization..." 68 | K.addMarkDown "## An example hvega visualization" 69 | _ <- K.addHvega Nothing (Just "From the cars data-set") exampleVis 70 | K.addMarkDown 71 | "## Example Diagrams visualizations, the second using the plots library." 72 | K.logLE K.Info "adding a Diagrams example and plots example..." 73 | _ <- K.addDiagramAsSVG Nothing (Just "Example diagram") 300 300 exampleDiagram 74 | _ <- K.addDiagramAsSVG 75 | Nothing 76 | (Just "Example diagrams visualization using the Plots library") 77 | 300 78 | 300 79 | samplePlot 80 | return () 81 | 82 | -- long running computation 83 | delay :: K.KnitEffects effs => Int -> Int -> K.Sem effs Int 84 | delay msDelay val = K.wrapPrefix ("delay") $ do 85 | K.logLE K.Info $ "delaying " <> (T.pack $ show val) <> " for " <> (T.pack $ show msDelay) <> " ms" 86 | K.liftKnit (threadDelay $ 1000 * msDelay) 87 | K.logLE K.Info $ "done (" <> (T.pack $ show val) <> ")" 88 | return val 89 | 90 | -- long running computation 91 | delayAndThrow :: K.KnitEffects effs => Int -> Int -> K.Sem effs Int 92 | delayAndThrow msDelayBefore val = K.wrapPrefix ("delay") $ do 93 | K.logLE K.Info $ "delaying " <> (T.pack $ show val) <> " for " <> (T.pack $ show msDelayBefore) <> " ms" 94 | K.liftKnit (threadDelay $ 1000 * msDelayBefore) 95 | K.knitError $ "Exception in " <> (T.pack $ show val) 96 | K.logLE K.Info $ "done (" <> (T.pack $ show val) <> ")" 97 | return val 98 | 99 | 100 | 101 | -- example using HVega 102 | exampleVis :: V.VegaLite 103 | exampleVis = 104 | let cars = 105 | V.dataFromUrl "https://vega.github.io/vega-datasets/data/cars.json" [] 106 | enc = 107 | V.encoding 108 | . V.position V.X [V.PName "Horsepower", V.PmType V.Quantitative] 109 | . V.position V.Y [V.PName "Miles_per_Gallon", V.PmType V.Quantitative] 110 | . V.color [V.MName "Origin", V.MmType V.Nominal] 111 | bkg = V.background "rgba(0, 0, 0, 0.05)" 112 | in V.toVegaLite [bkg, cars, V.mark V.Circle [], enc []] 113 | 114 | 115 | -- sampleDiagram 116 | -- from 117 | 118 | hilbert 0 = mempty 119 | hilbert n = 120 | hilbert' (n - 1) 121 | K.# K.reflectY 122 | <> K.vrule 1 123 | <> hilbert (n - 1) 124 | <> K.hrule 1 125 | <> hilbert (n - 1) 126 | <> K.vrule (-1) 127 | <> hilbert' (n - 1) 128 | K.# K.reflectX 129 | where hilbert' m = hilbert m K.# K.rotateBy (1 / 4) 130 | 131 | exampleDiagram :: K.Diagram K.SVG 132 | exampleDiagram = 133 | K.frame 1 . K.lw K.medium . K.lc K.darkred . K.strokeT $ hilbert 5 134 | 135 | 136 | -- example using Plots (as an example of using Diagrams) 137 | samplePlot :: K.Diagram K.SVG 138 | samplePlot = P.renderAxis logAxis 139 | 140 | logData = [K.V2 1 10, K.V2 2 100, K.V2 2.5 316, K.V2 3 1000] 141 | 142 | logAxis :: P.Axis K.SVG K.V2 Double 143 | logAxis = P.r2Axis K.&~ do 144 | P.scatterPlot' logData 145 | 146 | P.yAxis P.&= do 147 | P.logScale K..= P.LogAxis 148 | P.majorTicksFunction K..= P.logMajorTicks 5 -- <> pure [1] 149 | 150 | -------------------------------------------------------------------------------- /examples/RandomExample.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE FlexibleContexts #-} 2 | {-# LANGUAGE GADTs #-} 3 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} 4 | {-# LANGUAGE OverloadedStrings #-} 5 | {-# LANGUAGE QuasiQuotes #-} 6 | {-# LANGUAGE RankNTypes #-} 7 | {-# LANGUAGE TypeApplications #-} 8 | module Main where 9 | 10 | import qualified Knit.Report as K 11 | import qualified Polysemy.RandomFu as PR 12 | import qualified Polysemy.ConstraintAbsorber.MonadRandom 13 | as PR 14 | import qualified Colonnade as C 15 | 16 | import Control.Monad ( replicateM ) 17 | import Control.Monad.IO.Class ( MonadIO ) 18 | import qualified Data.Map as M 19 | import qualified Data.Text as T 20 | import Data.String.Here ( here ) 21 | import qualified Graphics.Vega.VegaLite as V 22 | 23 | import Data.Random ( sample 24 | , stdNormal 25 | , StatefulGen 26 | ) 27 | import Data.Random.Source (MonadRandom) 28 | import Data.Random.Source.PureMT ( newPureMT ) 29 | 30 | import Control.Monad.Reader ( ReaderT 31 | , ask 32 | , runReaderT 33 | ) 34 | 35 | templateVars :: M.Map String String 36 | templateVars = M.fromList 37 | [ ("lang" , "English") 38 | , ("author" , "Adam Conner-Sax") 39 | , ("pagetitle", "knit-haskell mtl example with random effect") 40 | -- , ("tufte","True") 41 | ] 42 | 43 | -- A demo application stack 44 | newtype MyApp env a = MyStack { unMyApp :: ReaderT env IO a } deriving (Functor, Applicative, Monad, MonadIO) 45 | 46 | type ExampleApp = MyApp T.Text 47 | 48 | runExampleApp :: T.Text -> ExampleApp a -> IO a 49 | runExampleApp t = flip runReaderT t . unMyApp 50 | 51 | getEnv :: MyApp env env 52 | getEnv = MyStack $ ask 53 | 54 | main :: IO () 55 | main = do 56 | let template = K.FromIncludedTemplateDir "mindoc-pandoc-KH.html" 57 | pandocWriterConfig <- K.mkPandocWriterConfig template 58 | templateVars 59 | K.mindocOptionsF 60 | let knitConfig = (K.defaultKnitConfig Nothing) 61 | { K.outerLogPrefix = Just "RandomExample.Main" 62 | , K.logIf = K.logAll 63 | , K.pandocWriterConfig = pandocWriterConfig 64 | } 65 | pureMTSource <- newPureMT 66 | resE <- 67 | runExampleApp "This is from the MyApp environment." 68 | $ K.knitHtml knitConfig 69 | $ PR.runRandomIOPureMT pureMTSource 70 | $ makeDoc 71 | case resE of 72 | Right htmlAsText -> 73 | K.writeAndMakePathLT "docs/random_example.html" htmlAsText 74 | Left err -> putStrLn $ "Pandoc error: " ++ show err 75 | 76 | md1 :: T.Text 77 | md1 = [here| 78 | ## Some example markdown 79 | * [Markdown][MarkdownLink] is a nice way to write formatted notes with a minimum of code. 80 | * It supports links and tables and some *styling* information. 81 | 82 | [MarkDownLink]: 83 | |] 84 | 85 | makeDoc 86 | :: (K.KnitOne effs 87 | , K.Member PR.RandomFu effs -- this one needs to be handled before knitting 88 | , K.KnitBase ExampleApp effs) 89 | => K.Sem effs () 90 | makeDoc = K.wrapPrefix "makeDoc" $ do 91 | K.logLE K.Info "adding some markdown..." 92 | K.addMarkDown md1 93 | 94 | K.logLE K.Info "adding some latex..." 95 | K.addMarkDown "## Some example latex" 96 | K.addLatex "Overused favorite equation: $e^{i\\pi} + 1 = 0$" 97 | 98 | K.logLE K.Info "adding a visualization..." 99 | K.addMarkDown "## An example hvega visualization" 100 | _ <- K.addHvega Nothing Nothing exampleVis 101 | 102 | K.logLE K.Info 103 | "Retrieving some text from the base monad and current date-time." 104 | envText <- K.liftKnit @ExampleApp getEnv 105 | curTime <- K.getCurrentTime 106 | K.addMarkDown 107 | "## An example of getting env from a base monad, and time from the Pandoc Effects." 108 | K.addMarkDown $ envText <> "\n\n" <> (T.pack $ show curTime) 109 | 110 | K.logLE K.Info "Using another polysemy effect, here RandomFu" 111 | let nDraws = 20 112 | someNormalDoubles <- PR.sampleRVar $ replicateM nDraws (stdNormal @Double) 113 | K.addMarkDown 114 | "## An example of using the RandomFu effect to get random numbers and using Colonnade to make tables." 115 | K.addMarkDown $ "some std normal draws: " 116 | K.addColonnadeTextTable 117 | ( C.headed "#" (T.pack . show . fst) 118 | <> C.headed "Draw" (T.pack . show . snd) 119 | ) 120 | $ zip [1 .. nDraws] someNormalDoubles 121 | {- 122 | moreNormalDoubles <- PR.absorbMonadRandom (monadRandomFunction nDraws) 123 | K.addMarkDown 124 | "## This time, using absorbMonadRandom to interoperate with a function using the MonadRandom constraint." 125 | K.addMarkDown $ "some std normal draws: " 126 | K.addColonnadeTextTable 127 | ( C.headed "#" (T.pack . show . fst) 128 | <> C.headed "Draw" (T.pack . show . snd) 129 | ) 130 | $ zip [1 .. nDraws] moreNormalDoubles 131 | -} 132 | 133 | --monadRandomFunction :: MonadRandom m => Int -> m [Double] 134 | --monadRandomFunction = sample . flip replicateM (stdNormal @Double) 135 | 136 | exampleVis :: V.VegaLite 137 | exampleVis = 138 | let cars = 139 | V.dataFromUrl "https://vega.github.io/vega-datasets/data/cars.json" [] 140 | enc = 141 | V.encoding 142 | . V.position V.X [V.PName "Horsepower", V.PmType V.Quantitative] 143 | . V.position V.Y [V.PName "Miles_per_Gallon", V.PmType V.Quantitative] 144 | . V.color [V.MName "Origin", V.MmType V.Nominal] 145 | bkg = V.background "rgba(0, 0, 0, 0.05)" 146 | in V.toVegaLite [bkg, cars, V.mark V.Circle [], enc []] 147 | -------------------------------------------------------------------------------- /docs/index.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 example index 36 | 37 | 38 | 39 | 40 | 41 |
42 |
43 |
44 |
45 |

Examples

46 |
    47 |
  • Simple 49 | demonstrates the bare bones features of the library. 50 | Creating a document from a few fragments and then "knitting" 51 | it into HTML text and writing that to a file. This includes 52 | hvega, diagrams and plots examples. Result here

  • 54 |
  • MultiDoc 56 | demonstrates how to build multiple documents. Results here 58 | and here.

  • 60 |
  • Mtl 62 | demonstrates the same simple features as above, but runs 63 | them atop an example mtl stack, allowing access to the mtl 64 | stack's functionality during document assembly. Result here.

  • 66 |
  • Random 68 | builds on the mtl example to show how you can also add an 69 | additional polysemy effect (in this case, Polysemy.RandomFu 70 | from polysemy-RandomFu) to your document-building. This one 71 | also demonstrates a use of colonnade for adding a formatted 72 | table to the document. Result here.

  • 74 |
  • Error: 76 | Similar to "Simple" but throws a user error during document 77 | assembly. Result [here][ErrorExample].

  • 78 |
  • Async: 80 | Similar to "SimpleExample" but uses Polysemy's 81 | sequenceConcurrently to run some example computations 82 | concurrently (as long as you compile with "-threaded"). 83 | Result here

  • 85 |
  • Cache: 87 | Similar to "SimpleExample" but uses the "AtomicCache" effect 88 | to store the result of a computation. Demonstrates the 89 | behavior of the cache when multiple threads attempt to 90 | access the same item--the first thread loads/creates the 91 | data while the other blocks until the data is in-memory. 92 | Also demonstrates use of time-stamps to force rebuilding 93 | when tracked inputs change. Result here.

  • 95 |
  • CustomCache: 97 | Similar to "CacheExample" but implements and uses a 98 | different serializer and persistence layer than the default. 99 | Result here.

  • 101 |
102 |
103 |
104 |
105 |
106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /src/Knit/Report.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ConstraintKinds #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE ExtendedDefaultRules #-} 4 | {-# LANGUAGE FlexibleContexts #-} 5 | {-# LANGUAGE GADTs #-} 6 | {-# LANGUAGE OverloadedStrings #-} 7 | {-# LANGUAGE PolyKinds #-} 8 | {-# LANGUAGE Rank2Types #-} 9 | {-# LANGUAGE ScopedTypeVariables #-} 10 | {-# LANGUAGE TypeOperators #-} 11 | {-# LANGUAGE TypeApplications #-} 12 | {-# LANGUAGE TypeFamilies #-} 13 | {-# LANGUAGE UndecidableInstances #-} 14 | {-| 15 | Module : Knit.Report 16 | Description : Re-export all the things required for pandoc-built reports. 17 | Copyright : (c) Adam Conner-Sax 2019 18 | License : BSD-3-Clause 19 | Maintainer : adam_conner_sax@yahoo.com 20 | Stability : experimental 21 | 22 | This module re-exports the basic pieces to build reports using Pandoc. 23 | That is, it is intended as one-stop-shopping for using this library to produce Html from various fragments which 24 | Pandoc can read. 25 | 26 | are available, and might be useful for seeing how all this works. 27 | 28 | Notes: 29 | 30 | 1. You can add logging from within document creation using 'logLE'. 31 | 2. The "Knit.Report.Input.MarkDown.PandocMarkDown" module is exported 32 | so if you want to use a different markdown flavor you may need to hide "addMarkDown" when you import this module. 33 | 3. If you use any other effects in your polysemy stack (e.g., Random or RandomFu), you will need to interpret/run them before calling knitHtml/knitHtmls. 34 | -} 35 | module Knit.Report 36 | ( 37 | -- * Report Building 38 | module Knit.Report.EffectStack 39 | , module Knit.Report.Error 40 | 41 | -- * Inputs 42 | , module Knit.Report.Input.Table.Colonnade 43 | , module Knit.Report.Input.MarkDown.PandocMarkDown 44 | , module Knit.Report.Input.Html 45 | , module Knit.Report.Input.Html.Blaze 46 | , module Knit.Report.Input.Html.Lucid 47 | , module Knit.Report.Input.Latex 48 | , module Knit.Report.Input.RST 49 | , module Knit.Report.Input.Visualization.Hvega 50 | , module Knit.Report.Input.Visualization.Diagrams 51 | 52 | -- * Output 53 | , module Knit.Report.Output 54 | , module Knit.Report.Output.Html 55 | 56 | -- * Effects 57 | , module Polysemy 58 | , module Knit.Effect.Pandoc 59 | , module Knit.Effect.Docs 60 | , module Knit.Effect.PandocMonad 61 | , module Knit.Effect.Logger 62 | , module Knit.Effect.UnusedId 63 | , module Knit.Effect.Serialize 64 | , module Knit.Effect.Timer 65 | 66 | , module Polysemy.Async 67 | , module Knit.Report.Cache 68 | 69 | -- * Utilities 70 | ) 71 | where 72 | 73 | import Knit.Report.EffectStack 74 | import Knit.Report.Error 75 | import Knit.Report.Cache 76 | import Knit.Report.Output 77 | import Knit.Report.Output.Html ( pandocWriterToBlazeDocument 78 | , mindocOptionsF 79 | , writeAllPandocResultsWithInfoAsHtml 80 | , writePandocResultWithInfoAsHtml 81 | ) 82 | 83 | import Polysemy ( Member 84 | , Members 85 | , Sem 86 | ) 87 | import Polysemy.Async ( async 88 | , await 89 | , sequenceConcurrently 90 | ) 91 | 92 | import Knit.Effect.Pandoc ( ToPandoc 93 | , Requirement(..) 94 | , PandocReadFormat(..) 95 | , PandocWriteFormat(..) 96 | , Pandocs 97 | , PandocInfo(..) 98 | , newPandoc 99 | ) 100 | import Knit.Effect.Docs ( DocWithInfo(..) ) 101 | import Knit.Effect.PandocMonad 102 | import Knit.Effect.Logger ( LogSeverity(..) 103 | , logAll 104 | , nonDiagnostic 105 | , logDebug 106 | , logDiagnostic 107 | , logLE 108 | , logCat 109 | , wrapPrefix 110 | , filteredLogEntriesToIO 111 | , PrefixedLogEffectsLE 112 | , LogWithPrefixesLE 113 | ) 114 | import Knit.Effect.UnusedId ( getNextUnusedId ) 115 | import Knit.Effect.Serialize (DefaultCacheData, DefaultSerializer) 116 | import Knit.Effect.Timer (WithTimer, start, snapshot, finish, timed, withTiming, logWithTiming, logTiming) 117 | import Knit.Report.Input.Table.Colonnade 118 | import Knit.Report.Input.MarkDown.PandocMarkDown 119 | ( addMarkDown ) 120 | import Knit.Report.Input.Html ( addStrictTextHtml 121 | , addLazyTextHtml 122 | ) 123 | import Knit.Report.Input.Html.Blaze ( addBlaze ) 124 | import Knit.Report.Input.Html.Lucid ( addLucid ) 125 | import Knit.Report.Input.Latex ( addLatex ) 126 | import Knit.Report.Input.RST ( addRST 127 | , addRSTFromFile) 128 | import Knit.Report.Input.Visualization.Hvega 129 | ( addHvega, addHvega' ) 130 | import Knit.Report.Input.Visualization.Diagrams hiding (snapshot, start) 131 | -------------------------------------------------------------------------------- /data/knit-haskell-templates/mindoc-pandoc-KH.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $if(description)$ 8 | 9 | $endif$ 10 | $for(author-meta)$ 11 | 12 | $endfor$ 13 | $if(date-meta)$ 14 | 15 | $endif$ 16 | $if(title-prefix)$$title-prefix$ - $endif$$pagetitle$ 17 | 18 | 19 | 20 | $if(tufte)$ 21 | 22 | 23 | $endif$ 24 | 25 | $for(css)$ 26 | 27 | $endfor$ 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 62 | 63 | $if(quotes)$ 64 | 65 | $endif$ 66 | $if(highlighting-css)$ 67 | 70 | $endif$ 71 | $for(css)$ 72 | 73 | $endfor$ 74 | $if(math)$ 75 | $math$ 76 | $endif$ 77 | $for(header-includes)$ 78 | $header-includes$ 79 | $endfor$ 80 | 81 | 82 |
83 |
84 |
85 | 86 | 87 | 88 | $for(include-before)$ 89 | $include-before$ 90 | $endfor$ 91 | $if(headnote)$ 92 |
$headnote$
93 | $endif$ 94 | $if(published)$ 95 |
$published$
96 | $endif$ 97 | $if(license)$ 98 |
$license$
$endif$ 99 | $if(title)$ 100 |
101 |

$title$

102 | $if(subtitle)$ 103 |

$subtitle$

104 | $endif$ 105 | $if(author)$ 106 | $for(author)$ 107 | $if(author.name)$ 108 |

$author.name$

109 | $else$ 110 |

$author$

111 | $endif$ 112 | $if(author.affiliation)$ 113 |
$author.affiliation$
114 | $endif$ 115 | $if(author.email)$ 116 | 117 | $endif$ 118 | $endfor$ 119 | $endif$ 120 | $if(version)$ 121 |

Version $version$

122 | $endif$ 123 | $if(date)$ 124 |

$date$

125 | $endif$ 126 |
127 | $endif$ 128 | $if(abstract)$ 129 |
130 |

Abstract

131 | $abstract$ 132 |
133 | $endif$ 134 | $if(toc)$ 135 | 138 | $endif$ 139 | $body$ 140 | $for(include-after)$ 141 | $include-after$ 142 | $endfor$ 143 | 144 | 145 | 146 |
147 |
148 |
149 |
150 | 151 |
152 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /examples/CacheExample.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE ConstraintKinds #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE GADTs #-} 5 | {-# LANGUAGE OverloadedStrings #-} 6 | {-# LANGUAGE QuasiQuotes #-} 7 | {-# LANGUAGE ScopedTypeVariables #-} 8 | {-# LANGUAGE TypeApplications #-} 9 | 10 | module Main where 11 | 12 | import qualified Knit.Report as Knit 13 | --import qualified Knit.Utilities.Streamly as Knit.Streamly 14 | 15 | import qualified Streamly.Prelude as Streamly 16 | 17 | import qualified Control.Concurrent as CC 18 | import qualified Control.Monad.IO.Class as MonadIO 19 | import qualified Data.Map as M 20 | import qualified Data.Text as T 21 | import Data.String.Here ( here ) 22 | import qualified Graphics.Vega.VegaLite as V 23 | import qualified Plots as P 24 | 25 | 26 | templateVars :: M.Map String String 27 | templateVars = M.fromList 28 | [ ("lang" , "English") 29 | , ("author" , "Adam Conner-Sax") 30 | , ("pagetitle", "knit-haskell cache example") 31 | -- , ("tufte","True") 32 | ] 33 | 34 | main :: IO () 35 | main = do 36 | let template = Knit.FromIncludedTemplateDir "pandoc-adaptive-bootstrap-KH.html" 37 | pandocWriterConfig <- Knit.mkPandocWriterConfig template 38 | templateVars 39 | Knit.mindocOptionsF 40 | 41 | let knitConfig = (Knit.defaultKnitConfig Nothing) 42 | { Knit.outerLogPrefix = Just "CacheExample.Main" 43 | , Knit.logIf = Knit.logAll 44 | , Knit.pandocWriterConfig = pandocWriterConfig 45 | } 46 | resE <- Knit.knitHtml knitConfig makeDoc 47 | 48 | case resE of 49 | Right htmlAsText -> 50 | Knit.writeAndMakePathLT "docs/cache_example.html" htmlAsText 51 | Left err -> putStrLn $ "Pandoc Error: " ++ show err 52 | 53 | md1 :: T.Text 54 | md1 = [here| 55 | ## Some example markdown 56 | * [Markdown][MarkdownLink] is a nice way to write formatted notes with a minimum of code. 57 | * It supports links and tables and some *styling* information. 58 | 59 | [MarkDownLink]: 60 | |] 61 | 62 | makeDoc :: (Knit.KnitOne r, Knit.CacheEffectsD r) => Knit.Sem r () 63 | makeDoc = Knit.wrapPrefix "makeDoc" $ do 64 | Knit.logLE Knit.Info "adding some markdown..." 65 | Knit.addMarkDown md1 66 | Knit.logLE Knit.Info "adding some latex..." 67 | Knit.addMarkDown "## Some example latex" 68 | Knit.addLatex "Overused favorite equation: $e^{i\\pi} + 1 = 0$" 69 | Knit.logLE Knit.Info "Clearing \"cacheExample/test.bin\" and \"cacheExample/test2.bin\" from cache..." 70 | Knit.clearIfPresent "cacheExample/test.sbin" 71 | Knit.clearIfPresent "cacheExample/test2.sbin" 72 | Knit.logLE Knit.Info $ "asynchronously retrieveOrMake stream of data, then retrieveOrMake on this thread, after a small delay, to test atomic cache." 73 | <> "the async retrieveOrMake will try the cache first" 74 | <> ", in-memory, then disk, then makes the data if those come up empty." 75 | Knit.logLE Knit.Info "At which point the main thread, blocked on the TMVar in the cache, will unblock and see the data in the in-memory-cache." 76 | testListA <- Knit.async $ Knit.wrapPrefix "ASYNC" $ do 77 | dat <- listLoader 78 | Knit.logLE Knit.Diagnostic "Waiting to return from Async" 79 | Knit.liftKnit $ CC.threadDelay 1000000 80 | return dat 81 | Knit.liftKnit $ CC.threadDelay 100000 82 | testList <- listLoader 83 | testListM <- Knit.await testListA 84 | case testListM of 85 | Nothing -> Knit.logLE Knit.Diagnostic "Error in async retrieve" 86 | Just l -> do 87 | Knit.logLE Knit.Diagnostic "List returned from async. Logging async then sync." 88 | Knit.logLE Knit.Diagnostic $ "async:" <> (T.pack $ show l) 89 | Knit.logLE Knit.Diagnostic $ "sync:" <> (T.pack $ show testList) 90 | Knit.logLE Knit.Info "Demonstrating cache dependencies" 91 | Knit.logLE Knit.Info "listLoader2 depends on listLoader and should rebuild if listLoader has been rebuilt since listLoader2 was cached." 92 | Knit.logLE Knit.Info "Calling listLoader2 the first time" 93 | sl2a <- listLoader2 -- builds the first time 94 | Knit.logLE Knit.Info "Calling listLoader2 again. Should load from cache." 95 | sl2b <- listLoader2 -- loads from cache 96 | Knit.logLE Knit.Info "Removing cached listLoader1 result, then calling listLoader 2 again. Should rebuild." 97 | Knit.clear "cacheExample/test.sbin" -- remove sl1 98 | sl2c <- listLoader2 -- should rebuild 99 | Knit.logLE Knit.Info $ "listLoader2=" <> (T.pack $ show sl2c) 100 | Knit.logLE Knit.Info "adding a visualization..." 101 | Knit.addMarkDown "## An example hvega visualization" 102 | _ <- Knit.addHvega Nothing (Just "From the cars data-set") exampleVis 103 | Knit.addMarkDown 104 | "## Example Diagrams visualizations, the second using the plots library." 105 | Knit.logLE Knit.Info "adding a Diagrams example and plots example..." 106 | _ <- Knit.addDiagramAsSVG Nothing (Just "Example diagram") 300 300 exampleDiagram 107 | _ <- Knit.addDiagramAsSVG 108 | Nothing 109 | (Just "Example diagrams visualization using the Plots library") 110 | 300 111 | 300 112 | samplePlot 113 | Knit.logLE Knit.Info "Retrieving that stuff from the cache or running if required." 114 | Knit.addMarkDown $ "## Caching: List=" <> (T.pack $ show testList) 115 | return () 116 | 117 | listLoader :: (Knit.KnitEffects q, Knit.CacheEffectsD q) => Knit.Sem q [Int] 118 | listLoader = Knit.ignoreCacheTimeM listLoaderWC 119 | 120 | listLoaderWC :: (Knit.KnitEffects q, Knit.CacheEffectsD q) 121 | => Knit.Sem q (Knit.ActionWithCacheTime q [Int]) 122 | listLoaderWC = Knit.wrapPrefix "listLoaderWC" $ do 123 | Knit.logLE Knit.Diagnostic $ "listLoaderWC called" 124 | Knit.retrieveOrMake "cacheExample/test.bin" (pure ()) $ const $ do 125 | Knit.logLE Knit.Diagnostic "Waiting to make..." 126 | Knit.liftKnit $ CC.threadDelay 1000000 127 | Knit.logLE Knit.Diagnostic "Making test data" 128 | return [1,10,100] 129 | 130 | 131 | listLoader2 ::(Knit.KnitEffects q, Knit.CacheEffectsD q) 132 | => Knit.Sem q [Int] 133 | listLoader2 = Knit.ignoreCacheTimeM $ do 134 | cachedList <- listLoaderWC 135 | Knit.retrieveOrMake "cacheExample/test2.bin" cachedList $ \lInt -> do 136 | return $ fmap (*2) lInt 137 | 138 | -- example using HVega 139 | exampleVis :: V.VegaLite 140 | exampleVis = 141 | let cars = 142 | V.dataFromUrl "https://vega.github.io/vega-datasets/data/cars.json" [] 143 | enc = 144 | V.encoding 145 | . V.position V.X [V.PName "Horsepower", V.PmType V.Quantitative] 146 | . V.position V.Y [V.PName "Miles_per_Gallon", V.PmType V.Quantitative] 147 | . V.color [V.MName "Origin", V.MmType V.Nominal] 148 | bkg = V.background "rgba(0, 0, 0, 0.05)" 149 | in V.toVegaLite [bkg, cars, V.mark V.Circle [], enc []] 150 | 151 | 152 | -- sampleDiagram 153 | -- from 154 | 155 | hilbert 0 = mempty 156 | hilbert n = 157 | hilbert' (n - 1) 158 | Knit.# Knit.reflectY 159 | <> Knit.vrule 1 160 | <> hilbert (n - 1) 161 | <> Knit.hrule 1 162 | <> hilbert (n - 1) 163 | <> Knit.vrule (-1) 164 | <> hilbert' (n - 1) 165 | Knit.# Knit.reflectX 166 | where hilbert' m = hilbert m Knit.# Knit.rotateBy (1 / 4) 167 | 168 | exampleDiagram :: Knit.Diagram Knit.SVG 169 | exampleDiagram = 170 | Knit.frame 1 . Knit.lw Knit.medium . Knit.lc Knit.darkred . Knit.strokeT $ hilbert 5 171 | 172 | 173 | -- example using Plots (as an example of using Diagrams) 174 | samplePlot :: Knit.Diagram Knit.SVG 175 | samplePlot = P.renderAxis logAxis 176 | 177 | logData = [Knit.V2 1 10, Knit.V2 2 100, Knit.V2 2.5 316, Knit.V2 3 1000] 178 | 179 | logAxis :: P.Axis Knit.SVG Knit.V2 Double 180 | logAxis = P.r2Axis Knit.&~ do 181 | P.scatterPlot' logData 182 | 183 | P.yAxis P.&= do 184 | P.logScale Knit..= P.LogAxis 185 | P.majorTicksFunction Knit..= P.logMajorTicks 5 -- <> pure [1] 186 | -------------------------------------------------------------------------------- /src/Knit/Report/Output/Html.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE CPP #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE ExtendedDefaultRules #-} 4 | {-# LANGUAGE FlexibleContexts #-} 5 | {-# LANGUAGE GADTs #-} 6 | {-# LANGUAGE OverloadedStrings #-} 7 | {-# LANGUAGE ScopedTypeVariables #-} 8 | {-# LANGUAGE TypeApplications #-} 9 | {-# LANGUAGE TypeOperators #-} 10 | 11 | {-| 12 | Module : Knit.Report.Output.Html 13 | Description : Output Pandoc as Html 14 | Copyright : (c) Adam Conner-Sax 2019 15 | License : BSD-3-Clause 16 | Maintainer : adam_conner_sax@yahoo.com 17 | Stability : experimental 18 | 19 | Functions to produce Html output for a Pandoc report. 20 | -} 21 | module Knit.Report.Output.Html 22 | ( 23 | -- * Default Options 24 | htmlWriterOptions 25 | 26 | -- * Formatted output 27 | , toBlazeDocument 28 | , pandocWriterToBlazeDocument 29 | 30 | -- * Options helper 31 | , mindocOptionsF 32 | 33 | -- * Other helpers 34 | , markDownTextToBlazeFragment 35 | 36 | -- * File writing helpers 37 | , writeAllPandocResultsWithInfoAsHtml 38 | , writePandocResultWithInfoAsHtml 39 | ) 40 | where 41 | 42 | 43 | import qualified Data.ByteString.Char8 as BS 44 | import qualified Data.Text as T 45 | import qualified Data.Text.Lazy as TL 46 | import qualified Data.Map as M 47 | import qualified Text.Blaze.Html as BH 48 | import qualified Text.Pandoc as PA 49 | 50 | 51 | import qualified Polysemy as P 52 | import qualified Knit.Effect.Pandoc as PE 53 | import qualified Knit.Effect.PandocMonad as PM 54 | 55 | import Knit.Report.Input.MarkDown.PandocMarkDown 56 | ( markDownReaderOptions ) 57 | import Knit.Report.Output as KO 58 | 59 | #if MIN_VERSION_pandoc (2,8,0) 60 | import qualified Text.DocTemplates as DT 61 | import qualified Control.Monad.Except as X 62 | #endif 63 | 64 | -- | Base Html writer options, with support for MathJax 65 | htmlWriterOptions :: PA.WriterOptions 66 | htmlWriterOptions = PA.def 67 | { PA.writerExtensions = PA.extensionsFromList [PA.Ext_raw_html] 68 | , PA.writerHTMLMathMethod = PA.MathJax "" 69 | } 70 | 71 | -- | Full writer options which use pandoc monad for template access 72 | #if MIN_VERSION_pandoc (2,8,0) 73 | htmlFullDocWriterOptions 74 | :: forall m. (PA.PandocMonad m, DT.TemplateMonad m) 75 | => Maybe FilePath -- ^ path to template to include, @Nothing@ for no template. 76 | -> M.Map String String -- ^ template Variable substitutions 77 | -> m PA.WriterOptions 78 | htmlFullDocWriterOptions pathM tVars = do 79 | let tContext = DT.Context $ M.mapKeys toText $ fmap (DT.toVal . toText) tVars 80 | makeTemplateM :: FilePath -> m T.Text -> m (Maybe (DT.Template T.Text)) 81 | makeTemplateM pfp getText = do 82 | txt <- getText 83 | compiled <- PA.compileTemplate pfp txt 84 | case compiled of 85 | Left msg -> X.throwError $ PA.PandocTemplateError $ toText msg 86 | Right tmplt -> return $ Just $ fmap (toText @String) tmplt 87 | defaultTemplateM = makeTemplateM "default.Html5" (PA.getDefaultTemplate "Html5") 88 | templateM <- case pathM of 89 | Nothing -> defaultTemplateM 90 | Just fp -> do 91 | exists <- PA.fileExists fp 92 | if exists 93 | then makeTemplateM "" $ fmap (toText . BS.unpack) (PA.readFileStrict fp) 94 | else 95 | PA.logOutput 96 | (PA.IgnoredIOError 97 | (PM.textToPandocText $ "Couldn't find " <> show fp) 98 | ) 99 | >> defaultTemplateM 100 | return $ htmlWriterOptions { PA.writerTemplate = templateM 101 | , PA.writerVariables = tContext --M.toList tVars 102 | , PA.writerSetextHeaders = True 103 | } 104 | 105 | -- Incudes support for template and template variables and changes to the default writer options 106 | toBlazeDocument 107 | :: PM.PandocEffects effs 108 | => KO.PandocWriterConfig 109 | -> PE.PandocWithRequirements -- ^ Document and union of input requirements 110 | -> P.Sem effs BH.Html 111 | toBlazeDocument writeConfig pdocWR = PM.absorbTemplateMonad $ PM.absorbPandocMonad $ do 112 | writerOptions <- htmlFullDocWriterOptions (templateFP writeConfig) 113 | (templateVars writeConfig) 114 | PE.fromPandoc PE.WriteHtml5 (optionsF writeConfig writerOptions) pdocWR 115 | 116 | #else 117 | htmlFullDocWriterOptions 118 | :: PA.PandocMonad m 119 | => Maybe FilePath -- ^ path to template to include, @Nothing@ for no template. 120 | -> M.Map String String -- ^ template Variable substitutions 121 | -> m PA.WriterOptions 122 | htmlFullDocWriterOptions pathM tVars = do 123 | template <- case pathM of 124 | Nothing -> PA.getDefaultTemplate "Html5" 125 | Just fp -> do 126 | exists <- PA.fileExists fp 127 | if exists 128 | then fmap BS.unpack (PA.readFileStrict fp) 129 | else 130 | PA.logOutput 131 | (PA.IgnoredIOError 132 | (PM.textToPandocText $ "Couldn't find " <> (T.pack $ show fp)) 133 | ) 134 | >> PA.getDefaultTemplate "Html5" 135 | return $ htmlWriterOptions { PA.writerTemplate = Just template 136 | , PA.writerVariables = M.toList tVars 137 | , PA.writerSetextHeaders = True 138 | } 139 | 140 | -- Incudes support for template and template variables and changes to the default writer options 141 | toBlazeDocument 142 | :: PM.PandocEffects effs 143 | => KO.PandocWriterConfig 144 | -> PE.PandocWithRequirements -- ^ Document and union of input requirements 145 | -> P.Sem effs BH.Html 146 | toBlazeDocument writeConfig pdocWR = PM.absorbPandocMonad $ do 147 | writerOptions <- htmlFullDocWriterOptions (templateFP writeConfig) 148 | (templateVars writeConfig) 149 | PE.fromPandoc PE.WriteHtml5 (optionsF writeConfig writerOptions) pdocWR 150 | #endif 151 | 152 | -- | Convert markDown to Blaze 153 | markDownTextToBlazeFragment 154 | :: PM.PandocEffects effs 155 | => T.Text -- ^ markDown Text 156 | -> P.Sem effs BH.Html 157 | markDownTextToBlazeFragment = 158 | PE.fromPandocE PE.WriteHtml5 htmlWriterOptions 159 | . PE.addFrom PE.ReadMarkDown markDownReaderOptions 160 | 161 | 162 | 163 | 164 | -- | Convert given Pandoc to Blaze Html. 165 | 166 | -- | Convert current Pandoc document (from the ToPandoc effect) into a Blaze Html document. 167 | -- Incudes support for template and template variables and changes to the default writer options. 168 | pandocWriterToBlazeDocument 169 | :: PM.PandocEffects effs 170 | => KO.PandocWriterConfig -- ^ Configuration info for the Pandoc writer 171 | -> P.Sem (PE.ToPandoc ': effs) () -- ^ Effects stack to run to get Pandoc 172 | -> P.Sem effs BH.Html -- ^ Blaze Html (in remaining effects) 173 | pandocWriterToBlazeDocument writeConfig pw = 174 | PE.runPandocWriter pw >>= toBlazeDocument writeConfig 175 | 176 | -- | options for the mindoc template 177 | mindocOptionsF :: PA.WriterOptions -> PA.WriterOptions 178 | mindocOptionsF op = op { PA.writerSectionDivs = True } 179 | 180 | 181 | -- file output 182 | -- | Write each lazy text from a list of 'KD.DocWithInfo' 183 | -- to disk. File names come from the 'KP.PandocInfo' 184 | -- Directory is a function arguments. 185 | -- File extension is "html" 186 | writeAllPandocResultsWithInfoAsHtml 187 | :: T.Text -> [PE.DocWithInfo PE.PandocInfo TL.Text] -> IO () 188 | writeAllPandocResultsWithInfoAsHtml dir = 189 | KO.writeAllPandocResultsWithInfo dir "html" 190 | 191 | -- | Write the Lazy Text in a 'KD.DocWithInfo' to disk, 192 | -- Name comes from the 'KP.PandocInfo' 193 | -- Directory is an argument to the function 194 | -- File extension is "html" 195 | -- Create the parent directory or directories, if necessary. 196 | writePandocResultWithInfoAsHtml 197 | :: T.Text -> PE.DocWithInfo PE.PandocInfo TL.Text -> IO () 198 | writePandocResultWithInfoAsHtml dir = 199 | KO.writePandocResultWithInfo dir "html" 200 | -------------------------------------------------------------------------------- /data/pandoc-data/data/templates/default.opendocument: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $automatic-styles$ 8 | 9 | $for(header-includes)$ 10 | $header-includes$ 11 | $endfor$ 12 | 13 | 14 | $if(title)$ 15 | $title$ 16 | $endif$ 17 | $for(author)$ 18 | $author$ 19 | $endfor$ 20 | $if(date)$ 21 | $date$ 22 | $endif$ 23 | $for(include-before)$ 24 | $include-before$ 25 | $endfor$ 26 | $if(toc)$ 27 | 28 | 29 | $toc-title$ 30 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 44 | 45 | 46 | 47 | 48 | 50 | 51 | 52 | 53 | 54 | 56 | 57 | 58 | 59 | 60 | 62 | 63 | 64 | 65 | 66 | 68 | 69 | 70 | 71 | 72 | 74 | 75 | 76 | 77 | 78 | 80 | 81 | 82 | 83 | 84 | 86 | 87 | 88 | 89 | 90 | 92 | 93 | 94 | 95 | 96 | 98 | 99 | 100 | 101 | 102 | 104 | 105 | 106 | 107 | 108 | 110 | 111 | 112 | 113 | 114 | 116 | 117 | 118 | 119 | 120 | 122 | 123 | 124 | 125 | 126 | 128 | 129 | 130 | 131 | 132 | 134 | 135 | 136 | 137 | 138 | 140 | 141 | 142 | 143 | 144 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | $endif$ 153 | $body$ 154 | $for(include-after)$ 155 | $include-after$ 156 | $endfor$ 157 | 158 | 159 | 160 | --------------------------------------------------------------------------------