@@ -33,25 +33,32 @@ import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT)
33import Control.Monad.Trans.State.Strict
33import Control.Monad.Trans.State.Strict
34import qualified Control.Monad.Trans.State.Lazy as L
34import qualified Control.Monad.Trans.State.Lazy as L
36+import Options.Applicative as Opts
36import System.Console.Haskeline
38import System.Console.Haskeline
37import System.Directory (createDirectoryIfMissing, getModificationTime, doesFileExist, findExecutable, getHomeDirectory, getCurrentDirectory)
39import System.Directory (createDirectoryIfMissing, getModificationTime, doesFileExist, findExecutable, getHomeDirectory, getCurrentDirectory)
38import System.Exit
40import System.Exit
39import System.FilePath (pathSeparator, takeDirectory, (</>), isPathSeparator)
41import System.FilePath (pathSeparator, takeDirectory, (</>), isPathSeparator)
40import System.IO.Error (tryIOError)
42import System.IO.Error (tryIOError)
41import System.Process (readProcessWithExitCode)
43import System.Process (readProcessWithExitCode)
42−import qualified System.Console.CmdTheLine as Cmd
43import qualified System.IO.UTF8 as U (writeFile, putStrLn, print, readFile)
44import qualified System.IO.UTF8 as U (writeFile, putStrLn, print, readFile)
45−import Text.Parsec (ParseError)
46+import qualified Text.Parsec as Par (ParseError)
47import qualified Language.PureScript as P
48import qualified Language.PureScript as P
48import qualified Language.PureScript.AST as D
49import qualified Language.PureScript.AST as D
49import qualified Language.PureScript.Names as N
50import qualified Language.PureScript.Names as N
50import qualified Paths_purescript as Paths
51import qualified Paths_purescript as Paths
52−import Commands
53+import Commands as C
53import Parser
54import Parser
57+data PSCiOptions = PSCiOptions
58+ { psciSingleLineFlag :: Bool
59+ , psciInputFile :: [FilePath]
56-- The PSCI state.
63-- The PSCI state.
57-- Holds a list of imported modules, loaded files, and partial let bindings.
64-- Holds a list of imported modules, loaded files, and partial let bindings.
@@ -125,7 +132,7 @@ loadModule filename = either (Left . show) Right . P.runIndentParser filename P.
126-- Load all modules, including the Prelude
133-- Load all modules, including the Prelude
128−loadAllModules :: [FilePath] -> IO (Either ParseError [(Either P.RebuildPolicy FilePath, P.Module)])
135+loadAllModules :: [FilePath] -> IO (Either Par.ParseError [(Either P.RebuildPolicy FilePath, P.Module)])
129loadAllModules files = do
136loadAllModules files = do
130 filesAndContent <- forM files $ \filename -> do
137 filesAndContent <- forM files $ \filename -> do
131 content <- U.readFile filename
138 content <- U.readFile filename
@@ -146,7 +153,7 @@ expandTilde p = return p
147helpMessage :: String
154helpMessage :: String
148helpMessage = "The following commands are available:\n\n " ++
155helpMessage = "The following commands are available:\n\n " ++
149− intercalate "\n " (map (intercalate " ") help)
156+ intercalate "\n " (map (intercalate " ") C.help)
152-- The welcome prologue.
159-- The welcome prologue.
@@ -180,10 +187,10 @@ completion :: CompletionFunc (StateT PSCiState IO)
180completion = completeWord Nothing " \t\n\r" findCompletions
187completion = completeWord Nothing " \t\n\r" findCompletions
182 findCompletions :: String -> StateT PSCiState IO [Completion]
189 findCompletions :: String -> StateT PSCiState IO [Completion]
183− findCompletions str = do
190+ findCompletions st = do
184 ms <- map snd . psciLoadedModules <$> get
191 ms <- map snd . psciLoadedModules <$> get
185− files <- listFiles str
192+ files <- listFiles st
186− let matches = filter (isPrefixOf str) (names ms)
193+ let matches = filter (isPrefixOf st) (names ms)
187 return $ sortBy sorter $ map simpleCompletion matches ++ files
194 return $ sortBy sorter $ map simpleCompletion matches ++ files
188 getDeclName :: Maybe [P.DeclarationRef] -> P.Declaration -> Maybe P.Ident
195 getDeclName :: Maybe [P.DeclarationRef] -> P.Declaration -> Maybe P.Ident
189 getDeclName Nothing (P.ValueDeclaration ident _ _ _) = Just ident
196 getDeclName Nothing (P.ValueDeclaration ident _ _ _) = Just ident
@@ -248,13 +255,13 @@ mkdirp = createDirectoryIfMissing True . takeDirectory
248-- Makes a volatile module to execute the current expression.
255-- Makes a volatile module to execute the current expression.
250createTemporaryModule :: Bool -> PSCiState -> P.Expr -> P.Module
257createTemporaryModule :: Bool -> PSCiState -> P.Expr -> P.Module
251−createTemporaryModule exec PSCiState{psciImportedModuleNames = imports, psciLetBindings = lets} value =
258+createTemporaryModule exec PSCiState{psciImportedModuleNames = imports, psciLetBindings = lets} val =
253 moduleName = P.ModuleName [P.ProperName "$PSCI"]
260 moduleName = P.ModuleName [P.ProperName "$PSCI"]
254 importDecl m = P.ImportDeclaration m P.Unqualified Nothing
261 importDecl m = P.ImportDeclaration m P.Unqualified Nothing
255 traceModule = P.ModuleName [P.ProperName "Debug", P.ProperName "Trace"]
262 traceModule = P.ModuleName [P.ProperName "Debug", P.ProperName "Trace"]
256 trace = P.Var (P.Qualified (Just traceModule) (P.Ident "print"))
263 trace = P.Var (P.Qualified (Just traceModule) (P.Ident "print"))
257− itValue = foldl (\x f -> f x) value lets
264+ itValue = foldl (\x f -> f x) val lets
258 mainValue = P.App trace (P.Var (P.Qualified Nothing (P.Ident "it")))
265 mainValue = P.App trace (P.Var (P.Qualified Nothing (P.Ident "it")))
259 itDecl = P.ValueDeclaration (P.Ident "it") P.Value [] $ Right itValue
266 itDecl = P.ValueDeclaration (P.Ident "it") P.Value [] $ Right itValue
260 mainDecl = P.ValueDeclaration (P.Ident "main") P.Value [] $ Right mainValue
267 mainDecl = P.ValueDeclaration (P.Ident "main") P.Value [] $ Right mainValue
@@ -295,9 +302,9 @@ indexFile = ".psci_modules" ++ pathSeparator : "index.js"
295-- Takes a value declaration and evaluates it with the current state.
302-- Takes a value declaration and evaluates it with the current state.
297handleDeclaration :: P.Expr -> PSCI ()
304handleDeclaration :: P.Expr -> PSCI ()
298−handleDeclaration value = do
305+handleDeclaration val = do
299 st <- PSCI $ lift get
306 st <- PSCI $ lift get
300− let m = createTemporaryModule True st value
307+ let m = createTemporaryModule True st val
301 e <- psciIO . runMake $ P.make modulesDir options (psciLoadedModules st ++ [(Left P.RebuildAlways, m)]) []
308 e <- psciIO . runMake $ P.make modulesDir options (psciLoadedModules st ++ [(Left P.RebuildAlways, m)]) []
302 case e of
309 case e of
303 Left err -> PSCI $ outputStrLn err
310 Left err -> PSCI $ outputStrLn err
@@ -363,9 +370,9 @@ handleImport moduleName = do
363-- Takes a value and prints its type
370-- Takes a value and prints its type
365handleTypeOf :: P.Expr -> PSCI ()
372handleTypeOf :: P.Expr -> PSCI ()
366−handleTypeOf value = do
373+handleTypeOf val = do
367 st <- PSCI $ lift get
374 st <- PSCI $ lift get
368− let m = createTemporaryModule False st value
375+ let m = createTemporaryModule False st val
369 e <- psciIO . runMake $ P.make modulesDir options (psciLoadedModules st ++ [(Left P.RebuildAlways, m)]) []
376 e <- psciIO . runMake $ P.make modulesDir options (psciLoadedModules st ++ [(Left P.RebuildAlways, m)]) []
370 case e of
377 case e of
371 Left err -> PSCI $ outputStrLn err
378 Left err -> PSCI $ outputStrLn err
@@ -434,7 +441,7 @@ handleKindOf typ = do
435-- Parses the input and returns either a Metacommand or an expression.
442-- Parses the input and returns either a Metacommand or an expression.
437−getCommand :: Bool -> InputT (StateT PSCiState IO) (Either ParseError (Maybe Command))
444+getCommand :: Bool -> InputT (StateT PSCiState IO) (Either Par.ParseError (Maybe Command))
438getCommand singleLineMode = do
445getCommand singleLineMode = do
439 firstLine <- getInputLine "> "
446 firstLine <- getInputLine "> "
440 case firstLine of
447 case firstLine of
@@ -478,17 +485,6 @@ handleCommand (Show "loaded") = handleShowLoadedModules
478handleCommand (Show "import") = handleShowImportedModules
485handleCommand (Show "import") = handleShowImportedModules
479handleCommand _ = PSCI $ outputStrLn "Unknown command"
486handleCommand _ = PSCI $ outputStrLn "Unknown command"
481−singleLineFlag :: Cmd.Term Bool
482−singleLineFlag = Cmd.value $ Cmd.flag $ (Cmd.optInfo ["single-line-mode"])
483− { Cmd.optName = "Single-line mode"
484− , Cmd.optDoc = "Run in single-line mode"
487−inputFiles :: Cmd.Term [FilePath]
488−inputFiles = Cmd.value $ Cmd.posAny [] $ Cmd.posInfo { Cmd.posName = "file(s)"
489− , Cmd.posDoc = "Optional .purs files to load on start"
492loadUserConfig :: IO (Maybe [Command])
488loadUserConfig :: IO (Maybe [Command])
493loadUserConfig = do
489loadUserConfig = do
494 configFile <- (</> ".psci") <$> getCurrentDirectory
490 configFile <- (</> ".psci") <$> getCurrentDirectory
@@ -505,8 +501,8 @@ loadUserConfig = do
506-- The PSCI main loop.
502-- The PSCI main loop.
508−loop :: Bool -> [FilePath] -> IO ()
504+loop :: PSCiOptions -> IO ()
509−loop singleLineMode files = do
505+loop (PSCiOptions singleLineMode files) = do
510 config <- loadUserConfig
506 config <- loadUserConfig
511 modulesOrFirstError <- loadAllModules files
507 modulesOrFirstError <- loadAllModules files
512 case modulesOrFirstError of
508 case modulesOrFirstError of
@@ -528,15 +524,25 @@ loop singleLineMode files = do
528 Right (Just Quit) -> outputStrLn quitMessage
524 Right (Just Quit) -> outputStrLn quitMessage
529 Right (Just c') -> runPSCI (handleCommand c') >> go
525 Right (Just c') -> runPSCI (handleCommand c') >> go
531−term :: Cmd.Term (IO ())
527+singleLineFlag :: Parser Bool
532−term = loop <$> singleLineFlag <*> inputFiles
528+singleLineFlag = switch $
529+ long "single-line-mode"
530+ <> Opts.help "Run in single-line mode"
534−termInfo :: Cmd.TermInfo
532+inputFile :: Parser FilePath
535−termInfo = Cmd.defTI
533+inputFile = strArgument $
536− { Cmd.termName = "psci"
534+ metavar "FILE"
537− , Cmd.version = showVersion Paths.version
535+ <> Opts.help "Optional .purs files to load on start"
538− , Cmd.termDoc = "Interactive mode for PureScript"
536+
539− }
537+psciOptions :: Parser PSCiOptions
538+psciOptions = PSCiOptions <$> singleLineFlag
541main :: IO ()
541main :: IO ()
542−main = Cmd.run (term, termInfo)
542+main = execParser opts >>= loop
544+ opts = info (helper <*> psciOptions) infoModList
545+ infoModList = fullDesc <> headerInfo <> footerInfo
546+ headerInfo = header "psci - Interactive mode for PureScript"
547+ footerInfo = footer $ "psci " ++ showVersion Paths.version