{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE NondecreasingIndentation #-} {-# LANGUAGE CPP #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE BangPatterns #-} {-# LANGUAGE ViewPatterns #-} {-# OPTIONS -fno-cse #-} -- -fno-cse is needed for GLOBAL_VAR's to behave properly ----------------------------------------------------------------------------- -- -- GHC Interactive User Interface -- -- (c) The GHC Team 2005-2006 -- ----------------------------------------------------------------------------- module InteractiveUI ( interactiveUI, GhciSettings(..), defaultGhciSettings, ghciCommands, ghciWelcomeMsg ) where #include "HsVersions.h" -- Intero import Intero.Compat #if __GLASGOW_HASKELL__ >= 800 import GHCi import GHCi.RemoteTypes #endif #if __GLASGOW_HASKELL__ >= 802 import GHCi.Signals #endif import qualified Paths_intero import Data.Version (showVersion) import qualified Data.Map as M import GhciInfo import GhciTypes import GhciFind -- GHCi #if __GLASGOW_HASKELL__ >= 800 import GHC.LanguageExtensions.Type import GHCi.BreakArray as GHC #endif import qualified GhciMonad ( args, runStmt ) import GhciMonad hiding ( args, runStmt ) import GhciTags import Debugger #if __GLASGOW_HASKELL__ >= 802 import qualified Completion #endif -- The GHC interface import Data.IORef import DynFlags import GhcMonad ( modifySession ) import qualified GHC import GHC ( LoadHowMuch(..), Target(..), TargetId(..), InteractiveImport(..), TyThing(..), Phase, BreakIndex, Resume, SingleStep, Ghc, handleSourceError ) import HsImpExp import HscTypes ( tyThingParent_maybe, handleFlagWarnings, getSafeMode, hsc_IC, setInteractivePrintName ) import Module import Name # if __GLASGOW_HASKELL__ >= 802 import Packages ( trusted, getPackageDetails, getInstalledPackageDetails, listVisibleModuleNames ) #elif __GLASGOW_HASKELL__ >= 710 import Packages ( trusted, getPackageDetails, listVisibleModuleNames ) #else import Packages ( trusted, getPackageDetails, exposed, exposedModules, pkgIdMap ) #endif import PprTyThing #if __GLASGOW_HASKELL__ >= 802 import IfaceSyn #endif import RdrName ( getGRE_NameQualifier_maybes ) import SrcLoc import qualified Lexer import StringBuffer #if __GLASGOW_HASKELL__ < 709 import UniqFM ( eltsUFM ) #endif #if __GLASGOW_HASKELL__ >= 802 import Outputable hiding ( printForUser, printForUserPartWay ) #else import Outputable hiding ( printForUser, printForUserPartWay, bold ) #endif -- Other random utilities import BasicTypes hiding ( isTopLevel ) import Config import Digraph import Encoding import FastString import Linker import Maybes ( orElse, expectJust ) import NameSet import Panic hiding ( showException ) import Util -- Haskell Libraries import System.Console.Haskeline as Haskeline import Network.Socket import qualified Network import Network.BSD import Control.Applicative hiding (empty) import Control.Monad as Monad import Control.Monad.Trans.Class import Control.Monad.IO.Class import Control.Concurrent (threadDelay, forkIO) #if MIN_VERSION_directory(1,2,3) import Data.Time (getCurrentTime) #endif import Data.Array import qualified Data.ByteString.Char8 as BS import Data.Char import Data.Function import Data.List ( find, group, intercalate, intersperse, isPrefixOf, nub, partition, sort, sortBy) import Data.Maybe #if __GLASGOW_HASKELL__ >= 802 import qualified Data.Set as Set #endif import Exception hiding (catch) import Foreign.C #if __GLASGOW_HASKELL__ < 709 import Foreign.Safe #else import Foreign #endif import System.Directory import System.Environment import System.Exit ( exitWith, ExitCode(..) ) import System.FilePath import System.IO import System.IO.Error import System.IO.Unsafe ( unsafePerformIO ) import System.Process import Text.Printf import Text.Read ( readMaybe ) #ifndef mingw32_HOST_OS import System.Posix hiding ( getEnv ) #else import qualified System.Win32 #endif import GHC.Exts ( unsafeCoerce# ) import GHC.IO.Exception ( IOErrorType(InvalidArgument) ) import GHC.IO.Handle ( hFlushAll ) import GHC.TopHandler ( topHandler ) pprTyThing', pprTyThingInContext' :: TyThing -> SDoc #if __GLASGOW_HASKELL__ >= 802 pprTyThing' = pprTyThingHdr pprTyThingInContext' = pprTyThingInContext showToHeader #else pprTyThing' = pprTyThing pprTyThingInContext' = pprTyThingInContext #endif #if __GLASGOW_HASKELL__ >= 802 modulePackage :: Module -> UnitId modulePackage = moduleUnitId #elif __GLASGOW_HASKELL__ >= 800 packageString :: UnitId -> String packageString = unitIdString modulePackage :: Module -> UnitId modulePackage = moduleUnitId #elif __GLASGOW_HASKELL__ >= 710 packageString :: PackageKey -> String packageString = packageKeyString modulePackage :: Module -> PackageKey modulePackage = modulePackageKey #else -- 7.8 and below packageString :: PackageId -> String packageString = packageIdString modulePackage :: Module -> PackageId modulePackage = modulePackageId #endif ----------------------------------------------------------------------------- data GhciSettings = GhciSettings { availableCommands :: [Command], shortHelpText :: String, fullHelpText :: String, defPrompt :: String, defPrompt2 :: String } defaultGhciSettings :: GhciSettings defaultGhciSettings = GhciSettings { availableCommands = ghciCommands, shortHelpText = defShortHelpText, fullHelpText = defFullHelpText, defPrompt = default_prompt, defPrompt2 = default_prompt2 } ghciWelcomeMsg :: String ghciWelcomeMsg = unlines [versionString ,"Type :intro and press enter for an introduction of the standard commands."] versionString :: [Char] versionString = "Intero " ++ showVersion Paths_intero.version ++ " (GHC " ++ cProjectVersion ++ ")" cmdName :: Command -> String cmdName (n,_,_) = n GLOBAL_VAR(macros_ref, [], [Command]) ghciCommands :: [Command] ghciCommands = [ -- Hugs users are accustomed to :e, so make sure it doesn't overlap ("?", keepGoing help, noCompletion), ("add", keepGoingPaths addModule, completeFilename), ("abandon", keepGoing abandonCmd, noCompletion), ("break", keepGoing breakCmd, completeIdentifier), ("back", keepGoing backCmd, noCompletion), ("browse", keepGoing' (browseCmd False), completeModule), ("browse!", keepGoing' (browseCmd True), completeModule), ("extensions", keepGoing' extensionsCmd, completeModule), ("cd", keepGoingPaths changeDirectory, completeFilename), ("sleep", keepGoing' sleepCommand, noCompletion), ("cd-ghc", keepGoingPaths changeDirectoryGHC, completeFilename), ("check", keepGoing' checkModule, completeHomeModule), ("continue", keepGoing continueCmd, noCompletion), ("complete", keepGoing completeCmd, noCompletion), ("completion", keepGoing completeCmdSet, completeMacro), ("cmd", keepGoing cmdCmd, completeExpression), ("ctags", keepGoing createCTagsWithLineNumbersCmd, completeFilename), ("ctags!", keepGoing createCTagsWithRegExesCmd, completeFilename), ("def", keepGoing (defineMacro False), completeExpression), ("def!", keepGoing (defineMacro True), completeExpression), ("delete", keepGoing deleteCmd, noCompletion), ("edit", keepGoing' editFile, completeFilename), ("etags", keepGoing createETagsFileCmd, completeFilename), ("force", keepGoing forceCmd, completeExpression), ("forward", keepGoing forwardCmd, noCompletion), ("help", keepGoing help, noCompletion), ("history", keepGoing historyCmd, noCompletion), ("info", keepGoing' (info False), completeIdentifier), ("info!", keepGoing' (info True), completeIdentifier), ("issafe", keepGoing' isSafeCmd, completeModule), ("intro", keepGoing intro, noCompletion), ("kind", keepGoing' (kindOfType False), completeIdentifier), ("kind!", keepGoing' (kindOfType True), completeIdentifier), ("load", keepGoingPaths loadModule_, completeHomeModuleOrFile), ("fill", keepGoing' (lifted fillCmd), noCompletion), ("list", keepGoing' listCmd, noCompletion), ("module", keepGoing moduleCmd, completeSetModule), ("move", keepGoing' moveCommand, completeFilename), ("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), ("seti", keepGoing setiCmd, completeSeti), ("show", keepGoing showCmd, completeShowOptions), ("showi", keepGoing showiCmd, completeShowiOptions), ("sprint", keepGoing sprintCmd, completeExpression), ("step", keepGoing stepCmd, completeIdentifier), ("steplocal", keepGoing stepLocalCmd, completeIdentifier), ("stepmodule",keepGoing stepModuleCmd, completeIdentifier), ("type", keepGoing' (lifted typeOfExpr), completeExpression), ("type-at", keepGoing' (lifted typeAt), noCompletion), ("all-types", keepGoing' (lifted allTypes), noCompletion), ("uses", keepGoing' (lifted findAllUses), noCompletion), ("loc-at", keepGoing' (lifted locationAt), noCompletion), ("complete-at", keepGoing' (lifted completeAt), noCompletion), ("trace", keepGoing traceCmd, completeExpression), ("undef", keepGoing undefineMacro, completeMacro), ("unset", keepGoing unsetOptions, completeSetOptions) ] where lifted m = \str -> lift (m stdout str) fillCmd :: Handle -> String -> GHCi () #if __GLASGOW_HASKELL__ >= 802 fillCmd h = withFillInput (\fp line col -> do infos <- fmap mod_infos getGHCiState mname <- guessModule infos fp case mname of Nothing -> liftIO (hPutStrLn h "Couldn't guess that module name. Does it exist?") Just name -> do case M.lookup name infos of Nothing -> liftIO (hPutStrLn h "Couldn't guess the module name. Is this module loaded?") Just module' -> do completable <- Completion.getCompletableModule (modinfoSummary module') case Completion.declarationByLine completable (Completion.LineNumber line) of Nothing -> liftIO (hPutStrLn h "Couldn't guess the declaration.") Just declaration -> do df <- GHC.getSessionDynFlags case find ((\rs -> srcSpanStartLine rs == line && srcSpanStartCol rs == col) . Completion.holeRealSrcSpan) (Completion.declarationHoles df declaration) of Nothing -> pure () Just hole -> do subs <- Completion.holeSubstitutions hole mapM_ (\sub -> liftIO (hPutStrLn h (Completion.substitutionString sub))) subs) #else fillCmd _ = withFillInput (\_ _ _ -> pure ()) #endif withFillInput :: (FilePath -> Int -> Int -> GHCi ()) -> String -> GHCi () withFillInput cont input = case words input of [read -> name, read -> line, read -> col] -> (cont name line col) _ -> liftIO (putStrLn "Invalid :fill call. Should be :fill ") readOnlyCommands :: [(String, Handle -> String -> GHCi ())] readOnlyCommands = [ ("type-at", typeAt) , ("all-types", allTypes) , ("uses", findAllUses) , ("loc-at", locationAt) , ("complete-at", completeAt) ] -- 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. word_break_chars :: String word_break_chars = let symbols = "!#$%&*+/<=>?@\\^|-~" specials = "(),;[]`{}" spaces = " \t\n" in spaces ++ specials ++ symbols flagWordBreakChars :: String flagWordBreakChars = " \t\n" keepGoing :: (String -> GHCi ()) -> (String -> InputT GHCi Bool) keepGoing a str = keepGoing' (lift . a) str keepGoing' :: Monad m => (String -> m ()) -> String -> m Bool keepGoing' a str = a str >> return False keepGoingPaths :: ([FilePath] -> InputT GHCi ()) -> (String -> InputT GHCi Bool) keepGoingPaths a str = do case toArgs str of Left err -> liftIO $ hPutStrLn stderr err Right args -> a args return False defShortHelpText :: String defShortHelpText = "use :? for help.\n" completions :: [(String, Bool, CompletionFunc GHCi)] completions = [ ( "none", True, noCompletion), ( "expr", True, completeExpression), ( "file", True, completeFilename), ( "module", True, completeModule), ( "home-module", False, completeHomeModule), ( "home-mod-file", False, completeHomeModuleOrFile), ( "identifier", True, completeIdentifier), ( "macro", False, completeMacro), ( "seti", False, completeSeti), ( "set-module", False, completeSetModule), ( "set-options", False, completeSetOptions), ( "showi", False, completeShowiOptions), ( "show-options", False, completeShowOptions) ] completionsHelpText :: String completionsHelpText = concat . intersperse "|" . map (\ (name, _, _) -> name) . filter (\ (_, enabled, _) -> enabled) $ completions defFullHelpText :: String defFullHelpText = " Commands available from the prompt:\n" ++ "\n" ++ " evaluate/run \n" ++ " : repeat last command\n" ++ " :{\\n ..lines.. \\n:}\\n multiline command\n" ++ " :add [*] ... add module(s) to the current target set\n" ++ " :browse[!] [[*]] display the names defined by module \n" ++ " (!: more details; *: all top-level names)\n" ++ " :extensions display the extensions enabled by module \n" ++ " :cd change directory to \n" ++ " :cmd run the commands returned by ::IO String\n" ++ " :complete [] list completions for partial input string\n" ++ " :completion Assign a certain completion type to a command:\n" ++ " " ++ completionsHelpText ++ "\n" ++ " :ctags[!] [] create tags file for Vi (default: \"tags\")\n" ++ " (!: use regex instead of line number)\n" ++ " :def define command : (later defined command has\n" ++ " precedence, :: is always a builtin command)\n" ++ " :edit edit file\n" ++ " :edit edit last module\n" ++ " :etags [] create tags file for Emacs (default: \"TAGS\")\n" ++ " :help, :? display this list of commands\n" ++ " :info[!] [ ...] display information about the given names\n" ++ " (!: do not filter instances)\n" ++ " :issafe [] display safe haskell information of module \n" ++ " :kind[!] show the kind of \n" ++ " (!: also print the normalised type)\n" ++ " :load [*] ... load module(s) and their dependents\n" ++ " :main [ ...] run the main function with the given arguments\n" ++ " :module [+/-] [*] ... set the context for expression evaluation\n" ++ " :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 \n" ++ " :type show the type of \n" ++ " :type-at show the type of of format: \n" ++ " \n" ++ " text is used for when the span is out of date\n" ++ " :undef undefine user-defined command :\n" ++ " :loc-at return the location of the identifier at of format: \n" ++ " \n" ++ " text is used for when the span is out of date\n" ++ " :all-types return a list of all types in the project including\n" ++ " sub-expressions and local bindings\n" ++ " :undef undefine user-defined command :\n" ++ " :! run the shell command \n" ++ "\n" ++ " -- Commands for debugging:\n" ++ "\n" ++ " :abandon at a breakpoint, abandon current computation\n" ++ " :back go back in the history (after :trace)\n" ++ " :break [] [] set a breakpoint at the specified location\n" ++ " :break set a breakpoint on the specified function\n" ++ " :continue resume after a breakpoint\n" ++ " :delete delete the specified breakpoint\n" ++ " :delete * delete all breakpoints\n" ++ " :force print , forcing unevaluated parts\n" ++ " :forward go forward in the history (after :back)\n" ++ " :history [] after :trace, show the execution history\n" ++ " :list show the source code around current breakpoint\n" ++ " :list show the source code for \n" ++ " :list [] show the source code around line number \n" ++ " :print [ ...] prints a value without forcing its computation\n" ++ " :sprint [ ...] simplifed version of :print\n" ++ " :step single-step after stopping at a breakpoint\n"++ " :step single-step into \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 evaluate with tracing on (see :history)\n"++ "\n" ++ " -- Commands for changing settings:\n" ++ "\n" ++ " :set