[project @ 2003-12-10 14:15:16 by simonmar]
[ghc-hetmet.git] / ghc / compiler / ghci / InteractiveUI.hs
index 125c899..a1ec764 100644 (file)
@@ -1,6 +1,6 @@
 {-# OPTIONS -#include "Linker.h" #-}
 -----------------------------------------------------------------------------
--- $Id: InteractiveUI.hs,v 1.146 2003/02/19 15:54:07 simonpj Exp $
+-- $Id: InteractiveUI.hs,v 1.162 2003/12/10 14:15:21 simonmar Exp $
 --
 -- GHC Interactive User Interface
 --
@@ -8,7 +8,7 @@
 --
 -----------------------------------------------------------------------------
 module InteractiveUI ( 
-       interactiveUI,  -- :: CmState -> [FilePath] -> [LibrarySpec] -> IO ()
+       interactiveUI,  -- :: CmState -> [FilePath] -> IO ()
        ghciWelcomeMsg
    ) where
 
@@ -17,22 +17,13 @@ module InteractiveUI (
 
 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 )
-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(..) )
@@ -45,8 +36,12 @@ import CmdLineOpts   ( DynFlag(..), DynFlags(..), getDynFlags, saveDynFlags,
 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
@@ -74,7 +69,7 @@ import GHC.Exts               ( unsafeCoerce# )
 
 import Data.IORef      ( IORef, newIORef, readIORef, writeIORef )
 
-import GHC.Posix       ( setNonBlockingFD )
+import System.Posix.Internals ( setNonBlockingFD )
 
 -----------------------------------------------------------------------------
 
@@ -152,40 +147,33 @@ helpText = "\
 \                         (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 = [] }
 
@@ -195,8 +183,8 @@ interactiveUI cmstate paths cmdline_objs = do
 
    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
@@ -228,38 +216,42 @@ runGHCi paths dflags = 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
 
 
@@ -274,7 +266,7 @@ interactiveLoop is_tty = do
 
 checkPerms :: String -> IO Bool
 checkPerms name =
-#ifdef mingw32_TARGET_OS
+#ifdef mingw32_HOST_OS
   return True
 #else
   DriverUtil.handle (\_ -> return False) $ do
@@ -381,10 +373,11 @@ runStmt stmt
  | 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 []
@@ -438,22 +431,22 @@ no_buf_cmd = "IO.hSetBuffering IO.stdout IO.NoBuffering" ++
             " 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 ()
@@ -477,11 +470,10 @@ info "" = throwDyn (CmdLineError "syntax: `:i <thing-you-want-info-about>'")
 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))))
          )
@@ -489,18 +481,21 @@ info s = do
 
     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)
@@ -526,28 +521,35 @@ info s = do
        | 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
@@ -567,9 +569,7 @@ 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 --
@@ -600,40 +600,43 @@ loadModule fs = timeIt (loadModule' fs)
 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"
@@ -660,9 +663,7 @@ modulesLoadedMsg ok mods dflags =
 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)
@@ -685,58 +686,25 @@ browseCmd m =
 
 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
@@ -755,10 +723,9 @@ setContext str
 
 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)
@@ -773,7 +740,6 @@ prel = "Prelude"
 
 addToContext mods = do
   cms <- getCmState
-  dflags <- io getDynFlags
   (as,bs) <- io (cmGetContext cms)
 
   (as',bs') <- separate cms mods [] []
@@ -781,14 +747,13 @@ addToContext mods = do
   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 [] []
@@ -796,7 +761,7 @@ removeFromContext mods = do
   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'
 
 ----------------------------------------------------------------------------
@@ -915,9 +880,9 @@ optToStr RevertCAFs = "r"
 
 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 []
 
@@ -952,7 +917,8 @@ showBindings = do
   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 ()
@@ -1067,3 +1033,15 @@ revertCAFs = do
 
 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