{-# OPTIONS -#include "Linker.h" #-}
-----------------------------------------------------------------------------
--- $Id: InteractiveUI.hs,v 1.147 2003/02/20 13:12:40 simonpj Exp $
+-- $Id: InteractiveUI.hs,v 1.162 2003/12/10 14:15:21 simonmar Exp $
--
-- GHC Interactive User Interface
--
--
-----------------------------------------------------------------------------
module InteractiveUI (
- interactiveUI, -- :: CmState -> [FilePath] -> [LibrarySpec] -> IO ()
+ interactiveUI, -- :: CmState -> [FilePath] -> IO ()
ghciWelcomeMsg
) where
import CompManager
import HscTypes ( TyThing(..), HomeModInfo(hm_linkable), HomePackageTable,
- isObjectLinkable )
-import HsSyn ( TyClDecl(..), ConDecl(..), Sig(..) )
-import MkIface ( ifaceTyThing )
+ isObjectLinkable, GhciMode(..) )
+import IfaceSyn ( IfaceDecl( ifName ) )
import DriverFlags
import DriverState
-import DriverUtil ( remove_spaces, handle )
-import Linker ( initLinker, showLinkerState, linkLibraries,
- linkPackages )
+import DriverUtil ( remove_spaces )
+import Linker ( showLinkerState, linkPackages )
import Util
-import IdInfo ( GlobalIdDetails(..) )
-import Id ( isImplicitId, idName, globalIdDetails )
-import Class ( className )
-import TyCon ( tyConName, tyConClass_maybe, isPrimTyCon, DataConDetails(..) )
-import DataCon ( dataConName )
-import FieldLabel ( fieldLabelTyCon )
-import SrcLoc ( isGoodSrcLoc )
import Module ( showModMsg, lookupModuleEnv )
import Name ( Name, isHomePackageName, nameSrcLoc, nameOccName,
NamedThing(..) )
import Panic hiding ( showException )
import Config
-#ifndef mingw32_TARGET_OS
+#ifndef mingw32_HOST_OS
+import DriverUtil( handle )
import System.Posix
+#if __GLASGOW_HASKELL__ > 504
+ hiding (getEnv)
+#endif
#endif
#if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
import Data.IORef ( IORef, newIORef, readIORef, writeIORef )
-import GHC.Posix ( setNonBlockingFD )
+import System.Posix.Internals ( setNonBlockingFD )
-----------------------------------------------------------------------------
\ (eg. -v2, -fglasgow-exts, etc.)\n\
\"
-interactiveUI :: CmState -> [FilePath] -> [FilePath] -> IO ()
-interactiveUI cmstate paths cmdline_objs = do
- hFlush stdout
- hSetBuffering stdout NoBuffering
-
+interactiveUI :: [FilePath] -> Maybe String -> IO ()
+interactiveUI srcs maybe_expr = do
dflags <- getDynFlags
- initLinker
-
- -- link packages requested explicitly on the command-line
- expl <- readIORef v_ExplicitPackages
- linkPackages dflags expl
+ cmstate <- cmInit Interactive dflags;
- -- link libraries from the command-line
- linkLibraries dflags cmdline_objs
+ hFlush stdout
+ hSetBuffering stdout NoBuffering
-- Initialise buffering for the *interpreted* I/O system
- cmstate <- initInterpBuffering cmstate dflags
+ initInterpBuffering cmstate
-- We don't want the cmd line to buffer any input that might be
-- intended for the program, so unbuffer stdin.
hSetBuffering stdin NoBuffering
-- initial context is just the Prelude
- cmstate <- cmSetContext cmstate dflags [] ["Prelude"]
+ cmstate <- cmSetContext cmstate [] ["Prelude"]
#if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
Readline.initialize
#endif
- startGHCi (runGHCi paths dflags)
+ startGHCi (runGHCi srcs dflags maybe_expr)
GHCiState{ progname = "<interactive>",
args = [],
- targets = paths,
+ targets = srcs,
cmstate = cmstate,
options = [] }
return ()
-runGHCi :: [FilePath] -> DynFlags -> GHCi ()
-runGHCi paths dflags = do
+runGHCi :: [FilePath] -> DynFlags -> Maybe String -> GHCi ()
+runGHCi paths dflags maybe_expr = do
read_dot_files <- io (readIORef v_Read_DotGHCi)
when (read_dot_files) $ do
Left e -> return ()
Right hdl -> fileLoop hdl False
- -- perform a :load for files given on the GHCi command line
+ -- Perform a :load for files given on the GHCi command line
when (not (null paths)) $
ghciHandle showException $
loadModule paths
- -- enter the interactive loop
-#if defined(mingw32_TARGET_OS)
- -- always show prompt, since hIsTerminalDevice returns True for Consoles
- -- only, which we may or may not be running under (cf. Emacs sub-shells.)
- interactiveLoop True
-#else
+ -- if verbosity is greater than 0, or we are connected to a
+ -- terminal, display the prompt in the interactive loop.
is_tty <- io (hIsTerminalDevice stdin)
- interactiveLoop is_tty
-#endif
+ let show_prompt = verbosity dflags > 0 || is_tty
+
+ case maybe_expr of
+ Nothing ->
+ -- enter the interactive loop
+ interactiveLoop is_tty show_prompt
+ Just expr -> do
+ -- just evaluate the expression we were given
+ runCommand expr
+ return ()
-- and finally, exit
io $ do when (verbosity dflags > 0) $ putStrLn "Leaving GHCi."
-interactiveLoop is_tty = do
- -- ignore ^C exceptions caught here
+interactiveLoop is_tty show_prompt = do
+ -- Ignore ^C exceptions caught here
ghciHandleDyn (\e -> case e of
- Interrupted -> ghciUnblock (interactiveLoop is_tty)
+ Interrupted -> ghciUnblock (interactiveLoop is_tty show_prompt)
_other -> return ()) $ do
-- read commands from stdin
#if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
if (is_tty)
then readlineLoop
- else fileLoop stdin False -- turn off prompt for non-TTY input
+ else fileLoop stdin show_prompt
#else
- fileLoop stdin is_tty
+ fileLoop stdin show_prompt
#endif
checkPerms :: String -> IO Bool
checkPerms name =
-#ifdef mingw32_TARGET_OS
+#ifdef mingw32_HOST_OS
return True
#else
DriverUtil.handle (\_ -> return False) $ do
| otherwise
= do st <- getGHCiState
dflags <- io getDynFlags
- let dflags' = dopt_unset dflags Opt_WarnUnusedBinds
+ let cm_state' = cmSetDFlags (cmstate st)
+ (dopt_unset dflags Opt_WarnUnusedBinds)
(new_cmstate, result) <-
io $ withProgName (progname st) $ withArgs (args st) $
- cmRunStmt (cmstate st) dflags' stmt
+ cmRunStmt cm_state' stmt
setGHCiState st{cmstate = new_cmstate}
case result of
CmRunFailed -> return []
" Prelude.>> IO.hSetBuffering IO.stderr IO.NoBuffering"
flush_cmd = "IO.hFlush IO.stdout Prelude.>> IO.hFlush IO.stderr"
-initInterpBuffering :: CmState -> DynFlags -> IO CmState
-initInterpBuffering cmstate dflags
- = do (cmstate, maybe_hval) <- cmCompileExpr cmstate dflags no_buf_cmd
+initInterpBuffering :: CmState -> IO ()
+initInterpBuffering cmstate
+ = do maybe_hval <- cmCompileExpr cmstate no_buf_cmd
case maybe_hval of
Just hval -> writeIORef turn_off_buffering (unsafeCoerce# hval :: IO ())
other -> panic "interactiveUI:setBuffering"
- (cmstate, maybe_hval) <- cmCompileExpr cmstate dflags flush_cmd
+ maybe_hval <- cmCompileExpr cmstate flush_cmd
case maybe_hval of
Just hval -> writeIORef flush_interp (unsafeCoerce# hval :: IO ())
_ -> panic "interactiveUI:flush"
turnOffBuffering -- Turn it off right now
- return cmstate
+ return ()
flushInterpBuffers :: GHCi ()
info s = do
let names = words s
init_cms <- getCmState
- dflags <- io getDynFlags
let
infoThings cms [] = return cms
infoThings cms (name:names) = do
- (cms, stuff) <- io (cmInfoThing cms dflags name)
+ stuff <- io (cmInfoThing cms name)
io (putStrLn (showSDocForUser unqual (
vcat (intersperse (text "") (map showThing stuff))))
)
unqual = cmGetPrintUnqual init_cms
- showThing (ty_thing, fixity)
- = vcat [ text "-- " <> showTyThing ty_thing,
- showFixity fixity (getName ty_thing),
- ppr (ifaceTyThing ty_thing) ]
+ showThing (decl, fixity)
+ = vcat [ text "-- " <> showTyThing decl,
+ showFixity fixity (ifName decl),
+ showTyThing decl ]
showFixity fix name
| fix == defaultFixity = empty
| otherwise = ppr fix <+>
- (if isSymOcc (nameOccName name)
- then ppr name
- else char '`' <> ppr name <> char '`')
+ (if isSymOcc name
+ then ppr name
+ else char '`' <> ppr name <> char '`')
+
+ showTyThing decl = ppr decl
+{-
showTyThing (AClass cl)
= hcat [ppr cl, text " is a class", showSrcLoc (className cl)]
showTyThing (ADataCon dc)
| otherwise
= empty
where loc = nameSrcLoc name
+-}
- cms <- infoThings init_cms names
- setCmState cms
+ infoThings init_cms names
return ()
addModule :: [FilePath] -> GHCi ()
addModule files = do
state <- getGHCiState
- dflags <- io (getDynFlags)
io (revertCAFs) -- always revert CAFs on load/add.
+ files <- mapM expandPath files
let new_targets = files ++ targets state
- graph <- io (cmDepAnal (cmstate state) dflags new_targets)
- (cmstate1, ok, mods) <- io (cmLoadModules (cmstate state) dflags graph)
+ graph <- io (cmDepAnal (cmstate state) new_targets)
+ (cmstate1, ok, mods) <- io (cmLoadModules (cmstate state) graph)
setGHCiState state{ cmstate = cmstate1, targets = new_targets }
setContextAfterLoad mods
+ dflags <- io getDynFlags
modulesLoadedMsg ok mods dflags
changeDirectory :: String -> GHCi ()
-changeDirectory ('~':d) = do
- tilde <- io (getEnv "HOME") -- will fail if HOME not defined
- io (setCurrentDirectory (tilde ++ '/':d))
-changeDirectory d = io (setCurrentDirectory d)
+changeDirectory dir = do
+ state <- getGHCiState
+ when (targets state /= []) $
+ io $ putStr "Warning: changing directory causes all loaded modules to be unloaded, \n\
+ \because the search path has changed.\n"
+ cmstate1 <- io (cmUnload (cmstate state))
+ setGHCiState state{ cmstate = cmstate1, targets = [] }
+ setContextAfterLoad []
+ dir <- expandPath dir
+ io (setCurrentDirectory dir)
defineMacro :: String -> GHCi ()
defineMacro s = do
-- compile the expression
cms <- getCmState
- dflags <- io getDynFlags
- (new_cmstate, maybe_hv) <- io (cmCompileExpr cms dflags new_expr)
- setCmState new_cmstate
+ maybe_hv <- io (cmCompileExpr cms new_expr)
case maybe_hv of
Nothing -> return ()
Just hv -> io (writeIORef commands --
loadModule' :: [FilePath] -> GHCi ()
loadModule' files = do
state <- getGHCiState
- dflags <- io getDynFlags
+
+ -- expand tildes
+ files <- mapM expandPath files
-- do the dependency anal first, so that if it fails we don't throw
-- away the current set of modules.
- graph <- io (cmDepAnal (cmstate state) dflags files)
+ graph <- io (cmDepAnal (cmstate state) files)
-- Dependency anal ok, now unload everything
- cmstate1 <- io (cmUnload (cmstate state) dflags)
+ cmstate1 <- io (cmUnload (cmstate state))
setGHCiState state{ cmstate = cmstate1, targets = [] }
io (revertCAFs) -- always revert CAFs on load.
- (cmstate2, ok, mods) <- io (cmLoadModules cmstate1 dflags graph)
+ (cmstate2, ok, mods) <- io (cmLoadModules cmstate1 graph)
setGHCiState state{ cmstate = cmstate2, targets = files }
setContextAfterLoad mods
+ dflags <- io (getDynFlags)
modulesLoadedMsg ok mods dflags
reloadModule :: String -> GHCi ()
reloadModule "" = do
state <- getGHCiState
- dflags <- io getDynFlags
case targets state of
[] -> io (putStr "no current target\n")
paths -> do
-- do the dependency anal first, so that if it fails we don't throw
-- away the current set of modules.
- graph <- io (cmDepAnal (cmstate state) dflags paths)
+ graph <- io (cmDepAnal (cmstate state) paths)
io (revertCAFs) -- always revert CAFs on reload.
(cmstate1, ok, mods)
- <- io (cmLoadModules (cmstate state) dflags graph)
+ <- io (cmLoadModules (cmstate state) graph)
setGHCiState state{ cmstate=cmstate1 }
setContextAfterLoad mods
+ dflags <- io getDynFlags
modulesLoadedMsg ok mods dflags
reloadModule _ = noArgs ":reload"
typeOfExpr :: String -> GHCi ()
typeOfExpr str
= do cms <- getCmState
- dflags <- io getDynFlags
- (new_cmstate, maybe_tystr) <- io (cmTypeOfExpr cms dflags str)
- setCmState new_cmstate
+ maybe_tystr <- io (cmTypeOfExpr cms str)
case maybe_tystr of
Nothing -> return ()
Just tystr -> io (putStrLn tystr)
browseModule m exports_only = do
cms <- getCmState
- dflags <- io getDynFlags
is_interpreted <- io (cmModuleIsInterpreted cms m)
when (not is_interpreted && not exports_only) $
throwDyn (CmdLineError ("module `" ++ m ++ "' is not interpreted"))
- -- temporarily set the context to the module we're interested in,
+ -- Temporarily set the context to the module we're interested in,
-- just so we can get an appropriate PrintUnqualified
(as,bs) <- io (cmGetContext cms)
- cms1 <- io (if exports_only then cmSetContext cms dflags [] [prel,m]
- else cmSetContext cms dflags [m] [])
- cms2 <- io (cmSetContext cms1 dflags as bs)
-
- (cms3, things) <- io (cmBrowseModule cms2 dflags m exports_only)
+ cms1 <- io (if exports_only then cmSetContext cms [] [prel,m]
+ else cmSetContext cms [m] [])
+ cms2 <- io (cmSetContext cms1 as bs)
- setCmState cms3
+ things <- io (cmBrowseModule cms2 m exports_only)
let unqual = cmGetPrintUnqual cms1 -- NOTE: cms1 with the new context
- things' = filter wantToSee things
-
- wantToSee (AnId id) = not (isImplicitId id)
- wantToSee (ADataCon _) = False -- They'll come via their TyCon
- wantToSee _ = True
-
- thing_names = map getName things
-
- thingDecl thing@(AnId id) = ifaceTyThing thing
-
- thingDecl thing@(AClass c) =
- let rn_decl = ifaceTyThing thing in
- case rn_decl of
- ClassDecl { tcdSigs = cons } ->
- rn_decl{ tcdSigs = filter methodIsVisible cons }
- other -> other
- where
- methodIsVisible (ClassOpSig n _ _ _) = n `elem` thing_names
-
- thingDecl thing@(ATyCon t) =
- let rn_decl = ifaceTyThing thing in
- case rn_decl of
- TyData { tcdCons = DataCons cons } ->
- rn_decl{ tcdCons = DataCons (filter conIsVisible cons) }
- other -> other
- where
- conIsVisible (ConDecl n _ _ _ _) = n `elem` thing_names
-
io (putStrLn (showSDocForUser unqual (
- vcat (map (ppr . thingDecl) things')))
- )
-
- where
+ vcat (map ppr things)
+ )))
-----------------------------------------------------------------------------
-- Setting the module context
newContext mods = do
cms <- getCmState
- dflags <- io getDynFlags
(as,bs) <- separate cms mods [] []
let bs' = if null as && prel `notElem` bs then prel:bs else bs
- cms' <- io (cmSetContext cms dflags as bs')
+ cms' <- io (cmSetContext cms as bs')
setCmState cms'
separate cmstate [] as bs = return (as,bs)
addToContext mods = do
cms <- getCmState
- dflags <- io getDynFlags
(as,bs) <- io (cmGetContext cms)
(as',bs') <- separate cms mods [] []
let as_to_add = as' \\ (as ++ bs)
bs_to_add = bs' \\ (as ++ bs)
- cms' <- io (cmSetContext cms dflags
+ cms' <- io (cmSetContext cms
(as ++ as_to_add) (bs ++ bs_to_add))
setCmState cms'
removeFromContext mods = do
cms <- getCmState
- dflags <- io getDynFlags
(as,bs) <- io (cmGetContext cms)
(as_to_remove,bs_to_remove) <- separate cms mods [] []
let as' = as \\ (as_to_remove ++ bs_to_remove)
bs' = bs \\ (as_to_remove ++ bs_to_remove)
- cms' <- io (cmSetContext cms dflags as' bs')
+ cms' <- io (cmSetContext cms as' bs')
setCmState cms'
----------------------------------------------------------------------------
newPackages new_pkgs = do -- The new packages are already in v_Packages
state <- getGHCiState
- dflags <- io getDynFlags
- cmstate1 <- io (cmUnload (cmstate state) dflags)
+ cmstate1 <- io (cmUnload (cmstate state))
setGHCiState state{ cmstate = cmstate1, targets = [] }
+ dflags <- io getDynFlags
io (linkPackages dflags new_pkgs)
setContextAfterLoad []
cms <- getCmState
let
unqual = cmGetPrintUnqual cms
- showBinding b = putStrLn (showSDocForUser unqual (ppr (ifaceTyThing b)))
+-- showBinding b = putStrLn (showSDocForUser unqual (ppr (ifaceTyThing b)))
+ showBinding b = putStrLn (showSDocForUser unqual (ppr (getName b)))
io (mapM_ showBinding (cmGetBindings cms))
return ()
foreign import ccall "revertCAFs" rts_revertCAFs :: IO ()
-- Make it "safe", just in case
+
+-- -----------------------------------------------------------------------------
+-- Utils
+
+expandPath :: String -> GHCi String
+expandPath path =
+ case dropWhile isSpace path of
+ ('~':d) -> do
+ tilde <- io (getEnv "HOME") -- will fail if HOME not defined
+ return (tilde ++ '/':d)
+ other ->
+ return other