module HsDev.Tools.Ghc.Base (
ghcRun, ghcRunWith,
interpretedFlags, noLinkFlags,
withFlags, modifyFlags,
clearTargets, makeTarget, loadTargets,
loadInteractive, reload,
collectMessages, collectMessages_,
formatType,
spanRegion,
withCurrentDirectory,
logToChan, logToNull
) where
import Control.Lens (view, over)
import Control.Monad
import Control.Monad.Except
import Data.Time.Clock (getCurrentTime)
import Data.String (fromString)
import Data.Text (Text)
import qualified Data.Text as T
import System.Directory (getCurrentDirectory, setCurrentDirectory)
import System.FilePath
import Exception (ExceptionMonad(..))
import GHC hiding (Warning, Module)
import Outputable
import FastString (unpackFS)
import StringBuffer
import Type
import qualified Pretty
import Control.Concurrent.FiniteChan
import System.Directory.Paths
import HsDev.Symbols.Location (Position(..), Region(..), region, ModuleLocation(..))
import HsDev.Tools.Types
import HsDev.Tools.Ghc.Compat
import qualified HsDev.Tools.Ghc.Compat as C (setLogAction, addLogAction, unqualStyle)
ghcRun :: GhcMonad m => [String] -> m a -> m a
ghcRun = ghcRunWith interpretedFlags
ghcRunWith :: GhcMonad m => (DynFlags -> DynFlags) -> [String] -> m a -> m a
ghcRunWith onFlags opts act = do
fs <- getSessionDynFlags
cleanupHandler fs $ do
(fs', _, _) <- parseDynamicFlags fs (map noLoc opts)
void $ setSessionDynFlags $ onFlags fs'
modifyFlags $ C.setLogAction logToNull
act
interpretedFlags :: DynFlags -> DynFlags
interpretedFlags fs = fs {
ghcMode = CompManager,
ghcLink = LinkInMemory,
hscTarget = HscInterpreted }
noLinkFlags :: DynFlags -> DynFlags
noLinkFlags fs = fs {
ghcMode = CompManager,
ghcLink = NoLink,
hscTarget = HscNothing }
withFlags :: GhcMonad m => m a -> m a
withFlags = gbracket getSessionDynFlags (void . setSessionDynFlags) . const
modifyFlags :: GhcMonad m => (DynFlags -> DynFlags) -> m ()
modifyFlags f = do
fs <- getSessionDynFlags
let
fs' = f fs
_ <- setSessionDynFlags fs'
return ()
clearTargets :: GhcMonad m => m ()
clearTargets = loadTargets []
makeTarget :: GhcMonad m => Text -> Maybe Text -> m Target
makeTarget name Nothing = guessTarget (T.unpack name) Nothing
makeTarget name (Just cts) = do
t <- guessTarget (T.unpack name) Nothing
tm <- liftIO getCurrentTime
return t { targetContents = Just (stringToStringBuffer $ T.unpack cts, tm) }
loadTargets :: GhcMonad m => [Target] -> m ()
loadTargets ts = setTargets ts >> load LoadAllTargets >> return ()
loadInteractive :: GhcMonad m => Path -> Maybe Text -> m ()
loadInteractive fpath mcts = do
fpath' <- liftIO $ canonicalize fpath
withCurrentDirectory (view path $ takeDir fpath') $ do
t <- makeTarget (over path takeFileName fpath') mcts
loadTargets [t]
g <- getModuleGraph
setContext [IIModule (ms_mod_name m) | m <- g]
reload :: GhcMonad m => m ()
reload = do
ts <- getTargets
ctx <- getContext
setContext []
clearTargets
setTargets ts
setContext ctx
collectMessages :: GhcMonad m => m a -> m (a, [Note OutputMessage])
collectMessages act = do
ch <- liftIO newChan
r <- gbracket (liftM log_action getSessionDynFlags) (\action' -> modifyFlags (\fs -> fs { log_action = action' })) $ \_ -> do
modifyFlags (C.addLogAction $ logToChan ch)
act
notes <- liftIO $ stopChan ch
return (r, notes)
collectMessages_ :: GhcMonad m => m () -> m [Note OutputMessage]
collectMessages_ = fmap snd . collectMessages
formatType :: GHC.DynFlags -> GHC.Type -> String
formatType dflag t = showOutputable dflag (removeForAlls t)
spanRegion :: SrcSpan -> Region
spanRegion (RealSrcSpan s) = Position (srcSpanStartLine s) (srcSpanStartCol s) `region` Position (srcSpanEndLine s) (srcSpanEndCol s)
spanRegion _ = Position 0 0 `region` Position 0 0
withCurrentDirectory :: GhcMonad m => FilePath -> m a -> m a
withCurrentDirectory dir act = gbracket (liftIO getCurrentDirectory) (liftIO . setCurrentDirectory) $
const (liftIO (setCurrentDirectory dir) >> act)
logToChan :: Chan (Note OutputMessage) -> LogAction
logToChan ch fs sev src msg
| Just sev' <- checkSev sev = do
src' <- canonicalize srcMod
void $ sendChan ch Note {
_noteSource = src',
_noteRegion = spanRegion src,
_noteLevel = Just sev',
_note = OutputMessage {
_message = fromString $ showSDoc fs msg,
_messageSuggestion = Nothing } }
| otherwise = return ()
where
checkSev SevWarning = Just Warning
checkSev SevError = Just Error
checkSev SevFatal = Just Error
checkSev _ = Nothing
srcMod = case src of
RealSrcSpan s' -> FileModule (fromFilePath $ unpackFS $ srcSpanFile s') Nothing
_ -> NoLocation
logToNull :: LogAction
logToNull _ _ _ _ = return ()
removeForAlls :: Type -> Type
removeForAlls ty = removeForAlls' ty' tty' where
ty' = dropForAlls ty
tty' = splitFunTy_maybe ty'
removeForAlls' :: Type -> Maybe (Type, Type) -> Type
removeForAlls' ty Nothing = ty
removeForAlls' ty (Just (pre, ftype))
| isPredTy pre = mkFunTy pre (dropForAlls ftype)
| otherwise = ty
showOutputable :: Outputable a => DynFlags -> a -> String
showOutputable dflag = unwords . lines . showUnqualifiedPage dflag . ppr
showUnqualifiedPage :: DynFlags -> SDoc -> String
showUnqualifiedPage dflag = renderStyle Pretty.LeftMode 0 . withPprStyleDoc dflag (C.unqualStyle dflag)