X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=compiler%2Fghci%2FInteractiveUI.hs;h=045cf63d5f0f064830c71edd7ff1118601331bc6;hb=806ab6331b967d6176b8790a0b1b551ec0e8e2b6;hp=31ca03f1e2dd949add2b3977eca59e9c430fe987;hpb=7f2529748488a004c1b4d748ddce0eecf71656be;p=ghc-hetmet.git diff --git a/compiler/ghci/InteractiveUI.hs b/compiler/ghci/InteractiveUI.hs index 31ca03f..045cf63 100644 --- a/compiler/ghci/InteractiveUI.hs +++ b/compiler/ghci/InteractiveUI.hs @@ -23,13 +23,14 @@ import GHC ( Session, LoadHowMuch(..), Target(..), TargetId(..), import PprTyThing import DynFlags -#ifdef USE_READLINE import Packages +#ifdef USE_READLINE import PackageConfig import UniqFM #endif import HscTypes ( implicitTyThings ) +import qualified RdrName ( getGRE_NameQualifier_maybes ) -- should this come via GHC? import Outputable hiding (printForUser) import Module -- for ModuleEnv import Name @@ -113,7 +114,8 @@ builtin_commands = [ ("abandon", keepGoing abandonCmd, False, completeNone), ("break", keepGoing breakCmd, False, completeIdentifier), ("back", keepGoing backCmd, False, completeNone), - ("browse", keepGoing browseCmd, False, completeModule), + ("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), @@ -163,8 +165,10 @@ helpText = " Commands available from the prompt:\n" ++ "\n" ++ " evaluate/run \n" ++ + " :{\\n ..lines.. \\n:}\\n multiline command\n" ++ " :add ... add module(s) to the current target set\n" ++ - " :browse [*] display the names defined by \n" ++ + " :browse[!] [-s] [[*]] display the names defined by module \n" ++ + " (!: more details; -s: sort; *: all top-level names)\n" ++ " :cd change directory to \n" ++ " :cmd run the commands returned by ::IO String\n" ++ " :ctags [] create tags file for Vi (default: \"tags\")\n" ++ @@ -223,6 +227,8 @@ helpText = " +t print type after evaluation\n" ++ " - most GHC command line flags can also be set here\n" ++ " (eg. -v2, -fglasgow-exts, etc.)\n" ++ + " for GHCi-specific flags, see User's Guide,\n"++ + " Flag reference, Interactive-mode options\n" ++ "\n" ++ " -- Commands for displaying information:\n" ++ "\n" ++ @@ -230,6 +236,8 @@ helpText = " :show breaks show the active breakpoints\n" ++ " :show context show the breakpoint context\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 show anything that can be set with :set (e.g. args)\n" ++ "\n" @@ -330,11 +338,11 @@ runGHCi paths maybe_expr = do either_hdl <- io (IO.try (openFile "./.ghci" ReadMode)) case either_hdl of Left _e -> return () - Right hdl -> fileLoop hdl False + Right hdl -> runCommands (fileLoop hdl False) when (read_dot_files) $ do -- Read in $HOME/.ghci - either_dir <- io (IO.try (getEnv "HOME")) + either_dir <- io (IO.try getHomeDirectory) case either_dir of Left _e -> return () Right dir -> do @@ -346,7 +354,7 @@ runGHCi paths maybe_expr = do either_hdl <- io (IO.try (openFile file ReadMode)) case either_hdl of Left _e -> return () - Right hdl -> fileLoop hdl False + Right hdl -> runCommands (fileLoop hdl False) -- Perform a :load for files given on the GHCi command line -- When in -e mode, if the load fails then we want to stop @@ -408,10 +416,10 @@ interactiveLoop is_tty show_prompt = -- read commands from stdin #ifdef USE_READLINE if (is_tty) - then readlineLoop - else fileLoop stdin show_prompt + then runCommands readlineLoop + else runCommands (fileLoop stdin show_prompt) #else - fileLoop stdin show_prompt + runCommands (fileLoop stdin show_prompt) #endif @@ -425,10 +433,11 @@ interactiveLoop is_tty show_prompt = -- the same directory while a process is running. checkPerms :: String -> IO Bool -checkPerms name = #ifdef mingw32_HOST_OS +checkPerms _ = return True #else +checkPerms name = Util.handle (\_ -> return False) $ do st <- getFileStatus name me <- getRealUserID @@ -446,26 +455,22 @@ checkPerms name = else return True #endif -fileLoop :: Handle -> Bool -> GHCi () +fileLoop :: Handle -> Bool -> GHCi (Maybe String) fileLoop hdl show_prompt = do when show_prompt $ do prompt <- mkPrompt (io (putStr prompt)) l <- io (IO.try (hGetLine hdl)) case l of - Left e | isEOFError e -> return () - | InvalidArgument <- etype -> return () - | otherwise -> io (ioError e) - where etype = ioeGetErrorType e - -- treat InvalidArgument in the same way as EOF: - -- this can happen if the user closed stdin, or - -- perhaps did getContents which closes stdin at - -- EOF. - Right l -> - case removeSpaces l of - "" -> fileLoop hdl show_prompt - l -> do quit <- runCommands l - if quit then return () else fileLoop hdl show_prompt + Left e | isEOFError e -> return Nothing + | InvalidArgument <- etype -> return Nothing + | otherwise -> io (ioError e) + where etype = ioeGetErrorType e + -- treat InvalidArgument in the same way as EOF: + -- this can happen if the user closed stdin, or + -- perhaps did getContents which closes stdin at + -- EOF. + Right l -> return (Just l) mkPrompt :: GHCi String mkPrompt = do @@ -505,41 +510,72 @@ mkPrompt = do #ifdef USE_READLINE -readlineLoop :: GHCi () +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 + -- readline sometimes puts stdin into blocking mode, + -- so we need to put it back for the IO library splatSavedSession case l of - Nothing -> return () - Just l -> - case removeSpaces l of - "" -> readlineLoop - l -> do - io (addHistory l) - quit <- runCommands l - if quit then return () else readlineLoop + Nothing -> return Nothing + Just l -> do + io (addHistory l) + return (Just l) #endif -runCommands :: String -> GHCi Bool -runCommands cmd = do - q <- ghciHandle handler (doCommand cmd) - if q then return True else runNext +queryQueue :: GHCi (Maybe String) +queryQueue = do + st <- getGHCiState + case cmdqueue st of + [] -> return Nothing + c:cs -> do setGHCiState st{ cmdqueue = cs } + return (Just c) + +runCommands :: GHCi (Maybe String) -> GHCi () +runCommands 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 where - runNext = do - st <- getGHCiState - case cmdqueue st of - [] -> return False - c:cs -> do setGHCiState st{ cmdqueue = cs } - runCommands c - - doCommand (':' : cmd) = specialCommand cmd - doCommand stmt = do timeIt $ runStmt stmt GHC.RunToCompletion - return False + noSpace q = q >>= maybe (return Nothing) + (\c->case removeSpaces c of + "" -> noSpace q + ":{" -> multiLineCmd q + c -> return (Just c) ) + multiLineCmd q = do + st <- getGHCiState + let p = prompt st + setGHCiState st{ prompt = "%s| " } + mb_cmd <- collectCommand q "" + getGHCiState >>= \st->setGHCiState st{ prompt = p } + return mb_cmd + -- we can't use removeSpaces for the sublines here, so + -- multiline commands are somewhat more brittle against + -- fileformat errors (such as \r in dos input on unix), + -- we get rid of any extra spaces for the ":}" test; + -- we also avoid silent failure if ":}" is not found; + -- and since there is no (?) valid occurrence of \r (as + -- opposed to its String representation, "\r") inside a + -- ghci command, we replace any such with ' ' (argh:-( + collectCommand q c = q >>= + maybe (io (ioError collectError)) + (\l->if removeSpaces l == ":}" + then return (Just $ removeSpaces c) + else collectCommand q (c++map normSpace l)) + where normSpace '\r' = ' ' + normSpace c = c + -- QUESTION: is userError the one to use here? + collectError = userError "unterminated multiline command :{ .. :}" + doCommand (':' : cmd) = specialCommand cmd + doCommand stmt = do timeIt $ runStmt stmt GHC.RunToCompletion + return False enqueueCommands :: [String] -> GHCi () enqueueCommands cmds = do @@ -1021,48 +1057,107 @@ shellEscape str = io (system str >> return False) ----------------------------------------------------------------------------- -- Browsing a module's contents -browseCmd :: String -> GHCi () -browseCmd m = +browseCmd :: Bool -> String -> GHCi () +browseCmd bang m = case words m of - ['*':m] | looksLikeModuleName m -> browseModule m False - [m] | looksLikeModuleName m -> browseModule m True + ['*':s] | looksLikeModuleName s -> do + m <- wantInterpretedModule s + browseModule bang m False + [s] | looksLikeModuleName s -> do + m <- lookupModule s + browseModule bang m True + [] -> do + s <- getSession + (as,bs) <- io $ GHC.getContext s + -- 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 ") -browseModule :: String -> Bool -> GHCi () -browseModule m exports_only = do +-- without bang, show items in context of their parents and omit children +-- with bang, show class methods and data constructors separately, and +-- indicate import modules, to aid qualifying unqualified names +-- with sorted, sort items alphabetically +browseModule :: Bool -> Module -> Bool -> GHCi () +browseModule bang modl exports_only = do s <- getSession - modl <- if exports_only then lookupModule m - else wantInterpretedModule m - -- 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) prel_mod <- getPrelude io (if exports_only then GHC.setContext s [] [prel_mod,modl] - else GHC.setContext s [modl] []) + else GHC.setContext s [modl] []) unqual <- io (GHC.getPrintUnqual s) io (GHC.setContext s as bs) mb_mod_info <- io $ GHC.getModuleInfo s modl case mb_mod_info of - Nothing -> throwDyn (CmdLineError ("unknown module: " ++ m)) + Nothing -> throwDyn (CmdLineError ("unknown module: " ++ + GHC.moduleNameString (GHC.moduleName modl))) Just mod_info -> do - let names - | exports_only = GHC.modInfoExports mod_info - | otherwise = GHC.modInfoTopLevelScope mod_info - `orElse` [] - - mb_things <- io $ mapM (GHC.lookupName s) names - let filtered_things = filterOutChildren (\t -> t) (catMaybes mb_things) - dflags <- getDynFlags - let pefas = dopt Opt_PrintExplicitForalls dflags - io (putStrLn (showSDocForUser unqual ( - vcat (map (pprTyThingInContext pefas) filtered_things) - ))) - -- ToDo: modInfoInstances currently throws an exception for - -- package modules. When it works, we can do this: - -- $$ vcat (map GHC.pprInstance (GHC.modInfoInstances mod_info)) + let names + | exports_only = GHC.modInfoExports mod_info + | otherwise = GHC.modInfoTopLevelScope mod_info + `orElse` [] + + -- sort alphabetically name, but putting + -- locally-defined identifiers first. + -- We would like to improve this; see #1799. + sorted_names = loc_sort local ++ occ_sort external + where + (local,external) = 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. + loc_sort names + | n:_ <- names, isGoodSrcSpan (nameSrcSpan n) + = sortBy (compare `on` nameSrcSpan) names + | otherwise + = occ_sort names + + mb_things <- io $ mapM (GHC.lookupName s) sorted_names + let filtered_things = filterOutChildren (\t -> t) (catMaybes mb_things) + + rdr_env <- io $ GHC.getGRE s + + let pefas = dopt Opt_PrintExplicitForalls dflags + things | bang = catMaybes mb_things + | otherwise = filtered_things + pretty | bang = pprTyThing + | otherwise = pprTyThingInContext + + labels [] = text "-- not currently imported" + labels l = text $ intercalate "\n" $ map qualifier l + qualifier = maybe "-- defined locally" + (("-- imported from "++) . intercalate ", " + . map GHC.moduleNameString) + importInfo = RdrName.getGRE_NameQualifier_maybes rdr_env + modNames = map (importInfo . GHC.getName) things + + -- annotate groups of imports with their import modules + -- the default ordering is somewhat arbitrary, so we group + -- by header and sort groups; the names themselves should + -- really come in order of source appearance.. (trac #1799) + annotate mts = concatMap (\(m,ts)->labels m:ts) + $ sortBy cmpQualifiers $ group mts + where cmpQualifiers = + compare `on` (map (fmap (map moduleNameFS)) . fst) + group [] = [] + group mts@((m,_):_) = (m,map snd g) : group ng + where (g,ng) = partition ((==m).fst) mts + + let prettyThings = map (pretty pefas) things + prettyThings' | bang = annotate $ zip modNames prettyThings + | otherwise = prettyThings + io (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)) ----------------------------------------------------------------------------- -- Setting the module context @@ -1148,6 +1243,28 @@ setCmd "" then text "none." else hsep (map (\o -> char '+' <> text (optToStr o)) opts) )) + dflags <- getDynFlags + io $ putStrLn (showSDoc ( + vcat (text "GHCi-specific dynamic flag settings:" + :map (flagSetting dflags) ghciFlags) + )) + io $ putStrLn (showSDoc ( + vcat (text "other dynamic, non-language, flag settings:" + :map (flagSetting dflags) nonLanguageDynFlags) + )) + 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) + DynFlags.fFlags + nonLanguageDynFlags = filter (\(_,f)->not $ f `elem` map snd xFlags) + others + flags = [Opt_PrintExplicitForalls + ,Opt_PrintBindResult + ,Opt_BreakOnException + ,Opt_BreakOnError + ,Opt_PrintEvldWithShow + ] setCmd str = case toArgs str of ("args":args) -> setArgs args @@ -1301,6 +1418,8 @@ showCmd str = do ["linker"] -> io showLinkerState ["breaks"] -> showBkptTable ["context"] -> showContext + ["packages"] -> showPackages + ["languages"] -> showLanguages _ -> throwDyn (CmdLineError "syntax: :show [args|prog|prompt|editor|stop|modules|bindings|breaks|context]") showModules :: GHCi () @@ -1346,6 +1465,26 @@ showContext = do ptext SLIT("--> ") <> text (GHC.resumeStmt resume) $$ nest 2 (ptext SLIT("Stopped at") <+> ppr (GHC.resumeSpan resume)) +showPackages :: GHCi () +showPackages = do + pkg_flags <- fmap packageFlags getDynFlags + io $ 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) pkg_ids + where showFlag (ExposePackage p) = text $ " -package " ++ p + showFlag (HidePackage p) = text $ " -hide-package " ++ p + showFlag (IgnorePackage p) = text $ " -ignore-package " ++ p + +showLanguages :: GHCi () +showLanguages = do + dflags <- getDynFlags + io $ putStrLn $ showSDoc $ vcat $ + text "active language flags:" : + [text (" -X" ++ str) | (str,f) <- DynFlags.xFlags, dopt f dflags] -- ----------------------------------------------------------------------------- -- Completion @@ -1522,7 +1661,7 @@ expandPath :: String -> GHCi String expandPath path = case dropWhile isSpace path of ('~':d) -> do - tilde <- io (getEnv "HOME") -- will fail if HOME not defined + tilde <- io getHomeDirectory -- will fail if HOME not defined return (tilde ++ '/':d) other -> return other