+{-# OPTIONS -fno-cse #-}
+-- -fno-cse is needed for GLOBAL_VAR's to behave properly
+
{-# OPTIONS -#include "Linker.h" #-}
-----------------------------------------------------------------------------
--
#include "HsVersions.h"
-import GhciMonad
+import qualified GhciMonad
+import GhciMonad hiding (runStmt)
import GhciTags
import Debugger
-- The GHC interface
-import qualified GHC
-import GHC ( Session, LoadHowMuch(..), Target(..), TargetId(..),
+import qualified GHC hiding (resume, runStmt)
+import GHC ( LoadHowMuch(..), Target(..), TargetId(..),
Module, ModuleName, TyThing(..), Phase,
- BreakIndex, SrcSpan, Resume, SingleStep, Id )
+ BreakIndex, SrcSpan, Resume, SingleStep,
+ Ghc, handleSourceError )
import PprTyThing
import DynFlags
import Packages
-#ifdef USE_READLINE
+#ifdef USE_EDITLINE
import PackageConfig
import UniqFM
#endif
-import HscTypes ( implicitTyThings )
+import HscTypes ( implicitTyThings, reflectGhc, reifyGhc
+ , handleFlagWarnings )
import qualified RdrName ( getGRE_NameQualifier_maybes ) -- should this come via GHC?
-import Outputable hiding (printForUser)
+import Outputable hiding (printForUser, printForUserPartWay)
import Module -- for ModuleEnv
import Name
import SrcLoc
-- Other random utilities
+import CmdLineParser
import Digraph
import BasicTypes hiding (isTopLevel)
import Panic hiding (showException)
import Linker
import Util
import NameSet
-import Maybes ( orElse )
+import Maybes ( orElse, expectJust )
import FastString
import Encoding
+import MonadUtils ( liftIO )
#ifndef mingw32_HOST_OS
import System.Posix hiding (getEnv)
import qualified System.Win32
#endif
-#ifdef USE_READLINE
+#ifdef USE_EDITLINE
import Control.Concurrent ( yield ) -- Used in readline loop
-import System.Console.Readline as Readline
+import System.Console.Editline.Readline as Readline
#endif
--import SystemExts
-import Control.Exception as Exception
+import Exception
-- import Control.Concurrent
+import System.FilePath
import qualified Data.ByteString.Char8 as BS
import Data.List
import Data.Maybe
import System.IO
import System.IO.Error as IO
import Data.Char
-import Data.Dynamic
import Data.Array
import Control.Monad as Monad
import Text.Printf
import Foreign
-import Foreign.C ( withCStringLen )
+import Foreign.C
import GHC.Exts ( unsafeCoerce# )
import GHC.IOBase ( IOErrorType(InvalidArgument) )
+import GHC.TopHandler
import Data.IORef ( IORef, readIORef, writeIORef )
-#ifdef USE_READLINE
-import System.Posix.Internals ( setNonBlockingFD )
-#endif
-
-----------------------------------------------------------------------------
ghciWelcomeMsg :: String
ghciWelcomeMsg = "GHCi, version " ++ cProjectVersion ++
": http://www.haskell.org/ghc/ :? for help"
-type Command = (String, String -> GHCi Bool, Bool, String -> IO [String])
-
cmdName :: Command -> String
cmdName (n,_,_,_) = n
-macros_ref :: IORef [Command]
GLOBAL_VAR(macros_ref, [], [Command])
builtin_commands :: [Command]
builtin_commands = [
-- Hugs users are accustomed to :e, so make sure it doesn't overlap
- ("?", keepGoing help, False, completeNone),
- ("add", keepGoingPaths addModule, False, completeFilename),
- ("abandon", keepGoing abandonCmd, False, completeNone),
- ("break", keepGoing breakCmd, False, completeIdentifier),
- ("back", keepGoing backCmd, False, completeNone),
- ("browse", keepGoing (browseCmd False), False, completeModule),
- ("browse!", keepGoing (browseCmd True), False, completeModule),
- ("cd", keepGoing changeDirectory, False, completeFilename),
- ("check", keepGoing checkModule, False, completeHomeModule),
- ("continue", keepGoing continueCmd, False, completeNone),
- ("cmd", keepGoing cmdCmd, False, completeIdentifier),
- ("ctags", keepGoing createCTagsFileCmd, False, completeFilename),
- ("def", keepGoing (defineMacro False), False, completeIdentifier),
- ("def!", keepGoing (defineMacro True), False, completeIdentifier),
- ("delete", keepGoing deleteCmd, False, completeNone),
- ("e", keepGoing editFile, False, completeFilename),
- ("edit", keepGoing editFile, False, completeFilename),
- ("etags", keepGoing createETagsFileCmd, False, completeFilename),
- ("force", keepGoing forceCmd, False, completeIdentifier),
- ("forward", keepGoing forwardCmd, False, completeNone),
- ("help", keepGoing help, False, completeNone),
- ("history", keepGoing historyCmd, False, completeNone),
- ("info", keepGoing info, False, completeIdentifier),
- ("kind", keepGoing kindOfType, False, completeIdentifier),
- ("load", keepGoingPaths loadModule_, False, completeHomeModuleOrFile),
- ("list", keepGoing listCmd, False, completeNone),
- ("module", keepGoing setContext, False, completeModule),
- ("main", keepGoing runMain, False, completeIdentifier),
- ("print", keepGoing printCmd, False, completeIdentifier),
- ("quit", quit, False, completeNone),
- ("reload", keepGoing reloadModule, False, completeNone),
- ("set", keepGoing setCmd, True, completeSetOptions),
- ("show", keepGoing showCmd, False, completeNone),
- ("sprint", keepGoing sprintCmd, False, completeIdentifier),
- ("step", keepGoing stepCmd, False, completeIdentifier),
- ("steplocal", keepGoing stepLocalCmd, False, completeIdentifier),
- ("stepmodule",keepGoing stepModuleCmd, False, completeIdentifier),
- ("type", keepGoing typeOfExpr, False, completeIdentifier),
- ("trace", keepGoing traceCmd, False, completeIdentifier),
- ("undef", keepGoing undefineMacro, False, completeMacro),
- ("unset", keepGoing unsetOptions, True, completeSetOptions)
+ ("?", keepGoing help, Nothing, completeNone),
+ ("add", keepGoingPaths addModule, Just filenameWordBreakChars, completeFilename),
+ ("abandon", keepGoing abandonCmd, Nothing, completeNone),
+ ("break", keepGoing breakCmd, Nothing, completeIdentifier),
+ ("back", keepGoing backCmd, Nothing, completeNone),
+ ("browse", keepGoing (browseCmd False), Nothing, completeModule),
+ ("browse!", keepGoing (browseCmd True), Nothing, completeModule),
+ ("cd", keepGoing changeDirectory, Just filenameWordBreakChars, completeFilename),
+ ("check", keepGoing checkModule, Nothing, completeHomeModule),
+ ("continue", keepGoing continueCmd, Nothing, completeNone),
+ ("cmd", keepGoing cmdCmd, Nothing, completeIdentifier),
+ ("ctags", keepGoing createCTagsFileCmd, Just filenameWordBreakChars, completeFilename),
+ ("def", keepGoing (defineMacro False), Nothing, completeIdentifier),
+ ("def!", keepGoing (defineMacro True), Nothing, completeIdentifier),
+ ("delete", keepGoing deleteCmd, Nothing, completeNone),
+ ("e", keepGoing editFile, Just filenameWordBreakChars, completeFilename),
+ ("edit", keepGoing editFile, Just filenameWordBreakChars, completeFilename),
+ ("etags", keepGoing createETagsFileCmd, Just filenameWordBreakChars, completeFilename),
+ ("force", keepGoing forceCmd, Nothing, completeIdentifier),
+ ("forward", keepGoing forwardCmd, Nothing, completeNone),
+ ("help", keepGoing help, Nothing, completeNone),
+ ("history", keepGoing historyCmd, Nothing, completeNone),
+ ("info", keepGoing info, Nothing, completeIdentifier),
+ ("kind", keepGoing kindOfType, Nothing, completeIdentifier),
+ ("load", keepGoingPaths loadModule_, Just filenameWordBreakChars, completeHomeModuleOrFile),
+ ("list", keepGoing listCmd, Nothing, completeNone),
+ ("module", keepGoing setContext, Nothing, completeModule),
+ ("main", keepGoing runMain, Nothing, completeIdentifier),
+ ("print", keepGoing printCmd, Nothing, completeIdentifier),
+ ("quit", quit, Nothing, completeNone),
+ ("reload", keepGoing reloadModule, Nothing, completeNone),
+ ("run", keepGoing runRun, Nothing, completeIdentifier),
+ ("set", keepGoing setCmd, Just flagWordBreakChars, completeSetOptions),
+ ("show", keepGoing showCmd, Nothing, completeShowOptions),
+ ("sprint", keepGoing sprintCmd, Nothing, completeIdentifier),
+ ("step", keepGoing stepCmd, Nothing, completeIdentifier),
+ ("steplocal", keepGoing stepLocalCmd, Nothing, completeIdentifier),
+ ("stepmodule",keepGoing stepModuleCmd, Nothing, completeIdentifier),
+ ("type", keepGoing typeOfExpr, Nothing, completeIdentifier),
+ ("trace", keepGoing traceCmd, Nothing, completeIdentifier),
+ ("undef", keepGoing undefineMacro, Nothing, completeMacro),
+ ("unset", keepGoing unsetOptions, Just flagWordBreakChars, completeSetOptions)
]
+
+-- We initialize readline (in the interactiveUI function) to use
+-- word_break_chars as the default set of completion word break characters.
+-- This can be overridden for a particular command (for example, filename
+-- expansion shouldn't consider '/' to be a word break) by setting the third
+-- entry in the Command tuple above.
+--
+-- NOTE: in order for us to override the default correctly, any custom entry
+-- must be a SUBSET of word_break_chars.
+#ifdef USE_EDITLINE
+word_break_chars :: String
+word_break_chars = let symbols = "!#$%&*+/<=>?@\\^|-~"
+ specials = "(),;[]`{}"
+ spaces = " \t\n"
+ in spaces ++ specials ++ symbols
+#endif
+
+flagWordBreakChars, filenameWordBreakChars :: String
+flagWordBreakChars = " \t\n"
+filenameWordBreakChars = " \t\n\\`@$><=;|&{(" -- bash defaults
+
+
keepGoing :: (String -> GHCi ()) -> (String -> GHCi Bool)
keepGoing a str = a str >> return False
keepGoingPaths :: ([FilePath] -> GHCi ()) -> (String -> GHCi Bool)
-keepGoingPaths a str = a (toArgs str) >> return False
+keepGoingPaths a str
+ = do case toArgs str of
+ Left err -> io (hPutStrLn stderr err)
+ Right args -> a args
+ return False
shortHelpText :: String
shortHelpText = "use :? for help.\n"
" Commands available from the prompt:\n" ++
"\n" ++
" <statement> evaluate/run <statement>\n" ++
+ " : repeat last command\n" ++
" :{\\n ..lines.. \\n:}\\n multiline command\n" ++
- " :add <filename> ... add module(s) to the current target set\n" ++
- " :browse[!] [-s] [[*]<mod>] display the names defined by module <mod>\n" ++
- " (!: more details; -s: sort; *: all top-level names)\n" ++
+ " :add [*]<module> ... add module(s) to the current target set\n" ++
+ " :browse[!] [[*]<mod>] display the names defined by module <mod>\n" ++
+ " (!: more details; *: all top-level names)\n" ++
" :cd <dir> change directory to <dir>\n" ++
" :cmd <expr> run the commands returned by <expr>::IO String\n" ++
" :ctags [<file>] create tags file for Vi (default: \"tags\")\n" ++
" :help, :? display this list of commands\n" ++
" :info [<name> ...] display information about the given names\n" ++
" :kind <type> show the kind of <type>\n" ++
- " :load <filename> ... load module(s) and their dependents\n" ++
+ " :load [*]<module> ... load module(s) and their dependents\n" ++
" :main [<arguments> ...] run the main function with the given arguments\n" ++
" :module [+/-] [*]<mod> ... set the context for expression evaluation\n" ++
" :quit exit GHCi\n" ++
" :reload reload the current module set\n" ++
+ " :run function [<arguments> ...] run the function with the given arguments\n" ++
" :type <expr> show the type of <expr>\n" ++
" :undef <cmd> undefine user-defined command :<cmd>\n" ++
" :!<command> run the shell command <command>\n" ++
" :delete * delete all breakpoints\n" ++
" :force <expr> print <expr>, forcing unevaluated parts\n" ++
" :forward go forward in the history (after :back)\n" ++
- " :history [<n>] show the last <n> items in the history (after :trace)\n" ++
+ " :history [<n>] after :trace, show the execution history\n" ++
+ " :list show the source code around current breakpoint\n" ++
+ " :list identifier show the source code for <identifier>\n" ++
+ " :list [<module>] <line> show the source code around line number <line>\n" ++
" :print [<name> ...] prints a value without forcing its computation\n" ++
" :sprint [<name> ...] simplifed version of :print\n" ++
" :step single-step after stopping at a breakpoint\n"++
" :step <expr> single-step into <expr>\n"++
- " :steplocal single-step restricted to the current top level decl.\n"++
+ " :steplocal single-step within the current top-level binding\n"++
" :stepmodule single-step restricted to the current module\n"++
" :trace trace after stopping at a breakpoint\n"++
- " :trace <expr> trace into <expr> (remembers breakpoints for :history)\n"++
+ " :trace <expr> evaluate <expr> with tracing on (see :history)\n"++
"\n" ++
" -- Commands for changing settings:\n" ++
" :set prog <progname> set the value returned by System.getProgName\n" ++
" :set prompt <prompt> set the prompt used in GHCi\n" ++
" :set editor <cmd> set the command used for :edit\n" ++
- " :set stop <cmd> set the command to run when a breakpoint is hit\n" ++
+ " :set stop [<n>] <cmd> set the command to run when a breakpoint is hit\n" ++
" :unset <option> ... unset options\n" ++
"\n" ++
" Options for ':set' and ':unset':\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" ++
- " :show <setting> show anything that can be set with :set (e.g. args)\n" ++
+ " :show <setting> show value of <setting>, which is one of\n" ++
+ " [args, prog, prompt, editor, stop]\n" ++
"\n"
findEditor :: IO String
getEnv "EDITOR"
`IO.catch` \_ -> do
#if mingw32_HOST_OS
- win <- System.Win32.getWindowsDirectory
- return (win `joinFileName` "notepad.exe")
+ win <- System.Win32.getWindowsDirectory
+ return (win </> "notepad.exe")
#else
- return ""
+ return ""
#endif
-interactiveUI :: Session -> [(FilePath, Maybe Phase)] -> Maybe String -> IO ()
-interactiveUI session srcs maybe_expr = do
+interactiveUI :: [(FilePath, Maybe Phase)] -> Maybe [String]
+ -> Ghc ()
+interactiveUI srcs maybe_exprs = withTerminalReset $ do
-- HACK! If we happen to get into an infinite loop (eg the user
-- types 'let x=x in x' at the prompt), then the thread will block
-- on a blackhole, and become unreachable during GC. The GC will
-- it refers to might be finalized, including the standard Handles.
-- This sounds like a bug, but we don't have a good solution right
-- now.
- newStablePtr stdin
- newStablePtr stdout
- newStablePtr stderr
+ liftIO $ newStablePtr stdin
+ liftIO $ newStablePtr stdout
+ liftIO $ newStablePtr stderr
-- Initialise buffering for the *interpreted* I/O system
- initInterpBuffering session
+ initInterpBuffering
- when (isNothing maybe_expr) $ do
+ liftIO $ when (isNothing maybe_exprs) $ do
-- Only for GHCi (not runghc and ghc -e):
-- Turn buffering off for the compiled program's stdout/stderr
-- intended for the program, so unbuffer stdin.
hSetBuffering stdin NoBuffering
- -- initial context is just the Prelude
- prel_mod <- GHC.findModule session (GHC.mkModuleName "Prelude")
- (Just basePackageId)
- GHC.setContext session [] [prel_mod]
-
-#ifdef USE_READLINE
- Readline.initialize
- Readline.setAttemptedCompletionFunction (Just completeWord)
- --Readline.parseAndBind "set show-all-if-ambiguous 1"
-
- let symbols = "!#$%&*+/<=>?@\\^|-~"
- specials = "(),;[]`{}"
- spaces = " \t\n"
- word_break_chars = spaces ++ specials ++ symbols
-
- Readline.setBasicWordBreakCharacters word_break_chars
- Readline.setCompleterWordBreakCharacters word_break_chars
+#ifdef USE_EDITLINE
+ is_tty <- hIsTerminalDevice stdin
+ when is_tty $ withReadline $ do
+ Readline.initialize
+
+ withGhcAppData
+ (\dir -> Readline.readHistory (dir </> "ghci_history"))
+ (return True)
+
+ Readline.setAttemptedCompletionFunction (Just completeWord)
+ --Readline.parseAndBind "set show-all-if-ambiguous 1"
+
+ Readline.setBasicWordBreakCharacters word_break_chars
+ Readline.setCompleterWordBreakCharacters word_break_chars
+ Readline.setCompletionAppendCharacter Nothing
#endif
- default_editor <- findEditor
+ -- initial context is just the Prelude
+ prel_mod <- GHC.findModule (GHC.mkModuleName "Prelude") Nothing
+ GHC.setContext [] [prel_mod]
- startGHCi (runGHCi srcs maybe_expr)
- GHCiState{ progname = "<interactive>",
- args = [],
+ default_editor <- liftIO $ findEditor
+
+ startGHCi (runGHCi srcs maybe_exprs)
+ GHCiState{ progname = "<interactive>",
+ args = [],
prompt = "%s> ",
stop = "",
- editor = default_editor,
- session = session,
- options = [],
+ editor = default_editor,
+-- session = session,
+ options = [],
prelude = prel_mod,
break_ctr = 0,
breaks = [],
tickarrays = emptyModuleEnv,
- cmdqueue = []
+ last_command = Nothing,
+ cmdqueue = [],
+ remembered_ctx = [],
+ ghc_e = isJust maybe_exprs
}
-#ifdef USE_READLINE
- Readline.resetTerminal Nothing
+#ifdef USE_EDITLINE
+ liftIO $ do
+ Readline.stifleHistory 100
+ withGhcAppData (\dir -> Readline.writeHistory (dir </> "ghci_history"))
+ (return True)
+ Readline.resetTerminal Nothing
#endif
return ()
-runGHCi :: [(FilePath, Maybe Phase)] -> Maybe String -> GHCi ()
-runGHCi paths maybe_expr = do
- let read_dot_files = not opt_IgnoreDotGhci
+withGhcAppData :: (FilePath -> IO a) -> IO a -> IO a
+withGhcAppData right left = do
+ either_dir <- IO.try (getAppUserDataDirectory "ghc")
+ case either_dir of
+ Right dir -> right dir
+ _ -> left
+
+-- libedit doesn't always restore the terminal settings correctly (as of at
+-- least 07/12/2008); see trac #2691. Work around this by manually resetting
+-- the terminal outselves.
+withTerminalReset :: Ghc () -> Ghc ()
+#ifdef mingw32_HOST_OS
+withTerminalReset = id
+#else
+withTerminalReset f = do
+ isTTY <- liftIO $ hIsTerminalDevice stdout
+ if not isTTY
+ then f
+ else gbracket (liftIO $ getTerminalAttributes stdOutput)
+ (\attrs -> liftIO $ setTerminalAttributes stdOutput attrs Immediately)
+ (const f)
+#endif
- when (read_dot_files) $ do
- -- Read in ./.ghci.
- let file = "./.ghci"
- exists <- io (doesFileExist file)
- when exists $ do
- dir_ok <- io (checkPerms ".")
- file_ok <- io (checkPerms file)
+runGHCi :: [(FilePath, Maybe Phase)] -> Maybe [String] -> GHCi ()
+runGHCi paths maybe_exprs = do
+ let
+ read_dot_files = not opt_IgnoreDotGhci
+
+ current_dir = return (Just ".ghci")
+
+ app_user_dir = io $ withGhcAppData
+ (\dir -> return (Just (dir </> "ghci.conf")))
+ (return Nothing)
+
+ home_dir = do
+ either_dir <- io $ IO.try (getEnv "HOME")
+ case either_dir of
+ Right home -> return (Just (home </> ".ghci"))
+ _ -> return Nothing
+
+ sourceConfigFile :: FilePath -> GHCi ()
+ sourceConfigFile file = do
+ exists <- io $ doesFileExist file
+ when exists $ do
+ dir_ok <- io $ checkPerms (getDirectory file)
+ file_ok <- io $ checkPerms file
when (dir_ok && file_ok) $ do
- either_hdl <- io (IO.try (openFile "./.ghci" ReadMode))
- case either_hdl of
- Left _e -> return ()
- Right hdl -> runCommands (fileLoop hdl False False)
-
+ either_hdl <- io $ IO.try (openFile file ReadMode)
+ case either_hdl of
+ Left _e -> return ()
+ Right hdl -> runCommands (fileLoop hdl False False)
+ where
+ getDirectory f = case takeDirectory f of "" -> "."; d -> d
+
when (read_dot_files) $ do
- -- Read in $HOME/.ghci
- either_dir <- io (IO.try getHomeDirectory)
- case either_dir of
- Left _e -> return ()
- Right dir -> do
- cwd <- io (getCurrentDirectory)
- when (dir /= cwd) $ do
- let file = dir ++ "/.ghci"
- ok <- io (checkPerms file)
- when ok $ do
- either_hdl <- io (IO.try (openFile file ReadMode))
- case either_hdl of
- Left _e -> return ()
- Right hdl -> runCommands (fileLoop hdl False False)
+ cfgs0 <- sequence [ current_dir, app_user_dir, home_dir ]
+ cfgs <- io $ mapM canonicalizePath (catMaybes cfgs0)
+ mapM_ sourceConfigFile (nub cfgs)
+ -- nub, because we don't want to read .ghci twice if the
+ -- CWD is $HOME.
-- Perform a :load for files given on the GHCi command line
-- When in -e mode, if the load fails then we want to stop
-- immediately rather than going on to evaluate the expression.
when (not (null paths)) $ do
- ok <- ghciHandle (\e -> do showException e; return Failed) $
- loadModule paths
- when (isJust maybe_expr && failed ok) $
- io (exitWith (ExitFailure 1))
+ ok <- ghciHandle (\e -> do showException e; return Failed) $
+ loadModule paths
+ when (isJust maybe_exprs && failed ok) $
+ io (exitWith (ExitFailure 1))
-- if verbosity is greater than 0, or we are connected to a
-- terminal, display the prompt in the interactive loop.
dflags <- getDynFlags
let show_prompt = verbosity dflags > 0 || is_tty
- case maybe_expr of
+ case maybe_exprs of
Nothing ->
do
#if defined(mingw32_HOST_OS)
#endif
-- enter the interactive loop
interactiveLoop is_tty show_prompt
- Just expr -> do
+ Just exprs -> do
-- just evaluate the expression we were given
- runCommandEval expr
- return ()
+ enqueueCommands exprs
+ let handle e = do st <- getGHCiState
+ -- Jump through some hoops to get the
+ -- current progname in the exception text:
+ -- <progname>: <exception>
+ io $ withProgName (progname st)
+ -- this used to be topHandlerFastExit, see #2228
+ $ topHandler e
+ runCommands' handle (return Nothing)
-- and finally, exit
io $ do when (verbosity dflags > 0) $ putStrLn "Leaving GHCi."
-
interactiveLoop :: Bool -> Bool -> GHCi ()
interactiveLoop is_tty show_prompt =
-- Ignore ^C exceptions caught here
- ghciHandleDyn (\e -> case e of
+ ghciHandleGhcException (\e -> case e of
Interrupted -> do
#if defined(mingw32_HOST_OS)
io (putStrLn "")
-- exception handler above.
-- read commands from stdin
-#ifdef USE_READLINE
+#ifdef USE_EDITLINE
if (is_tty)
then runCommands readlineLoop
else runCommands (fileLoop stdin show_prompt is_tty)
return True
#else
checkPerms name =
- Util.handle (\_ -> return False) $ do
+ handleIO (\_ -> return False) $ do
st <- getFileStatus name
me <- getRealUserID
if fileOwner st /= me then do
mkPrompt :: GHCi String
mkPrompt = do
- session <- getSession
- (toplevs,exports) <- io (GHC.getContext session)
- resumes <- io $ GHC.getResumeContext session
+ (toplevs,exports) <- GHC.getContext
+ resumes <- GHC.getResumeContext
+ -- st <- getGHCiState
context_bit <-
case resumes of
then return (brackets (ppr (GHC.resumeSpan r)) <> space)
else do
let hist = GHC.resumeHistory r !! (ix-1)
- span <- io$ GHC.getHistorySpan session hist
+ span <- GHC.getHistorySpan hist
return (brackets (ppr (negate ix) <> char ':'
<+> ppr span) <> space)
let
dots | _:rs <- resumes, not (null rs) = text "... "
| otherwise = empty
+
+
modules_bit =
- hsep (map (\m -> char '*' <> ppr (GHC.moduleName m)) toplevs) <+>
+ -- 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 (map (\m -> char '*' <> ppr (GHC.moduleName m)) toplevs) <+>
hsep (map (ppr . GHC.moduleName) exports)
deflt_prompt = dots <> context_bit <> modules_bit
return (showSDoc (f (prompt st)))
-#ifdef USE_READLINE
+#ifdef USE_EDITLINE
readlineLoop :: GHCi (Maybe String)
readlineLoop = do
io yield
saveSession -- for use by completion
prompt <- mkPrompt
- l <- io (readline prompt `finally` setNonBlockingFD 0)
- -- readline sometimes puts stdin into blocking mode,
- -- so we need to put it back for the IO library
+ l <- io $ withReadline (readline prompt)
splatSavedSession
case l of
Nothing -> return Nothing
+ Just "" -> return (Just "") -- Don't put empty lines in the history
Just l -> do
io (addHistory l)
str <- io $ consoleInputToUnicode True l
return (Just str)
+
+withReadline :: IO a -> IO a
+withReadline = bracket_ stopTimer startTimer
+ -- editline doesn't handle some of its system calls returning
+ -- EINTR, so our timer signal confuses it, hence we turn off
+ -- the timer signal when making calls to editline. (#2277)
+ -- If editline is ever fixed, we can remove this.
+
+-- These come from the RTS
+foreign import ccall unsafe startTimer :: IO ()
+foreign import ccall unsafe stopTimer :: IO ()
#endif
queryQueue :: GHCi (Maybe String)
return (Just c)
runCommands :: GHCi (Maybe String) -> GHCi ()
-runCommands getCmd = do
+runCommands = runCommands' handler
+
+runCommands' :: (SomeException -> GHCi Bool) -- Exception handler
+ -> GHCi (Maybe String) -> GHCi ()
+runCommands' eh getCmd = do
mb_cmd <- noSpace queryQueue
mb_cmd <- maybe (noSpace getCmd) (return . Just) mb_cmd
case mb_cmd of
Nothing -> return ()
Just c -> do
- b <- ghciHandle handler (doCommand c)
- if b then return () else runCommands getCmd
+ b <- ghciHandle eh $
+ handleSourceError printErrorAndKeepGoing
+ (doCommand c)
+ if b then return () else runCommands' eh getCmd
where
+ printErrorAndKeepGoing err = do
+ GHC.printExceptionAndWarnings err
+ return False
+
noSpace q = q >>= maybe (return Nothing)
(\c->case removeSpaces c of
"" -> noSpace q
setGHCiState st{ cmdqueue = cmds ++ cmdqueue st }
--- This version is for the GHC command-line option -e. The only difference
--- from runCommand is that it catches the ExitException exception and
--- exits, rather than printing out the exception.
-runCommandEval :: String -> GHCi Bool
-runCommandEval c = ghciHandle handleEval (doCommand c)
- where
- handleEval (ExitException code) = io (exitWith code)
- handleEval e = do handler e
- io (exitWith (ExitFailure 1))
-
- doCommand (':' : command) = specialCommand command
- doCommand stmt
- = do r <- runStmt stmt GHC.RunToCompletion
- case r of
- False -> io (exitWith (ExitFailure 1))
- -- failure to run the command causes exit(1) for ghc -e.
- _ -> return True
-
runStmt :: String -> SingleStep -> GHCi Bool
runStmt stmt step
| null (filter (not.isSpace) stmt) = return False
| ["import", mod] <- words stmt = keepGoing setContext ('+':mod)
| otherwise
- = do st <- getGHCiState
- session <- getSession
- result <- io $ withProgName (progname st) $ withArgs (args st) $
- GHC.runStmt session stmt step
+ = do result <- GhciMonad.runStmt stmt step
afterRunStmt (const True) result
-
--afterRunStmt :: GHC.RunResult -> GHCi Bool
-- False <=> the statement failed to compile
afterRunStmt :: (SrcSpan -> Bool) -> GHC.RunResult -> GHCi Bool
afterRunStmt _ (GHC.RunException e) = throw e
afterRunStmt step_here run_result = do
- session <- getSession
- resumes <- io $ GHC.getResumeContext session
+ resumes <- GHC.getResumeContext
case run_result of
GHC.RunOk names -> do
show_types <- isOptionSet ShowType
- when show_types $ printTypeOfNames session names
- GHC.RunBreak _ names mb_info
- | isNothing mb_info ||
+ when show_types $ printTypeOfNames names
+ GHC.RunBreak _ names mb_info
+ | isNothing mb_info ||
step_here (GHC.resumeSpan $ head resumes) -> do
- printForUser $ ptext SLIT("Stopped at") <+>
- ppr (GHC.resumeSpan $ head resumes)
--- printTypeOfNames session names
- let namesSorted = sortBy compareNames names
- tythings <- catMaybes `liftM`
- io (mapM (GHC.lookupName session) namesSorted)
-
- printTypeAndContents session [id | AnId id <- tythings]
- maybe (return ()) runBreakCmd mb_info
+ mb_id_loc <- toBreakIdAndLocation mb_info
+ let breakCmd = maybe "" ( \(_,l) -> onBreakCmd l ) mb_id_loc
+ if (null breakCmd)
+ then printStoppedAtBreakInfo (head resumes) names
+ else enqueueCommands [breakCmd]
-- run the command set with ":set stop <cmd>"
st <- getGHCiState
enqueueCommands [stop st]
return ()
- | otherwise -> io(GHC.resume session GHC.SingleStep) >>=
+ | otherwise -> resume step_here GHC.SingleStep >>=
afterRunStmt step_here >> return ()
_ -> return ()
flushInterpBuffers
io installSignalHandlers
b <- isOptionSet RevertCAFs
- io (when b revertCAFs)
+ when b revertCAFs
return (case run_result of GHC.RunOk _ -> True; _ -> False)
-runBreakCmd :: GHC.BreakInfo -> GHCi ()
-runBreakCmd info = do
+toBreakIdAndLocation ::
+ Maybe GHC.BreakInfo -> GHCi (Maybe (Int, BreakLocation))
+toBreakIdAndLocation Nothing = return Nothing
+toBreakIdAndLocation (Just info) = do
let mod = GHC.breakInfo_module info
nm = GHC.breakInfo_number info
st <- getGHCiState
- case [ loc | (_,loc) <- breaks st,
- breakModule loc == mod, breakTick loc == nm ] of
- [] -> return ()
- loc:_ | null cmd -> return ()
- | otherwise -> do enqueueCommands [cmd]; return ()
- where cmd = onBreakCmd loc
-
-printTypeOfNames :: Session -> [Name] -> GHCi ()
-printTypeOfNames session names
- = mapM_ (printTypeOfName session) $ sortBy compareNames names
+ return $ listToMaybe [ id_loc | id_loc@(_,loc) <- breaks st,
+ breakModule loc == mod,
+ breakTick loc == nm ]
+
+printStoppedAtBreakInfo :: Resume -> [Name] -> GHCi ()
+printStoppedAtBreakInfo resume names = do
+ printForUser $ ptext (sLit "Stopped at") <+>
+ ppr (GHC.resumeSpan resume)
+ -- printTypeOfNames session names
+ let namesSorted = sortBy compareNames names
+ tythings <- catMaybes `liftM` mapM GHC.lookupName namesSorted
+ docs <- pprTypeAndContents [id | AnId id <- tythings]
+ printForUserPartWay docs
+
+printTypeOfNames :: [Name] -> GHCi ()
+printTypeOfNames names
+ = mapM_ (printTypeOfName ) $ sortBy compareNames names
compareNames :: Name -> Name -> Ordering
n1 `compareNames` n2 = compareWith n1 `compare` compareWith n2
where compareWith n = (getOccString n, getSrcSpan n)
-printTypeOfName :: Session -> Name -> GHCi ()
-printTypeOfName session n
- = do maybe_tything <- io (GHC.lookupName session n)
+printTypeOfName :: Name -> GHCi ()
+printTypeOfName n
+ = do maybe_tything <- GHC.lookupName n
case maybe_tything of
Nothing -> return ()
Just thing -> printTyThing thing
-printTypeAndContents :: Session -> [Id] -> GHCi ()
-printTypeAndContents session ids = do
- dflags <- getDynFlags
- let pefas = dopt Opt_PrintExplicitForalls dflags
- pcontents = dopt Opt_PrintBindContents dflags
- if pcontents
- then do
- let depthBound = 100
- terms <- mapM (io . GHC.obtainTermB session depthBound False) ids
- docs_terms <- mapM (io . showTerm session) terms
- printForUser $ vcat $ zipWith (\ty cts -> ty <+> equals <+> cts)
- (map (pprTyThing pefas . AnId) ids)
- docs_terms
- else printForUser $ vcat $ map (pprTyThing pefas . AnId) ids
+data MaybeCommand = GotCommand Command | BadCommand | NoLastCommand
specialCommand :: String -> GHCi Bool
specialCommand ('!':str) = shellEscape (dropWhile isSpace str)
specialCommand str = do
let (cmd,rest) = break isSpace str
- maybe_cmd <- io (lookupCommand cmd)
+ maybe_cmd <- lookupCommand cmd
case maybe_cmd of
- Nothing -> io (hPutStr stdout ("unknown command ':" ++ cmd ++ "'\n"
- ++ shortHelpText) >> return False)
- Just (_,f,_,_) -> f (dropWhile isSpace rest)
-
-lookupCommand :: String -> IO (Maybe Command)
+ GotCommand (_,f,_,_) -> f (dropWhile isSpace rest)
+ BadCommand ->
+ do io $ hPutStr stdout ("unknown command ':" ++ cmd ++ "'\n"
+ ++ shortHelpText)
+ return False
+ NoLastCommand ->
+ do io $ hPutStr stdout ("there is no last command to perform\n"
+ ++ shortHelpText)
+ return False
+
+lookupCommand :: String -> GHCi (MaybeCommand)
+lookupCommand "" = do
+ st <- getGHCiState
+ case last_command st of
+ Just c -> return $ GotCommand c
+ Nothing -> return NoLastCommand
lookupCommand str = do
+ mc <- io $ lookupCommand' str
+ st <- getGHCiState
+ setGHCiState st{ last_command = mc }
+ return $ case mc of
+ Just c -> GotCommand c
+ Nothing -> BadCommand
+
+lookupCommand' :: String -> IO (Maybe Command)
+lookupCommand' str = do
macros <- readIORef macros_ref
let cmds = builtin_commands ++ macros
-- look for exact match first, then the first prefix match
- case [ c | c <- cmds, str == cmdName c ] of
- c:_ -> return (Just c)
- [] -> case [ c | c@(s,_,_,_) <- cmds, str `isPrefixOf` s ] of
- [] -> return Nothing
- c:_ -> return (Just c)
-
+ return $ case [ c | c <- cmds, str == cmdName c ] of
+ c:_ -> Just c
+ [] -> case [ c | c@(s,_,_,_) <- cmds, str `isPrefixOf` s ] of
+ [] -> Nothing
+ c:_ -> Just c
getCurrentBreakSpan :: GHCi (Maybe SrcSpan)
getCurrentBreakSpan = do
- session <- getSession
- resumes <- io $ GHC.getResumeContext session
+ resumes <- GHC.getResumeContext
case resumes of
[] -> return Nothing
(r:_) -> do
then return (Just (GHC.resumeSpan r))
else do
let hist = GHC.resumeHistory r !! (ix-1)
- span <- io $ GHC.getHistorySpan session hist
+ span <- GHC.getHistorySpan hist
return (Just span)
getCurrentBreakModule :: GHCi (Maybe Module)
getCurrentBreakModule = do
- session <- getSession
- resumes <- io $ GHC.getResumeContext session
+ resumes <- GHC.getResumeContext
case resumes of
[] -> return Nothing
(r:_) -> do
help _ = io (putStr helpText)
info :: String -> GHCi ()
-info "" = throwDyn (CmdLineError "syntax: ':i <thing-you-want-info-about>'")
-info s = do { let names = words s
- ; session <- getSession
+info "" = ghcError (CmdLineError "syntax: ':i <thing-you-want-info-about>'")
+info s = handleSourceError GHC.printExceptionAndWarnings $ do
+ { let names = words s
; dflags <- getDynFlags
; let pefas = dopt Opt_PrintExplicitForalls dflags
- ; mapM_ (infoThing pefas session) names }
+ ; mapM_ (infoThing pefas) names }
where
- infoThing pefas session str = io $ do
- names <- GHC.parseName session str
- mb_stuffs <- mapM (GHC.getInfo session) names
+ infoThing pefas str = do
+ names <- GHC.parseName str
+ mb_stuffs <- mapM GHC.getInfo names
let filtered = filterOutChildren (\(t,_f,_i) -> t) (catMaybes mb_stuffs)
- unqual <- GHC.getPrintUnqual session
- putStrLn (showSDocForUser unqual $
- vcat (intersperse (text "") $
- map (pprInfo pefas) filtered))
+ unqual <- GHC.getPrintUnqual
+ liftIO $
+ putStrLn (showSDocForUser unqual $
+ vcat (intersperse (text "") $
+ map (pprInfo pefas) filtered))
-- Filter out names whose parent is also there Good
-- example is '[]', which is both a type and data
| otherwise = ppr fix <+> ppr (GHC.getName thing)
runMain :: String -> GHCi ()
-runMain args = do
- let ss = concat $ intersperse "," (map (\ s -> ('"':s)++"\"") (toArgs args))
- enqueueCommands ['[': ss ++ "] `System.Environment.withArgs` main"]
+runMain s = case toArgs s of
+ Left err -> io (hPutStrLn stderr err)
+ Right args ->
+ do dflags <- getDynFlags
+ case mainFunIs dflags of
+ Nothing -> doWithArgs args "main"
+ Just f -> doWithArgs args f
+
+runRun :: String -> GHCi ()
+runRun s = case toCmdArgs s of
+ Left err -> io (hPutStrLn stderr err)
+ Right (cmd, args) -> doWithArgs args cmd
+
+doWithArgs :: [String] -> String -> GHCi ()
+doWithArgs args cmd = enqueueCommands ["System.Environment.withArgs " ++
+ show args ++ " (" ++ cmd ++ ")"]
addModule :: [FilePath] -> GHCi ()
addModule files = do
- io (revertCAFs) -- always revert CAFs on load/add.
+ revertCAFs -- always revert CAFs on load/add.
files <- mapM expandPath files
- targets <- mapM (\m -> io (GHC.guessTarget m Nothing)) files
- session <- getSession
- io (mapM_ (GHC.addTarget session) targets)
- ok <- io (GHC.load session LoadAllTargets)
- afterLoad ok session Nothing
+ 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
changeDirectory :: String -> GHCi ()
+changeDirectory "" = do
+ -- :cd on its own changes to the user's home directory
+ either_dir <- io (IO.try getHomeDirectory)
+ case either_dir of
+ Left _e -> return ()
+ Right dir -> changeDirectory dir
changeDirectory dir = do
- session <- getSession
- graph <- io (GHC.getModuleGraph session)
+ graph <- GHC.getModuleGraph
when (not (null graph)) $
io $ putStr "Warning: changing directory causes all loaded modules to be unloaded,\nbecause the search path has changed.\n"
- io (GHC.setTargets session [])
- io (GHC.load session LoadAllTargets)
- setContextAfterLoad session []
- io (GHC.workingDirectoryChanged session)
+ prev_context <- GHC.getContext
+ GHC.setTargets []
+ GHC.load LoadAllTargets
+ setContextAfterLoad prev_context False []
+ GHC.workingDirectoryChanged
dir <- expandPath dir
io (setCurrentDirectory dir)
+trySuccess :: GHC.GhcMonad m => m SuccessFlag -> m SuccessFlag
+trySuccess act =
+ handleSourceError (\e -> do GHC.printExceptionAndWarnings e
+ return Failed) $ do
+ act
+
editFile :: String -> GHCi ()
editFile str =
do file <- if null str then chooseEditFile else return str
st <- getGHCiState
let cmd = editor st
when (null cmd)
- $ throwDyn (CmdLineError "editor not set, use :set editor")
+ $ ghcError (CmdLineError "editor not set, use :set editor")
io $ system (cmd ++ ' ':file)
return ()
-- of those.
chooseEditFile :: GHCi String
chooseEditFile =
- do session <- getSession
- let hasFailed x = io $ fmap not $ GHC.isLoaded session $ GHC.ms_mod_name x
+ do let hasFailed x = fmap not $ GHC.isLoaded $ GHC.ms_mod_name x
- graph <- io (GHC.getModuleGraph session)
+ graph <- GHC.getModuleGraph
failed_graph <- filterM hasFailed graph
let order g = flattenSCCs $ GHC.topSortModuleGraph True g Nothing
pick xs = case xs of
case pick (order failed_graph) of
Just file -> return file
Nothing ->
- do targets <- io (GHC.getTargets session)
+ do targets <- GHC.getTargets
case msum (map fromTarget targets) of
Just file -> return file
- Nothing -> throwDyn (CmdLineError "No files to edit.")
+ Nothing -> ghcError (CmdLineError "No files to edit.")
- where fromTarget (GHC.Target (GHC.TargetFile f _) _) = Just f
+ where fromTarget (GHC.Target (GHC.TargetFile f _) _ _) = Just f
fromTarget _ = Nothing -- when would we get a module target?
defineMacro :: Bool{-overwrite-} -> String -> GHCi ()
unlines defined)
else do
if (not overwrite && macro_name `elem` defined)
- then throwDyn (CmdLineError
+ then ghcError (CmdLineError
("macro '" ++ macro_name ++ "' is already defined"))
else do
let new_expr = '(' : definition ++ ") :: String -> IO String"
-- compile the expression
- cms <- getSession
- maybe_hv <- io (GHC.compileExpr cms new_expr)
- case maybe_hv of
- Nothing -> return ()
- Just hv -> io (writeIORef macros_ref --
- (filtered ++ [(macro_name, runMacro hv, False, completeNone)]))
+ handleSourceError (\e -> GHC.printExceptionAndWarnings e) $ do
+ hv <- GHC.compileExpr new_expr
+ io (writeIORef macros_ref --
+ (filtered ++ [(macro_name, runMacro hv, Nothing, completeNone)]))
runMacro :: GHC.HValue{-String -> IO String-} -> String -> GHCi Bool
runMacro fun s = do
str <- io ((unsafeCoerce# fun :: String -> IO String) s)
+ -- make sure we force any exceptions in the result, while we are still
+ -- inside the exception handler for commands:
+ seqList str (return ())
enqueueCommands (lines str)
return False
where undef macro_name = do
cmds <- io (readIORef macros_ref)
if (macro_name `notElem` map cmdName cmds)
- then throwDyn (CmdLineError
+ then ghcError (CmdLineError
("macro '" ++ macro_name ++ "' is not defined"))
else do
io (writeIORef macros_ref (filter ((/= macro_name) . cmdName) cmds))
cmdCmd :: String -> GHCi ()
cmdCmd str = do
let expr = '(' : str ++ ") :: IO String"
- session <- getSession
- maybe_hv <- io (GHC.compileExpr session expr)
- case maybe_hv of
- Nothing -> return ()
- Just hv -> do
- cmds <- io $ (unsafeCoerce# hv :: IO String)
- enqueueCommands (lines cmds)
- return ()
+ handleSourceError (\e -> GHC.printExceptionAndWarnings e) $ do
+ hv <- GHC.compileExpr expr
+ cmds <- io $ (unsafeCoerce# hv :: IO String)
+ enqueueCommands (lines cmds)
+ return ()
loadModule :: [(FilePath, Maybe Phase)] -> GHCi SuccessFlag
loadModule fs = timeIt (loadModule' fs)
loadModule' :: [(FilePath, Maybe Phase)] -> GHCi SuccessFlag
loadModule' files = do
- session <- getSession
+ prev_context <- GHC.getContext
-- unload first
+ GHC.abandonAll
discardActiveBreakPoints
- io (GHC.setTargets session [])
- io (GHC.load session LoadAllTargets)
+ GHC.setTargets []
+ GHC.load LoadAllTargets
-- expand tildes
let (filenames, phases) = unzip files
exp_filenames <- mapM expandPath filenames
let files' = zip exp_filenames phases
- targets <- io (mapM (uncurry GHC.guessTarget) files')
+ targets <- mapM (uncurry GHC.guessTarget) files'
-- NOTE: we used to do the dependency anal first, so that if it
-- fails we didn't throw away the current set of modules. This would
-- require some re-working of the GHC interface, so we'll leave it
-- as a ToDo for now.
- io (GHC.setTargets session targets)
- doLoad session False LoadAllTargets
+ GHC.setTargets targets
+ doLoad False prev_context LoadAllTargets
checkModule :: String -> GHCi ()
checkModule m = do
let modl = GHC.mkModuleName m
- session <- getSession
- result <- io (GHC.checkModule session modl False)
- case result of
- Nothing -> io $ putStrLn "Nothing"
- Just r -> io $ putStrLn (showSDoc (
- case GHC.checkedModuleInfo r of
- Just cm | Just scope <- GHC.modInfoTopLevelScope cm ->
+ prev_context <- GHC.getContext
+ ok <- handleSourceError (\e -> GHC.printExceptionAndWarnings e >> return False) $ do
+ r <- GHC.typecheckModule =<< GHC.parseModule =<< GHC.getModSummary modl
+ io $ putStrLn (showSDoc (
+ case GHC.moduleInfo r of
+ cm | Just scope <- GHC.modInfoTopLevelScope cm ->
let
- (local,global) = partition ((== modl) . GHC.moduleName . GHC.nameModule) scope
+ (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))
- afterLoad (successIf (isJust result)) session Nothing
+ _ -> empty))
+ return True
+ afterLoad (successIf ok) False prev_context
reloadModule :: String -> GHCi ()
reloadModule m = do
- session <- getSession
- doLoad session True $ if null m then LoadAllTargets
- else LoadUpTo (GHC.mkModuleName m)
+ prev_context <- GHC.getContext
+ doLoad True prev_context $
+ if null m then LoadAllTargets
+ else LoadUpTo (GHC.mkModuleName m)
return ()
-doLoad :: Session -> Bool -> LoadHowMuch -> GHCi SuccessFlag
-doLoad session retain_context howmuch = do
+doLoad :: Bool -> ([Module],[Module]) -> LoadHowMuch -> GHCi SuccessFlag
+doLoad retain_context prev_context howmuch = do
-- turn off breakpoints before we load: we can't turn them off later, because
-- the ModBreaks will have gone away.
discardActiveBreakPoints
- context <- io $ GHC.getContext session
- ok <- io (GHC.load session howmuch)
- afterLoad ok session (if retain_context then Just context else Nothing)
+ ok <- trySuccess $ GHC.load howmuch
+ afterLoad ok retain_context prev_context
return ok
-afterLoad :: SuccessFlag -> Session -> Maybe ([Module],[Module]) -> GHCi ()
-afterLoad ok session maybe_context = do
- io (revertCAFs) -- always revert CAFs on load.
+afterLoad :: SuccessFlag -> Bool -> ([Module],[Module]) -> GHCi ()
+afterLoad ok retain_context prev_context = do
+ revertCAFs -- always revert CAFs on load.
discardTickArrays
- loaded_mods <- getLoadedModules session
-
- -- try to retain the old module context for :reload. This might
- -- not be possible, for example if some modules have gone away, so
- -- we attempt to set the same context, backing off to the default
- -- context if that fails.
- case maybe_context of
- Nothing -> setContextAfterLoad session loaded_mods
- Just (as,bs) -> do
- r <- io $ Exception.try (GHC.setContext session as bs)
- case r of
- Left _err -> setContextAfterLoad session loaded_mods
- Right _ -> return ()
+ 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
- modulesLoadedMsg ok (map GHC.ms_mod_name loaded_mods)
+ setContextAfterLoad prev_context retain_context loaded_mod_summaries
-setContextAfterLoad :: Session -> [GHC.ModSummary] -> GHCi ()
-setContextAfterLoad session [] = do
+
+setContextAfterLoad :: ([Module],[Module]) -> Bool -> [GHC.ModSummary] -> GHCi ()
+setContextAfterLoad prev keep_ctxt [] = do
prel_mod <- getPrelude
- io (GHC.setContext session [] [prel_mod])
-setContextAfterLoad session ms = do
+ setContextKeepingPackageModules prev keep_ctxt ([], [prel_mod])
+setContextAfterLoad prev keep_ctxt ms = do
-- load a target if one is available, otherwise load the topmost module.
- targets <- io (GHC.getTargets session)
+ targets <- GHC.getTargets
case [ m | Just m <- map (findTarget ms) targets ] of
[] ->
let graph' = flattenSCCs (GHC.topSortModuleGraph True ms Nothing) in
[] -> Nothing
(m:_) -> Just m
- summary `matches` Target (TargetModule m) _
+ summary `matches` Target (TargetModule m) _ _
= GHC.ms_mod_name summary == m
- summary `matches` Target (TargetFile f _) _
+ summary `matches` Target (TargetFile f _) _ _
| Just f' <- GHC.ml_hs_file (GHC.ms_location summary) = f == f'
_ `matches` _
= False
load_this summary | m <- GHC.ms_mod summary = do
- b <- io (GHC.moduleIsInterpreted session m)
- if b then io (GHC.setContext session [m] [])
+ b <- GHC.moduleIsInterpreted m
+ if b then setContextKeepingPackageModules prev keep_ctxt ([m], [])
else do
- prel_mod <- getPrelude
- io (GHC.setContext session [] [prel_mod,m])
-
+ prel_mod <- getPrelude
+ setContextKeepingPackageModules prev keep_ctxt ([],[prel_mod,m])
+
+-- | Keep any package modules (except Prelude) when changing the context.
+setContextKeepingPackageModules
+ :: ([Module],[Module]) -- previous context
+ -> Bool -- re-execute :module commands
+ -> ([Module],[Module]) -- new context
+ -> GHCi ()
+setContextKeepingPackageModules prev_context keep_ctxt (as,bs) = do
+ let (_,bs0) = prev_context
+ prel_mod <- getPrelude
+ let pkg_modules = filter (\p -> not (isHomeModule p) && p /= prel_mod) bs0
+ let bs1 = if null as then nub (prel_mod : bs) else bs
+ GHC.setContext as (nub (bs1 ++ pkg_modules))
+ if keep_ctxt
+ then do
+ st <- getGHCiState
+ mapM_ (playCtxtCmd False) (remembered_ctx st)
+ else do
+ st <- getGHCiState
+ setGHCiState st{ remembered_ctx = [] }
+
+isHomeModule :: Module -> Bool
+isHomeModule mod = GHC.modulePackageId mod == mainPackageId
modulesLoadedMsg :: SuccessFlag -> [ModuleName] -> GHCi ()
modulesLoadedMsg ok mods = do
typeOfExpr :: String -> GHCi ()
typeOfExpr str
- = do cms <- getSession
- maybe_ty <- io (GHC.exprType cms str)
- case maybe_ty of
- Nothing -> return ()
- Just ty -> do dflags <- getDynFlags
- let pefas = dopt Opt_PrintExplicitForalls dflags
- printForUser $ text str <+> dcolon
- <+> pprTypeForUser pefas ty
+ = handleSourceError (\e -> GHC.printExceptionAndWarnings e) $ do
+ ty <- GHC.exprType str
+ dflags <- getDynFlags
+ let pefas = dopt Opt_PrintExplicitForalls dflags
+ printForUser $ sep [text str, nest 2 (dcolon <+> pprTypeForUser pefas ty)]
kindOfType :: String -> GHCi ()
kindOfType str
- = do cms <- getSession
- maybe_ty <- io (GHC.typeKind cms str)
- case maybe_ty of
- Nothing -> return ()
- Just ty -> printForUser $ text str <+> dcolon <+> ppr ty
+ = handleSourceError (\e -> GHC.printExceptionAndWarnings e) $ do
+ ty <- GHC.typeKind str
+ printForUser $ text str <+> dcolon <+> ppr ty
quit :: String -> GHCi Bool
quit _ = return True
m <- lookupModule s
browseModule bang m True
[] -> do
- s <- getSession
- (as,bs) <- io $ GHC.getContext s
+ (as,bs) <- GHC.getContext
-- Guess which module the user wants to browse. Pick
-- modules that are interpreted first. The most
-- recently-added module occurs last, it seems.
case (as,bs) of
(as@(_:_), _) -> browseModule bang (last as) True
([], bs@(_:_)) -> browseModule bang (last bs) True
- ([], []) -> throwDyn (CmdLineError ":browse: no current module")
- _ -> throwDyn (CmdLineError "syntax: :browse <module>")
+ ([], []) -> ghcError (CmdLineError ":browse: no current module")
+ _ -> ghcError (CmdLineError "syntax: :browse <module>")
-- without bang, show items in context of their parents and omit children
-- with bang, show class methods and data constructors separately, and
-- with sorted, sort items alphabetically
browseModule :: Bool -> Module -> Bool -> GHCi ()
browseModule bang modl exports_only = do
- s <- getSession
+ -- :browse! reports qualifiers wrt current context
+ current_unqual <- GHC.getPrintUnqual
-- Temporarily set the context to the module we're interested in,
-- just so we can get an appropriate PrintUnqualified
- (as,bs) <- io (GHC.getContext s)
+ (as,bs) <- GHC.getContext
prel_mod <- getPrelude
- io (if exports_only then GHC.setContext s [] [prel_mod,modl]
- else GHC.setContext s [modl] [])
- unqual <- io (GHC.getPrintUnqual s)
- io (GHC.setContext s as bs)
+ if exports_only then GHC.setContext [] [prel_mod,modl]
+ else GHC.setContext [modl] []
+ target_unqual <- GHC.getPrintUnqual
+ GHC.setContext as bs
+
+ let unqual = if bang then current_unqual else target_unqual
- mb_mod_info <- io $ GHC.getModuleInfo s modl
+ mb_mod_info <- GHC.getModuleInfo modl
case mb_mod_info of
- Nothing -> throwDyn (CmdLineError ("unknown module: " ++
+ Nothing -> ghcError (CmdLineError ("unknown module: " ++
GHC.moduleNameString (GHC.moduleName modl)))
Just mod_info -> do
dflags <- getDynFlags
-- We would like to improve this; see #1799.
sorted_names = loc_sort local ++ occ_sort external
where
- (local,external) = partition ((==modl) . nameModule) names
+ (local,external) = ASSERT( all isExternalName names )
+ partition ((==modl) . nameModule) names
occ_sort = sortBy (compare `on` nameOccName)
-- try to sort by src location. If the first name in
-- our list has a good source location, then they all should.
| otherwise
= occ_sort names
- mb_things <- io $ mapM (GHC.lookupName s) sorted_names
+ mb_things <- mapM GHC.lookupName sorted_names
let filtered_things = filterOutChildren (\t -> t) (catMaybes mb_things)
- rdr_env <- io $ GHC.getGRE s
+ rdr_env <- GHC.getGRE
let pefas = dopt Opt_PrintExplicitForalls dflags
things | bang = catMaybes mb_things
labels [] = text "-- not currently imported"
labels l = text $ intercalate "\n" $ map qualifier l
qualifier = maybe "-- defined locally"
- (("-- imported from "++) . intercalate ", "
+ (("-- imported via "++) . intercalate ", "
. map GHC.moduleNameString)
importInfo = RdrName.getGRE_NameQualifier_maybes rdr_env
modNames = map (importInfo . GHC.getName) things
setContext :: String -> GHCi ()
setContext str
- | all sensible mods = fn mods
- | otherwise = throwDyn (CmdLineError "syntax: :module [+/-] [*]M1 ... [*]Mn")
+ | all sensible strs = do
+ playCtxtCmd True (cmd, as, bs)
+ st <- getGHCiState
+ setGHCiState st{ remembered_ctx = remembered_ctx st ++ [(cmd,as,bs)] }
+ | otherwise = ghcError (CmdLineError "syntax: :module [+/-] [*]M1 ... [*]Mn")
where
- (fn, mods) = case str of
- '+':stuff -> (addToContext, words stuff)
- '-':stuff -> (removeFromContext, words stuff)
- stuff -> (newContext, words stuff)
+ (cmd, strs, as, bs) =
+ case str of
+ '+':stuff -> rest AddModules stuff
+ '-':stuff -> rest RemModules stuff
+ stuff -> rest SetContext stuff
+
+ rest cmd stuff = (cmd, strs, as, bs)
+ where strs = words stuff
+ (as,bs) = partitionWith starred strs
sensible ('*':m) = looksLikeModuleName m
sensible m = looksLikeModuleName m
-separate :: Session -> [String] -> [Module] -> [Module]
- -> GHCi ([Module],[Module])
-separate _ [] as bs = return (as,bs)
-separate session (('*':str):ms) as bs = do
- m <- wantInterpretedModule str
- separate session ms (m:as) bs
-separate session (str:ms) as bs = do
- m <- lookupModule str
- separate session ms as (m:bs)
-
-newContext :: [String] -> GHCi ()
-newContext strs = do
- s <- getSession
- (as,bs) <- separate s strs [] []
- prel_mod <- getPrelude
- let bs' = if null as && prel_mod `notElem` bs then prel_mod:bs else bs
- io $ GHC.setContext s as bs'
-
-
-addToContext :: [String] -> GHCi ()
-addToContext strs = do
- s <- getSession
- (as,bs) <- io $ GHC.getContext s
-
- (new_as,new_bs) <- separate s strs [] []
-
- let as_to_add = new_as \\ (as ++ bs)
- bs_to_add = new_bs \\ (as ++ bs)
-
- io $ GHC.setContext s (as ++ as_to_add) (bs ++ bs_to_add)
-
-
-removeFromContext :: [String] -> GHCi ()
-removeFromContext strs = do
- s <- getSession
- (as,bs) <- io $ GHC.getContext s
-
- (as_to_remove,bs_to_remove) <- separate s strs [] []
-
- let as' = as \\ (as_to_remove ++ bs_to_remove)
- bs' = bs \\ (as_to_remove ++ bs_to_remove)
-
- io $ GHC.setContext s as' bs'
+ starred ('*':m) = Left m
+ starred m = Right m
+
+playCtxtCmd :: Bool -> (CtxtCmd, [String], [String]) -> GHCi ()
+playCtxtCmd fail (cmd, as, bs)
+ = do
+ (as',bs') <- do_checks fail
+ (prev_as,prev_bs) <- GHC.getContext
+ (new_as, new_bs) <-
+ case cmd of
+ SetContext -> do
+ prel_mod <- getPrelude
+ let bs'' = if null as && prel_mod `notElem` bs' then prel_mod:bs'
+ else bs'
+ return (as',bs'')
+ AddModules -> do
+ let as_to_add = as' \\ (prev_as ++ prev_bs)
+ bs_to_add = bs' \\ (prev_as ++ prev_bs)
+ return (prev_as ++ as_to_add, prev_bs ++ bs_to_add)
+ RemModules -> do
+ let new_as = prev_as \\ (as' ++ bs')
+ new_bs = prev_bs \\ (as' ++ bs')
+ return (new_as, new_bs)
+ GHC.setContext new_as new_bs
+ where
+ do_checks True = do
+ as' <- mapM wantInterpretedModule as
+ bs' <- mapM lookupModule bs
+ return (as',bs')
+ do_checks False = do
+ as' <- mapM (trymaybe . wantInterpretedModule) as
+ bs' <- mapM (trymaybe . lookupModule) bs
+ return (catMaybes as', catMaybes bs')
+
+ trymaybe m = do
+ r <- ghciTry m
+ case r of
+ Left _ -> return Nothing
+ Right a -> return (Just a)
----------------------------------------------------------------------------
-- Code for `:set'
vcat (text "other dynamic, non-language, flag settings:"
:map (flagSetting dflags) nonLanguageDynFlags)
))
- where flagSetting dflags (str,f)
+ where flagSetting dflags (str, f, _)
| dopt f dflags = text " " <> text "-f" <> text str
| otherwise = text " " <> text "-fno-" <> text str
- (ghciFlags,others) = partition (\(_,f)->f `elem` flags)
+ (ghciFlags,others) = partition (\(_, f, _) -> f `elem` flags)
DynFlags.fFlags
- nonLanguageDynFlags = filter (\(_,f)->not $ f `elem` map snd xFlags)
- others
+ nonLanguageDynFlags = filterOut (\(_, f, _) -> f `elem` languageOptions)
+ others
flags = [Opt_PrintExplicitForalls
,Opt_PrintBindResult
,Opt_BreakOnException
,Opt_PrintEvldWithShow
]
setCmd str
- = case toArgs str of
- ("args":args) -> setArgs args
- ("prog":prog) -> setProg prog
- ("prompt":_) -> setPrompt (after 6)
- ("editor":_) -> setEditor (after 6)
- ("stop":_) -> setStop (after 4)
- wds -> setOptions wds
- where after n = dropWhile isSpace $ drop n $ dropWhile isSpace str
-
-setArgs, setProg, setOptions :: [String] -> GHCi ()
-setEditor, setStop, setPrompt :: String -> GHCi ()
+ = case getCmd str of
+ Right ("args", rest) ->
+ case toArgs rest of
+ Left err -> io (hPutStrLn stderr err)
+ Right args -> setArgs args
+ Right ("prog", rest) ->
+ case toArgs rest of
+ Right [prog] -> setProg prog
+ _ -> io (hPutStrLn stderr "syntax: :set prog <progname>")
+ Right ("prompt", rest) -> setPrompt $ dropWhile isSpace rest
+ Right ("editor", rest) -> setEditor $ dropWhile isSpace rest
+ Right ("stop", rest) -> setStop $ dropWhile isSpace rest
+ _ -> case toArgs str of
+ Left err -> io (hPutStrLn stderr err)
+ Right wds -> setOptions wds
+
+setArgs, setOptions :: [String] -> GHCi ()
+setProg, setEditor, setStop, setPrompt :: String -> GHCi ()
setArgs args = do
st <- getGHCiState
setGHCiState st{ args = args }
-setProg [prog] = do
+setProg prog = do
st <- getGHCiState
setGHCiState st{ progname = prog }
-setProg _ = do
- io (hPutStrLn stderr "syntax: :set prog <progname>")
setEditor cmd = do
st <- getGHCiState
st <- getGHCiState
if null value
then io $ hPutStrLn stderr $ "syntax: :set prompt <prompt>, currently \"" ++ prompt st ++ "\""
- else setGHCiState st{ prompt = remQuotes value }
- where
- remQuotes ('\"':xs) | not (null xs) && last xs == '\"' = init xs
- remQuotes x = x
+ else case value of
+ '\"' : _ -> case reads value of
+ [(value', xs)] | all isSpace xs ->
+ setGHCiState (st { prompt = value' })
+ _ ->
+ io $ hPutStrLn stderr "Can't parse prompt string. Use Haskell syntax."
+ _ -> setGHCiState (st { prompt = value })
setOptions wds =
do -- first, deal with the GHCi opts (+s, +t, etc.)
newDynFlags minus_opts = do
dflags <- getDynFlags
let pkg_flags = packageFlags dflags
- (dflags',leftovers) <- io $ GHC.parseDynamicFlags dflags minus_opts
+ (dflags', leftovers, warns) <- io $ GHC.parseDynamicFlags dflags $ map noLoc minus_opts
+ handleFlagWarnings dflags' warns
if (not (null leftovers))
- then throwDyn (CmdLineError ("unrecognised flags: " ++
- unwords leftovers))
- else return ()
+ then ghcError $ errorsToGhcException leftovers
+ else return ()
new_pkgs <- setDynFlags dflags'
-- and link the new packages.
dflags <- getDynFlags
when (packageFlags dflags /= pkg_flags) $ do
- io $ hPutStrLn stderr "package flags have changed, ressetting and loading new packages..."
- session <- getSession
- io (GHC.setTargets session [])
- io (GHC.load session LoadAllTargets)
+ io $ hPutStrLn stderr "package flags have changed, resetting and loading new packages..."
+ GHC.setTargets []
+ GHC.load LoadAllTargets
io (linkPackages dflags new_pkgs)
- setContextAfterLoad session []
+ -- package flags changed, we can't re-use any of the old context
+ setContextAfterLoad ([],[]) False []
return ()
mapM_ unsetOpt plus_opts
let no_flag ('-':'f':rest) = return ("-fno-" ++ rest)
- no_flag f = throwDyn (ProgramError ("don't know how to reverse " ++ f))
+ no_flag f = ghcError (ProgramError ("don't know how to reverse " ++ f))
no_flags <- mapM no_flag minus_opts
newDynFlags no_flags
["context"] -> showContext
["packages"] -> showPackages
["languages"] -> showLanguages
- _ -> throwDyn (CmdLineError "syntax: :show [args|prog|prompt|editor|stop|modules|bindings|breaks|context]")
+ _ -> ghcError (CmdLineError ("syntax: :show [ args | prog | prompt | editor | stop | modules | bindings\n"++
+ " | breaks | context | packages | languages ]"))
showModules :: GHCi ()
showModules = do
- session <- getSession
- loaded_mods <- getLoadedModules session
+ loaded_mods <- getLoadedModules
-- we want *loaded* modules only, see #1734
- let show_one ms = do m <- io (GHC.showModule session ms); io (putStrLn m)
+ let show_one ms = do m <- GHC.showModule ms; io (putStrLn m)
mapM_ show_one loaded_mods
-getLoadedModules :: GHC.Session -> GHCi [GHC.ModSummary]
-getLoadedModules session = do
- graph <- io (GHC.getModuleGraph session)
- filterM (io . GHC.isLoaded session . GHC.ms_mod_name) graph
+getLoadedModules :: GHCi [GHC.ModSummary]
+getLoadedModules = do
+ graph <- GHC.getModuleGraph
+ filterM (GHC.isLoaded . GHC.ms_mod_name) graph
showBindings :: GHCi ()
showBindings = do
- s <- getSession
- bindings <- io (GHC.getBindings s)
- printTypeAndContents s [ id | AnId id <- sortBy compareTyThings bindings]
+ bindings <- GHC.getBindings
+ docs <- pprTypeAndContents
+ [ id | AnId id <- sortBy compareTyThings bindings]
+ printForUserPartWay docs
compareTyThings :: TyThing -> TyThing -> Ordering
t1 `compareTyThings` t2 = getName t1 `compareNames` getName t2
showContext :: GHCi ()
showContext = do
- session <- getSession
- resumes <- io $ GHC.getResumeContext session
+ resumes <- GHC.getResumeContext
printForUser $ vcat (map pp_resume (reverse resumes))
where
pp_resume resume =
- ptext SLIT("--> ") <> text (GHC.resumeStmt resume)
- $$ nest 2 (ptext SLIT("Stopped at") <+> ppr (GHC.resumeSpan resume))
+ ptext (sLit "--> ") <> text (GHC.resumeStmt resume)
+ $$ nest 2 (ptext (sLit "Stopped at") <+> ppr (GHC.resumeSpan resume))
showPackages :: GHCi ()
showPackages = do
pkg_ids <- fmap (preloadPackages . pkgState) getDynFlags
io $ putStrLn $ showSDoc $ vcat $
text "packages currently loaded:"
- : map (nest 2 . text . packageIdString) pkg_ids
+ : map (nest 2 . text . packageIdString)
+ (sortBy (compare `on` packageIdFS) pkg_ids)
where showFlag (ExposePackage p) = text $ " -package " ++ p
showFlag (HidePackage p) = text $ " -hide-package " ++ p
showFlag (IgnorePackage p) = text $ " -ignore-package " ++ p
dflags <- getDynFlags
io $ putStrLn $ showSDoc $ vcat $
text "active language flags:" :
- [text (" -X" ++ str) | (str,f) <- DynFlags.xFlags, dopt f dflags]
+ [text (" -X" ++ str) | (str, f, _) <- DynFlags.xFlags, dopt f dflags]
-- -----------------------------------------------------------------------------
-- Completion
completeNone _w = return []
completeMacro, completeIdentifier, completeModule,
- completeHomeModule, completeSetOptions, completeFilename,
- completeHomeModuleOrFile
+ completeHomeModule, completeSetOptions, completeShowOptions,
+ completeFilename, completeHomeModuleOrFile
:: String -> IO [String]
-#ifdef USE_READLINE
+#ifdef USE_EDITLINE
completeWord :: String -> Int -> Int -> IO (Maybe (String, [String]))
completeWord w start end = do
line <- Readline.getLineBuffer
':':_ | all isSpace (take (start-1) line) -> wrapCompleter completeCmd w
_other
| ((':':c) : _) <- line_words -> do
- maybe_cmd <- lookupCommand c
- let (n,w') = selectWord (words' 0 line)
- case maybe_cmd of
- Nothing -> return Nothing
- Just (_,_,False,complete) -> wrapCompleter complete w
- Just (_,_,True,complete) -> let complete' w = do rets <- complete w
- return (map (drop n) rets)
- in wrapCompleter complete' w'
+ completionVars <- lookupCompletionVars c
+ case completionVars of
+ (Nothing,complete) -> wrapCompleter complete w
+ (Just breakChars,complete)
+ -> let (n,w') = selectWord
+ (words' (`elem` breakChars) 0 line)
+ complete' w = do rets <- complete w
+ return (map (drop n) rets)
+ in wrapCompleter complete' w'
| ("import" : _) <- line_words ->
wrapCompleter completeModule w
| otherwise -> do
--printf "complete %s, start = %d, end = %d\n" w start end
wrapCompleter completeIdentifier w
- where words' _ [] = []
- words' n str = let (w,r) = break isSpace str
- (s,r') = span isSpace r
- in (n,w):words' (n+length w+length s) r'
+ where words' _ _ [] = []
+ words' isBreak n str = let (w,r) = break isBreak str
+ (s,r') = span isBreak r
+ in (n,w):words' isBreak (n+length w+length s) r'
-- In a Haskell expression we want to parse 'a-b' as three words
- -- where a compiler flag (ie. -fno-monomorphism-restriction) should
+ -- where a compiler flag (e.g. -ddump-simpl) should
-- only be a single word.
selectWord [] = (0,w)
selectWord ((offset,x):xs)
| offset+length x >= start = (start-offset,take (end-offset) x)
| otherwise = selectWord xs
+
+ lookupCompletionVars ('!':_) = return (Just filenameWordBreakChars,
+ completeFilename)
+ lookupCompletionVars c = do
+ maybe_cmd <- lookupCommand' c
+ case maybe_cmd of
+ Just (_,_,ws,f) -> return (ws,f)
+ Nothing -> return (Just filenameWordBreakChars,
+ completeFilename)
+
completeCmd :: String -> IO [String]
completeCmd w = do
return (filter (w `isPrefixOf`) (map cmdName cmds))
completeIdentifier w = do
- s <- restoreSession
- rdrs <- GHC.getRdrNamesInScope s
+ rdrs <- withRestoredSession GHC.getRdrNamesInScope
return (filter (w `isPrefixOf`) (map (showSDoc.ppr) rdrs))
completeModule w = do
- s <- restoreSession
- dflags <- GHC.getSessionDynFlags s
+ dflags <- withRestoredSession GHC.getSessionDynFlags
let pkg_mods = allExposedModules dflags
return (filter (w `isPrefixOf`) (map (showSDoc.ppr) pkg_mods))
completeHomeModule w = do
- s <- restoreSession
- g <- GHC.getModuleGraph s
+ g <- withRestoredSession GHC.getModuleGraph
let home_mods = map GHC.ms_mod_name g
return (filter (w `isPrefixOf`) (map (showSDoc.ppr) home_mods))
completeSetOptions w = do
return (filter (w `isPrefixOf`) options)
- where options = "args":"prog":allFlags
+ where options = "args":"prog":"prompt":"editor":"stop":flagList
+ flagList = map head $ group $ sort allFlags
-completeFilename = Readline.filenameCompletionFunction
+completeShowOptions w = do
+ return (filter (w `isPrefixOf`) options)
+ where options = ["args", "prog", "prompt", "editor", "stop",
+ "modules", "bindings", "linker", "breaks",
+ "context", "packages", "languages"]
+
+completeFilename w = do
+ ws <- Readline.filenameCompletionFunction w
+ case ws of
+ -- If we only found one result, and it's a directory,
+ -- add a trailing slash.
+ [file] -> do
+ isDir <- expandPathIO file >>= doesDirectoryExist
+ if isDir && last file /= '/'
+ then return [file ++ "/"]
+ else return [file]
+ _ -> return ws
+
completeHomeModuleOrFile = unionComplete completeHomeModule completeFilename
wrapCompleter fun w = do
strs <- fun w
case strs of
- [] -> return Nothing
- [x] -> return (Just (x,[]))
+ [] -> Readline.setAttemptedCompletionOver True >> return Nothing
+ [x] -> -- Add a trailing space, unless it already has an appended slash.
+ let appended = if last x == '/' then x else x ++ " "
+ in return (Just (appended,[]))
xs -> case getCommonPrefix xs of
"" -> return (Just ("",xs))
pref -> return (Just (pref,xs))
where
pkg_db = pkgIdMap (pkgState dflags)
#else
-completeMacro = completeNone
-completeIdentifier = completeNone
-completeModule = completeNone
-completeHomeModule = completeNone
-completeSetOptions = completeNone
-completeFilename = completeNone
+completeMacro = completeNone
+completeIdentifier = completeNone
+completeModule = completeNone
+completeHomeModule = completeNone
+completeSetOptions = completeNone
+completeShowOptions = completeNone
+completeFilename = completeNone
completeHomeModuleOrFile=completeNone
#endif
-- raising another exception. We therefore don't put the recursive
-- handler arond the flushing operation, so if stderr is closed
-- GHCi will just die gracefully rather than going into an infinite loop.
-handler :: Exception -> GHCi Bool
+handler :: SomeException -> GHCi Bool
handler exception = do
flushInterpBuffers
io installSignalHandlers
ghciHandle handler (showException exception >> return False)
-showException :: Exception -> GHCi ()
-showException (DynException dyn) =
- case fromDynamic dyn of
- Nothing -> io (putStrLn ("*** Exception: (unknown)"))
- Just Interrupted -> io (putStrLn "Interrupted.")
- Just (CmdLineError s) -> io (putStrLn s) -- omit the location for CmdLineError
- Just ph@PhaseFailed{} -> io (putStrLn (showGhcException ph "")) -- ditto
- Just other_ghc_ex -> io (print other_ghc_ex)
-
-showException other_exception
- = io (putStrLn ("*** Exception: " ++ show other_exception))
+showException :: SomeException -> GHCi ()
+showException se =
+ io $ case fromException se of
+ Just Interrupted -> putStrLn "Interrupted."
+ -- omit the location for CmdLineError:
+ Just (CmdLineError s) -> putStrLn s
+ -- ditto:
+ Just ph@(PhaseFailed {}) -> putStrLn (showGhcException ph "")
+ Just other_ghc_ex -> print other_ghc_ex
+ Nothing -> putStrLn ("*** Exception: " ++ show se)
-----------------------------------------------------------------------------
-- recursive exception handlers
-- in an exception loop (eg. let a = error a in a) the ^C exception
-- may never be delivered. Thanks to Marcin for pointing out the bug.
-ghciHandle :: (Exception -> GHCi a) -> GHCi a -> GHCi a
+ghciHandle :: (SomeException -> GHCi a) -> GHCi a -> GHCi a
ghciHandle h (GHCi m) = GHCi $ \s ->
- Exception.catch (m s)
+ gcatch (m s)
(\e -> unGHCi (ghciUnblock (h e)) s)
ghciUnblock :: GHCi a -> GHCi a
-ghciUnblock (GHCi a) = GHCi $ \s -> Exception.unblock (a s)
+ghciUnblock (GHCi a) =
+ GHCi $ \s -> reifyGhc $ \gs ->
+ Exception.unblock (reflectGhc (a s) gs)
+ghciTry :: GHCi a -> GHCi (Either SomeException a)
+ghciTry (GHCi m) = GHCi $ \s -> gtry (m s)
-- ----------------------------------------------------------------------------
-- Utils
expandPath :: String -> GHCi String
-expandPath path =
+expandPath path = io (expandPathIO path)
+
+expandPathIO :: String -> IO String
+expandPathIO path =
case dropWhile isSpace path of
('~':d) -> do
- tilde <- io getHomeDirectory -- will fail if HOME not defined
+ tilde <- getHomeDirectory -- will fail if HOME not defined
return (tilde ++ '/':d)
other ->
return other
wantInterpretedModule :: String -> GHCi Module
wantInterpretedModule str = do
- session <- getSession
modl <- lookupModule str
- is_interpreted <- io (GHC.moduleIsInterpreted session modl)
+ dflags <- getDynFlags
+ when (GHC.modulePackageId modl /= thisPackage dflags) $
+ ghcError (CmdLineError ("module '" ++ str ++ "' is from another package;\nthis command requires an interpreted module"))
+ is_interpreted <- GHC.moduleIsInterpreted modl
when (not is_interpreted) $
- throwDyn (CmdLineError ("module '" ++ str ++ "' is not interpreted"))
+ ghcError (CmdLineError ("module '" ++ str ++ "' is not interpreted; try \':add *" ++ str ++ "' first"))
return modl
wantNameFromInterpretedModule :: (Name -> SDoc -> GHCi ()) -> String
-> (Name -> GHCi ())
-> GHCi ()
-wantNameFromInterpretedModule noCanDo str and_then = do
- session <- getSession
- names <- io $ GHC.parseName session str
+wantNameFromInterpretedModule noCanDo str and_then =
+ handleSourceError (GHC.printExceptionAndWarnings) $ do
+ names <- GHC.parseName str
case names of
[] -> return ()
(n:_) -> do
- let modl = GHC.nameModule n
+ let modl = ASSERT( isExternalName n ) GHC.nameModule n
if not (GHC.isExternalName n)
then noCanDo n $ ppr n <>
text " is not defined in an interpreted module"
else do
- is_interpreted <- io (GHC.moduleIsInterpreted session modl)
+ is_interpreted <- GHC.moduleIsInterpreted modl
if not is_interpreted
then noCanDo n $ text "module " <> ppr modl <>
text " is not interpreted"
pprintCommand :: Bool -> Bool -> String -> GHCi ()
pprintCommand bind force str = do
- session <- getSession
- io $ pprintClosureCommand session bind force str
+ pprintClosureCommand bind force str
stepCmd :: String -> GHCi ()
stepCmd [] = doContinue (const True) GHC.SingleStep
Nothing -> stepCmd []
Just _ -> do
Just span <- getCurrentBreakSpan
- let f some_span = optSrcSpanFileName span == optSrcSpanFileName some_span
+ let f some_span = srcSpanFileName_maybe span == srcSpanFileName_maybe some_span
doContinue f GHC.SingleStep
stepModuleCmd expression = stepCmd expression
-- doContinue :: SingleStep -> GHCi ()
doContinue :: (SrcSpan -> Bool) -> SingleStep -> GHCi ()
doContinue pred step = do
- session <- getSession
- runResult <- io $ GHC.resume session step
+ runResult <- resume pred step
afterRunStmt pred runResult
return ()
abandonCmd :: String -> GHCi ()
abandonCmd = noArgs $ do
- s <- getSession
- b <- io $ GHC.abandon s -- the prompt will change to indicate the new context
+ b <- GHC.abandon -- the prompt will change to indicate the new context
when (not b) $ io $ putStrLn "There is no computation running."
return ()
| otherwise = io $ putStrLn "Syntax: :history [num]"
where
history num = do
- s <- getSession
- resumes <- io $ GHC.getResumeContext s
+ resumes <- GHC.getResumeContext
case resumes of
[] -> io $ putStrLn "Not stopped at a breakpoint"
(r:_) -> do
let hist = GHC.resumeHistory r
(took,rest) = splitAt num hist
- spans <- mapM (io . GHC.getHistorySpan s) took
- let nums = map (printf "-%-3d:") [(1::Int)..]
- let names = map GHC.historyEnclosingDecl took
- printForUser (vcat(zipWith3
- (\x y z -> x <+> y <+> z)
- (map text nums)
- (map (bold . ppr) names)
- (map (parens . ppr) spans)))
- io $ putStrLn $ if null rest then "<end of history>" else "..."
+ case hist of
+ [] -> io $ putStrLn $
+ "Empty history. Perhaps you forgot to use :trace?"
+ _ -> do
+ spans <- mapM GHC.getHistorySpan took
+ let nums = map (printf "-%-3d:") [(1::Int)..]
+ names = map GHC.historyEnclosingDecl took
+ printForUser (vcat(zipWith3
+ (\x y z -> x <+> y <+> z)
+ (map text nums)
+ (map (bold . ppr) names)
+ (map (parens . ppr) spans)))
+ io $ putStrLn $ if null rest then "<end of history>" else "..."
bold :: SDoc -> SDoc
bold c | do_bold = text start_bold <> c <> text end_bold
backCmd :: String -> GHCi ()
backCmd = noArgs $ do
- s <- getSession
- (names, _, span) <- io $ GHC.back s
- printForUser $ ptext SLIT("Logged breakpoint at") <+> ppr span
- printTypeOfNames s names
+ (names, _, span) <- GHC.back
+ printForUser $ ptext (sLit "Logged breakpoint at") <+> ppr span
+ printTypeOfNames names
-- run the command set with ":set stop <cmd>"
st <- getGHCiState
enqueueCommands [stop st]
forwardCmd :: String -> GHCi ()
forwardCmd = noArgs $ do
- s <- getSession
- (names, ix, span) <- io $ GHC.forward s
+ (names, ix, span) <- GHC.forward
printForUser $ (if (ix == 0)
- then ptext SLIT("Stopped at")
- else ptext SLIT("Logged breakpoint at")) <+> ppr span
- printTypeOfNames s names
+ then ptext (sLit "Stopped at")
+ else ptext (sLit "Logged breakpoint at")) <+> ppr span
+ printTypeOfNames names
-- run the command set with ":set stop <cmd>"
st <- getGHCiState
enqueueCommands [stop st]
-- handle the "break" command
breakCmd :: String -> GHCi ()
breakCmd argLine = do
- session <- getSession
- breakSwitch session $ words argLine
+ breakSwitch $ words argLine
-breakSwitch :: Session -> [String] -> GHCi ()
-breakSwitch _session [] = do
+breakSwitch :: [String] -> GHCi ()
+breakSwitch [] = do
io $ putStrLn "The break command requires at least one argument."
-breakSwitch session (arg1:rest)
- | looksLikeModuleName arg1 = do
+breakSwitch (arg1:rest)
+ | looksLikeModuleName arg1 && not (null rest) = do
mod <- wantInterpretedModule arg1
breakByModule mod rest
| all isDigit arg1 = do
- (toplevel, _) <- io $ GHC.getContext session
+ (toplevel, _) <- GHC.getContext
case toplevel of
(mod : _) -> breakByModuleLine mod (read arg1) rest
[] -> do
wantNameFromInterpretedModule noCanDo arg1 $ \name -> do
let loc = GHC.srcSpanStart (GHC.nameSrcSpan name)
if GHC.isGoodSrcLoc loc
- then findBreakAndSet (GHC.nameModule name) $
+ then ASSERT( isExternalName name )
+ findBreakAndSet (GHC.nameModule name) $
findBreakByCoord (Just (GHC.srcLocFile loc))
(GHC.srcLocLine loc,
GHC.srcLocCol loc)
| otherwise = breakSyntax
breakSyntax :: a
-breakSyntax = throwDyn (CmdLineError "Syntax: :break [<mod>] <line> [<column>]")
+breakSyntax = ghcError (CmdLineError "Syntax: :break [<mod>] <line> [<column>]")
findBreakAndSet :: Module -> (TickArray -> Maybe (Int, SrcSpan)) -> GHCi ()
findBreakAndSet mod lookupTickTree = do
do_bold :: Bool
do_bold = (`isPrefixOf` unsafePerformIO mTerm) `any` ["xterm", "linux"]
where mTerm = System.Environment.getEnv "TERM"
- `Exception.catch` \_ -> return "TERM not set"
+ `catchIO` \_ -> return "TERM not set"
start_bold :: String
start_bold = "\ESC[1m"
listCmd "" = do
mb_span <- getCurrentBreakSpan
case mb_span of
- Nothing -> printForUser $ text "not stopped at a breakpoint; nothing to list"
- Just span | GHC.isGoodSrcSpan span -> io $ listAround span True
- | otherwise -> printForUser $ text "unable to list source for" <+> ppr span
+ Nothing ->
+ printForUser $ text "Not stopped at a breakpoint; nothing to list"
+ Just span
+ | GHC.isGoodSrcSpan span -> io $ listAround span True
+ | otherwise ->
+ do resumes <- GHC.getResumeContext
+ case resumes of
+ [] -> panic "No resumes"
+ (r:_) ->
+ do let traceIt = case GHC.resumeHistory r of
+ [] -> text "rerunning with :trace,"
+ _ -> empty
+ doWhat = traceIt <+> text ":back then :list"
+ printForUser (text "Unable to list source for" <+>
+ ppr span
+ $$ text "Try" <+> doWhat)
listCmd str = list2 (words str)
list2 :: [String] -> GHCi ()
list2 [arg] | all isDigit arg = do
- session <- getSession
- (toplevel, _) <- io $ GHC.getContext session
+ (toplevel, _) <- GHC.getContext
case toplevel of
[] -> io $ putStrLn "No module to list"
(mod : _) -> listModuleLine mod (read arg)
let loc = GHC.srcSpanStart (GHC.nameSrcSpan name)
if GHC.isGoodSrcLoc loc
then do
- tickArray <- getTickArray (GHC.nameModule name)
+ tickArray <- ASSERT( isExternalName name )
+ getTickArray (GHC.nameModule name)
let mb_span = findBreakByCoord (Just (GHC.srcLocFile loc))
(GHC.srcLocLine loc, GHC.srcLocCol loc)
tickArray
listModuleLine :: Module -> Int -> GHCi ()
listModuleLine modl line = do
- session <- getSession
- graph <- io (GHC.getModuleGraph session)
+ graph <- GHC.getModuleGraph
let this = filter ((== modl) . GHC.ms_mod) graph
case this of
[] -> panic "listModuleLine"
summ:_ -> do
- let filename = fromJust (ml_hs_file (GHC.ms_location summ))
+ let filename = expectJust "listModuleLine" (ml_hs_file (GHC.ms_location summ))
loc = GHC.mkSrcLoc (mkFastString (filename)) line 0
io $ listAround (GHC.srcLocSpan loc) False
-- | list a section of a source file around a particular SrcSpan.
-- If the highlight flag is True, also highlight the span using
--- start_bold/end_bold.
+-- start_bold\/end_bold.
listAround :: SrcSpan -> Bool -> IO ()
listAround span do_highlight = do
contents <- BS.readFile (unpackFS file)
lookupModule :: String -> GHCi Module
lookupModule modName
- = do session <- getSession
- io (GHC.findModule session (GHC.mkModuleName modName) Nothing)
+ = GHC.findModule (GHC.mkModuleName modName) Nothing
-- don't reset the counter back to zero?
discardActiveBreakPoints :: GHCi ()
getModBreak :: Module -> GHCi (GHC.BreakArray, Array Int SrcSpan)
getModBreak mod = do
- session <- getSession
- Just mod_info <- io $ GHC.getModuleInfo session mod
+ Just mod_info <- GHC.getModuleInfo mod
let modBreaks = GHC.modInfoModBreaks mod_info
let array = GHC.modBreaks_flags modBreaks
let ticks = GHC.modBreaks_locs modBreaks
setBreakFlag toggle array index
| toggle = GHC.setBreakOn array index
| otherwise = GHC.setBreakOff array index
-