{-# LANGUAGE OverloadedStrings #-}
module CodeBlockExecutor
(
applyFilterToBlock,
runCodeBlock,
processResults
) where
import Text.Pandoc
import Language.Haskell.Ghcid
import Control.Applicative
import Control.Exception
import Data.String
import Data.List as L
import Data.Map.Strict as M
import Data.Maybe
import qualified Data.Text as T
removeAll:: T.Text -> T.Text -> T.Text
removeAll pat str = if (T.replace pat "" str) == str then str
else removeAll pat (T.replace pat "" str)
isInteractive :: T.Text -> Bool
isInteractive cmd = T.isPrefixOf ">>" cmd
updateSuffixForInteractiveCmd :: Data.String.IsString p => T.Text -> p
updateSuffixForInteractiveCmd cmd = if isInteractive cmd then
if T.last cmd == '\n' then "" else "\n"
else "\n\n"
intercalateCmdAndResults :: T.Text -> T.Text -> T.Text
intercalateCmdAndResults cmd result =
T.concat [cmd, updateSuffixForInteractiveCmd cmd, result, trailResult result] where
trailResult r = if r /= "" then "\n" else ""
processResults :: [T.Text]
-> [T.Text]
-> String
processResults cmds results =
let cmd_result = getZipList $ intercalateCmdAndResults <$> ZipList cmds <*> ZipList results
in
(T.unpack . T.concat) $ cmd_result
applyFilterToBlock:: Block
-> IO Block
applyFilterToBlock c@(CodeBlock (_, classes, key_values) _) = let
attrs = M.fromList key_values
haskell_in_class = L.find (== "haskell") classes
code_filter_flag = maybe "On" id (M.lookup ("code-filter") attrs)
in
if code_filter_flag == "On" && isJust haskell_in_class then runCodeBlock c
else (return c)
applyFilterToBlock b = return b
runCodeBlock:: Block
-> IO Block
runCodeBlock (CodeBlock attr str) = bracket startGhciProcess' stopGhci runCommands
where
startGhciProcess' = do
(ghci_handle, _) <- startGhci "stack ghci" (Just ".") (\_ _ -> return ())
return ghci_handle
runCommands g = do
let cmds = L.filter (\s -> s /= "") $ T.splitOn "\n\n" $ T.pack str
results <- mapM (runCmd g) cmds
let results''' = processResults cmds results
return (CodeBlock attr results''')
runCodeBlock b = return b
runCmd :: Ghci
-> T.Text
-> IO T.Text
runCmd g cmd = do
let executeStatement = exec g
cmd_ = T.concat [":{\n", T.replace ">>" "" cmd, "\n:}\n"]
result <- executeStatement . T.unpack $ cmd_
probe <- exec g ":{\nshow (\"PANDOC_FILTER_PROBE_PROMPT_INTERNAL\"::String)\n:}\n"
let current_prompt = preparePrompt probe
where
preparePrompt probe' =
let prompt = T.replace " \"\\\"PANDOC_FILTER_PROBE_PROMPT_INTERNAL\\\"\"\n" "" (T.pack . unlines $ probe')
in
T.concat [T.takeWhile (/='|') prompt, "|"]
result' = T.stripStart $ removeAll current_prompt (T.pack . unlines $ result)
return $ result'