X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=blobdiff_plain;f=ghc%2FInteractiveUI.hs;h=0f68607a92fdbb1e170e6c8d26c2bc880d901b54;hp=24079bb49bff78c23bbe83caa6394bbf3ac9e7e7;hb=b2524b3960999fffdb3767900f58825903f6560f;hpb=1d227d655c7391143baa7ce777be8103d5426e82 diff --git a/ghc/InteractiveUI.hs b/ghc/InteractiveUI.hs index 24079bb..0f68607 100644 --- a/ghc/InteractiveUI.hs +++ b/ghc/InteractiveUI.hs @@ -27,6 +27,8 @@ import GHC ( LoadHowMuch(..), Target(..), TargetId(..), Ghc, handleSourceError ) import PprTyThing import DynFlags +import qualified Lexer +import StringBuffer import Packages -- import PackageConfig @@ -36,13 +38,12 @@ import HscTypes ( handleFlagWarnings ) import HsImpExp import qualified RdrName ( getGRE_NameQualifier_maybes ) -- should this come via GHC? import RdrName (RdrName) -import Outputable hiding (printForUser, printForUserPartWay) +import Outputable hiding (printForUser, printForUserPartWay, bold) import Module -- for ModuleEnv import Name import SrcLoc -- Other random utilities -import CmdLineParser import Digraph import BasicTypes hiding (isTopLevel) import Panic hiding (showException) @@ -81,7 +82,7 @@ import System.Environment import System.Exit ( exitWith, ExitCode(..) ) import System.Directory import System.IO -import System.IO.Error as IO +import System.IO.Error import Data.Char import Data.Array import Control.Monad as Monad @@ -89,12 +90,8 @@ import Text.Printf import Foreign import GHC.Exts ( unsafeCoerce# ) -#if __GLASGOW_HASKELL__ >= 611 import GHC.IO.Exception ( IOErrorType(InvalidArgument) ) import GHC.IO.Handle ( hFlushAll ) -#else -import GHC.IOBase ( IOErrorType(InvalidArgument) ) -#endif import GHC.TopHandler @@ -140,12 +137,13 @@ builtin_commands = [ ("kind", keepGoing' kindOfType, completeIdentifier), ("load", keepGoingPaths loadModule_, completeHomeModuleOrFile), ("list", keepGoing' listCmd, noCompletion), - ("module", keepGoing setContext, completeModule), + ("module", keepGoing setContext, completeSetModule), ("main", keepGoing runMain, completeFilename), ("print", keepGoing printCmd, completeExpression), ("quit", quit, noCompletion), ("reload", keepGoing' reloadModule, noCompletion), ("run", keepGoing runRun, completeFilename), + ("script", keepGoing' scriptCmd, completeFilename), ("set", keepGoing setCmd, completeSetOptions), ("show", keepGoing showCmd, completeShowOptions), ("sprint", keepGoing sprintCmd, completeExpression), @@ -220,6 +218,7 @@ helpText = " :quit exit GHCi\n" ++ " :reload reload the current module set\n" ++ " :run function [ ...] run the function with the given arguments\n" ++ + " :script run the script " ++ " :type show the type of \n" ++ " :undef undefine user-defined command :\n" ++ " :! run the shell command \n" ++ @@ -261,6 +260,7 @@ helpText = "\n" ++ " Options for ':set' and ':unset':\n" ++ "\n" ++ + " +m allow multiline commands\n" ++ " +r revert top-level expressions after each evaluation\n" ++ " +s print timing/memory stats after each evaluation\n" ++ " +t print type after evaluation\n" ++ @@ -284,7 +284,7 @@ helpText = findEditor :: IO String findEditor = do getEnv "EDITOR" - `IO.catch` \_ -> do + `catchIO` \_ -> do #if mingw32_HOST_OS win <- System.Win32.getWindowsDirectory return (win "notepad.exe") @@ -294,6 +294,14 @@ findEditor = do foreign import ccall unsafe "rts_isProfiled" isProfiled :: IO CInt +default_progname, default_prompt, default_stop :: String +default_progname = "" +default_prompt = "%s> " +default_stop = "" + +default_args :: [String] +default_args = [] + interactiveUI :: [(FilePath, Maybe Phase)] -> Maybe [String] -> Ghc () interactiveUI srcs maybe_exprs = do @@ -330,7 +338,7 @@ interactiveUI srcs maybe_exprs = do -- We don't want the cmd line to buffer any input that might be -- intended for the program, so unbuffer stdin. hSetBuffering stdin NoBuffering -#if defined(mingw32_HOST_OS) && __GLASGOW_HASKELL__ >= 611 +#if defined(mingw32_HOST_OS) -- On Unix, stdin will use the locale encoding. The IO library -- doesn't do this on Windows (yet), so for now we use UTF-8, -- for consistency with GHC 6.10 and to make the tests work. @@ -344,14 +352,15 @@ interactiveUI srcs maybe_exprs = do default_editor <- liftIO $ findEditor startGHCi (runGHCi srcs maybe_exprs) - GHCiState{ progname = "", - args = [], - prompt = "%s> ", - stop = "", + GHCiState{ progname = default_progname, + args = default_args, + prompt = default_prompt, + stop = default_stop, editor = default_editor, -- session = session, options = [], prelude = prel_mod, + line_number = 1, break_ctr = 0, breaks = [], tickarrays = emptyModuleEnv, @@ -365,24 +374,26 @@ interactiveUI srcs maybe_exprs = do 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 + either_dir <- tryIO (getAppUserDataDirectory "ghc") + case either_dir of + Right dir -> + do createDirectoryIfMissing False dir `catchIO` \_ -> return () + right dir + _ -> left runGHCi :: [(FilePath, Maybe Phase)] -> Maybe [String] -> GHCi () runGHCi paths maybe_exprs = do - let + let read_dot_files = not opt_IgnoreDotGhci current_dir = return (Just ".ghci") - app_user_dir = io $ withGhcAppData + app_user_dir = liftIO $ withGhcAppData (\dir -> return (Just (dir "ghci.conf"))) (return Nothing) home_dir = do - either_dir <- io $ IO.try (getEnv "HOME") + either_dir <- liftIO $ tryIO (getEnv "HOME") case either_dir of Right home -> return (Just (home ".ghci")) _ -> return Nothing @@ -393,25 +404,27 @@ runGHCi paths maybe_exprs = do sourceConfigFile :: FilePath -> GHCi () sourceConfigFile file = do - exists <- io $ doesFileExist file + exists <- liftIO $ doesFileExist file when exists $ do - dir_ok <- io $ checkPerms (getDirectory file) - file_ok <- io $ checkPerms file + dir_ok <- liftIO $ checkPerms (getDirectory file) + file_ok <- liftIO $ checkPerms file when (dir_ok && file_ok) $ do - either_hdl <- io $ IO.try (openFile file ReadMode) + either_hdl <- liftIO $ tryIO (openFile file ReadMode) case either_hdl of Left _e -> return () -- NOTE: this assumes that runInputT won't affect the terminal; -- can we assume this will always be the case? -- This would be a good place for runFileInputT. - Right hdl -> runInputTWithPrefs defaultPrefs defaultSettings $ do - runCommands $ fileLoop hdl + Right hdl -> + do runInputTWithPrefs defaultPrefs defaultSettings $ + runCommands False $ fileLoop hdl + liftIO (hClose hdl `catchIO` \_ -> return ()) where getDirectory f = case takeDirectory f of "" -> "."; d -> d when (read_dot_files) $ do mcfgs0 <- sequence [ current_dir, app_user_dir, home_dir ] - mcfgs <- io $ mapM canonicalizePath' (catMaybes mcfgs0) + mcfgs <- liftIO $ mapM canonicalizePath' (catMaybes mcfgs0) mapM_ sourceConfigFile $ nub $ catMaybes mcfgs -- nub, because we don't want to read .ghci twice if the -- CWD is $HOME. @@ -427,11 +440,11 @@ runGHCi paths maybe_exprs = do filePaths' <- mapM (Encoding.decode . BS.pack) filePaths loadModule (zip filePaths' phases) when (isJust maybe_exprs && failed ok) $ - io (exitWith (ExitFailure 1)) + liftIO (exitWith (ExitFailure 1)) -- if verbosity is greater than 0, or we are connected to a -- terminal, display the prompt in the interactive loop. - is_tty <- io (hIsTerminalDevice stdin) + is_tty <- liftIO (hIsTerminalDevice stdin) dflags <- getDynFlags let show_prompt = verbosity dflags > 0 || is_tty @@ -439,7 +452,7 @@ runGHCi paths maybe_exprs = do Nothing -> do -- enter the interactive loop - runGHCiInput $ runCommands $ nextInputLine show_prompt is_tty + runGHCiInput $ runCommands True $ nextInputLine show_prompt is_tty Just exprs -> do -- just evaluate the expression we were given enqueueCommands exprs @@ -449,19 +462,19 @@ runGHCi paths maybe_exprs = do -- Jump through some hoops to get the -- current progname in the exception text: -- : - io $ withProgName (progname st) + liftIO $ withProgName (progname st) -- this used to be topHandlerFastExit, see #2228 - $ topHandler e + $ topHandler e runInputTWithPrefs defaultPrefs defaultSettings $ do - runCommands' handle (return Nothing) + runCommands' handle True (return Nothing) -- and finally, exit - io $ do when (verbosity dflags > 0) $ putStrLn "Leaving GHCi." + liftIO $ when (verbosity dflags > 0) $ putStrLn "Leaving GHCi." runGHCiInput :: InputT GHCi a -> GHCi a runGHCiInput f = do - histFile <- io $ withGhcAppData (\dir -> return (Just (dir "ghci_history"))) - (return Nothing) + histFile <- liftIO $ withGhcAppData (\dir -> return (Just (dir "ghci_history"))) + (return Nothing) let settings = setComplete ghciCompleteWord $ defaultSettings {historyFile = histFile} runInputT settings f @@ -507,9 +520,15 @@ checkPerms name = else return True #endif -fileLoop :: MonadIO m => Handle -> InputT m (Maybe String) +incrementLines :: InputT GHCi () +incrementLines = do + st <- lift $ getGHCiState + let ln = 1+(line_number st) + lift $ setGHCiState st{line_number=ln} + +fileLoop :: Handle -> InputT GHCi (Maybe String) fileLoop hdl = do - l <- liftIO $ IO.try $ hGetLine hdl + l <- liftIO $ tryIO $ hGetLine hdl case l of Left e | isEOFError e -> return Nothing | InvalidArgument <- etype -> return Nothing @@ -519,7 +538,9 @@ fileLoop hdl = do -- this can happen if the user closed stdin, or -- perhaps did getContents which closes stdin at -- EOF. - Right l -> return (Just l) + Right l -> do + incrementLines + return (Just l) mkPrompt :: GHCi String mkPrompt = do @@ -570,37 +591,44 @@ queryQueue = do c:cs -> do setGHCiState st{ cmdqueue = cs } return (Just c) -runCommands :: InputT GHCi (Maybe String) -> InputT GHCi () +runCommands :: Bool -> InputT GHCi (Maybe String) -> InputT GHCi () runCommands = runCommands' handler runCommands' :: (SomeException -> GHCi Bool) -- Exception handler + -> Bool -> InputT GHCi (Maybe String) -> InputT GHCi () -runCommands' eh getCmd = do +runCommands' eh resetLineTo1 getCmd = do + when resetLineTo1 $ lift $ do st <- getGHCiState + setGHCiState $ st { line_number = 0 } b <- ghandle (\e -> case fromException e of - Just UserInterrupt -> return False + Just UserInterrupt -> return $ Just False _ -> case fromException e of Just ghc_e -> do liftIO (print (ghc_e :: GhcException)) - return True + return Nothing _other -> liftIO (Exception.throwIO e)) (runOneCommand eh getCmd) - if b then return () else runCommands' eh getCmd + case b of + Nothing -> return () + Just _ -> runCommands' eh resetLineTo1 getCmd runOneCommand :: (SomeException -> GHCi Bool) -> InputT GHCi (Maybe String) - -> InputT GHCi Bool + -> InputT GHCi (Maybe Bool) runOneCommand eh getCmd = do mb_cmd <- noSpace (lift queryQueue) mb_cmd <- maybe (noSpace getCmd) (return . Just) mb_cmd case mb_cmd of - Nothing -> return True - Just c -> ghciHandle (lift . eh) $ + Nothing -> return Nothing + Just c -> ghciHandle (\e -> lift $ eh e >>= return . Just) $ handleSourceError printErrorAndKeepGoing (doCommand c) + -- source error's are handled by runStmt + -- is the handler necessary here? where printErrorAndKeepGoing err = do - GHC.printExceptionAndWarnings err - return False + GHC.printException err + return $ Just True noSpace q = q >>= maybe (return Nothing) (\c->case removeSpaces c of @@ -631,9 +659,64 @@ runOneCommand eh getCmd = do normSpace c = c -- QUESTION: is userError the one to use here? collectError = userError "unterminated multiline command :{ .. :}" - doCommand (':' : cmd) = specialCommand cmd - doCommand stmt = do _ <- timeIt $ lift $ runStmt stmt GHC.RunToCompletion - return False + doCommand (':' : cmd) = do + result <- specialCommand cmd + case result of + True -> return Nothing + _ -> return $ Just True + doCommand stmt = do + ml <- lift $ isOptionSet Multiline + if ml + then do + mb_stmt <- checkInputForLayout stmt getCmd + case mb_stmt of + Nothing -> return $ Just True + Just ml_stmt -> do + result <- timeIt $ lift $ runStmt ml_stmt GHC.RunToCompletion + return $ Just result + else do + result <- timeIt $ lift $ runStmt stmt GHC.RunToCompletion + return $ Just result + +-- #4316 +-- lex the input. If there is an unclosed layout context, request input +checkInputForLayout :: String -> InputT GHCi (Maybe String) + -> InputT GHCi (Maybe String) +checkInputForLayout stmt getStmt = do + dflags' <- lift $ getDynFlags + let dflags = xopt_set dflags' Opt_AlternativeLayoutRule + st <- lift $ getGHCiState + let buf = stringToStringBuffer stmt + loc = mkSrcLoc (fsLit (progname st)) (line_number st) 1 + pstate = Lexer.mkPState dflags buf loc + case Lexer.unP goToEnd pstate of + (Lexer.POk _ False) -> return $ Just stmt + _other -> do + st <- lift getGHCiState + let p = prompt st + lift $ setGHCiState st{ prompt = "%s| " } + mb_stmt <- ghciHandle (\ex -> case fromException ex of + Just UserInterrupt -> return Nothing + _ -> case fromException ex of + Just ghc_e -> + do liftIO (print (ghc_e :: GhcException)) + return Nothing + _other -> liftIO (Exception.throwIO ex)) + getStmt + lift $ getGHCiState >>= \st->setGHCiState st{ prompt = p } + -- the recursive call does not recycle parser state + -- as we use a new string buffer + case mb_stmt of + Nothing -> return Nothing + Just str -> if str == "" + then return $ Just stmt + else do + checkInputForLayout (stmt++"\n"++str) getStmt + where goToEnd = do + eof <- Lexer.nextIsEOF + if eof + then Lexer.activeContext + else Lexer.lexer return >> goToEnd enqueueCommands :: [String] -> GHCi () enqueueCommands cmds = do @@ -648,16 +731,13 @@ runStmt stmt step | "import " `isPrefixOf` stmt = do newContextCmd (Import stmt); return False | otherwise - = do -#if __GLASGOW_HASKELL__ >= 611 - -- In the new IO library, read handles buffer data even if the Handle + = 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 -- are really two stdin Handles. So we flush any bufferred data in -- GHCi's stdin Handle here (only relevant if stdin is attached to -- a file, otherwise the read buffer can't be flushed). - _ <- liftIO $ IO.try $ hFlushAll stdin -#endif - result <- withFlattenedDynflags $ GhciMonad.runStmt stmt step + _ <- liftIO $ tryIO $ hFlushAll stdin + result <- GhciMonad.runStmt stmt step afterRunStmt (const True) result --afterRunStmt :: GHC.RunResult -> GHCi Bool @@ -687,7 +767,7 @@ afterRunStmt step_here run_result = do _ -> return () flushInterpBuffers - io installSignalHandlers + liftIO installSignalHandlers b <- isOptionSet RevertCAFs when b revertCAFs @@ -755,7 +835,7 @@ lookupCommand "" = do Just c -> return $ GotCommand c Nothing -> return NoLastCommand lookupCommand str = do - mc <- io $ lookupCommand' str + mc <- liftIO $ lookupCommand' str st <- getGHCiState setGHCiState st{ last_command = mc } return $ case mc of @@ -768,8 +848,11 @@ lookupCommand' str' = do macros <- readIORef macros_ref let{ (str, cmds) = case str' of ':' : rest -> (rest, builtin_commands) - _ -> (str', macros ++ builtin_commands) } + _ -> (str', builtin_commands ++ macros) } -- look for exact match first, then the first prefix match + -- We consider builtin commands first: since new macros are appended + -- on the *end* of the macros list, this is consistent with the view + -- that things defined earlier should take precedence. See also #3858 return $ case [ c | c <- cmds, str == cmdName c ] of c:_ -> Just c [] -> case [ c | c@(s,_,_) <- cmds, str `isPrefixOf` s ] of @@ -808,16 +891,15 @@ getCurrentBreakModule = do noArgs :: GHCi () -> String -> GHCi () noArgs m "" = m -noArgs _ _ = io $ putStrLn "This command takes no arguments" +noArgs _ _ = liftIO $ putStrLn "This command takes no arguments" help :: String -> GHCi () -help _ = io (putStr helpText) +help _ = liftIO (putStr helpText) info :: String -> InputT GHCi () info "" = ghcError (CmdLineError "syntax: ':i '") -info s = handleSourceError GHC.printExceptionAndWarnings $ - withFlattenedDynflags $ do - { let names = words s +info s = handleSourceError GHC.printException $ + do { let names = words s ; dflags <- getDynFlags ; let pefas = dopt Opt_PrintExplicitForalls dflags ; mapM_ (infoThing pefas) names } @@ -827,7 +909,7 @@ info s = handleSourceError GHC.printExceptionAndWarnings $ mb_stuffs <- mapM GHC.getInfo names let filtered = filterOutChildren (\(t,_f,_i) -> t) (catMaybes mb_stuffs) unqual <- GHC.getPrintUnqual - outputStrLn $ showSDocForUser unqual $ + liftIO $ putStrLn $ showSDocForUser unqual $ vcat (intersperse (text "") $ map (pprInfo pefas) filtered) @@ -855,17 +937,16 @@ pprInfo pefas (thing, fixity, insts) runMain :: String -> GHCi () runMain s = case toArgs s of - Left err -> io (hPutStrLn stderr err) + Left err -> liftIO (hPutStrLn stderr err) Right args -> - withFlattenedDynflags $ do - dflags <- getDynFlags + 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) + Left err -> liftIO (hPutStrLn stderr err) Right (cmd, args) -> doWithArgs args cmd doWithArgs :: [String] -> String -> GHCi () @@ -887,14 +968,14 @@ addModule files = do changeDirectory :: String -> InputT GHCi () changeDirectory "" = do -- :cd on its own changes to the user's home directory - either_dir <- liftIO $ IO.try getHomeDirectory + either_dir <- liftIO $ tryIO getHomeDirectory case either_dir of Left _e -> return () Right dir -> changeDirectory dir changeDirectory dir = do graph <- GHC.getModuleGraph when (not (null graph)) $ - outputStr "Warning: changing directory causes all loaded modules to be unloaded,\nbecause the search path has changed.\n" + 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 @@ -905,7 +986,7 @@ changeDirectory dir = do trySuccess :: GHC.GhcMonad m => m SuccessFlag -> m SuccessFlag trySuccess act = - handleSourceError (\e -> do GHC.printExceptionAndWarnings e + handleSourceError (\e -> do GHC.printException e return Failed) $ do act @@ -916,7 +997,7 @@ editFile str = let cmd = editor st when (null cmd) $ ghcError (CmdLineError "editor not set, use :set editor") - _ <- io $ system (cmd ++ ' ':file) + _ <- liftIO $ system (cmd ++ ' ':file) return () -- The user didn't specify a file so we pick one for them. @@ -953,16 +1034,16 @@ chooseEditFile = defineMacro :: Bool{-overwrite-} -> String -> GHCi () defineMacro _ (':':_) = - io $ putStrLn "macro name cannot start with a colon" + liftIO $ putStrLn "macro name cannot start with a colon" defineMacro overwrite s = do let (macro_name, definition) = break isSpace s - macros <- io (readIORef macros_ref) + macros <- liftIO (readIORef macros_ref) let defined = map cmdName macros if (null macro_name) then if null defined - then io $ putStrLn "no macros defined" - else io $ putStr ("the following macros are defined:\n" ++ - unlines defined) + then liftIO $ putStrLn "no macros defined" + else liftIO $ putStr ("the following macros are defined:\n" ++ + unlines defined) else do if (not overwrite && macro_name `elem` defined) then ghcError (CmdLineError @@ -976,15 +1057,15 @@ defineMacro overwrite s = do let new_expr = '(' : definition ++ ") :: String -> IO String" -- compile the expression - handleSourceError (\e -> GHC.printExceptionAndWarnings e) $ - withFlattenedDynflags $ do + handleSourceError (\e -> GHC.printException e) $ + do hv <- GHC.compileExpr new_expr - io (writeIORef macros_ref -- - (filtered ++ [(macro_name, lift . runMacro hv, noCompletion)])) + liftIO (writeIORef macros_ref -- + (filtered ++ [(macro_name, lift . runMacro hv, noCompletion)])) runMacro :: GHC.HValue{-String -> IO String-} -> String -> GHCi Bool runMacro fun s = do - str <- io ((unsafeCoerce# fun :: String -> IO String) s) + str <- liftIO ((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 ()) @@ -994,20 +1075,20 @@ runMacro fun s = do undefineMacro :: String -> GHCi () undefineMacro str = mapM_ undef (words str) where undef macro_name = do - cmds <- io (readIORef macros_ref) + cmds <- liftIO (readIORef macros_ref) if (macro_name `notElem` map cmdName cmds) then ghcError (CmdLineError ("macro '" ++ macro_name ++ "' is not defined")) else do - io (writeIORef macros_ref (filter ((/= macro_name) . cmdName) cmds)) + liftIO (writeIORef macros_ref (filter ((/= macro_name) . cmdName) cmds)) cmdCmd :: String -> GHCi () cmdCmd str = do let expr = '(' : str ++ ") :: IO String" - handleSourceError (\e -> GHC.printExceptionAndWarnings e) $ - withFlattenedDynflags $ do + handleSourceError (\e -> GHC.printException e) $ + do hv <- GHC.compileExpr expr - cmds <- io $ (unsafeCoerce# hv :: IO String) + cmds <- liftIO $ (unsafeCoerce# hv :: IO String) enqueueCommands (lines cmds) return () @@ -1047,9 +1128,9 @@ checkModule :: String -> InputT GHCi () checkModule m = do let modl = GHC.mkModuleName m prev_context <- GHC.getContext - ok <- handleSourceError (\e -> GHC.printExceptionAndWarnings e >> return False) $ do + ok <- handleSourceError (\e -> GHC.printException e >> return False) $ do r <- GHC.typecheckModule =<< GHC.parseModule =<< GHC.getModSummary modl - outputStrLn (showSDoc ( + liftIO $ putStrLn $ showSDoc $ case GHC.moduleInfo r of cm | Just scope <- GHC.modInfoTopLevelScope cm -> let @@ -1058,7 +1139,7 @@ checkModule m = do in (text "global names: " <+> ppr global) $$ (text "local names: " <+> ppr local) - _ -> empty)) + _ -> empty return True afterLoad (successIf ok) False prev_context @@ -1088,7 +1169,7 @@ afterLoad ok retain_context prev_context = do loaded_mod_names = map GHC.moduleName loaded_mods modulesLoadedMsg ok loaded_mod_names - withFlattenedDynflags $ lift $ setContextAfterLoad prev_context retain_context loaded_mod_summaries + lift $ setContextAfterLoad prev_context retain_context loaded_mod_summaries setContextAfterLoad :: ([Module],[(Module, Maybe (ImportDecl RdrName))]) -> Bool -> [GHC.ModSummary] -> GHCi () @@ -1161,15 +1242,14 @@ modulesLoadedMsg ok mods = do punctuate comma (map ppr mods)) <> text "." case ok of Failed -> - outputStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)) + liftIO $ putStrLn $ showSDoc (text "Failed, modules loaded: " <> mod_commas) Succeeded -> - outputStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)) + liftIO $ putStrLn $ showSDoc (text "Ok, modules loaded: " <> mod_commas) typeOfExpr :: String -> InputT GHCi () typeOfExpr str - = handleSourceError (\e -> GHC.printExceptionAndWarnings e) - $ withFlattenedDynflags + = handleSourceError GHC.printException $ do ty <- GHC.exprType str dflags <- getDynFlags @@ -1178,8 +1258,7 @@ typeOfExpr str kindOfType :: String -> InputT GHCi () kindOfType str - = handleSourceError (\e -> GHC.printExceptionAndWarnings e) - $ withFlattenedDynflags + = handleSourceError GHC.printException $ do ty <- GHC.typeKind str printForUser $ text str <+> dcolon <+> ppr ty @@ -1188,14 +1267,40 @@ quit :: String -> InputT GHCi Bool quit _ = return True shellEscape :: String -> GHCi Bool -shellEscape str = io (system str >> return False) +shellEscape str = liftIO (system str >> return False) -withFlattenedDynflags :: GHC.GhcMonad m => m a -> m a -withFlattenedDynflags m - = do dflags <- GHC.getSessionDynFlags - gbracket (GHC.setSessionDynFlags (ensureFlattenedExtensionFlags dflags)) - (\_ -> GHC.setSessionDynFlags dflags) - (\_ -> m) +----------------------------------------------------------------------------- +-- running a script file #1363 + +scriptCmd :: String -> InputT GHCi () +scriptCmd s = do + case words s of + [s] -> runScript s + _ -> ghcError (CmdLineError "syntax: :script ") + +runScript :: String -- ^ filename + -> InputT GHCi () +runScript filename = do + either_script <- liftIO $ tryIO (openFile filename ReadMode) + case either_script of + Left _err -> ghcError (CmdLineError $ "IO error: \""++filename++"\" " + ++(ioeGetErrorString _err)) + Right script -> do + st <- lift $ getGHCiState + let prog = progname st + line = line_number st + lift $ setGHCiState st{progname=filename,line_number=0} + scriptLoop script + liftIO $ hClose script + new_st <- lift $ getGHCiState + lift $ setGHCiState new_st{progname=prog,line_number=line} + where scriptLoop script = do + res <- runOneCommand handler $ fileLoop script + case res of + Nothing -> return () + Just succ -> if succ + then scriptLoop script + else return () ----------------------------------------------------------------------------- -- Browsing a module's contents @@ -1225,7 +1330,7 @@ browseCmd bang m = -- indicate import modules, to aid qualifying unqualified names -- with sorted, sort items alphabetically browseModule :: Bool -> Module -> Bool -> InputT GHCi () -browseModule bang modl exports_only = withFlattenedDynflags $ do +browseModule bang modl exports_only = do -- :browse! reports qualifiers wrt current context current_unqual <- GHC.getPrintUnqual -- Temporarily set the context to the module we're interested in, @@ -1300,7 +1405,7 @@ browseModule bang modl exports_only = withFlattenedDynflags $ do let prettyThings = map (pretty pefas) things prettyThings' | bang = annotate $ zip modNames prettyThings | otherwise = prettyThings - outputStrLn $ showSDocForUser unqual (vcat prettyThings') + liftIO $ putStrLn $ showSDocForUser unqual (vcat prettyThings') -- ToDo: modInfoInstances currently throws an exception for -- package modules. When it works, we can do this: -- $$ vcat (map GHC.pprInstance (GHC.modInfoInstances mod_info)) @@ -1338,7 +1443,6 @@ setContext str playCtxtCmd:: Bool -> CtxtCmd -> GHCi () playCtxtCmd fail cmd = do - withFlattenedDynflags $ do (prev_as,prev_bs) <- GHC.getContext case cmd of SetContext as bs -> do @@ -1406,18 +1510,18 @@ setCmd :: String -> GHCi () setCmd "" = do st <- getGHCiState let opts = options st - io $ putStrLn (showSDoc ( + liftIO $ putStrLn (showSDoc ( text "options currently set: " <> if null opts then text "none." else hsep (map (\o -> char '+' <> text (optToStr o)) opts) )) dflags <- getDynFlags - io $ putStrLn (showSDoc ( + liftIO $ putStrLn (showSDoc ( vcat (text "GHCi-specific dynamic flag settings:" :map (flagSetting dflags) ghciFlags) )) - io $ putStrLn (showSDoc ( + liftIO $ putStrLn (showSDoc ( vcat (text "other dynamic, non-language, flag settings:" :map (flagSetting dflags) others) )) @@ -1436,17 +1540,17 @@ setCmd str = case getCmd str of Right ("args", rest) -> case toArgs rest of - Left err -> io (hPutStrLn stderr err) + Left err -> liftIO (hPutStrLn stderr err) Right args -> setArgs args Right ("prog", rest) -> case toArgs rest of Right [prog] -> setProg prog - _ -> io (hPutStrLn stderr "syntax: :set prog ") + _ -> liftIO (hPutStrLn stderr "syntax: :set prog ") 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) + Left err -> liftIO (hPutStrLn stderr err) Right wds -> setOptions wds setArgs, setOptions :: [String] -> GHCi () @@ -1484,13 +1588,13 @@ setStop cmd = do setPrompt value = do st <- getGHCiState if null value - then io $ hPutStrLn stderr $ "syntax: :set prompt , currently \"" ++ prompt st ++ "\"" + then liftIO $ hPutStrLn stderr $ "syntax: :set prompt , currently \"" ++ prompt st ++ "\"" 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." + liftIO $ hPutStrLn stderr "Can't parse prompt string. Use Haskell syntax." _ -> setGHCiState (st { prompt = value }) setOptions wds = @@ -1504,11 +1608,13 @@ newDynFlags :: [String] -> GHCi () newDynFlags minus_opts = do dflags <- getDynFlags let pkg_flags = packageFlags dflags - (dflags', leftovers, warns) <- io $ GHC.parseDynamicFlags dflags $ map noLoc minus_opts - handleFlagWarnings dflags' warns + (dflags', leftovers, warns) <- liftIO $ GHC.parseDynamicFlags dflags $ map noLoc minus_opts + liftIO $ handleFlagWarnings dflags' warns if (not (null leftovers)) - then ghcError $ errorsToGhcException leftovers + then ghcError . CmdLineError + $ "Some flags have not been recognized: " + ++ (concat . intersperse ", " $ map unLoc leftovers) else return () new_pkgs <- setDynFlags dflags' @@ -1517,10 +1623,10 @@ newDynFlags minus_opts = do -- and link the new packages. dflags <- getDynFlags when (packageFlags dflags /= pkg_flags) $ do - io $ hPutStrLn stderr "package flags have changed, resetting and loading new packages..." + liftIO $ hPutStrLn stderr "package flags have changed, resetting and loading new packages..." GHC.setTargets [] _ <- GHC.load LoadAllTargets - io (linkPackages dflags new_pkgs) + liftIO (linkPackages dflags new_pkgs) -- package flags changed, we can't re-use any of the old context setContextAfterLoad ([],[]) False [] return () @@ -1528,22 +1634,32 @@ newDynFlags minus_opts = do unsetOptions :: String -> GHCi () unsetOptions str - = do -- first, deal with the GHCi opts (+s, +t, etc.) - let opts = words str - (minus_opts, rest1) = partition isMinus opts - (plus_opts, rest2) = partitionWith isPlus rest1 - - if (not (null rest2)) - then io (putStrLn ("unknown option: '" ++ head rest2 ++ "'")) - else do + = -- first, deal with the GHCi opts (+s, +t, etc.) + let opts = words str + (minus_opts, rest1) = partition isMinus opts + (plus_opts, rest2) = partitionWith isPlus rest1 + (other_opts, rest3) = partition (`elem` map fst defaulters) rest2 + + defaulters = + [ ("args" , setArgs default_args) + , ("prog" , setProg default_progname) + , ("prompt", setPrompt default_prompt) + , ("editor", liftIO findEditor >>= setEditor) + , ("stop" , setStop default_stop) + ] + + no_flag ('-':'f':rest) = return ("-fno-" ++ rest) + no_flag f = ghcError (ProgramError ("don't know how to reverse " ++ f)) + + in if (not (null rest3)) + then liftIO (putStrLn ("unknown option: '" ++ head rest3 ++ "'")) + else do + mapM_ (fromJust.flip lookup defaulters) other_opts - mapM_ unsetOpt plus_opts - - let no_flag ('-':'f':rest) = return ("-fno-" ++ rest) - no_flag f = ghcError (ProgramError ("don't know how to reverse " ++ f)) + mapM_ unsetOpt plus_opts - no_flags <- mapM no_flag minus_opts - newDynFlags no_flags + no_flags <- mapM no_flag minus_opts + newDynFlags no_flags isMinus :: String -> Bool isMinus ('-':_) = True @@ -1557,21 +1673,23 @@ setOpt, unsetOpt :: String -> GHCi () setOpt str = case strToGHCiOpt str of - Nothing -> io (putStrLn ("unknown option: '" ++ str ++ "'")) + Nothing -> liftIO (putStrLn ("unknown option: '" ++ str ++ "'")) Just o -> setOption o unsetOpt str = case strToGHCiOpt str of - Nothing -> io (putStrLn ("unknown option: '" ++ str ++ "'")) + Nothing -> liftIO (putStrLn ("unknown option: '" ++ str ++ "'")) Just o -> unsetOption o strToGHCiOpt :: String -> (Maybe GHCiOption) +strToGHCiOpt "m" = Just Multiline strToGHCiOpt "s" = Just ShowTiming strToGHCiOpt "t" = Just ShowType strToGHCiOpt "r" = Just RevertCAFs strToGHCiOpt _ = Nothing optToStr :: GHCiOption -> String +optToStr Multiline = "m" optToStr ShowTiming = "s" optToStr ShowType = "t" optToStr RevertCAFs = "r" @@ -1580,17 +1698,17 @@ optToStr RevertCAFs = "r" -- code for `:show' showCmd :: String -> GHCi () -showCmd str = withFlattenedDynflags $ do +showCmd str = do st <- getGHCiState case words str of - ["args"] -> io $ putStrLn (show (args st)) - ["prog"] -> io $ putStrLn (show (progname st)) - ["prompt"] -> io $ putStrLn (show (prompt st)) - ["editor"] -> io $ putStrLn (show (editor st)) - ["stop"] -> io $ putStrLn (show (stop st)) + ["args"] -> liftIO $ putStrLn (show (args st)) + ["prog"] -> liftIO $ putStrLn (show (progname st)) + ["prompt"] -> liftIO $ putStrLn (show (prompt st)) + ["editor"] -> liftIO $ putStrLn (show (editor st)) + ["stop"] -> liftIO $ putStrLn (show (stop st)) ["modules" ] -> showModules ["bindings"] -> showBindings - ["linker"] -> io showLinkerState + ["linker"] -> liftIO showLinkerState ["breaks"] -> showBkptTable ["context"] -> showContext ["packages"] -> showPackages @@ -1602,7 +1720,7 @@ showModules :: GHCi () showModules = do loaded_mods <- getLoadedModules -- we want *loaded* modules only, see #1734 - let show_one ms = do m <- GHC.showModule ms; io (putStrLn m) + let show_one ms = do m <- GHC.showModule ms; liftIO (putStrLn m) mapM_ show_one loaded_mods getLoadedModules :: GHC.GhcMonad m => m [GHC.ModSummary] @@ -1642,14 +1760,9 @@ showContext = do showPackages :: GHCi () showPackages = do pkg_flags <- fmap packageFlags getDynFlags - io $ putStrLn $ showSDoc $ vcat $ + liftIO $ putStrLn $ showSDoc $ vcat $ text ("active package flags:"++if null pkg_flags then " none" else "") : map showFlag pkg_flags - pkg_ids <- fmap (preloadPackages . pkgState) getDynFlags - io $ putStrLn $ showSDoc $ vcat $ - text "packages currently loaded:" - : 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 @@ -1658,14 +1771,15 @@ showPackages = do showLanguages :: GHCi () showLanguages = do dflags <- getDynFlags - io $ putStrLn $ showSDoc $ vcat $ + liftIO $ putStrLn $ showSDoc $ vcat $ text "active language flags:" : - [text (" -X" ++ str) | (str, f, _) <- DynFlags.xFlags, dopt f dflags] + [text (" -X" ++ str) | (str, f, _) <- DynFlags.xFlags, xopt f dflags] -- ----------------------------------------------------------------------------- -- Completion completeCmd, completeMacro, completeIdentifier, completeModule, + completeSetModule, completeHomeModule, completeSetOptions, completeShowOptions, completeHomeModuleOrFile, completeExpression :: CompletionFunc GHCi @@ -1711,6 +1825,18 @@ completeModule = wrapIdentCompleter $ \w -> do return $ filter (w `isPrefixOf`) $ map (showSDoc.ppr) $ loaded_mods ++ pkg_mods +completeSetModule = wrapIdentCompleterWithModifier "+-" $ \m w -> do + modules <- case m of + Just '-' -> do + (toplevs, exports) <- GHC.getContext + return $ map GHC.moduleName (nub (map fst exports) ++ toplevs) + _ -> do + dflags <- GHC.getSessionDynFlags + let pkg_mods = allExposedModules dflags + loaded_mods <- liftM (map GHC.ms_mod_name) getLoadedModules + return $ loaded_mods ++ pkg_mods + return $ filter (w `isPrefixOf`) $ map (showSDoc.ppr) modules + completeHomeModule = wrapIdentCompleter listHomeModules listHomeModules :: String -> GHCi [String] @@ -1748,6 +1874,12 @@ wrapCompleter breakChars fun = completeWord Nothing breakChars wrapIdentCompleter :: (String -> GHCi [String]) -> CompletionFunc GHCi wrapIdentCompleter = wrapCompleter word_break_chars +wrapIdentCompleterWithModifier :: String -> (Maybe Char -> String -> GHCi [String]) -> CompletionFunc GHCi +wrapIdentCompleterWithModifier modifChars fun = completeWordWithPrev Nothing word_break_chars + $ \rest -> fmap (map simpleCompletion) . fmap sort . fun (getModifier rest) + where + getModifier = find (`elem` modifChars) + allExposedModules :: DynFlags -> [ModuleName] allExposedModules dflags = concat (map exposedModules (filter exposed (eltsUFM pkg_db))) @@ -1774,21 +1906,21 @@ handler :: SomeException -> GHCi Bool handler exception = do flushInterpBuffers - io installSignalHandlers + liftIO installSignalHandlers ghciHandle handler (showException exception >> return False) showException :: SomeException -> GHCi () showException se = - io $ case fromException se of - -- 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 -> - case fromException se of - Just UserInterrupt -> putStrLn "Interrupted." - _other -> putStrLn ("*** Exception: " ++ show se) + liftIO $ case fromException se of + -- 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 -> + case fromException se of + Just UserInterrupt -> putStrLn "Interrupted." + _ -> putStrLn ("*** Exception: " ++ show se) ----------------------------------------------------------------------------- -- recursive exception handlers @@ -1840,7 +1972,7 @@ wantNameFromInterpretedModule :: GHC.GhcMonad m -> (Name -> m ()) -> m () wantNameFromInterpretedModule noCanDo str and_then = - handleSourceError (GHC.printExceptionAndWarnings) $ do + handleSourceError GHC.printException $ do names <- GHC.parseName str case names of [] -> return () @@ -1866,7 +1998,7 @@ forceCmd = pprintCommand False True pprintCommand :: Bool -> Bool -> String -> GHCi () pprintCommand bind force str = do - withFlattenedDynflags $ pprintClosureCommand bind force str + pprintClosureCommand bind force str stepCmd :: String -> GHCi () stepCmd [] = doContinue (const True) GHC.SingleStep @@ -1923,16 +2055,15 @@ doContinue pred step = do abandonCmd :: String -> GHCi () abandonCmd = noArgs $ do b <- GHC.abandon -- the prompt will change to indicate the new context - when (not b) $ io $ putStrLn "There is no computation running." - return () + when (not b) $ liftIO $ putStrLn "There is no computation running." deleteCmd :: String -> GHCi () deleteCmd argLine = do deleteSwitch $ words argLine where deleteSwitch :: [String] -> GHCi () - deleteSwitch [] = - io $ putStrLn "The delete command requires at least one argument." + deleteSwitch [] = + liftIO $ putStrLn "The delete command requires at least one argument." -- delete all break points deleteSwitch ("*":_rest) = discardActiveBreakPoints deleteSwitch idents = do @@ -1947,28 +2078,28 @@ historyCmd :: String -> GHCi () historyCmd arg | null arg = history 20 | all isDigit arg = history (read arg) - | otherwise = io $ putStrLn "Syntax: :history [num]" + | otherwise = liftIO $ putStrLn "Syntax: :history [num]" where history num = do resumes <- GHC.getResumeContext case resumes of - [] -> io $ putStrLn "Not stopped at a breakpoint" + [] -> liftIO $ putStrLn "Not stopped at a breakpoint" (r:_) -> do let hist = GHC.resumeHistory r (took,rest) = splitAt num hist case hist of - [] -> io $ putStrLn $ + [] -> liftIO $ 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 + names = map GHC.historyEnclosingDecls took printForUser (vcat(zipWith3 (\x y z -> x <+> y <+> z) (map text nums) - (map (bold . ppr) names) + (map (bold . hcat . punctuate colon . map text) names) (map (parens . ppr) spans))) - io $ putStrLn $ if null rest then "" else "..." + liftIO $ putStrLn $ if null rest then "" else "..." bold :: SDoc -> SDoc bold c | do_bold = text start_bold <> c <> text end_bold @@ -1997,11 +2128,11 @@ forwardCmd = noArgs $ do -- handle the "break" command breakCmd :: String -> GHCi () breakCmd argLine = do - withFlattenedDynflags $ breakSwitch $ words argLine + breakSwitch $ words argLine breakSwitch :: [String] -> GHCi () breakSwitch [] = do - io $ putStrLn "The break command requires at least one argument." + liftIO $ putStrLn "The break command requires at least one argument." breakSwitch (arg1:rest) | looksLikeModuleName arg1 && not (null rest) = do mod <- wantInterpretedModule arg1 @@ -2011,8 +2142,8 @@ breakSwitch (arg1:rest) case toplevel of (mod : _) -> breakByModuleLine mod (read arg1) rest [] -> do - io $ putStrLn "Cannot find default module for breakpoint." - io $ putStrLn "Perhaps no modules are loaded for debugging?" + liftIO $ putStrLn "Cannot find default module for breakpoint." + liftIO $ putStrLn "Perhaps no modules are loaded for debugging?" | otherwise = do -- try parsing it as an identifier wantNameFromInterpretedModule noCanDo arg1 $ \name -> do let loc = GHC.srcSpanStart (GHC.nameSrcSpan name) @@ -2049,9 +2180,9 @@ findBreakAndSet mod lookupTickTree = do tickArray <- getTickArray mod (breakArray, _) <- getModBreak mod case lookupTickTree tickArray of - Nothing -> io $ putStrLn $ "No breakpoints found at that location." + Nothing -> liftIO $ putStrLn $ "No breakpoints found at that location." Just (tick, span) -> do - success <- io $ setBreakFlag True breakArray tick + success <- liftIO $ setBreakFlag True breakArray tick if success then do (alreadySet, nm) <- @@ -2130,7 +2261,7 @@ end_bold :: String end_bold = "\ESC[0m" listCmd :: String -> InputT GHCi () -listCmd c = withFlattenedDynflags $ listCmd' c +listCmd c = listCmd' c listCmd' :: String -> InputT GHCi () listCmd' "" = do @@ -2158,7 +2289,7 @@ list2 :: [String] -> InputT GHCi () list2 [arg] | all isDigit arg = do (toplevel, _) <- GHC.getContext case toplevel of - [] -> outputStrLn "No module to list" + [] -> liftIO $ putStrLn "No module to list" (mod : _) -> listModuleLine mod (read arg) list2 [arg1,arg2] | looksLikeModuleName arg1, all isDigit arg2 = do mod <- wantInterpretedModule arg1 @@ -2183,7 +2314,7 @@ list2 [arg] = do noCanDo n why = printForUser $ text "cannot list source code for " <> ppr n <> text ": " <> why list2 _other = - outputStrLn "syntax: :list [ | | ]" + liftIO $ putStrLn "syntax: :list [ | | ]" listModuleLine :: Module -> Int -> InputT GHCi () listModuleLine modl line = do @@ -2224,7 +2355,7 @@ listAround span do_highlight = do let output = BS.intercalate (BS.pack "\n") prefixed utf8Decoded <- liftIO $ BS.useAsCStringLen output $ \(p,n) -> utf8DecodeString (castPtr p) n - outputStrLn utf8Decoded + liftIO $ putStrLn utf8Decoded where file = GHC.srcSpanFile span line1 = GHC.srcSpanStartLine span @@ -2324,7 +2455,7 @@ deleteBreak identity = do turnOffBreak :: BreakLocation -> GHCi Bool turnOffBreak loc = do (arr, _) <- getModBreak (breakModule loc) - io $ setBreakFlag False arr (breakTick loc) + liftIO $ setBreakFlag False arr (breakTick loc) getModBreak :: Module -> GHCi (GHC.BreakArray, Array Int SrcSpan) getModBreak mod = do