module Fay.Types.Printer
( PrintReader(..)
, defaultPrintReader
, PrintWriter(..)
, pwOutputString
, PrintState(..)
, defaultPrintState
, Printer(..)
, Printable(..)
, execPrinter
, indented
, newline
, write
, askIf
, mapping
) where
import Control.Monad.RWS
import Data.List (elemIndex)
import Data.String
import Language.Haskell.Exts
import SourceMap.Types
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 Monoid PrintWriter where
mempty = PrintWriter [] id
mappend (PrintWriter a b) (PrintWriter x y) = PrintWriter (a ++ x) (b . y)
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 Monoid Printer where
mempty = Printer $ return ()
mappend (Printer p) (Printer q) = Printer (p >> q)
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 = case elemIndex '\n' (reverse x) of
Just i -> i
Nothing -> psColumn ps + length 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] }