import qualified Id ( setIdType )
import IdInfo ( GlobalIdDetails(..) )
import Linker ( HValue, extendLinkEnv, withExtendedLinkEnv,
- initDynLinker, linkPackages )
+ initDynLinker )
import PrelNames ( breakpointJumpName, breakpointCondJumpName )
#endif
import Panic ( panic, installSignalHandlers )
import Config
import StaticFlags ( opt_IgnoreDotGhci )
-import Linker ( showLinkerState )
+import Linker ( showLinkerState, linkPackages )
import Util ( removeSpaces, handle, global, toArgs,
- looksLikeModuleName, prefixMatch, sortLe )
+ looksLikeModuleName, prefixMatch, sortLe,
+ joinFileName )
#ifndef mingw32_HOST_OS
import System.Posix
#else
import GHC.ConsoleHandler ( flushConsole )
import System.Win32 ( setConsoleCP, setConsoleOutputCP )
+import qualified System.Win32
#endif
#ifdef USE_READLINE
import Numeric
import Data.List
import Data.Int ( Int64 )
-import Data.Maybe ( isJust, fromMaybe, catMaybes )
+import Data.Maybe ( isJust, isNothing, fromMaybe, catMaybes )
import System.Cmd
import System.CPUTime
import System.Environment
("browse", keepGoing browseCmd, False, completeModule),
("cd", keepGoing changeDirectory, False, completeFilename),
("def", keepGoing defineMacro, False, completeIdentifier),
+ ("e", keepGoing editFile, False, completeFilename),
+ -- Hugs users are accustomed to :e, so make sure it doesn't overlap
+ ("edit", keepGoing editFile, False, completeFilename),
("help", keepGoing help, False, completeNone),
("?", keepGoing help, False, completeNone),
("info", keepGoing info, False, completeIdentifier),
" :browse [*]<module> display the names defined by <module>\n" ++
" :cd <dir> change directory to <dir>\n" ++
" :def <cmd> <expr> define a command :<cmd>\n" ++
+ " :edit <file> edit file\n" ++
+ " :edit edit last module\n" ++
" :help, :? display this list of commands\n" ++
" :info [<name> ...] display information about the given names\n" ++
" :load <filename> ... load module(s) and their dependents\n" ++
" :set args <arg> ... set the arguments returned by System.getArgs\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 comand used for :edit\n" ++
"\n" ++
" :show modules show the currently loaded modules\n" ++
" :show bindings show the current bindings made at the prompt\n" ++
writeIORef ref (hsc_env { hsc_IC = new_ic })
is_tty <- hIsTerminalDevice stdin
prel_mod <- GHC.findModule session prel_name Nothing
+ default_editor <- findEditor
withExtendedLinkEnv (zip names hValues) $
startGHCi (interactiveLoop is_tty True)
GHCiState{ progname = "<interactive>",
args = [],
prompt = location++"> ",
+ editor = default_editor,
session = session,
options = [],
prelude = prel_mod }
return b
#endif
+findEditor = do
+ getEnv "EDITOR"
+ `IO.catch` \_ -> do
+#if mingw32_HOST_OS
+ win <- System.Win32.getWindowsDirectory
+ return (win `joinFileName` "notepad.exe")
+#else
+ return ""
+#endif
+
interactiveUI :: Session -> [(FilePath, Maybe Phase)] -> Maybe String -> IO ()
interactiveUI session srcs maybe_expr = do
#if defined(GHCI) && defined(BREAKPOINT)
newStablePtr stdout
newStablePtr stderr
- hFlush stdout
- hSetBuffering stdout NoBuffering
-
-- Initialise buffering for the *interpreted* I/O system
initInterpBuffering session
+ when (isNothing maybe_expr) $ do
+ -- Only for GHCi (not runghc and ghc -e):
+ -- Turn buffering off for the compiled program's stdout/stderr
+ turnOffBuffering
+ -- Turn buffering off for GHCi's stdout
+ hFlush stdout
+ hSetBuffering stdout NoBuffering
-- We don't want the cmd line to buffer any input that might be
-- intended for the program, so unbuffer stdin.
- hSetBuffering stdin NoBuffering
+ hSetBuffering stdin NoBuffering
-- initial context is just the Prelude
prel_mod <- GHC.findModule session prel_name Nothing
Readline.setCompleterWordBreakCharacters word_break_chars
#endif
+ default_editor <- findEditor
+
startGHCi (runGHCi srcs maybe_expr)
GHCiState{ progname = "<interactive>",
args = [],
prompt = "%s> ",
+ editor = default_editor,
session = session,
options = [],
prelude = prel_mod }
Just hval -> writeIORef flush_interp (unsafeCoerce# hval :: IO ())
_ -> panic "interactiveUI:flush"
- turnOffBuffering -- Turn it off right now
-
return ()
dir <- expandPath dir
io (setCurrentDirectory dir)
+editFile :: String -> GHCi ()
+editFile str
+ | null str = do
+ -- find the name of the "topmost" file loaded
+ session <- getSession
+ graph0 <- io (GHC.getModuleGraph session)
+ graph1 <- filterM (io . GHC.isLoaded session . GHC.ms_mod_name) graph0
+ let graph2 = flattenSCCs (GHC.topSortModuleGraph True graph1 Nothing)
+ case GHC.ml_hs_file (GHC.ms_location (last graph2)) of
+ Just file -> do_edit file
+ Nothing -> throwDyn (CmdLineError "unknown file name")
+ | otherwise = do_edit str
+ where
+ do_edit file = do
+ st <- getGHCiState
+ let cmd = editor st
+ when (null cmd) $
+ throwDyn (CmdLineError "editor not set, use :set editor")
+ io $ system (cmd ++ ' ':file)
+ return ()
+
defineMacro :: String -> GHCi ()
defineMacro s = do
let (macro_name, definition) = break isSpace s
else hsep (map (\o -> char '+' <> text (optToStr o)) opts)
))
setCmd str
- = case words str of
+ = case toArgs str of
("args":args) -> setArgs args
("prog":prog) -> setProg prog
- ("prompt":prompt) -> setPrompt (dropWhile isSpace $ drop 6 $ dropWhile isSpace str)
+ ("prompt":prompt) -> setPrompt (after 6)
+ ("editor":cmd) -> setEditor (after 6)
wds -> setOptions wds
+ where after n = dropWhile isSpace $ drop n $ dropWhile isSpace str
setArgs args = do
st <- getGHCiState
setProg _ = do
io (hPutStrLn stderr "syntax: :set prog <progname>")
+setEditor cmd = do
+ st <- getGHCiState
+ setGHCiState st{ editor = cmd }
+
setPrompt value = do
st <- getGHCiState
if null value
progname :: String,
args :: [String],
prompt :: String,
+ editor :: String,
session :: GHC.Session,
options :: [GHCiOption],
prelude :: Module