{-# LANGUAGE FlexibleContexts #-}
module Fay.Types.Printer
( PrintReader(..)
, defaultPrintReader
, PrintWriter(..)
, pwOutputString
, PrintState(..)
, defaultPrintState
, Printer(..)
, Printable(..)
, execPrinter
, indented
, newline
, write
, askIf
, mapping
) where
import Fay.Compiler.Prelude
import Control.Monad.RWS (RWS, asks, execRWS, get, modify, put, tell)
import Data.List (elemIndex)
import Data.Maybe (fromMaybe)
import Data.String
import Language.Haskell.Exts
import SourceMap.Types
import qualified Data.Semigroup as SG
data PrintReader = PrintReader
{ prPretty :: Bool
, prPrettyThunks :: Bool
, prPrettyOperators :: Bool
}
defaultPrintReader :: PrintReader
defaultPrintReader = PrintReader False False False
data PrintWriter = PrintWriter
{ pwMappings :: [Mapping]
, pwOutput :: ShowS
}
pwOutputString :: PrintWriter -> String
pwOutputString (PrintWriter _ out) = out ""
instance SG.Semigroup PrintWriter where
(PrintWriter a b) <> (PrintWriter x y) = PrintWriter (a ++ x) (b . y)
instance Monoid PrintWriter where
mempty = PrintWriter [] id
mappend = (<>)
data PrintState = PrintState
{ psLine :: Int
, psColumn :: Int
, psIndentLevel :: Int
, psNewline :: Bool
}
defaultPrintState :: PrintState
defaultPrintState = PrintState 0 0 0 False
newtype Printer = Printer
{ runPrinter :: RWS PrintReader PrintWriter PrintState () }
execPrinter :: Printer -> PrintReader -> PrintWriter
execPrinter (Printer p) r = snd $ execRWS p r defaultPrintState
instance SG.Semigroup Printer where
(Printer p) <> (Printer q) = Printer (p >> q)
instance Monoid Printer where
mempty = Printer $ return ()
mappend = (<>)
class Printable a where
printJS :: a -> Printer
indented :: Printer -> Printer
indented (Printer p) = Printer $ asks prPretty >>= \pretty ->
when pretty (addToIndentLevel 1) >> p >> when pretty (addToIndentLevel (-1))
where addToIndentLevel d = modify (\ps -> ps { psIndentLevel = psIndentLevel ps + d })
newline :: Printer
newline = Printer $ asks prPretty >>= flip when writeNewline
where writeNewline = writeRWS "\n" >> modify (\s -> s { psNewline = True })
write :: String -> Printer
write = Printer . writeRWS
writeRWS :: String -> RWS PrintReader PrintWriter PrintState ()
writeRWS x = do
ps <- get
let out = if psNewline ps
then replicate (2 * psIndentLevel ps) ' ' ++ x
else x
tell mempty { pwOutput = (out++) }
let newLines = length (filter (== '\n') x)
put ps { psLine = psLine ps + newLines
, psColumn = fromMaybe (psColumn ps + length x) . elemIndex '\n' $ reverse x
, psNewline = False
}
instance IsString Printer where
fromString = write
askIf :: (PrintReader -> Bool) -> Printer -> Printer -> Printer
askIf f (Printer p) (Printer q) = Printer $ asks f >>= (\b -> if b then p else q)
mapping :: SrcSpan -> Printer
mapping srcSpan = Printer $ get >>= \ps ->
let m = Mapping { mapGenerated = Pos (fromIntegral (psLine ps))
(fromIntegral (psColumn ps))
, mapOriginal = Just (Pos (fromIntegral (srcSpanStartLine srcSpan))
(fromIntegral (srcSpanStartColumn srcSpan) - 1))
, mapSourceFile = Just (srcSpanFilename srcSpan)
, mapName = Nothing
}
in tell $ mempty { pwMappings = [m] }