1{-# LANGUAGE DoAndIfThenElse #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE MultiParamTypeClasses #-}
4{-# LANGUAGE TupleSections #-}
5{-# LANGUAGE DataKinds #-}
7module Language.PureScript.Interactive
11 -- TODO: remove these exports
19import Data.List (intercalate, nub, sort, find, foldl')
20import Data.Maybe (mapMaybe)
21import qualified Data.Map as M
23import Control.Monad.IO.Class (MonadIO, liftIO)
24import Control.Monad.State.Class
25import Control.Monad.Reader.Class
26import Control.Monad.Trans.Except (ExceptT(..), runExceptT)
27import Control.Monad.Trans.State.Strict (StateT, runStateT)
28import Control.Monad.Writer.Strict (Writer(), runWriter)
30import qualified Language.PureScript as P
31import qualified Language.PureScript.Names as N
33import Language.PureScript.Interactive.Completion as Interactive
34import Language.PureScript.Interactive.IO as Interactive
35import Language.PureScript.Interactive.Message as Interactive
36import Language.PureScript.Interactive.Module as Interactive
37import Language.PureScript.Interactive.Parser as Interactive
38import Language.PureScript.Interactive.Printer as Interactive
39import Language.PureScript.Interactive.Types as Interactive
42import System.Process (readProcessWithExitCode)
44-- | Pretty-print errors
45printErrors :: MonadIO m => P.MultipleErrors -> m ()
46printErrors = liftIO . putStrLn . P.prettyPrintMultipleErrors P.defaultPPEOptions
48-- | This is different than the runMake in 'Language.PureScript.Make' in that it specifies the
49-- options and ignores the warning messages.
50runMake :: P.Make a -> IO (Either P.MultipleErrors a)
51runMake mk = fst <$> P.runMake P.defaultOptions mk
53-- | Rebuild a module, using the cached externs data for dependencies.
57 -> P.Make (P.ExternsFile, P.Environment)
58rebuild loadedExterns m = do
59 externs <- P.rebuildModule buildActions loadedExterns m
60 return (externs, foldl' (flip P.applyExternsFileToEnvironment) P.initEnvironment (loadedExterns ++ [externs]))
62 buildActions :: P.MakeActions P.Make
64 (P.buildMakeActions modulesDir
67 False) { P.progress = const (return ()) }
69 filePathMap :: M.Map P.ModuleName (Either P.RebuildPolicy FilePath)
70 filePathMap = M.singleton (P.getModuleName m) (Left P.RebuildAlways)
72-- | Build the collection of modules from scratch. This is usually done on startup.
74 :: [(FilePath, P.Module)]
75 -> P.Make ([P.ExternsFile], P.Environment)
77 foreignFiles <- P.inferForeignModules filePathMap
78 externs <- P.make (buildActions foreignFiles) (map snd ms)
79 return (externs, foldl' (flip P.applyExternsFileToEnvironment) P.initEnvironment externs)
81 buildActions :: M.Map P.ModuleName FilePath -> P.MakeActions P.Make
82 buildActions foreignFiles =
83 P.buildMakeActions modulesDir
88 filePathMap :: M.Map P.ModuleName (Either P.RebuildPolicy FilePath)
89 filePathMap = M.fromList $ map (\(fp, m) -> (P.getModuleName m, Right fp)) ms
91-- | Performs a PSCi command
93 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
96handleCommand ShowHelp = liftIO $ putStrLn helpMessage
97handleCommand ResetState = handleResetState
98handleCommand (Expression val) = handleExpression val
99handleCommand (Import im) = handleImport im
100handleCommand (Decls l) = handleDecls l
101handleCommand (TypeOf val) = handleTypeOf val
102handleCommand (KindOf typ) = handleKindOf typ
103handleCommand (BrowseModule moduleName) = handleBrowse moduleName
104handleCommand (ShowInfo QueryLoaded) = handleShowLoadedModules
105handleCommand (ShowInfo QueryImport) = handleShowImportedModules
106handleCommand QuitPSCi = P.internalError "`handleCommand QuitPSCi` was called. This is a bug."
108-- | Reset the application state
110 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
113 modify $ updateImportedModules (const [])
114 . updateLets (const [])
115 files <- asks psciLoadedFiles
117 modules <- ExceptT . liftIO $ loadAllModules files
118 (externs, _) <- ExceptT . liftIO . runMake . make $ modules
119 return (map snd modules, externs)
121 Left errs -> printErrors errs
122 Right (modules, externs) -> modify (updateLoadedExterns (const (zip modules externs)))
124-- | Takes a value expression and evaluates it with the current state.
126-- TODO: factor out the Node process runner, so that we can use PSCi in other settings.
128 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
131handleExpression val = do
133 let m = createTemporaryModule True st val
134 nodeArgs <- asks ((++ [indexFile]) . psciNodeFlags)
135 e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m
137 Left errs -> printErrors errs
139 liftIO $ writeFile indexFile "require('$PSCI')['$main']();"
140 process <- liftIO findNodeProcess
141 result <- liftIO $ traverse (\node -> readProcessWithExitCode node nodeArgs "") process
143 Just (ExitSuccess, out, _) -> liftIO $ putStrLn out
144 Just (ExitFailure _, _, err) -> liftIO $ putStrLn err
145 Nothing -> liftIO $ putStrLn "Couldn't find node.js"
148-- Takes a list of declarations and updates the environment, then run a make. If the declaration fails,
149-- restore the original environment.
152 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
156 st <- gets (updateLets (++ ds))
157 let m = createTemporaryModule False st (P.Literal (P.ObjectLiteral []))
158 e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m
160 Left err -> printErrors err
163-- | Show actual loaded modules in psci.
164handleShowLoadedModules
165 :: (MonadState PSCiState m, MonadIO m)
167handleShowLoadedModules = do
168 loadedModules <- gets psciLoadedExterns
169 liftIO $ putStrLn (readModules loadedModules)
171 readModules = unlines . sort . nub . map (P.runModuleName . P.getModuleName . fst)
173-- | Show the imported modules in psci.
174handleShowImportedModules
175 :: (MonadState PSCiState m, MonadIO m)
177handleShowImportedModules = do
178 PSCiState { psciImportedModules = importedModules } <- get
179 liftIO $ showModules importedModules >>= putStrLn
182 showModules = return . unlines . sort . map showModule
183 showModule (mn, declType, asQ) =
184 "import " ++ N.runModuleName mn ++ showDeclType declType ++
185 foldMap (\mn' -> " as " ++ N.runModuleName mn') asQ
187 showDeclType P.Implicit = ""
188 showDeclType (P.Explicit refs) = refsList refs
189 showDeclType (P.Hiding refs) = " hiding " ++ refsList refs
190 refsList refs = " (" ++ commaList (mapMaybe showRef refs) ++ ")"
192 showRef :: P.DeclarationRef -> Maybe String
193 showRef (P.TypeRef pn dctors) =
194 Just $ N.runProperName pn ++ "(" ++ maybe ".." (commaList . map N.runProperName) dctors ++ ")"
195 showRef (P.TypeOpRef op) =
196 Just $ "type " ++ N.showOp op
197 showRef (P.ValueRef ident) =
198 Just $ N.runIdent ident
199 showRef (P.ValueOpRef op) =
201 showRef (P.TypeClassRef pn) =
202 Just $ "class " ++ N.runProperName pn
203 showRef (P.TypeInstanceRef ident) =
204 Just $ N.runIdent ident
205 showRef (P.ModuleRef name) =
206 Just $ "module " ++ N.runModuleName name
207 showRef (P.ReExportRef _ _) =
209 showRef (P.PositionedDeclarationRef _ _ ref) =
212 commaList :: [String] -> String
213 commaList = intercalate ", "
215-- | Imports a module, preserving the initial state on failure.
217 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
221 st <- gets (updateImportedModules (im :))
222 let m = createTemporaryModuleForImports st
223 e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m
225 Left errs -> printErrors errs
228-- | Takes a value and prints its type
230 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
235 let m = createTemporaryModule False st val
236 e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m
238 Left errs -> printErrors errs
240 case M.lookup (P.ModuleName [P.ProperName "$PSCI"], P.Ident "it") (P.names env') of
241 Just (ty, _, _) -> liftIO . putStrLn . P.prettyPrintType $ ty
242 Nothing -> liftIO $ putStrLn "Could not find type"
244-- | Takes a type and prints its kind
246 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
251 let m = createTemporaryModuleForKind st typ
252 mName = P.ModuleName [P.ProperName "$PSCI"]
253 e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m
255 Left errs -> printErrors errs
257 case M.lookup (P.Qualified (Just mName) $ P.ProperName "IT") (P.typeSynonyms env') of
259 let chk = (P.emptyCheckState env') { P.checkCurrentModule = Just mName }
260 k = check (P.kindOf typ') chk
262 check :: StateT P.CheckState (ExceptT P.MultipleErrors (Writer P.MultipleErrors)) a -> P.CheckState -> Either P.MultipleErrors (a, P.CheckState)
263 check sew = fst . runWriter . runExceptT . runStateT sew
265 Left err -> printErrors err
266 Right (kind, _) -> liftIO . putStrLn . P.prettyPrintKind $ kind
267 Nothing -> liftIO $ putStrLn "Could not find kind"
269-- | Browse a module and displays its signature
271 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
274handleBrowse moduleName = do
276 env <- asks psciEnvironment
277 if isModInEnv moduleName st
278 then liftIO . putStrLn $ printModuleSignatures moduleName env
279 else case lookupUnQualifiedModName moduleName st of
280 Just unQualifiedName ->
281 if isModInEnv unQualifiedName st
282 then liftIO . putStrLn $ printModuleSignatures unQualifiedName env
283 else failNotInEnv moduleName
285 failNotInEnv moduleName
288 any ((== modName) . P.getModuleName . fst) . psciLoadedExterns
289 failNotInEnv modName =
290 liftIO $ putStrLn $ "Module '" ++ N.runModuleName modName ++ "' is not valid."
291 lookupUnQualifiedModName quaModName st =
292 (\(modName,_,_) -> modName) <$> find ( \(_, _, mayQuaName) -> mayQuaName == Just quaModName) (psciImportedModules st)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list