packages feed

ihaskell-rlangqq-0.2.0.0: IHaskell/Display/Rlangqq.hs

{-# OPTIONS_GHC -fno-warn-missing-fields #-}
{-# LANGUAGE TupleSections, TemplateHaskell #-}
module IHaskell.Display.Rlangqq
  ( module RlangQQ,
    rDisp,
    rDisplayAll,
    rOutputParsed,
    rOutput,
    getPlotNames,
    getCaptions,
  ) where

import RlangQQ
import RlangQQ.ParseKnitted

import System.Directory
import System.FilePath
import Data.Maybe
import Data.List
import Text.Read
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as Char
import qualified Data.ByteString.Base64 as Base64
import IHaskell.Display
import IHaskell.Display.Blaze () -- to confirm it's installed
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as H
import Data.Monoid
import Data.Char
import Control.Monad
import Data.Ord
import Data.List.Split
import Text.XFormat.Show hiding ((<>))
import Control.Applicative
import Control.Concurrent
import Data.Monoid
import Data.Typeable

import Control.Concurrent.STM
import Language.Haskell.TH.Quote

-- | same as 'RlangQQ.r', but displays plots at the end too
rDisp = QuasiQuoter { quoteExp = \s -> [| do
  result <- $(quoteExp r s)
  p <- rDisplayAll
  printDisplay p
  return result
  |] }

rOutput :: IO [Int]
rOutput = do
  fs <- mapMaybe (readMaybe <=< stripPrefix "raw" <=< stripSuffix ".md")
    <$> getDirectoryContents "Rtmp"
  fs' <- forM fs $ \f -> (,f) <$> getModificationTime (showf ("Rtmp/raw"%Int%".md") f)
  return $ map snd $ sortBy (flip (comparing fst)) fs'

-- | like 'stripPrefix' except on the end
stripSuffix :: String -> String -> Maybe String
stripSuffix s x = fmap reverse $ stripPrefix (reverse s) $ reverse x

rOutputParsed :: IO [KnitInteraction]
rOutputParsed = do
  ns <- rOutput
  case ns of
    [] -> return []
    n : _ -> parseKnitted <$> readFile (showf ("Rtmp/raw"%Int%".md") n)


getPlotNames :: IO [String]
getPlotNames = do
  interactions <- rOutputParsed
  return [ p |  KnitInteraction _ is <- interactions, KnitImage _ p <- is ]
            
getCaptions :: IO [String]
getCaptions = do
  interactions <- rOutputParsed
  return [ c |  KnitInteraction _ is <- interactions,
                KnitImage c _ <- is,
                not (isBoringCaption c) ]

-- | true when the caption name looks like one knitr will automatically
-- generate
isBoringCaption :: String -> Bool
isBoringCaption s = maybe False
                      (all isDigit)
                      (stripPrefix "plot of chunk unnamed-chunk-" s)

rDisplayAll :: IO Display
rDisplayAll = do
 ns <- rOutputParsed
 imgs <- sequence [ displayInteraction o | KnitInteraction _ os <- ns, o <- os]
 display (mconcat imgs)


displayInteraction :: KnitOutput -> IO Display
displayInteraction (KnitPrint c) = display (plain c)
displayInteraction (KnitWarning c) = display (plain c)
displayInteraction (KnitError c) = display (plain c)
displayInteraction (KnitAsIs c) = display (plain c)
displayInteraction (KnitImage cap img) = do
  let caption
          | isBoringCaption cap = mempty
          | otherwise = H.p (H.toMarkup cap)
  encoded <- Base64.encode <$> B.readFile img
  display $ H.img H.! H.src (H.unsafeByteStringValue
                         -- assumes you use the default device which is png
                        (Char.pack "data:image/png;base64," <> encoded))
              <> caption