1{-# LANGUAGE DoAndIfThenElse #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE MultiParamTypeClasses #-}
5{-# LANGUAGE RecordWildCards #-}
6{-# LANGUAGE TupleSections #-}
7{-# LANGUAGE DataKinds #-}
9module Language.PureScript.Interactive
13 -- TODO: remove these exports
21import Data.List (intercalate, nub, sort, find, foldl')
22import qualified Data.Map as M
24import Control.Monad.IO.Class (MonadIO, liftIO)
25import Control.Monad.State.Class
26import Control.Monad.Reader.Class
27import Control.Monad.Trans.Except (ExceptT(..), runExceptT)
28import Control.Monad.Trans.State.Strict (StateT, runStateT)
29import Control.Monad.Writer.Strict (Writer(), runWriter)
31import qualified Language.PureScript as P
32import qualified Language.PureScript.Names as N
34import Language.PureScript.Interactive.Completion as Interactive
35import Language.PureScript.Interactive.IO as Interactive
36import Language.PureScript.Interactive.Message as Interactive
37import Language.PureScript.Interactive.Module as Interactive
38import Language.PureScript.Interactive.Parser as Interactive
39import Language.PureScript.Interactive.Printer as Interactive
40import Language.PureScript.Interactive.Types as Interactive
43import System.Process (readProcessWithExitCode)
45-- | Pretty-print errors
46printErrors :: MonadIO m => P.MultipleErrors -> m ()
47printErrors = liftIO . putStrLn . P.prettyPrintMultipleErrors P.defaultPPEOptions
49-- | This is different than the runMake in 'Language.PureScript.Make' in that it specifies the
50-- options and ignores the warning messages.
51runMake :: P.Make a -> IO (Either P.MultipleErrors a)
52runMake mk = fst <$> P.runMake P.defaultOptions mk
54-- | Rebuild a module, using the cached externs data for dependencies.
58 -> P.Make (P.ExternsFile, P.Environment)
59rebuild loadedExterns m = do
60 externs <- P.rebuildModule buildActions loadedExterns m
61 return (externs, foldl' (flip P.applyExternsFileToEnvironment) P.initEnvironment (loadedExterns ++ [externs]))
63 buildActions :: P.MakeActions P.Make
65 (P.buildMakeActions modulesDir
68 False) { P.progress = const (return ()) }
70 filePathMap :: M.Map P.ModuleName (Either P.RebuildPolicy FilePath)
71 filePathMap = M.singleton (P.getModuleName m) (Left P.RebuildAlways)
73-- | Build the collection of modules from scratch. This is usually done on startup.
75 :: [(FilePath, P.Module)]
76 -> P.Make ([P.ExternsFile], P.Environment)
78 foreignFiles <- P.inferForeignModules filePathMap
79 externs <- P.make (buildActions foreignFiles) (map snd ms)
80 return (externs, foldl' (flip P.applyExternsFileToEnvironment) P.initEnvironment externs)
82 buildActions :: M.Map P.ModuleName FilePath -> P.MakeActions P.Make
83 buildActions foreignFiles =
84 P.buildMakeActions modulesDir
89 filePathMap :: M.Map P.ModuleName (Either P.RebuildPolicy FilePath)
90 filePathMap = M.fromList $ map (\(fp, m) -> (P.getModuleName m, Right fp)) ms
92-- | Performs a PSCi command
94 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
97handleCommand ShowHelp = liftIO $ putStrLn helpMessage
98handleCommand ResetState = handleResetState
99handleCommand (Expression val) = handleExpression val
100handleCommand (Import im) = handleImport im
101handleCommand (Decls l) = handleDecls l
102handleCommand (TypeOf val) = handleTypeOf val
103handleCommand (KindOf typ) = handleKindOf typ
104handleCommand (BrowseModule moduleName) = handleBrowse moduleName
105handleCommand (ShowInfo QueryLoaded) = handleShowLoadedModules
106handleCommand (ShowInfo QueryImport) = handleShowImportedModules
107handleCommand QuitPSCi = P.internalError "`handleCommand QuitPSCi` was called. This is a bug."
109-- | Reset the application state
111 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
114 modify $ updateImportedModules (const [])
115 . updateLets (const [])
116 files <- asks psciLoadedFiles
118 modules <- ExceptT . liftIO $ loadAllModules files
119 (externs, _) <- ExceptT . liftIO . runMake . make $ modules
120 return (map snd modules, externs)
122 Left errs -> printErrors errs
123 Right (modules, externs) -> modify (updateLoadedExterns (const (zip modules externs)))
125-- | Takes a value expression and evaluates it with the current state.
127-- TODO: factor out the Node process runner, so that we can use PSCi in other settings.
129 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
132handleExpression val = do
134 let m = createTemporaryModule True st val
135 nodeArgs <- asks ((++ [indexFile]) . psciNodeFlags)
136 e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m
138 Left errs -> printErrors errs
140 liftIO $ writeFile indexFile "require('$PSCI')['$main']();"
141 process <- liftIO findNodeProcess
142 result <- liftIO $ traverse (\node -> readProcessWithExitCode node nodeArgs "") process
144 Just (ExitSuccess, out, _) -> liftIO $ putStrLn out
145 Just (ExitFailure _, _, err) -> liftIO $ putStrLn err
146 Nothing -> liftIO $ putStrLn "Couldn't find node.js"
149-- Takes a list of declarations and updates the environment, then run a make. If the declaration fails,
150-- restore the original environment.
153 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
157 st <- gets (updateLets (++ ds))
158 let m = createTemporaryModule False st (P.Literal (P.ObjectLiteral []))
159 e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m
161 Left err -> printErrors err
164-- | Show actual loaded modules in psci.
165handleShowLoadedModules
166 :: (MonadState PSCiState m, MonadIO m)
168handleShowLoadedModules = do
169 loadedModules <- gets psciLoadedExterns
170 liftIO $ putStrLn (readModules loadedModules)
172 readModules = unlines . sort . nub . map (P.runModuleName . P.getModuleName . fst)
174-- | Show the imported modules in psci.
175handleShowImportedModules
176 :: (MonadState PSCiState m, MonadIO m)
178handleShowImportedModules = do
179 PSCiState { psciImportedModules = importedModules } <- get
180 liftIO $ showModules importedModules >>= putStrLn
183 showModules = return . unlines . sort . map showModule
184 showModule (mn, declType, asQ) =
185 "import " ++ N.runModuleName mn ++ showDeclType declType ++
186 foldMap (\mn' -> " as " ++ N.runModuleName mn') asQ
188 showDeclType P.Implicit = ""
189 showDeclType (P.Explicit refs) = refsList refs
190 showDeclType (P.Hiding refs) = " hiding " ++ refsList refs
191 refsList refs = " (" ++ commaList (map showRef refs) ++ ")"
193 showRef :: P.DeclarationRef -> String
194 showRef (P.TypeRef pn dctors) = N.runProperName pn ++ "(" ++ maybe ".." (commaList . map N.runProperName) dctors ++ ")"
195 showRef (P.TypeOpRef op) = "type " ++ N.showOp op
196 showRef (P.ValueRef ident) = N.runIdent ident
197 showRef (P.ValueOpRef op) = N.showOp op
198 showRef (P.TypeClassRef pn) = "class " ++ N.runProperName pn
199 showRef (P.TypeInstanceRef ident) = N.runIdent ident
200 showRef (P.ModuleRef name) = "module " ++ N.runModuleName name
201 showRef (P.PositionedDeclarationRef _ _ ref) = showRef ref
203 commaList :: [String] -> String
204 commaList = intercalate ", "
206-- | Imports a module, preserving the initial state on failure.
208 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
212 st <- gets (updateImportedModules (im :))
213 let m = createTemporaryModuleForImports st
214 e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m
216 Left errs -> printErrors errs
219-- | Takes a value and prints its type
221 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
226 let m = createTemporaryModule False st val
227 e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m
229 Left errs -> printErrors errs
231 case M.lookup (P.ModuleName [P.ProperName "$PSCI"], P.Ident "it") (P.names env') of
232 Just (ty, _, _) -> liftIO . putStrLn . P.prettyPrintType $ ty
233 Nothing -> liftIO $ putStrLn "Could not find type"
235-- | Takes a type and prints its kind
237 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
242 let m = createTemporaryModuleForKind st typ
243 mName = P.ModuleName [P.ProperName "$PSCI"]
244 e <- liftIO . runMake $ rebuild (map snd (psciLoadedExterns st)) m
246 Left errs -> printErrors errs
248 case M.lookup (P.Qualified (Just mName) $ P.ProperName "IT") (P.typeSynonyms env') of
250 let chk = (P.emptyCheckState env') { P.checkCurrentModule = Just mName }
251 k = check (P.kindOf typ') chk
253 check :: StateT P.CheckState (ExceptT P.MultipleErrors (Writer P.MultipleErrors)) a -> P.CheckState -> Either P.MultipleErrors (a, P.CheckState)
254 check sew = fst . runWriter . runExceptT . runStateT sew
256 Left err -> printErrors err
257 Right (kind, _) -> liftIO . putStrLn . P.prettyPrintKind $ kind
258 Nothing -> liftIO $ putStrLn "Could not find kind"
260-- | Browse a module and displays its signature
262 :: (MonadReader PSCiConfig m, MonadState PSCiState m, MonadIO m)
265handleBrowse moduleName = do
267 env <- asks psciEnvironment
268 if isModInEnv moduleName st
269 then liftIO . putStrLn $ printModuleSignatures moduleName env
270 else case lookupUnQualifiedModName moduleName st of
271 Just unQualifiedName ->
272 if isModInEnv unQualifiedName st
273 then liftIO . putStrLn $ printModuleSignatures unQualifiedName env
274 else failNotInEnv moduleName
276 failNotInEnv moduleName
279 any ((== modName) . P.getModuleName . fst) . psciLoadedExterns
280 failNotInEnv modName =
281 liftIO $ putStrLn $ "Module '" ++ N.runModuleName modName ++ "' is not valid."
282 lookupUnQualifiedModName quaModName st =
283 (\(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