{-# LANGUAGE BangPatterns #-}
module HIE.Bios.Ghc.Logger (
withLogger
) where
import Bag (Bag, bagToList)
import CoreMonad (liftIO)
import DynFlags (LogAction, dopt, DumpFlag(Opt_D_dump_splices))
import ErrUtils
import Exception (ghandle)
import FastString (unpackFS)
import GHC (DynFlags(..), SrcSpan(..), Severity(SevError), GhcMonad)
import qualified GHC as G
import HscTypes (SourceError, srcErrorMessages)
import Outputable (PprStyle, SDoc)
import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef)
import Data.Maybe (fromMaybe)
import System.FilePath (normalise)
import HIE.Bios.Ghc.Doc (showPage, getStyle)
import HIE.Bios.Ghc.Api (withDynFlags)
type Builder = [String] -> [String]
newtype LogRef = LogRef (IORef Builder)
newLogRef :: IO LogRef
newLogRef = LogRef <$> newIORef id
readAndClearLogRef :: LogRef -> IO String
readAndClearLogRef (LogRef ref) = do
b <- readIORef ref
writeIORef ref id
return $! unlines (b [])
appendLogRef :: DynFlags -> LogRef -> LogAction
appendLogRef df (LogRef ref) _ _ sev src style msg = do
let !l = ppMsg src sev df style msg
modifyIORef ref (\b -> b . (l:))
withLogger ::
(GhcMonad m)
=> (DynFlags -> DynFlags) -> m () -> m (Either String String)
withLogger setDF body = ghandle sourceError $ do
logref <- liftIO newLogRef
withDynFlags (setLogger logref . setDF) $ do
body
liftIO $ Right <$> readAndClearLogRef logref
where
setLogger logref df = df { log_action = appendLogRef df logref }
sourceError ::
(GhcMonad m)
=> SourceError -> m (Either String String)
sourceError err = do
dflag <- G.getSessionDynFlags
style <- getStyle dflag
let ret = unlines . errBagToStrList dflag style . srcErrorMessages $ err
return (Left ret)
errBagToStrList :: DynFlags -> PprStyle -> Bag ErrMsg -> [String]
errBagToStrList dflag style = map (ppErrMsg dflag style) . reverse . bagToList
ppErrMsg :: DynFlags -> PprStyle -> ErrMsg -> String
ppErrMsg dflag style err = ppMsg spn SevError dflag style msg
where
spn = errMsgSpan err
msg = pprLocErrMsg err
ppMsg :: SrcSpan -> Severity-> DynFlags -> PprStyle -> SDoc -> String
ppMsg spn sev dflag style msg = prefix ++ cts
where
cts = showPage dflag style msg
defaultPrefix
| isDumpSplices dflag = ""
| otherwise = checkErrorPrefix
prefix = fromMaybe defaultPrefix $ do
(line,col,_,_) <- getSrcSpan spn
file <- normalise <$> getSrcFile spn
let severityCaption = showSeverityCaption sev
return $ file ++ ":" ++ show line ++ ":" ++ show col ++ ":" ++ severityCaption
checkErrorPrefix :: String
checkErrorPrefix = "Dummy:0:0:Error:"
showSeverityCaption :: Severity -> String
showSeverityCaption SevWarning = "Warning: "
showSeverityCaption _ = ""
getSrcFile :: SrcSpan -> Maybe String
getSrcFile (G.RealSrcSpan spn) = Just . unpackFS . G.srcSpanFile $ spn
getSrcFile _ = Nothing
isDumpSplices :: DynFlags -> Bool
isDumpSplices dflag = dopt Opt_D_dump_splices dflag
getSrcSpan :: SrcSpan -> Maybe (Int,Int,Int,Int)
getSrcSpan (RealSrcSpan spn) = Just ( G.srcSpanStartLine spn
, G.srcSpanStartCol spn
, G.srcSpanEndLine spn
, G.srcSpanEndCol spn)
getSrcSpan _ = Nothing