MyNixOS website logo
Description

Pandoc-filter to evaluate code section in markdown and auto-embed output.

pandoc-markdown-ghci-filter

A Pandoc filter that identifies code blocks(Haskell) in Pandoc supported formats, executes the code in GHCI and embeds the results in the returned output by updating the AST provided by Pandoc.

Quick Overview

Often a markdown(or any pandoc supported document) for any Haskell related documentation or a technical blog post involves code blocks. The code block could include definitions and also a demonstration of output with an interactive prompt. For, example, take this code block:

-- README.md

-- definition
increment:: Integer -> Integer
increment x = x + 1

-- interactive prompt to demonstrate the working of definitions so far

>> increment 41

It would be nice if this code block was automatically evaluated and output of increment 41 is automatically recorded below >> increment 41, as follows:

-- README.md

-- definition
increment:: Integer -> Integer
increment x = x + 1

-- interactive prompt to demonstrate the working of definitions so far

>> increment 41
42

Notice, that the 42 is automatically populated by this filter while transforming the original document.

To transform the document, we need to run the document through the pandoc filter, as follows:


-- set up pandoc_filter to the executable of this program (see Installation)

pandoc -s -t json README.md | pandoc_filter | pandoc -f json -t markdown

To read more about how filter work, visit the this page.

Installation

Requirements

- [Stack](https://docs.haskellstack.org/en/stable/README/)

From Source


git clone https://github.com/gdevanla/pandoc-markdown-ghci-filter.git
cd pandoc-markdown-ghci-filter

stack build

From Stackage/Hackage

stack build pandoc-markdown-ghci-filter # executable only available to local stack environment

or

stack install pandoc-markdown-ghci-filter # if you want to across all stack environments

Running the filter

pandoc -s -t json test.md | pandoc-markdown-ghci-filter-exe | pandoc -f json -t markdown

Usage Notes/Caveats

  1. All interactive statements (prefixed with >>) need to be preceded by \n to let the filter respect original new line spacing. If this is not followed, \n may be truncated.
  2. The program internally wraps all commands inside the GHCi multi-line construct :{..:}. Therefore, the code segments should not have multi-line constructs as part of code blocks.
  3. If you want the filter to ignore a certain code block, you can turn-off the filter by setting the code block attribute as follows

{.haskell code_filter="Off"}

-- do not run this code through GHCi

>> putStrLn "This line will not be expanded by the filter"

Note, the default value is "On"

Limitations/Open Issues

  1. Attaching different formattting properties to output.
  2. As explained in Usage Notes, all interactive statements should be preceded by an empty line, for the filter to maintain the \n characters as given by the input.

More examples

Sample Markdown Before Transformation

Sample markdown as fed into filter through pandoc.

Example 1

import Data.Text

>> putStrLn "This string should show up in the output"

Example 2

addOne:: Integer -> Integer
addOne x = x + 1

>> addOne 13

multBy2:: Integer -> Integer
multBy2 x = x * 2

>> (addOne 10) + (multBy2 20)

Example 3

Any errors that occur while executing statements in the code block are also rendered.


wrongFuncDefinition:: Integer -> Integer
wrongFuncDefintion = x + 1

>> functionNotInScope 10

Markdown after transformation

Example 1

import Data.Text

>> putStrLn "This string should show up in the output"
This string should show up in the output

Example 2

addOne:: Integer -> Integer
addOne x = x + 1

>> addOne 13
14

multBy2:: Integer -> Integer
multBy2 x = x * 2

>> (addOne 10) + (multBy2 20)
51

Example 3

Any errors that occur while executing statements in the code block are also rendered.


wrongFuncDefinition:: Integer -> Integer
wrongFuncDefintion = x + 1

<interactive>:16:1: error:
    The type signature for ‘wrongFuncDefinition’
      lacks an accompanying binding

>> functionNotInScope 10
<interactive>:29:2: error:
    Variable not in scope: functionNotInScope :: Integer -> t

Fun Fact: This document was generated using this same tool it describes. This README-pre-process.md was used to generate this document. Here is the command that was used:

pandoc -s -t json README-pre-process.md | stack runhaskell app/Main.hs | pandoc -f json -t markdown > README.md
Metadata

Version

0.1.0.0

License

Platforms (75)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    WASI
    Windows
Show all
  • aarch64-darwin
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64_be-none
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-darwin
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-darwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • vc4-none
  • wasm32-wasi
  • wasm64-wasi
  • x86_64-cygwin
  • x86_64-darwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-windows