Commit 79d6745f authored by Simon Marlow's avatar Simon Marlow

Clean up the handling of the import and :module commands in GHCi

Previously we remembered the whole history of commands and replayed
them on every :load/:reload, which lead to some non-linear performance
characteristics (#5317).  The handling of the implicit Prelude import
and the implicit imports of recently loaded modules was also
complicated and wrong in various obscure ways.

The Prelude import works just like the implicit Prelude import in a
Haskell module: it can be overriden with an explicit Prelude
import.

I have added a new ":show imports" command to show which imports are
currently in force.

Prelude> :show imports
import Prelude -- implicit
Prelude> import Prelude ()
Prelude> :show imports
import Prelude ()
Prelude> map

<interactive>:0:1: Not in scope: `map'
Prelude>

Full documentation in the User's Guide.

There are various other little tweaks and improvements, such as when a
module is imported with 'as', we now show the 'as' name in the prompt
rather than the original name.
parent 3b8d1287
......@@ -111,7 +111,7 @@ data IE name
| IEGroup Int HsDocString -- ^ Doc section heading
| IEDoc HsDocString -- ^ Some documentation
| IEDocNamed String -- ^ Reference to named doc
deriving (Data, Typeable)
deriving (Eq, Data, Typeable)
\end{code}
\begin{code}
......
......@@ -582,8 +582,12 @@ xs :: [Integer]
<screen>Prelude></screen>
<para>Which indicates that everything from the module
<literal>Prelude</literal> is currently in scope. If we now
load a file into GHCi, the prompt will change:</para>
<literal>Prelude</literal> is currently in scope; the visible
identifiers are exactly those that would be visible in a Haskell
source file with no <literal>import</literal>
declarations.</para>
<para>If we now load a file into GHCi, the prompt will change:</para>
<screen>
Prelude> :load Main.hs
......@@ -620,26 +624,59 @@ Compiling Main ( Main.hs, interpreted )
interpreted version of a module, add the <literal>*</literal>
when loading the module, e.g. <literal>:load *M</literal>.</para>
<para>The scope is manipulated using the
<literal>:module</literal> command. For example, if the current
scope is <literal>Prelude</literal>, then we can bring into
scope the exports from the module <literal>IO</literal> like
so:</para>
<para>To add modules to the scope, use ordinary Haskell
<literal>import</literal> syntax:</para>
<screen>
Prelude> :module +IO
Prelude IO> hPutStrLn stdout "hello\n"
Prelude> import System.IO
Prelude System.IO> hPutStrLn stdout "hello\n"
hello
Prelude IO>
Prelude System.IO>
</screen>
<para>(Note: you can use conventional
haskell <literal>import</literal> syntax as
well, but this does not support
<literal>*</literal> forms).
<literal>:module</literal> can also be shortened to
<literal>:m</literal>. The full syntax of the
<literal>:module</literal> command is:</para>
<para>The full Haskell import syntax is supported, including
<literal>hiding</literal> and <literal>as</literal> clauses.
The prompt shows the modules that are currently imported, but it
omits details about <literal>hiding</literal>,
<literal>as</literal>, and so on. To see the full story, use
<literal>:show imports</literal>:</para>
<screen>
Prelude> import System.IO
Prelude System.IO> import Data.Map as Map
Prelude System.IO Map> :show imports
import Prelude -- implicit
import System.IO
import Data.Map as Map
Prelude System.IO Map>
</screen>
<para>Note that the <literal>Prelude</literal> import is marked
as implicit. It can be overriden with an explicit
<literal>Prelude</literal> import, just like in a Haskell
module.</para>
<para>Another way to manipulate the scope is to use the
<literal>:module</literal> command, which provides a way to do
two things that cannot be done with ordinary
<literal>import</literal> declarations:
<itemizedlist>
<listitem>
<para><literal>:module</literal> supports the
<literal>*</literal> modifier on modules, which opens the
full top-level scope of a module, rather than just its
exports.</para>
</listitem>
<listitem>
<para>Imports can be <emphasis>removed</emphasis> from the
context, using the syntax <literal>:module -M</literal>.
The <literal>import</literal> syntax is cumulative (as in a
Haskell module), so this is the only way to subtract from
the scope.</para>
</listitem>
</itemizedlist>
The full syntax of the <literal>:module</literal> command
is:</para>
<screen>
:module <optional>+|-</optional> <optional>*</optional><replaceable>mod<subscript>1</subscript></replaceable> ... <optional>*</optional><replaceable>mod<subscript>n</subscript></replaceable>
......@@ -650,14 +687,12 @@ Prelude IO>
scope, and <literal>-</literal> removes them. Without either
<literal>+</literal> or <literal>-</literal>, the current scope
is replaced by the set of modules specified. Note that if you
use this form and leave out <literal>Prelude</literal>, GHCi
will assume that you really wanted the
<literal>Prelude</literal> and add it in for you (if you don't
want the <literal>Prelude</literal>, then ask to remove it with
<literal>:m -Prelude</literal>).</para>
<para>The scope is automatically set after a
<literal>:load</literal> command, to the most recently loaded
use this form and leave out <literal>Prelude</literal>, an
implicit <literal>Prelude</literal> import will be added
automatically.</para>
<para>After a <literal>:load</literal> command, an automatic
import is added to the scope for the most recently loaded
"target" module, in a <literal>*</literal>-form if possible.
For example, if you say <literal>:load foo.hs bar.hs</literal>
and <filename>bar.hs</filename> contains module
......@@ -666,7 +701,23 @@ Prelude IO>
interpreted, or if <literal>Bar</literal> is compiled it will be
set to <literal>Prelude Bar</literal> (GHCi automatically adds
<literal>Prelude</literal> if it isn't present and there aren't
any <literal>*</literal>-form modules).</para>
any <literal>*</literal>-form modules). These
automatically-added imports can be seen with
<literal>:show imports</literal>:
<screen>
Prelude> :load hello.hs
[1 of 1] Compiling Main ( hello.hs, interpreted )
Ok, modules loaded: Main.
*Main> :show imports
:module +*Main -- added automatically
*Main>
</screen>
and the automatically-added import is replaced the next time you
use <literal>:load</literal>, <literal>:add</literal>, or
<literal>:reload</literal>. It can also be removed by
<literal>:module</literal> as with normal imports.</para>
<para>With multiple modules in scope, especially multiple
<literal>*</literal>-form modules, it is likely that name
......@@ -692,20 +743,20 @@ Prelude IO>
<itemizedlist>
<listitem>
<para>The set of modules that are
currently <emphasis>loaded</emphasis>. This set is
modified
by <literal>:load</literal>, <literal>:add</literal>
and <literal>:reload</literal>.
<para>The set of modules that are currently
<emphasis>loaded</emphasis>. This set is modified by
<literal>:load</literal>, <literal>:add</literal> and
<literal>:reload</literal>, and can be shown with
<literal>:show modules</literal>.
</para>
</listitem>
<listitem>
<para>The set of modules that are currently <emphasis>in
scope</emphasis> at the prompt. This set is modified
by <literal>:module</literal>, and it is also set
automatically
after <literal>:load</literal>, <literal>:add</literal>,
and <literal>:reload</literal>.</para>
scope</emphasis> at the prompt. This set is modified by
<literal>import</literal>, <literal>:module</literal>, and
it is also modified automatically after
<literal>:load</literal>, <literal>:add</literal>, and
<literal>:reload</literal>, as described above.</para>
</listitem>
</itemizedlist>
......@@ -2534,6 +2585,18 @@ bar
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>:show imports</literal>
<indexterm><primary><literal>:show imports</literal></primary></indexterm>
</term>
<listitem>
<para>Show the imports that are currently in force, as
created by <literal>import</literal> and
<literal>:module</literal> commands.</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<literal>:show modules</literal>
......
......@@ -56,7 +56,6 @@ data GHCiState = GHCiState
editor :: String,
stop :: String,
options :: [GHCiOption],
prelude :: GHC.ModuleName,
line_number :: !Int, -- input line
break_ctr :: !Int,
breaks :: ![(Int, BreakLocation)],
......@@ -68,23 +67,27 @@ data GHCiState = GHCiState
-- remember is here:
last_command :: Maybe Command,
cmdqueue :: [String],
remembered_ctx :: [CtxtCmd],
-- we remember the :module commands between :loads, so that
-- on a :reload we can replay them. See bugs #2049,
-- \#1873, #1360. Previously we tried to remember modules that
-- were supposed to be in the context but currently had errors,
-- but this was complicated. Just replaying the :module commands
-- seems to be the right thing.
remembered_ctx :: [InteractiveImport],
-- the imports that the user has asked for, via import
-- declarations and :module commands. This list is
-- persistent over :reloads (but any imports for modules
-- that are not loaded are temporarily ignored). After a
-- :load, all the home-package imports are stripped from
-- this list.
-- See bugs #2049, #1873, #1360
transient_ctx :: [InteractiveImport],
-- An import added automatically after a :load, usually of
-- the most recently compiled module. May be empty if
-- there are no modules loaded. This list is replaced by
-- :load, :reload, and :add. In between it may be modified
-- by :module.
ghc_e :: Bool -- True if this is 'ghc -e' (or runghc)
}
data CtxtCmd -- In each case, the first [String] are the starred modules
-- and the second are the unstarred ones
= SetContext [String] [String]
| AddModules [String] [String]
| RemModules [String] [String]
| Import String
type TickArray = Array Int [(BreakIndex,SrcSpan)]
data GHCiOption
......@@ -161,6 +164,8 @@ getGHCiState :: GHCi GHCiState
getGHCiState = GHCi $ \r -> liftIO $ readIORef r
setGHCiState :: GHCiState -> GHCi ()
setGHCiState s = GHCi $ \r -> liftIO $ writeIORef r s
modifyGHCiState :: (GHCiState -> GHCiState) -> GHCi ()
modifyGHCiState f = GHCi $ \r -> liftIO $ readIORef r >>= writeIORef r . f
liftGhc :: Ghc a -> GHCi a
liftGhc m = GHCi $ \_ -> m
......@@ -209,10 +214,6 @@ instance ExceptionMonad (InputT GHCi) where
gblock = Haskeline.block
gunblock = Haskeline.unblock
-- for convenience...
getPrelude :: GHCi ModuleName
getPrelude = getGHCiState >>= return . prelude
getDynFlags :: GhcMonad m => m DynFlags
getDynFlags = do
GHC.getSessionDynFlags
......
......@@ -50,7 +50,6 @@ import Linker
import Util
import NameSet
import Maybes ( orElse, expectJust )
import ListSetOps ( removeRedundant )
import FastString
import Encoding
import Foreign.C
......@@ -84,6 +83,7 @@ import Control.Monad as Monad
import Text.Printf
import Foreign.Safe
import GHC.Exts ( unsafeCoerce# )
import Control.Applicative hiding (empty)
import GHC.IO.Exception ( IOErrorType(InvalidArgument) )
import GHC.IO.Handle ( hFlushAll )
......@@ -271,6 +271,7 @@ helpText =
" :show bindings show the current bindings made at the prompt\n" ++
" :show breaks show the active breakpoints\n" ++
" :show context show the breakpoint context\n" ++
" :show imports show the current imports\n" ++
" :show modules show the currently loaded modules\n" ++
" :show packages show the currently active package flags\n" ++
" :show languages show the currently active language flags\n" ++
......@@ -342,10 +343,6 @@ interactiveUI srcs maybe_exprs = do
hSetEncoding stdin utf8
#endif
-- initial context is just the Prelude
let prel_mn = GHC.mkModuleName "Prelude"
GHC.setContext [IIDecl (simpleImportDecl prel_mn)]
default_editor <- liftIO $ findEditor
startGHCi (runGHCi srcs maybe_exprs)
......@@ -355,7 +352,6 @@ interactiveUI srcs maybe_exprs = do
stop = default_stop,
editor = default_editor,
options = [],
prelude = prel_mn,
line_number = 1,
break_ctr = 0,
breaks = [],
......@@ -363,6 +359,7 @@ interactiveUI srcs maybe_exprs = do
last_command = Nothing,
cmdqueue = [],
remembered_ctx = [],
transient_ctx = [],
ghc_e = isJust maybe_exprs
}
......@@ -417,6 +414,9 @@ runGHCi paths maybe_exprs = do
liftIO (hClose hdl `catchIO` \_ -> return ())
where
getDirectory f = case takeDirectory f of "" -> "."; d -> d
--
setGHCContext []
when (read_dot_files) $ do
mcfgs0 <- sequence $ [ current_dir, app_user_dir, home_dir ]
......@@ -559,13 +559,15 @@ mkPrompt = do
dots | _:rs <- resumes, not (null rs) = text "... "
| otherwise = empty
modules_bit =
-- ToDo: maybe...
-- let (btoplevs, bexports) = fromMaybe ([],[]) (remembered_ctx st) in
-- hsep (map (\m -> text "!*" <> ppr (GHC.moduleName m)) btoplevs) <+>
-- hsep (map (\m -> char '!' <> ppr (GHC.moduleName m)) bexports) <+>
hsep [ char '*' <> ppr (GHC.moduleName m) | IIModule m <- imports ] <+>
hsep (map ppr (nub [unLoc (ideclName d) | IIDecl d <- imports]))
rev_imports = reverse imports -- rightmost are the most recent
modules_bit =
hsep [ char '*' <> ppr (GHC.moduleName m)
| IIModule m <- rev_imports ] <+>
hsep (map ppr [ myIdeclName d | IIDecl d <- rev_imports ])
-- use the 'as' name if there is one
myIdeclName d | Just m <- ideclAs d = m
| otherwise = unLoc (ideclName d)
deflt_prompt = dots <> context_bit <> modules_bit
......@@ -724,7 +726,7 @@ runStmt stmt step
| null (filter (not.isSpace) stmt)
= return False
| "import " `isPrefixOf` stmt
= do newContextCmd (Import stmt); return False
= do addImportToContext stmt; return False
| otherwise
= do -- In the new IO library, read handles buffer data even if the Handle
-- is set to NoBuffering. This causes problems for GHCi where there
......@@ -823,6 +825,9 @@ specialCommand str = do
++ shortHelpText)
return False
shellEscape :: String -> GHCi Bool
shellEscape str = liftIO (system str >> return False)
lookupCommand :: String -> GHCi (MaybeCommand)
lookupCommand "" = do
st <- getGHCiState
......@@ -882,7 +887,10 @@ getCurrentBreakModule = do
return $ Just $ GHC.getHistoryModule hist
-----------------------------------------------------------------------------
--
-- Commands
--
-----------------------------------------------------------------------------
noArgs :: GHCi () -> String -> GHCi ()
noArgs m "" = m
......@@ -896,9 +904,15 @@ withSandboxOnly cmd this = do
ptext (sLit "is not supported with -fno-ghci-sandbox"))
else this
-----------------------------------------------------------------------------
-- :help
help :: String -> GHCi ()
help _ = liftIO (putStr helpText)
-----------------------------------------------------------------------------
-- :info
info :: String -> InputT GHCi ()
info "" = ghcError (CmdLineError "syntax: ':i <thing-you-want-info-about>'")
info s = handleSourceError GHC.printException $
......@@ -938,6 +952,9 @@ pprInfo pefas (thing, fixity, insts)
| fix == GHC.defaultFixity = empty
| otherwise = ppr fix <+> ppr (GHC.getName thing)
-----------------------------------------------------------------------------
-- :main
runMain :: String -> GHCi ()
runMain s = case toArgs s of
Left err -> liftIO (hPutStrLn stderr err)
......@@ -947,6 +964,9 @@ runMain s = case toArgs s of
Nothing -> doWithArgs args "main"
Just f -> doWithArgs args f
-----------------------------------------------------------------------------
-- :run
runRun :: String -> GHCi ()
runRun s = case toCmdArgs s of
Left err -> liftIO (hPutStrLn stderr err)
......@@ -956,17 +976,8 @@ doWithArgs :: [String] -> String -> GHCi ()
doWithArgs args cmd = enqueueCommands ["System.Environment.withArgs " ++
show args ++ " (" ++ cmd ++ ")"]
addModule :: [FilePath] -> InputT GHCi ()
addModule files = do
lift revertCAFs -- always revert CAFs on load/add.
files <- mapM expandPath files
targets <- mapM (\m -> GHC.guessTarget m Nothing) files
-- remove old targets with the same id; e.g. for :add *M
mapM_ GHC.removeTarget [ tid | Target tid _ _ <- targets ]
mapM_ GHC.addTarget targets
prev_context <- GHC.getContext
ok <- trySuccess $ GHC.load LoadAllTargets
afterLoad ok False prev_context
-----------------------------------------------------------------------------
-- :cd
changeDirectory :: String -> InputT GHCi ()
changeDirectory "" = do
......@@ -979,10 +990,9 @@ changeDirectory dir = do
graph <- GHC.getModuleGraph
when (not (null graph)) $
liftIO $ putStrLn "Warning: changing directory causes all loaded modules to be unloaded,\nbecause the search path has changed."
prev_context <- GHC.getContext
GHC.setTargets []
_ <- GHC.load LoadAllTargets
lift $ setContextAfterLoad prev_context False []
lift $ setContextAfterLoad False []
GHC.workingDirectoryChanged
dir <- expandPath dir
liftIO $ setCurrentDirectory dir
......@@ -993,6 +1003,9 @@ trySuccess act =
return Failed) $ do
act
-----------------------------------------------------------------------------
-- :edit
editFile :: String -> GHCi ()
editFile str =
do file <- if null str then chooseEditFile else return str
......@@ -1035,6 +1048,10 @@ chooseEditFile =
where fromTarget (GHC.Target (GHC.TargetFile f _) _ _) = Just f
fromTarget _ = Nothing -- when would we get a module target?
-----------------------------------------------------------------------------
-- :def
defineMacro :: Bool{-overwrite-} -> String -> GHCi ()
defineMacro _ (':':_) =
liftIO $ putStrLn "macro name cannot start with a colon"
......@@ -1075,6 +1092,10 @@ runMacro fun s = do
enqueueCommands (lines str)
return False
-----------------------------------------------------------------------------
-- :undef
undefineMacro :: String -> GHCi ()
undefineMacro str = mapM_ undef (words str)
where undef macro_name = do
......@@ -1085,6 +1106,10 @@ undefineMacro str = mapM_ undef (words str)
else do
liftIO (writeIORef macros_ref (filter ((/= macro_name) . cmdName) cmds))
-----------------------------------------------------------------------------
-- :cmd
cmdCmd :: String -> GHCi ()
cmdCmd str = do
let expr = '(' : str ++ ") :: IO String"
......@@ -1095,8 +1120,31 @@ cmdCmd str = do
enqueueCommands (lines cmds)
return ()
loadModuleName :: GHC.GhcMonad m => ImportDecl RdrName -> m Module
loadModuleName = flip GHC.findModule Nothing . unLoc . ideclName
-----------------------------------------------------------------------------
-- :check
checkModule :: String -> InputT GHCi ()
checkModule m = do
let modl = GHC.mkModuleName m
ok <- handleSourceError (\e -> GHC.printException e >> return False) $ do
r <- GHC.typecheckModule =<< GHC.parseModule =<< GHC.getModSummary modl
liftIO $ putStrLn $ showSDoc $
case GHC.moduleInfo r of
cm | Just scope <- GHC.modInfoTopLevelScope cm ->
let
(local,global) = ASSERT( all isExternalName scope )
partition ((== modl) . GHC.moduleName . GHC.nameModule) scope
in
(text "global names: " <+> ppr global) $$
(text "local names: " <+> ppr local)
_ -> empty
return True
afterLoad (successIf ok) False
-----------------------------------------------------------------------------
-- :load, :add, :reload
loadModule :: [(FilePath, Maybe Phase)] -> InputT GHCi SuccessFlag
loadModule fs = timeIt (loadModule' fs)
......@@ -1106,8 +1154,6 @@ loadModule_ fs = loadModule (zip fs (repeat Nothing)) >> return ()
loadModule' :: [(FilePath, Maybe Phase)] -> InputT GHCi SuccessFlag
loadModule' files = do
prev_context <- GHC.getContext
-- unload first
_ <- GHC.abandonAll
lift discardActiveBreakPoints
......@@ -1125,59 +1171,58 @@ loadModule' files = do
-- as a ToDo for now.
GHC.setTargets targets
doLoad False prev_context LoadAllTargets
doLoad False LoadAllTargets
-- :add
addModule :: [FilePath] -> InputT GHCi ()
addModule files = do
lift revertCAFs -- always revert CAFs on load/add.
files <- mapM expandPath files
targets <- mapM (\m -> GHC.guessTarget m Nothing) files
-- remove old targets with the same id; e.g. for :add *M
mapM_ GHC.removeTarget [ tid | Target tid _ _ <- targets ]
mapM_ GHC.addTarget targets
_ <- doLoad False LoadAllTargets
return ()
checkModule :: String -> InputT GHCi ()
checkModule m = do
let modl = GHC.mkModuleName m
prev_context <- GHC.getContext
ok <- handleSourceError (\e -> GHC.printException e >> return False) $ do
r <- GHC.typecheckModule =<< GHC.parseModule =<< GHC.getModSummary modl
liftIO $ putStrLn $ showSDoc $
case GHC.moduleInfo r of
cm | Just scope <- GHC.modInfoTopLevelScope cm ->
let
(local,global) = ASSERT( all isExternalName scope )
partition ((== modl) . GHC.moduleName . GHC.nameModule) scope
in
(text "global names: " <+> ppr global) $$
(text "local names: " <+> ppr local)
_ -> empty
return True
afterLoad (successIf ok) False prev_context
-- :reload
reloadModule :: String -> InputT GHCi ()
reloadModule m = do
prev_context <- GHC.getContext
_ <- doLoad True prev_context $
_ <- doLoad True $
if null m then LoadAllTargets
else LoadUpTo (GHC.mkModuleName m)
return ()
doLoad :: Bool -> [InteractiveImport] -> LoadHowMuch -> InputT GHCi SuccessFlag
doLoad retain_context prev_context howmuch = do
doLoad :: Bool -> LoadHowMuch -> InputT GHCi SuccessFlag
doLoad retain_context howmuch = do
-- turn off breakpoints before we load: we can't turn them off later, because
-- the ModBreaks will have gone away.
lift discardActiveBreakPoints
ok <- trySuccess $ GHC.load howmuch
afterLoad ok retain_context prev_context
afterLoad ok retain_context
return ok
afterLoad :: SuccessFlag -> Bool -> [InteractiveImport] -> InputT GHCi ()
afterLoad ok retain_context prev_context = do
afterLoad :: SuccessFlag
-> Bool -- keep the remembered_ctx, as far as possible (:reload)
-> InputT GHCi ()
afterLoad ok retain_context = do
lift revertCAFs -- always revert CAFs on load.
lift discardTickArrays
loaded_mod_summaries <- getLoadedModules
let loaded_mods = map GHC.ms_mod loaded_mod_summaries
loaded_mod_names = map GHC.moduleName loaded_mods
modulesLoadedMsg ok loaded_mod_names
lift $ setContextAfterLoad retain_context loaded_mod_summaries
lift $ setContextAfterLoad prev_context retain_context loaded_mod_summaries
setContextAfterLoad :: [InteractiveImport] -> Bool -> [GHC.ModSummary] -> GHCi ()
setContextAfterLoad prev keep_ctxt [] = do
setContextKeepingPackageModules prev keep_ctxt []
setContextAfterLoad prev keep_ctxt ms = do
setContextAfterLoad :: Bool -> [GHC.ModSummary] -> GHCi ()
setContextAfterLoad keep_ctxt [] = do
setContextKeepingPackageModules keep_ctxt []
setContextAfterLoad keep_ctxt ms = do
-- load a target if one is available, otherwise load the topmost module.
targets <- GHC.getTargets
case [ m | Just m <- map (findTarget ms) targets ] of
......@@ -1200,46 +1245,43 @@ setContextAfterLoad prev keep_ctxt ms = do
= False
load_this summary | m <- GHC.ms_mod summary = do
b <- GHC.moduleIsInterpreted m
if b then setContextKeepingPackageModules prev keep_ctxt [IIModule m]
else do
setContextKeepingPackageModules prev keep_ctxt
[IIDecl $ simpleImportDecl (GHC.moduleName m)]
is_interp <- GHC.moduleIsInterpreted m
let new_ctx | is_interp = [IIModule m]
| otherwise = [IIDecl $ simpleImportDecl (GHC.moduleName m)]
setContextKeepingPackageModules keep_ctxt new_ctx
-- | Keep any package modules (except Prelude) when changing the context.
setContextKeepingPackageModules
:: [InteractiveImport] -- previous context
-> Bool -- re-execute :module commands
-> [InteractiveImport] -- new context