@@ -1,6 +1,6 @@
1-----------------------------------------------------------------------------
1-----------------------------------------------------------------------------
3−-- Module : Main
3+-- Module : PSCi
4-- Copyright : (c) Phil Freeman 2013
4-- Copyright : (c) Phil Freeman 2013
5-- License : MIT
5-- License : MIT
@@ -19,14 +19,12 @@
19{-# LANGUAGE GeneralizedNewtypeDeriving #-}
19{-# LANGUAGE GeneralizedNewtypeDeriving #-}
20{-# LANGUAGE RecordWildCards #-}
20{-# LANGUAGE RecordWildCards #-}
22−module Main where
22+module PSCi where
24import Data.Foldable (traverse_)
24import Data.Foldable (traverse_)
25−import Data.List (intercalate, isPrefixOf, nub, sortBy, sort)
25+import Data.List (intercalate, nub, sort)
26−import Data.Maybe (mapMaybe)
27import Data.Traversable (traverse)
26import Data.Traversable (traverse)
28import Data.Version (showVersion)
27import Data.Version (showVersion)
29−import Data.Char (isSpace)
30import qualified Data.Map as M
28import qualified Data.Map as M
32import Control.Applicative
30import Control.Applicative
@@ -50,70 +48,21 @@ import System.Process (readProcessWithExitCode)
50import qualified Text.Parsec as Par (ParseError)
48import qualified Text.Parsec as Par (ParseError)
52import qualified Language.PureScript as P
50import qualified Language.PureScript as P
53−import qualified Language.PureScript.AST as D
54import qualified Language.PureScript.Names as N
51import qualified Language.PureScript.Names as N
55import qualified Paths_purescript as Paths
52import qualified Paths_purescript as Paths
57−import qualified Commands as C
58import qualified Directive as D
54import qualified Directive as D
59−import Parser
55+import Parser (parseCommand)
56+import Completion (completion)
61−data PSCiOptions = PSCiOptions
62− { psciMultiLineMode :: Bool
63− , psciInputFile :: [FilePath]
64− , psciInputNodeFlags :: [String]
69−-- Holds a list of imported modules, loaded files, and partial let bindings.
70−-- The let bindings are partial,
71−-- because it makes more sense to apply the binding to the final evaluated expression.
73−data PSCiState = PSCiState
74− { psciImportedFilenames :: [FilePath]
75− , psciImportedModules :: [C.ImportedModule]
76− , psciLoadedModules :: [(Either P.RebuildPolicy FilePath, P.Module)]
77− , psciLetBindings :: [P.Declaration]
78− , psciNodeFlags :: [String]
81−psciImportedModuleNames :: PSCiState -> [P.ModuleName]
82−psciImportedModuleNames (PSCiState{psciImportedModules = is}) =
83− map (\(mn, _, _) -> mn) is
88−-- Updates the state to have more imported modules.
90−updateImportedFiles :: FilePath -> PSCiState -> PSCiState
91−updateImportedFiles filename st = st { psciImportedFilenames = filename : psciImportedFilenames st }
94−-- Updates the state to have more imported modules.
96−updateImportedModules :: C.ImportedModule -> PSCiState -> PSCiState
97−updateImportedModules im st = st { psciImportedModules = im : psciImportedModules st }
100−-- Updates the state to have more loaded files.
102−updateModules :: [(Either P.RebuildPolicy FilePath, P.Module)] -> PSCiState -> PSCiState
103−updateModules modules st = st { psciLoadedModules = psciLoadedModules st ++ modules }
106−-- Updates the state to have more let bindings.
108−updateLets :: [P.Declaration] -> PSCiState -> PSCiState
109−updateLets ds st = st { psciLetBindings = psciLetBindings st ++ ds }
111-- File helpers
60-- File helpers
113-- Load the necessary modules.
62-- Load the necessary modules.
115−defaultImports :: [C.ImportedModule]
64+defaultImports :: [ImportedModule]
116−defaultImports = [(P.ModuleName [P.ProperName "Prelude"], D.Implicit, Nothing)]
65+defaultImports = [(P.ModuleName [P.ProperName "Prelude"], P.Implicit, Nothing)]
119-- Locates the node executable.
68-- Locates the node executable.
@@ -177,7 +126,7 @@ helpMessage :: String
177helpMessage = "The following commands are available:\n\n " ++
126helpMessage = "The following commands are available:\n\n " ++
178 intercalate "\n " (map line D.help)
127 intercalate "\n " (map line D.help)
180− line :: (D.Directive, String, String) -> String
129+ line :: (Directive, String, String) -> String
181 line (dir, arg, desc) = intercalate " "
130 line (dir, arg, desc) = intercalate " "
183 , replicate (11 - length cmd) ' '
132 , replicate (11 - length cmd) ' '
@@ -185,7 +134,7 @@ helpMessage = "The following commands are available:\n\n " ++
185 , replicate (11 - length arg) ' '
134 , replicate (11 - length arg) ' '
188− where cmd = ":" ++ head (D.commands dir)
137+ where cmd = ":" ++ D.stringFor dir
191-- The welcome prologue.
140-- The welcome prologue.
@@ -208,129 +157,6 @@ prologueMessage = intercalate "\n"
208quitMessage :: String
157quitMessage :: String
209quitMessage = "See ya!"
158quitMessage = "See ya!"
211−-- Haskeline completions
213−data CompletionContext = Command String | FilePath String | Module | Identifier
214− | Type | Fixed [String] | Multiple [CompletionContext]
218−-- Decide what kind of completion we need based on input.
219−completionContext :: String -> String -> Maybe CompletionContext
220−completionContext cmd@"" _ = Just $ Multiple [Command cmd, Identifier]
221−completionContext (':' : cmd) word =
222− case D.parseDirective dstr of
223− Just directive | dstr `elem` D.commands directive -> context directive
224− _ -> Just $ Command cmd
227− dstr = takeWhile (not . isSpace) cmd
229− context :: D.Directive -> Maybe CompletionContext
230− context D.Browse = Just Module
231− context D.Load = Just $ FilePath word
232− context D.Quit = Nothing
233− context D.Reset = Nothing
234− context D.Help = Nothing
235− context D.Show = Just $ Fixed ["import", "loaded"]
236− context D.Type = Just Identifier
237− context D.Kind = Just Type
238−completionContext _ _ = Just Identifier
241−-- Loads module, function, and file completions.
243−completion :: CompletionFunc (StateT PSCiState IO)
244−completion = completeWordWithPrev Nothing " \t\n\r" findCompletions
246− findCompletions :: String -> String -> StateT PSCiState IO [Completion]
247− findCompletions prev word = do
248− let ctx = completionContext ((dropWhile isSpace (reverse prev)) ++ word) word
249− completions <- case ctx of
250− Nothing -> return []
251− (Just c) -> (mapMaybe $ either (\cand -> if word `isPrefixOf` cand
252− then Just $ simpleCompletion cand
255− return $ sortBy sorter completions
257− getCompletion :: CompletionContext -> StateT PSCiState IO [Either String Completion]
258− getCompletion (FilePath f) = (map Right) <$> listFiles f
259− getCompletion Module = (map Left) <$> getModuleNames
260− getCompletion Identifier = (map Left) <$> ((++) <$> getIdentNames <*> getDctorNames)
261− getCompletion Type = (map Left) <$> getTypeNames
262− getCompletion (Fixed list) = return $ (map Left) list
263− getCompletion (Multiple contexts) = concat <$> mapM getCompletion contexts
264− getCompletion (Command cmd) = return . map (Left . (":" ++)) . nub $ matching
266− matching :: [String]
267− matching = filter (isPrefixOf cmd) . concatMap (D.commands) $ D.directives
269− getLoadedModules :: StateT PSCiState IO [P.Module]
270− getLoadedModules = map snd . psciLoadedModules <$> get
272− getModuleNames :: StateT PSCiState IO [String]
273− getModuleNames = moduleNames <$> getLoadedModules
275− mapLoadedModulesAndQualify :: (Show a) => (P.Module -> [a]) -> StateT PSCiState IO [String]
276− mapLoadedModulesAndQualify f = do
277− ms <- getLoadedModules
278− q <- sequence [qualifyIfNeeded m (f m) | m <- ms]
281− getIdentNames :: StateT PSCiState IO [String]
282− getIdentNames = mapLoadedModulesAndQualify identNames
284− getDctorNames :: StateT PSCiState IO [String]
285− getDctorNames = mapLoadedModulesAndQualify dctorNames
287− getTypeNames :: StateT PSCiState IO [String]
288− getTypeNames = mapLoadedModulesAndQualify typeDecls
290− qualifyIfNeeded :: (Show a) => P.Module -> [a] -> StateT PSCiState IO [String]
291− qualifyIfNeeded m decls = do
292− let name = P.getModuleName m
293− imported <- psciImportedModuleNames <$> get
294− let qualified = map (P.Qualified $ Just name) decls
295− if name `elem` imported then
296− return $ map show $ qualified ++ (map (P.Qualified Nothing) decls)
298− return $ map show qualified
300− typeDecls :: P.Module -> [N.ProperName]
301− typeDecls m = mapMaybe getTypeName $ filter P.isDataDecl (P.exportedDeclarations m)
302− where getTypeName :: P.Declaration -> Maybe N.ProperName
303− getTypeName (P.TypeSynonymDeclaration name _ _) = Just name
304− getTypeName (P.DataDeclaration _ name _ _) = Just name
305− getTypeName (P.PositionedDeclaration _ _ d) = getTypeName d
306− getTypeName _ = Nothing
308− identNames :: P.Module -> [N.Ident]
309− identNames (P.Module _ _ ds exports) = nub [ ident | ident <- mapMaybe (getDeclName exports) (D.flattenDecls ds) ]
310− where getDeclName :: Maybe [P.DeclarationRef] -> P.Declaration -> Maybe P.Ident
311− getDeclName exts decl@(P.ValueDeclaration ident _ _ _) | P.isExported exts decl = Just ident
312− getDeclName exts decl@(P.ExternDeclaration _ ident _ _) | P.isExported exts decl = Just ident
313− getDeclName exts (P.PositionedDeclaration _ _ d) = getDeclName exts d
314− getDeclName _ _ = Nothing
316− dctorNames :: P.Module -> [N.ProperName]
317− dctorNames m = nub $ concat $ map (P.exportedDctors m) dnames
318− where getDataDeclName :: P.Declaration -> Maybe N.ProperName
319− getDataDeclName (P.DataDeclaration _ name _ _) = Just name
320− getDataDeclName (P.PositionedDeclaration _ _ d) = getDataDeclName d
321− getDataDeclName _ = Nothing
323− dnames :: [N.ProperName]
324− dnames = (mapMaybe getDataDeclName onlyDataDecls)
326− onlyDataDecls :: [P.Declaration]
327− onlyDataDecls = (filter P.isDataDecl (P.exportedDeclarations m))
329− moduleNames :: [P.Module] -> [String]
330− moduleNames ms = nub [show moduleName | P.Module _ moduleName _ _ <- ms]
332− sorter :: Completion -> Completion -> Ordering
333− sorter (Completion _ d1 _) (Completion _ d2 _) = if ":" `isPrefixOf` d1 then LT else compare d1 d2
335-- Compilation
161-- Compilation
@@ -407,7 +233,7 @@ createTemporaryModuleForImports PSCiState{psciImportedModules = imports} =
408 P.Module [] moduleName (importDecl `map` imports) Nothing
234 P.Module [] moduleName (importDecl `map` imports) Nothing
410−importDecl :: C.ImportedModule -> P.Declaration
236+importDecl :: ImportedModule -> P.Declaration
411importDecl (mn, declType, asQ) = P.ImportDeclaration mn declType asQ
237importDecl (mn, declType, asQ) = P.ImportDeclaration mn declType asQ
413modulesDir :: FilePath
239modulesDir :: FilePath
@@ -459,7 +285,7 @@ handleShowLoadedModules = do
459 psciIO $ readModules loadedModules >>= putStrLn
285 psciIO $ readModules loadedModules >>= putStrLn
460 return ()
286 return ()
461 where readModules = return . unlines . sort . nub . map toModuleName
287 where readModules = return . unlines . sort . nub . map toModuleName
462− toModuleName = N.runModuleName . (\ (D.Module _ mdName _ _) -> mdName) . snd
288+ toModuleName = N.runModuleName . (\ (P.Module _ mdName _ _) -> mdName) . snd
465-- Show the imported modules in psci.
291-- Show the imported modules in psci.
@@ -476,17 +302,17 @@ handleShowImportedModules = do
476 Just mn' -> "qualified " ++ N.runModuleName mn ++ " as " ++ N.runModuleName mn'
302 Just mn' -> "qualified " ++ N.runModuleName mn ++ " as " ++ N.runModuleName mn'
477 Nothing -> N.runModuleName mn ++ " " ++ showDeclType declType
303 Nothing -> N.runModuleName mn ++ " " ++ showDeclType declType
479− showDeclType D.Implicit = ""
305+ showDeclType P.Implicit = ""
480− showDeclType (D.Explicit refs) = refsList refs
306+ showDeclType (P.Explicit refs) = refsList refs
481− showDeclType (D.Hiding refs) = "hiding " ++ refsList refs
307+ showDeclType (P.Hiding refs) = "hiding " ++ refsList refs
482 refsList refs = "(" ++ commaList (map showRef refs) ++ ")"
308 refsList refs = "(" ++ commaList (map showRef refs) ++ ")"
484 showRef :: P.DeclarationRef -> String
310 showRef :: P.DeclarationRef -> String
485− showRef (D.TypeRef pn dctors) = show pn ++ "(" ++ maybe ".." (commaList . map N.runProperName) dctors ++ ")"
311+ showRef (P.TypeRef pn dctors) = show pn ++ "(" ++ maybe ".." (commaList . map N.runProperName) dctors ++ ")"
486− showRef (D.ValueRef ident) = show ident
312+ showRef (P.ValueRef ident) = show ident
487− showRef (D.TypeClassRef pn) = show pn
313+ showRef (P.TypeClassRef pn) = show pn
488− showRef (D.TypeInstanceRef ident) = show ident
314+ showRef (P.TypeInstanceRef ident) = show ident
489− showRef (D.PositionedDeclarationRef _ _ ref) = showRef ref
315+ showRef (P.PositionedDeclarationRef _ _ ref) = showRef ref
491 commaList :: [String] -> String
317 commaList :: [String] -> String
492 commaList = intercalate ", "
318 commaList = intercalate ", "
@@ -494,7 +320,7 @@ handleShowImportedModules = do
495-- Imports a module, preserving the initial state on failure.
321-- Imports a module, preserving the initial state on failure.
497−handleImport :: C.ImportedModule -> PSCI ()
323+handleImport :: ImportedModule -> PSCI ()
498handleImport im = do
324handleImport im = do
499 st <- updateImportedModules im <$> PSCI (lift get)
325 st <- updateImportedModules im <$> PSCI (lift get)
500 let m = createTemporaryModuleForImports st
326 let m = createTemporaryModuleForImports st
@@ -578,9 +404,9 @@ handleKindOf typ = do
578-- Commands
404-- Commands
581−-- Parses the input and returns either a Metacommand or an expression.
407+-- Parses the input and returns either a Metacommand, or an error as a string.
583−getCommand :: Bool -> InputT (StateT PSCiState IO) (Either String (Maybe C.Command))
409+getCommand :: Bool -> InputT (StateT PSCiState IO) (Either String (Maybe Command))
584getCommand singleLineMode = do
410getCommand singleLineMode = do
585 firstLine <- getInputLine "> "
411 firstLine <- getInputLine "> "
586 case firstLine of
412 case firstLine of
@@ -593,14 +419,14 @@ getCommand singleLineMode = do
593 go ls = maybe (return . unlines $ reverse ls) (go . (:ls)) =<< getInputLine " "
419 go ls = maybe (return . unlines $ reverse ls) (go . (:ls)) =<< getInputLine " "
596−-- Performs an action for each meta-command given, and also for expressions..
422+-- Performs an action for each meta-command given, and also for expressions.
598−handleCommand :: C.Command -> PSCI ()
424+handleCommand :: Command -> PSCI ()
599−handleCommand (C.Expression val) = handleDeclaration val
425+handleCommand (Expression val) = handleDeclaration val
600−handleCommand C.Help = PSCI $ outputStrLn helpMessage
426+handleCommand ShowHelp = PSCI $ outputStrLn helpMessage
601−handleCommand (C.Import im) = handleImport im
427+handleCommand (Import im) = handleImport im
602−handleCommand (C.Decls l) = handleDecls l
428+handleCommand (Decls l) = handleDecls l
603−handleCommand (C.LoadFile filePath) = do
429+handleCommand (LoadFile filePath) = do
604 absPath <- psciIO $ expandTilde filePath
430 absPath <- psciIO $ expandTilde filePath
605 exists <- psciIO $ doesFileExist absPath
431 exists <- psciIO $ doesFileExist absPath
606 if exists then do
432 if exists then do
@@ -611,7 +437,7 @@ handleCommand (C.LoadFile filePath) = do
611 Right mods -> PSCI . lift $ modify (updateModules (map ((,) (Right absPath)) mods))
437 Right mods -> PSCI . lift $ modify (updateModules (map ((,) (Right absPath)) mods))
613 PSCI . outputStrLn $ "Couldn't locate: " ++ filePath
439 PSCI . outputStrLn $ "Couldn't locate: " ++ filePath
614−handleCommand C.Reset = do
440+handleCommand ResetState = do
615 files <- psciImportedFilenames <$> PSCI (lift get)
441 files <- psciImportedFilenames <$> PSCI (lift get)
616 PSCI . lift . modify $ \st -> st
442 PSCI . lift . modify $ \st -> st
617 { psciImportedFilenames = files
443 { psciImportedFilenames = files
@@ -619,14 +445,14 @@ handleCommand C.Reset = do
619 , psciLetBindings = []
445 , psciLetBindings = []
621 loadAllImportedModules
447 loadAllImportedModules
622−handleCommand (C.TypeOf val) = handleTypeOf val
448+handleCommand (TypeOf val) = handleTypeOf val
623−handleCommand (C.KindOf typ) = handleKindOf typ
449+handleCommand (KindOf typ) = handleKindOf typ
624−handleCommand (C.Browse moduleName) = handleBrowse moduleName
450+handleCommand (BrowseModule moduleName) = handleBrowse moduleName
625−handleCommand (C.Show "loaded") = handleShowLoadedModules
451+handleCommand (ShowInfo QueryLoaded) = handleShowLoadedModules
626−handleCommand (C.Show "import") = handleShowImportedModules
452+handleCommand (ShowInfo QueryImport) = handleShowImportedModules
627−handleCommand _ = PSCI $ outputStrLn "Unknown command"
453+handleCommand QuitPSCi = error "`handleCommand QuitPSCi` was called. This is a bug."
629−loadUserConfig :: IO (Maybe [C.Command])
455+loadUserConfig :: IO (Maybe [Command])
630loadUserConfig = do
456loadUserConfig = do
631 configFile <- (</> ".psci") <$> getCurrentDirectory
457 configFile <- (</> ".psci") <$> getCurrentDirectory
632 exists <- doesFileExist configFile
458 exists <- doesFileExist configFile
@@ -662,7 +488,7 @@ loop PSCiOptions{..} = do
662 case c of
488 case c of
663 Left err -> outputStrLn err >> go
489 Left err -> outputStrLn err >> go
664 Right Nothing -> go
490 Right Nothing -> go
665− Right (Just C.Quit) -> outputStrLn quitMessage
491+ Right (Just QuitPSCi) -> outputStrLn quitMessage
666 Right (Just c') -> runPSCI (loadAllImportedModules >> handleCommand c') >> go
492 Right (Just c') -> runPSCI (loadAllImportedModules >> handleCommand c') >> go
668multiLineMode :: Parser Bool
494multiLineMode :: Parser Bool
@@ -690,8 +516,8 @@ psciOptions = PSCiOptions <$> multiLineMode
690 <*> many inputFile
516 <*> many inputFile
691 <*> nodeFlagsFlag
517 <*> nodeFlagsFlag
693−main :: IO ()
519+runPSCi :: IO ()
694−main = execParser opts >>= loop
520+runPSCi = execParser opts >>= loop
696 opts = info (version <*> helper <*> psciOptions) infoModList
522 opts = info (version <*> helper <*> psciOptions) infoModList
697 infoModList = fullDesc <> headerInfo <> footerInfo
523 infoModList = fullDesc <> headerInfo <> footerInfo