1-----------------------------------------------------------------------------
3-- Module : Language.PureScript.Ide
4-- Description : Interface for the psc-ide-server
5-- Copyright : Christoph Hegemann 2016
6-- License : MIT (http://opensource.org/licenses/MIT)
8-- Maintainer : Christoph Hegemann <christoph.hegemann1337@gmail.com>
9-- Stability : experimental
12-- Interface for the psc-ide-server
13-----------------------------------------------------------------------------
15{-# LANGUAGE PackageImports #-}
16{-# LANGUAGE TemplateHaskell #-}
18module Language.PureScript.Ide
24import "monad-logger" Control.Monad.Logger
25import qualified Language.PureScript as P
26import qualified Language.PureScript.Ide.CaseSplit as CS
27import Language.PureScript.Ide.Command
28import Language.PureScript.Ide.Completion
29import Language.PureScript.Ide.Error
30import Language.PureScript.Ide.Externs
31import Language.PureScript.Ide.Filter
32import Language.PureScript.Ide.Imports hiding (Import)
33import Language.PureScript.Ide.Matcher
34import Language.PureScript.Ide.Pursuit
35import Language.PureScript.Ide.Rebuild
36import Language.PureScript.Ide.SourceFile
37import Language.PureScript.Ide.State
38import Language.PureScript.Ide.Types
39import Language.PureScript.Ide.Util
40import System.Directory (getCurrentDirectory, getDirectoryContents, doesDirectoryExist, doesFileExist)
41import System.FilePath ((</>))
42import System.FilePath.Glob (glob)
44-- | Accepts a Commmand and runs it against psc-ide's State. This is the main
45-- entry point for the server.
46handleCommand :: (Ide m, MonadLogger m, MonadError IdeError m) =>
48handleCommand c = case c of
50 findAvailableExterns >>= loadModulesAsync
52 loadModulesAsync modules
54 findAvailableExterns >>= loadModulesSync
56 loadModulesSync modules
57 Type search filters currentModule ->
58 findType search filters currentModule
59 Complete filters matcher currentModule ->
60 findCompletions filters matcher currentModule
61 Pursuit query Package ->
62 findPursuitPackages query
63 Pursuit query Identifier ->
64 findPursuitCompletions query
67 List AvailableModules ->
70 ImportList <$> getImportsForFile fp
71 CaseSplit l b e wca t ->
74 MultilineTextResult <$> CS.addClause l wca
75 Import fp outfp _ (AddImplicitImport mn) -> do
76 rs <- addImplicitImport fp mn
77 answerRequest outfp rs
78 Import fp outfp filters (AddImportForIdentifier ident) -> do
79 rs <- addImportForIdentifier fp ident filters
81 Right rs' -> answerRequest outfp rs'
83 pure (CompletionResult (map (completionFromMatch . map withEmptyAnn) question))
89 TextResult . toS <$> liftIO getCurrentDirectory
91 resetIdeState $> TextResult "State has been reset."
95findCompletions :: Ide m =>
96 [Filter] -> Matcher IdeDeclarationAnn -> Maybe P.ModuleName -> m Success
97findCompletions filters matcher currentModule = do
98 modules <- getAllModules currentModule
99 pure . CompletionResult . map completionFromMatch . getCompletions filters matcher $ modules
102 Text -> [Filter] -> Maybe P.ModuleName -> m Success
103findType search filters currentModule = do
104 modules <- getAllModules currentModule
105 pure . CompletionResult . map completionFromMatch . getExactMatches search filters $ modules
107findPursuitCompletions :: MonadIO m =>
108 PursuitQuery -> m Success
109findPursuitCompletions (PursuitQuery q) =
110 PursuitResult <$> liftIO (searchPursuitForDeclarations q)
112findPursuitPackages :: MonadIO m =>
113 PursuitQuery -> m Success
114findPursuitPackages (PursuitQuery q) =
115 PursuitResult <$> liftIO (findPackagesForModuleIdent q)
117printModules :: Ide m => m Success
118printModules = ModuleList . map P.runModuleName <$> getLoadedModulenames
120outputDirectory :: Ide m => m FilePath
122 outputPath <- confOutputPath . ideConfiguration <$> ask
123 cwd <- liftIO getCurrentDirectory
124 pure (cwd </> outputPath)
126listAvailableModules :: Ide m => m Success
127listAvailableModules = do
128 oDir <- outputDirectory
130 contents <- getDirectoryContents oDir
131 let cleaned = filter (`notElem` [".", ".."]) contents
132 return (ModuleList (map toS cleaned))
134caseSplit :: (Ide m, MonadError IdeError m) =>
135 Text -> Int -> Int -> CS.WildcardAnnotations -> Text -> m Success
136caseSplit l b e csa t = do
137 patterns <- CS.makePattern l b e csa <$> CS.caseSplit t
138 pure (MultilineTextResult patterns)
140-- | Finds all the externs.json files inside the output folder and returns the
141-- corresponding Modulenames
142findAvailableExterns :: (Ide m, MonadError IdeError m) => m [P.ModuleName]
143findAvailableExterns = do
144 oDir <- outputDirectory
145 unlessM (liftIO (doesDirectoryExist oDir))
146 (throwError (GeneralError "Couldn't locate your output directory."))
148 directories <- getDirectoryContents oDir
149 moduleNames <- filterM (containsExterns oDir) directories
150 pure (P.moduleNameFromString . toS <$> moduleNames)
152 -- Takes the output directory and a filepath like "Monad.Control.Eff" and
153 -- looks up, whether that folder contains an externs.json
154 containsExterns :: FilePath -> FilePath -> IO Bool
155 containsExterns oDir d
156 | d `elem` [".", ".."] = pure False
158 let file = oDir </> d </> "externs.json"
161-- | Finds all matches for the globs specified at the commandline
162findAllSourceFiles :: Ide m => m [FilePath]
163findAllSourceFiles = do
164 globs <- confGlobs . ideConfiguration <$> ask
165 liftIO (concatMapM glob globs)
167-- | Looks up the ExternsFiles for the given Modulenames and loads them into the
168-- server state. Then proceeds to parse all the specified sourcefiles and
169-- inserts their ASTs into the state. Finally kicks off an async worker, which
170-- populates Stage 2 and 3 of the state.
172 :: (Ide m, MonadError IdeError m, MonadLogger m)
175loadModulesAsync moduleNames = do
176 tr <- loadModules moduleNames
178 -- Finally we kick off the worker with @async@ and return the number of
179 -- successfully parsed modules.
181 let ll = confLogLevel (ideConfiguration env)
182 -- populateStage2 and 3 return Unit for now, so it's fine to discard this
183 -- result. We might want to block on this in a benchmarking situation.
184 _ <- liftIO (async (runLogger ll (runReaderT (populateStage2 *> populateStage3) env)))
188 :: (Ide m, MonadError IdeError m, MonadLogger m)
191loadModulesSync moduleNames = do
192 tr <- loadModules moduleNames
193 populateStage2 *> populateStage3
197 :: (Ide m, MonadError IdeError m, MonadLogger m)
200loadModules moduleNames = do
201 -- We resolve all the modulenames to externs files and load these into memory.
202 oDir <- outputDirectory
204 map (\mn -> oDir </> toS (P.runModuleName mn) </> "externs.json") moduleNames
205 efiles <- traverse readExternFile efPaths
206 traverse_ insertExterns efiles
208 -- We parse all source files, log eventual parse failures and insert the
209 -- successful parses into the state.
210 (failures, allModules) <-
211 partitionEithers <$> (traverse parseModule =<< findAllSourceFiles)
212 unless (null failures) $
213 $(logWarn) ("Failed to parse: " <> show failures)
214 traverse_ insertModule allModules
216 pure (TextResult ("Loaded " <> show (length efiles) <> " modules and "
217 <> show (length allModules) <> " source files."))
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