@@ -7,16 +7,14 @@
8-- PureScript Compiler Interactive.
8-- PureScript Compiler Interactive.
10−module PSCi where
10+module PSCi (runPSCi) where
12import Prelude ()
12import Prelude ()
13import Prelude.Compat
13import Prelude.Compat
15import Data.Foldable (traverse_)
15import Data.Foldable (traverse_)
16−import Data.Maybe (mapMaybe)
17−import Data.List (intersperse, intercalate, nub, sort, find)
16+import Data.List (intercalate, nub, sort, find)
18import Data.Tuple (swap)
17import Data.Tuple (swap)
19−import Data.Version (showVersion)
20import qualified Data.Map as M
18import qualified Data.Map as M
22import Control.Arrow (first)
20import Control.Arrow (first)
@@ -24,102 +22,87 @@ import Control.Monad
24import Control.Monad.Error.Class (MonadError(..))
22import Control.Monad.Error.Class (MonadError(..))
25import Control.Monad.Trans.Class
23import Control.Monad.Trans.Class
26import Control.Monad.Trans.Except (ExceptT(), runExceptT)
24import Control.Monad.Trans.Except (ExceptT(), runExceptT)
27−import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT)
28import Control.Monad.Trans.State.Strict
25import Control.Monad.Trans.State.Strict
29import Control.Monad.IO.Class (liftIO)
26import Control.Monad.IO.Class (liftIO)
30import Control.Monad.Writer.Strict (Writer(), runWriter)
27import Control.Monad.Writer.Strict (Writer(), runWriter)
32−import Options.Applicative as Opts
34import System.Console.Haskeline
29import System.Console.Haskeline
35−import System.Directory (doesFileExist, findExecutable, getHomeDirectory, getCurrentDirectory)
30+import System.Directory (doesFileExist, getHomeDirectory, getCurrentDirectory)
36import System.Exit
31import System.Exit
37−import System.FilePath (pathSeparator, (</>), isPathSeparator)
32+import System.FilePath ((</>))
38import System.FilePath.Glob (glob)
33import System.FilePath.Glob (glob)
39import System.Process (readProcessWithExitCode)
34import System.Process (readProcessWithExitCode)
40import System.IO.Error (tryIOError)
35import System.IO.Error (tryIOError)
41−import qualified Text.PrettyPrint.Boxes as Box
43import qualified Language.PureScript as P
37import qualified Language.PureScript as P
44import qualified Language.PureScript.Names as N
38import qualified Language.PureScript.Names as N
45−import qualified Paths_purescript as Paths
47−import qualified PSCi.Directive as D
48import PSCi.Completion (completion)
40import PSCi.Completion (completion)
49−import PSCi.IO (mkdirp)
50import PSCi.Parser (parseCommand)
41import PSCi.Parser (parseCommand)
51import PSCi.Types
43import PSCi.Types
53−-- | The name of the PSCI support module
54−supportModuleName :: P.ModuleName
55−supportModuleName = P.ModuleName [P.ProperName "$PSCI", P.ProperName "Support"]
57−-- | Support module, contains code to evaluate terms
58−supportModule :: P.Module
60− case P.parseModulesFromFiles id [("", code)] of
61− Right [(_, P.Module ss cs _ ds exps)] -> P.Module ss cs supportModuleName ds exps
62− _ -> P.internalError "Support module could not be parsed"
68− , "import Prelude"
44+import PSCi.Message
69− , "import Control.Monad.Eff"
45+import PSCi.IO
70− , "import Control.Monad.Eff.Console"
46+import PSCi.Printer
71− , "import Control.Monad.Eff.Unsafe"
47+import PSCi.Module
73− , "class Eval a where"
74− , " eval :: a -> Eff (console :: CONSOLE) Unit"
76− , "instance evalShow :: (Show a) => Eval a where"
79− , "instance evalEff :: (Eval a) => Eval (Eff eff a) where"
80− , " eval x = unsafeInterleaveEff x >>= eval"
85−onFirstFileMatching :: Monad m => (b -> m (Maybe a)) -> [b] -> m (Maybe a)
86−onFirstFileMatching f pathVariants = runMaybeT . msum $ map (MaybeT . f) pathVariants
89−-- Locates the node executable.
50+-- PSCI monad
90−-- Checks for either @nodejs@ or @node@.
92−findNodeProcess :: IO (Maybe String)
52+newtype PSCI a = PSCI { runPSCI :: InputT (StateT PSCiState IO) a } deriving (Functor, Applicative, Monad)
93−findNodeProcess = onFirstFileMatching findExecutable names
94− where names = ["nodejs", "node"]
96−-- |
54+psciIO :: IO a -> PSCI a
97−-- Grabs the filename where the history is stored.
55+psciIO io = PSCI . lift $ lift io
99−getHistoryFilename :: IO FilePath
100−getHistoryFilename = do
101− home <- getHomeDirectory
102− let filename = home </> ".purescript" </> "psci_history"
107−-- Loads a file for use with imports.
58+-- The runner
109−loadModule :: FilePath -> IO (Either String [P.Module])
60+runPSCi :: IO ()
110−loadModule filename = do
61+runPSCi = getOpt >>= loop
111− content <- readFile filename
112− return $ either (Left . P.prettyPrintMultipleErrors False) (Right . map snd) $ P.parseModulesFromFiles id [(filename, content)]
115−-- Load all modules.
64+-- The PSCI main loop.
117−loadAllModules :: [FilePath] -> IO (Either P.MultipleErrors [(FilePath, P.Module)])
66+loop :: PSCiOptions -> IO ()
118−loadAllModules files = do
67+loop PSCiOptions{..} = do
119− filesAndContent <- forM files $ \filename -> do
68+ config <- loadUserConfig
120− content <- readFile filename
69+ inputFiles <- concat <$> traverse glob psciInputFile
121− return (filename, content)
70+ foreignFiles <- concat <$> traverse glob psciForeignInputFiles
122− return $ P.parseModulesFromFiles id filesAndContent
71+ modulesOrFirstError <- loadAllModules inputFiles
72+ case modulesOrFirstError of
73+ Left errs -> putStrLn (P.prettyPrintMultipleErrors False errs) >> exitFailure
75+ historyFilename <- getHistoryFilename
76+ let settings = defaultSettings { historyFile = Just historyFilename }
77+ foreignsOrError <- runMake $ do
78+ foreignFilesContent <- forM foreignFiles (\inFile -> (inFile,) <$> makeIO (const (P.ErrorMessage [] $ P.CannotReadFile inFile)) (readFile inFile))
79+ P.parseForeignModulesFromFiles foreignFilesContent
80+ case foreignsOrError of
81+ Left errs -> putStrLn (P.prettyPrintMultipleErrors False errs) >> exitFailure
83+ flip evalStateT (mkPSCiState [] modules foreigns [] psciInputNodeFlags) . runInputT (setComplete completion settings) $ do
84+ outputStrLn prologueMessage
85+ traverse_ (traverse_ (runPSCI . handleCommand)) config
86+ modules' <- lift $ gets psciLoadedModules
87+ unless (consoleIsDefined (map snd modules')) . outputStrLn $ unlines
88+ [ "PSCi requires the purescript-console module to be installed."
89+ , "For help getting started, visit http://wiki.purescript.org/PSCi"
93+ go :: InputT (StateT PSCiState IO) ()
95+ c <- getCommand (not psciMultiLineMode)
97+ Left err -> outputStrLn err >> go
99+ Right (Just QuitPSCi) -> outputStrLn quitMessage
100+ Right (Just c') -> do
101+ handleInterrupt (outputStrLn "Interrupted.")
102+ (withInterrupt (runPSCI (loadAllImportedModules >> handleCommand c')))
105+-- Compile the module
125-- Load all modules, updating the application state
108-- Load all modules, updating the application state
@@ -129,116 +112,9 @@ loadAllImportedModules = do
129 files <- PSCI . lift $ fmap psciImportedFilenames get
112 files <- PSCI . lift $ fmap psciImportedFilenames get
130 modulesOrFirstError <- psciIO $ loadAllModules files
113 modulesOrFirstError <- psciIO $ loadAllModules files
131 case modulesOrFirstError of
114 case modulesOrFirstError of
132− Left errs -> printErrors errs
115+ Left errs -> PSCI $ printErrors errs
133 Right modules -> PSCI . lift . modify $ updateModules modules
116 Right modules -> PSCI . lift . modify $ updateModules modules
136−-- Expands tilde in path.
138−expandTilde :: FilePath -> IO FilePath
139−expandTilde ('~':p:rest) | isPathSeparator p = (</> rest) <$> getHomeDirectory
140−expandTilde p = return p
147−helpMessage :: String
148−helpMessage = "The following commands are available:\n\n " ++
149− intercalate "\n " (map line D.help) ++
152− line :: (Directive, String, String) -> String
153− line (dir, arg, desc) =
154− let cmd = ':' : D.stringFor dir
156− , replicate (11 - length cmd) ' '
158− , replicate (11 - length arg) ' '
163− "Further information is available on the PureScript wiki:\n" ++
164− " --> https://github.com/purescript/purescript/wiki/psci"
168−-- The welcome prologue.
170−prologueMessage :: String
171−prologueMessage = intercalate "\n"
173− , "| _ \\ _ _ _ __ ___/ ___| ___ _ __(_)_ __ | |_ "
174− , "| |_) | | | | '__/ _ \\___ \\ / __| '__| | '_ \\| __|"
175− , "| __/| |_| | | | __/___) | (__| | | | |_) | |_ "
176− , "|_| \\__,_|_| \\___|____/ \\___|_| |_| .__/ \\__|"
185−quitMessage :: String
186−quitMessage = "See ya!"
191−newtype PSCI a = PSCI { runPSCI :: InputT (StateT PSCiState IO) a } deriving (Functor, Applicative, Monad)
193−psciIO :: IO a -> PSCI a
194−psciIO io = PSCI . lift $ lift io
197−-- Makes a volatile module to execute the current expression.
199−createTemporaryModule :: Bool -> PSCiState -> P.Expr -> P.Module
200−createTemporaryModule exec PSCiState{psciImportedModules = imports, psciLetBindings = lets} val =
202− moduleName = P.ModuleName [P.ProperName "$PSCI"]
203− trace = P.Var (P.Qualified (Just supportModuleName) (P.Ident "eval"))
204− mainValue = P.App trace (P.Var (P.Qualified Nothing (P.Ident "it")))
205− itDecl = P.ValueDeclaration (P.Ident "it") P.Public [] $ Right val
206− mainDecl = P.ValueDeclaration (P.Ident "$main") P.Public [] $ Right mainValue
207− decls = if exec then [itDecl, mainDecl] else [itDecl]
209− P.Module (P.internalModuleSourceSpan "<internal>") [] moduleName ((importDecl `map` imports) ++ lets ++ decls) Nothing
213−-- Makes a volatile module to hold a non-qualified type synonym for a fully-qualified data type declaration.
215−createTemporaryModuleForKind :: PSCiState -> P.Type -> P.Module
216−createTemporaryModuleForKind PSCiState{psciImportedModules = imports, psciLetBindings = lets} typ =
218− moduleName = P.ModuleName [P.ProperName "$PSCI"]
219− itDecl = P.TypeSynonymDeclaration (P.ProperName "IT") [] typ
221− P.Module (P.internalModuleSourceSpan "<internal>") [] moduleName ((importDecl `map` imports) ++ lets ++ [itDecl]) Nothing
224−-- Makes a volatile module to execute the current imports.
226−createTemporaryModuleForImports :: PSCiState -> P.Module
227−createTemporaryModuleForImports PSCiState{psciImportedModules = imports} =
229− moduleName = P.ModuleName [P.ProperName "$PSCI"]
231− P.Module (P.internalModuleSourceSpan "<internal>") [] moduleName (importDecl `map` imports) Nothing
233−importDecl :: ImportedModule -> P.Declaration
234−importDecl (mn, declType, asQ) = P.ImportDeclaration mn declType asQ False
236−indexFile :: FilePath
237−indexFile = ".psci_modules" ++ pathSeparator : "index.js"
239−modulesDir :: FilePath
240−modulesDir = ".psci_modules" ++ pathSeparator : "node_modules"
242-- | This is different than the runMake in 'Language.PureScript.Make' in that it specifies the
118-- | This is different than the runMake in 'Language.PureScript.Make' in that it specifies the
243-- options and ignores the warning messages.
119-- options and ignores the warning messages.
244runMake :: P.Make a -> IO (Either P.MultipleErrors a)
120runMake :: P.Make a -> IO (Either P.MultipleErrors a)
@@ -258,6 +134,58 @@ make st@PSCiState{..} ms = P.make actions' (map snd loadedModules ++ ms)
258 loadedModules = psciLoadedModules st
134 loadedModules = psciLoadedModules st
259 allModules = map (first Right) loadedModules ++ map (Left P.RebuildAlways,) ms
135 allModules = map (first Right) loadedModules ++ map (Left P.RebuildAlways,) ms
141+-- Parses the input and returns either a Metacommand, or an error as a string.
143+getCommand :: Bool -> InputT (StateT PSCiState IO) (Either String (Maybe Command))
144+getCommand singleLineMode = handleInterrupt (return (Right Nothing)) $ do
145+ firstLine <- withInterrupt $ getInputLine "> "
147+ Nothing -> return (Right (Just QuitPSCi)) -- Ctrl-D when input is empty
148+ Just "" -> return (Right Nothing)
149+ Just s | singleLineMode || head s == ':' -> return .fmap Just $ parseCommand s
150+ Just s -> fmap Just . parseCommand <$> go [s]
152+ go :: [String] -> InputT (StateT PSCiState IO) String
153+ go ls = maybe (return . unlines $ reverse ls) (go . (:ls)) =<< getInputLine " "
156+-- Performs an action for each meta-command given, and also for expressions.
158+handleCommand :: Command -> PSCI ()
159+handleCommand (Expression val) = handleExpression val
160+handleCommand ShowHelp = PSCI $ outputStrLn helpMessage
161+handleCommand (Import im) = handleImport im
162+handleCommand (Decls l) = handleDecls l
163+handleCommand (LoadFile filePath) = PSCI $ whenFileExists filePath $ \absPath -> do
164+ m <- lift . lift $ loadModule absPath
166+ Left err -> outputStrLn err
167+ Right mods -> lift $ modify (updateModules (map (absPath,) mods))
168+handleCommand (LoadForeign filePath) = PSCI $ whenFileExists filePath $ \absPath -> do
169+ foreignsOrError <- lift . lift . runMake $ do
170+ foreignFile <- makeIO (const (P.ErrorMessage [] $ P.CannotReadFile absPath)) (readFile absPath)
171+ P.parseForeignModulesFromFiles [(absPath, foreignFile)]
172+ case foreignsOrError of
173+ Left err -> outputStrLn $ P.prettyPrintMultipleErrors False err
174+ Right foreigns -> lift $ modify (updateForeignFiles foreigns)
175+handleCommand ResetState = do
176+ PSCI . lift . modify $ \st ->
177+ st { psciImportedModules = []
178+ , psciLetBindings = []
180+ loadAllImportedModules
181+handleCommand (TypeOf val) = handleTypeOf val
182+handleCommand (KindOf typ) = handleKindOf typ
183+handleCommand (BrowseModule moduleName) = handleBrowse moduleName
184+handleCommand (ShowInfo QueryLoaded) = handleShowLoadedModules
185+handleCommand (ShowInfo QueryImport) = handleShowImportedModules
186+handleCommand QuitPSCi = P.internalError "`handleCommand QuitPSCi` was called. This is a bug."
262-- Takes a value expression and evaluates it with the current state.
190-- Takes a value expression and evaluates it with the current state.
@@ -268,7 +196,7 @@ handleExpression val = do
268 let nodeArgs = psciNodeFlags st ++ [indexFile]
196 let nodeArgs = psciNodeFlags st ++ [indexFile]
269 e <- psciIO . runMake $ make st [supportModule, m]
197 e <- psciIO . runMake $ make st [supportModule, m]
270 case e of
198 case e of
271− Left errs -> printErrors errs
199+ Left errs -> PSCI $ printErrors errs
272 Right _ -> do
200 Right _ -> do
273 psciIO $ writeFile indexFile "require('$PSCI')['$main']();"
201 psciIO $ writeFile indexFile "require('$PSCI')['$main']();"
274 process <- psciIO findNodeProcess
202 process <- psciIO findNodeProcess
@@ -289,7 +217,7 @@ handleDecls ds = do
289 let m = createTemporaryModule False st' (P.ObjectLiteral [])
217 let m = createTemporaryModule False st' (P.ObjectLiteral [])
290 e <- psciIO . runMake $ make st' [m]
218 e <- psciIO . runMake $ make st' [m]
291 case e of
219 case e of
292− Left err -> printErrors err
220+ Left err -> PSCI $ printErrors err
293 Right _ -> PSCI $ lift (put st')
221 Right _ -> PSCI $ lift (put st')
@@ -344,7 +272,7 @@ handleImport im = do
344 let m = createTemporaryModuleForImports st
272 let m = createTemporaryModuleForImports st
345 e <- psciIO . runMake $ make st [m]
273 e <- psciIO . runMake $ make st [m]
346 case e of
274 case e of
347− Left errs -> printErrors errs
275+ Left errs -> PSCI $ printErrors errs
348 Right _ -> do
276 Right _ -> do
349 PSCI $ lift $ put st
277 PSCI $ lift $ put st
350 return ()
278 return ()
@@ -358,124 +286,12 @@ handleTypeOf val = do
358 let m = createTemporaryModule False st val
286 let m = createTemporaryModule False st val
359 e <- psciIO . runMake $ make st [m]
287 e <- psciIO . runMake $ make st [m]
360 case e of
288 case e of
361− Left errs -> printErrors errs
289+ Left errs -> PSCI $ printErrors errs
362 Right env' ->
290 Right env' ->
363 case M.lookup (P.ModuleName [P.ProperName "$PSCI"], P.Ident "it") (P.names env') of
291 case M.lookup (P.ModuleName [P.ProperName "$PSCI"], P.Ident "it") (P.names env') of
364 Just (ty, _, _) -> PSCI . outputStrLn . P.prettyPrintType $ ty
292 Just (ty, _, _) -> PSCI . outputStrLn . P.prettyPrintType $ ty
365 Nothing -> PSCI $ outputStrLn "Could not find type"
293 Nothing -> PSCI $ outputStrLn "Could not find type"
368−-- Pretty print a module's signatures
370−printModuleSignatures :: P.ModuleName -> P.Environment -> PSCI ()
371−printModuleSignatures moduleName (P.Environment {..}) =
373− -- get relevant components of a module from environment
374− let moduleNamesIdent = (filter ((== moduleName) . fst) . M.keys) names
375− moduleTypeClasses = (filter (\(P.Qualified maybeName _) -> maybeName == Just moduleName) . M.keys) typeClasses
376− moduleTypes = (filter (\(P.Qualified maybeName _) -> maybeName == Just moduleName) . M.keys) types
379− -- print each component
380− (outputStr . unlines . map trimEnd . lines . Box.render . Box.vsep 1 Box.left)
381− [ printModule's (mapMaybe (showTypeClass . findTypeClass typeClasses)) moduleTypeClasses -- typeClasses
382− , printModule's (mapMaybe (showType typeClasses dataConstructors typeSynonyms . findType types)) moduleTypes -- types
383− , printModule's (map (showNameType . findNameType names)) moduleNamesIdent -- functions
386− where printModule's showF = Box.vsep 1 Box.left . showF
388− findNameType :: M.Map (P.ModuleName, P.Ident) (P.Type, P.NameKind, P.NameVisibility) -> (P.ModuleName, P.Ident) -> (P.Ident, Maybe (P.Type, P.NameKind, P.NameVisibility))
389− findNameType envNames m@(_, mIdent) = (mIdent, M.lookup m envNames)
391− showNameType :: (P.Ident, Maybe (P.Type, P.NameKind, P.NameVisibility)) -> Box.Box
392− showNameType (mIdent, Just (mType, _, _)) = Box.text (P.showIdent mIdent ++ " :: ") Box.<> P.typeAsBox mType
393− showNameType _ = P.internalError "The impossible happened in printModuleSignatures."
396− :: M.Map (P.Qualified (P.ProperName 'P.ClassName)) ([(String, Maybe P.Kind)], [(P.Ident, P.Type)], [P.Constraint])
397− -> P.Qualified (P.ProperName 'P.ClassName)
398− -> (P.Qualified (P.ProperName 'P.ClassName), Maybe ([(String, Maybe P.Kind)], [(P.Ident, P.Type)], [P.Constraint]))
399− findTypeClass envTypeClasses name = (name, M.lookup name envTypeClasses)
402− :: (P.Qualified (P.ProperName 'P.ClassName), Maybe ([(String, Maybe P.Kind)], [(P.Ident, P.Type)], [P.Constraint]))
404− showTypeClass (_, Nothing) = Nothing
405− showTypeClass (P.Qualified _ name, Just (vars, body, constrs)) =
410− Box.<> Box.hcat Box.left (intersperse (Box.text ", ") $ map (\(P.Qualified _ pn, lt) -> Box.text (P.runProperName pn) Box.<+> Box.hcat Box.left (map P.typeAtomAsBox lt)) constrs)
411− Box.<> Box.text ") <= "
413− Box.text (P.runProperName name)
414− Box.<> Box.text (concatMap ((' ':) . fst) vars)
416− Box.vcat Box.top (map (\(i, t) -> Box.text (P.showIdent i ++ " ::") Box.<+> P.typeAsBox t) body)
423− Box.<+> if null body then Box.text "" else Box.text "where")
424− Box.// Box.moveRight 2 classBody
428− :: M.Map (P.Qualified (P.ProperName 'P.TypeName)) (P.Kind, P.TypeKind)
429− -> P.Qualified (P.ProperName 'P.TypeName)
430− -> (P.Qualified (P.ProperName 'P.TypeName), Maybe (P.Kind, P.TypeKind))
431− findType envTypes name = (name, M.lookup name envTypes)
434− :: M.Map (P.Qualified (P.ProperName 'P.ClassName)) ([(String, Maybe P.Kind)], [(P.Ident, P.Type)], [P.Constraint])
435− -> M.Map (P.Qualified (P.ProperName 'P.ConstructorName)) (P.DataDeclType, P.ProperName 'P.TypeName, P.Type, [P.Ident])
436− -> M.Map (P.Qualified (P.ProperName 'P.TypeName)) ([(String, Maybe P.Kind)], P.Type)
437− -> (P.Qualified (P.ProperName 'P.TypeName), Maybe (P.Kind, P.TypeKind))
439− showType typeClassesEnv dataConstructorsEnv typeSynonymsEnv (n@(P.Qualified modul name), typ) =
440− case (typ, M.lookup n typeSynonymsEnv) of
441− (Just (_, P.TypeSynonym), Just (typevars, dtType)) ->
442− if M.member (fmap P.coerceProperName n) typeClassesEnv
447− Box.text ("type " ++ P.runProperName name ++ concatMap ((' ':) . fst) typevars)
448− Box.// Box.moveRight 2 (Box.text "=" Box.<+> P.typeAsBox dtType)
450− (Just (_, P.DataType typevars pt), _) ->
453− [(dtProperName,_)] ->
454− case M.lookup (P.Qualified modul dtProperName) dataConstructorsEnv of
455− Just (dataDeclType, _, _, _) -> P.showDataDeclType dataDeclType
460− Just $ Box.text (prefix ++ " " ++ P.runProperName name ++ concatMap ((' ':) . fst) typevars) Box.// printCons pt
465− where printCons pt =
468− mapFirstRest (Box.text "=" Box.<+>) (Box.text "|" Box.<+>) $
469− map (\(cons,idents) -> (Box.text (P.runProperName cons) Box.<> Box.hcat Box.left (map prettyPrintType idents))) pt
471− prettyPrintType t = Box.text " " Box.<> P.typeAtomAsBox t
473− mapFirstRest _ _ [] = []
474− mapFirstRest f g (x:xs) = f x : map g xs
476− trimEnd = reverse . dropWhile (== ' ') . reverse
480-- Browse a module and displays its signature (if module exists).
296-- Browse a module and displays its signature (if module exists).
@@ -484,14 +300,14 @@ handleBrowse moduleName = do
484 st <- PSCI $ lift get
300 st <- PSCI $ lift get
485 env <- psciIO . runMake $ make st []
301 env <- psciIO . runMake $ make st []
486 case env of
302 case env of
487− Left errs -> printErrors errs
303+ Left errs -> PSCI $ printErrors errs
488 Right env' ->
304 Right env' ->
489 if isModInEnv moduleName st
305 if isModInEnv moduleName st
490− then printModuleSignatures moduleName env'
306+ then PSCI $ printModuleSignatures moduleName env'
491 else case lookupUnQualifiedModName moduleName st of
307 else case lookupUnQualifiedModName moduleName st of
492 Just unQualifiedName ->
308 Just unQualifiedName ->
493 if isModInEnv unQualifiedName st
309 if isModInEnv unQualifiedName st
494− then printModuleSignatures unQualifiedName env'
310+ then PSCI $ printModuleSignatures unQualifiedName env'
495 else failNotInEnv moduleName
311 else failNotInEnv moduleName
496 Nothing ->
312 Nothing ->
497 failNotInEnv moduleName
313 failNotInEnv moduleName
@@ -503,10 +319,6 @@ handleBrowse moduleName = do
503 lookupUnQualifiedModName quaModName st =
319 lookupUnQualifiedModName quaModName st =
504 (\(modName,_,_) -> modName) <$> find ( \(_, _, mayQuaName) -> mayQuaName == Just quaModName) (psciImportedModules st)
320 (\(modName,_,_) -> modName) <$> find ( \(_, _, mayQuaName) -> mayQuaName == Just quaModName) (psciImportedModules st)
506−-- | Pretty-print errors
507−printErrors :: P.MultipleErrors -> PSCI ()
508−printErrors = PSCI . outputStrLn . P.prettyPrintMultipleErrors False
511-- Takes a value and prints its kind
323-- Takes a value and prints its kind
@@ -517,7 +329,7 @@ handleKindOf typ = do
517 mName = P.ModuleName [P.ProperName "$PSCI"]
329 mName = P.ModuleName [P.ProperName "$PSCI"]
518 e <- psciIO . runMake $ make st [m]
330 e <- psciIO . runMake $ make st [m]
519 case e of
331 case e of
520− Left errs -> printErrors errs
332+ Left errs -> PSCI $ printErrors errs
521 Right env' ->
333 Right env' ->
522 case M.lookup (P.Qualified (Just mName) $ P.ProperName "IT") (P.typeSynonyms env') of
334 case M.lookup (P.Qualified (Just mName) $ P.ProperName "IT") (P.typeSynonyms env') of
523 Just (_, typ') -> do
335 Just (_, typ') -> do
@@ -531,63 +343,7 @@ handleKindOf typ = do
531 Right (kind, _) -> PSCI . outputStrLn . P.prettyPrintKind $ kind
343 Right (kind, _) -> PSCI . outputStrLn . P.prettyPrintKind $ kind
532 Nothing -> PSCI $ outputStrLn "Could not find kind"
344 Nothing -> PSCI $ outputStrLn "Could not find kind"
534−-- Commands
346+-- Misc
537−-- Parses the input and returns either a Metacommand, or an error as a string.
539−getCommand :: Bool -> InputT (StateT PSCiState IO) (Either String (Maybe Command))
540−getCommand singleLineMode = handleInterrupt (return (Right Nothing)) $ do
541− firstLine <- withInterrupt $ getInputLine "> "
543− Nothing -> return (Right (Just QuitPSCi)) -- Ctrl-D when input is empty
544− Just "" -> return (Right Nothing)
545− Just s | singleLineMode || head s == ':' -> return .fmap Just $ parseCommand s
546− Just s -> fmap Just . parseCommand <$> go [s]
548− go :: [String] -> InputT (StateT PSCiState IO) String
549− go ls = maybe (return . unlines $ reverse ls) (go . (:ls)) =<< getInputLine " "
552−-- Performs an action for each meta-command given, and also for expressions.
554−handleCommand :: Command -> PSCI ()
555−handleCommand (Expression val) = handleExpression val
556−handleCommand ShowHelp = PSCI $ outputStrLn helpMessage
557−handleCommand (Import im) = handleImport im
558−handleCommand (Decls l) = handleDecls l
559−handleCommand (LoadFile filePath) = whenFileExists filePath $ \absPath -> do
560− m <- psciIO $ loadModule absPath
562− Left err -> PSCI $ outputStrLn err
563− Right mods -> PSCI . lift $ modify (updateModules (map (absPath,) mods))
564−handleCommand (LoadForeign filePath) = whenFileExists filePath $ \absPath -> do
565− foreignsOrError <- psciIO . runMake $ do
566− foreignFile <- makeIO (const (P.ErrorMessage [] $ P.CannotReadFile absPath)) (readFile absPath)
567− P.parseForeignModulesFromFiles [(absPath, foreignFile)]
568− case foreignsOrError of
569− Left err -> PSCI $ outputStrLn $ P.prettyPrintMultipleErrors False err
570− Right foreigns -> PSCI . lift $ modify (updateForeignFiles foreigns)
571−handleCommand ResetState = do
572− PSCI . lift . modify $ \st ->
573− st { psciImportedModules = []
574− , psciLetBindings = []
576− loadAllImportedModules
577−handleCommand (TypeOf val) = handleTypeOf val
578−handleCommand (KindOf typ) = handleKindOf typ
579−handleCommand (BrowseModule moduleName) = handleBrowse moduleName
580−handleCommand (ShowInfo QueryLoaded) = handleShowLoadedModules
581−handleCommand (ShowInfo QueryImport) = handleShowImportedModules
582−handleCommand QuitPSCi = P.internalError "`handleCommand QuitPSCi` was called. This is a bug."
584−whenFileExists :: FilePath -> (FilePath -> PSCI ()) -> PSCI ()
585−whenFileExists filePath f = do
586− absPath <- psciIO $ expandTilde filePath
587− exists <- psciIO $ doesFileExist absPath
590− else PSCI . outputStrLn $ "Couldn't locate: " ++ filePath
593-- Attempts to read initial commands from '.psci' in the present working
349-- Attempts to read initial commands from '.psci' in the present working
@@ -610,92 +366,6 @@ loadUserConfig = onFirstFileMatching readCommands pathGetters
611 return Nothing
367 return Nothing
614-- | Checks if the Console module is defined
369-- | Checks if the Console module is defined
615consoleIsDefined :: [P.Module] -> Bool
370consoleIsDefined :: [P.Module] -> Bool
616consoleIsDefined = any ((== P.ModuleName (map P.ProperName [ "Control", "Monad", "Eff", "Console" ])) . P.getModuleName)
371consoleIsDefined = any ((== P.ModuleName (map P.ProperName [ "Control", "Monad", "Eff", "Console" ])) . P.getModuleName)
619−-- The PSCI main loop.
621−loop :: PSCiOptions -> IO ()
622−loop PSCiOptions{..} = do
623− config <- loadUserConfig
624− inputFiles <- concat <$> traverse glob psciInputFile
625− foreignFiles <- concat <$> traverse glob psciForeignInputFiles
626− modulesOrFirstError <- loadAllModules inputFiles
627− case modulesOrFirstError of
628− Left errs -> putStrLn (P.prettyPrintMultipleErrors False errs) >> exitFailure
630− historyFilename <- getHistoryFilename
631− let settings = defaultSettings { historyFile = Just historyFilename }
632− foreignsOrError <- runMake $ do
633− foreignFilesContent <- forM foreignFiles (\inFile -> (inFile,) <$> makeIO (const (P.ErrorMessage [] $ P.CannotReadFile inFile)) (readFile inFile))
634− P.parseForeignModulesFromFiles foreignFilesContent
635− case foreignsOrError of
636− Left errs -> putStrLn (P.prettyPrintMultipleErrors False errs) >> exitFailure
638− flip evalStateT (mkPSCiState [] modules foreigns [] psciInputNodeFlags) . runInputT (setComplete completion settings) $ do
639− outputStrLn prologueMessage
640− traverse_ (traverse_ (runPSCI . handleCommand)) config
641− modules' <- lift $ gets psciLoadedModules
642− unless (consoleIsDefined (map snd modules')) . outputStrLn $ unlines
643− [ "PSCi requires the purescript-console module to be installed."
644− , "For help getting started, visit http://wiki.purescript.org/PSCi"
648− go :: InputT (StateT PSCiState IO) ()
650− c <- getCommand (not psciMultiLineMode)
652− Left err -> outputStrLn err >> go
654− Right (Just QuitPSCi) -> outputStrLn quitMessage
655− Right (Just c') -> do
656− handleInterrupt (outputStrLn "Interrupted.")
657− (withInterrupt (runPSCI (loadAllImportedModules >> handleCommand c')))
660−multiLineMode :: Parser Bool
661−multiLineMode = switch $
662− long "multi-line-mode"
664− <> Opts.help "Run in multi-line mode (use ^D to terminate commands)"
666−inputFile :: Parser FilePath
667−inputFile = strArgument $
669− <> Opts.help "Optional .purs files to load on start"
671−inputForeignFile :: Parser FilePath
672−inputForeignFile = strOption $
675− <> help "The input .js file(s) providing foreign import implementations"
677−nodeFlagsFlag :: Parser [String]
678−nodeFlagsFlag = option parser $
680− <> metavar "NODE_OPTS"
682− <> Opts.help "Flags to pass to node, separated by spaces"
684− parser = words <$> str
686−psciOptions :: Parser PSCiOptions
687−psciOptions = PSCiOptions <$> multiLineMode
689− <*> many inputForeignFile
693−runPSCi = execParser opts >>= loop
695− opts = info (version <*> helper <*> psciOptions) infoModList
696− infoModList = fullDesc <> headerInfo <> footerInfo
697− headerInfo = header "psci - Interactive mode for PureScript"
698− footerInfo = footer $ "psci " ++ showVersion Paths.version
700− version :: Parser (a -> a)
701− version = abortOption (InfoMsg (showVersion Paths.version)) $ long "version" <> Opts.help "Show the version number" <> hidden