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 OverloadedStrings #-}
16{-# LANGUAGE PackageImports #-}
17{-# LANGUAGE TemplateHaskell #-}
19module Language.PureScript.Ide
25import "monad-logger" Control.Monad.Logger
26import qualified Language.PureScript as P
27import qualified Language.PureScript.Ide.CaseSplit as CS
28import Language.PureScript.Ide.Command
29import Language.PureScript.Ide.Completion
30import Language.PureScript.Ide.Error
31import Language.PureScript.Ide.Externs
32import Language.PureScript.Ide.Filter
33import Language.PureScript.Ide.Imports hiding (Import)
34import Language.PureScript.Ide.Matcher
35import Language.PureScript.Ide.Pursuit
36import Language.PureScript.Ide.Rebuild
37import Language.PureScript.Ide.SourceFile
38import Language.PureScript.Ide.State
39import Language.PureScript.Ide.Types
40import Language.PureScript.Ide.Util
41import System.Directory (getCurrentDirectory, getDirectoryContents, doesDirectoryExist, doesFileExist)
42import System.FilePath ((</>))
43import System.FilePath.Glob (glob)
45-- | Accepts a Commmand and runs it against psc-ide's State. This is the main
46-- entry point for the server.
47handleCommand :: (Ide m, MonadLogger m, MonadError PscIdeError m) =>
49handleCommand c = case c of
51 findAvailableExterns >>= loadModules
54 Type search filters currentModule ->
55 findType search filters currentModule
56 Complete filters matcher currentModule ->
57 findCompletions filters matcher currentModule
58 Pursuit query Package ->
59 findPursuitPackages query
60 Pursuit query Identifier ->
61 findPursuitCompletions query
64 List AvailableModules ->
67 ImportList <$> getImportsForFile fp
68 CaseSplit l b e wca t ->
71 MultilineTextResult <$> CS.addClause l wca
72 Import fp outfp _ (AddImplicitImport mn) -> do
73 rs <- addImplicitImport fp mn
74 answerRequest outfp rs
75 Import fp outfp filters (AddImportForIdentifier ident) -> do
76 rs <- addImportForIdentifier fp ident filters
78 Right rs' -> answerRequest outfp rs'
80 pure (CompletionResult (map (completionFromMatch . map withEmptyAnn) question))
84 TextResult . toS <$> liftIO getCurrentDirectory
86 resetIdeState $> TextResult "State has been reset."
90findCompletions :: Ide m =>
91 [Filter] -> Matcher IdeDeclarationAnn -> Maybe P.ModuleName -> m Success
92findCompletions filters matcher currentModule = do
93 modules <- getAllModules currentModule
94 pure . CompletionResult . map completionFromMatch . getCompletions filters matcher $ modules
97 Text -> [Filter] -> Maybe P.ModuleName -> m Success
98findType search filters currentModule = do
99 modules <- getAllModules currentModule
100 pure . CompletionResult . map completionFromMatch . getExactMatches search filters $ modules
102findPursuitCompletions :: MonadIO m =>
103 PursuitQuery -> m Success
104findPursuitCompletions (PursuitQuery q) =
105 PursuitResult <$> liftIO (searchPursuitForDeclarations q)
107findPursuitPackages :: MonadIO m =>
108 PursuitQuery -> m Success
109findPursuitPackages (PursuitQuery q) =
110 PursuitResult <$> liftIO (findPackagesForModuleIdent q)
112printModules :: Ide m => m Success
113printModules = ModuleList . map runModuleNameT <$> getLoadedModulenames
115outputDirectory :: Ide m => m FilePath
117 outputPath <- confOutputPath . ideConfiguration <$> ask
118 cwd <- liftIO getCurrentDirectory
119 pure (cwd </> outputPath)
121listAvailableModules :: Ide m => m Success
122listAvailableModules = do
123 oDir <- outputDirectory
125 contents <- getDirectoryContents oDir
126 let cleaned = filter (`notElem` [".", ".."]) contents
127 return (ModuleList (map toS cleaned))
129caseSplit :: (Ide m, MonadError PscIdeError m) =>
130 Text -> Int -> Int -> CS.WildcardAnnotations -> Text -> m Success
131caseSplit l b e csa t = do
132 patterns <- CS.makePattern l b e csa <$> CS.caseSplit t
133 pure (MultilineTextResult patterns)
135-- | Finds all the externs.json files inside the output folder and returns the
136-- corresponding Modulenames
137findAvailableExterns :: (Ide m, MonadError PscIdeError m) => m [P.ModuleName]
138findAvailableExterns = do
139 oDir <- outputDirectory
140 unlessM (liftIO (doesDirectoryExist oDir))
141 (throwError (GeneralError "Couldn't locate your output directory."))
143 directories <- getDirectoryContents oDir
144 moduleNames <- filterM (containsExterns oDir) directories
145 pure (P.moduleNameFromString . toS <$> moduleNames)
147 -- Takes the output directory and a filepath like "Monad.Control.Eff" and
148 -- looks up, whether that folder contains an externs.json
149 containsExterns :: FilePath -> FilePath -> IO Bool
150 containsExterns oDir d
151 | d `elem` [".", ".."] = pure False
153 let file = oDir </> d </> "externs.json"
156-- | Finds all matches for the globs specified at the commandline
157findAllSourceFiles :: Ide m => m [FilePath]
158findAllSourceFiles = do
159 globs <- confGlobs . ideConfiguration <$> ask
160 liftIO (concatMapM glob globs)
162-- | Looks up the ExternsFiles for the given Modulenames and loads them into the
163-- server state. Then proceeds to parse all the specified sourcefiles and
164-- inserts their ASTs into the state. Finally kicks off an async worker, which
165-- populates Stage 2 and 3 of the state.
167 :: (Ide m, MonadError PscIdeError m, MonadLogger m)
170loadModules moduleNames = do
171 -- We resolve all the modulenames to externs files and load these into memory.
172 oDir <- outputDirectory
174 map (\mn -> oDir </> toS (P.runModuleName mn) </> "externs.json") moduleNames
175 efiles <- traverse readExternFile efPaths
176 traverse_ insertExterns efiles
178 -- We parse all source files, log eventual parse failures if the debug flag
179 -- was set and insert the succesful parses into the state.
180 (failures, allModules) <-
181 partitionEithers <$> (traverse parseModule =<< findAllSourceFiles)
182 unless (null failures) $
183 $(logDebug) ("Failed to parse: " <> show failures)
184 traverse_ insertModule allModules
186 -- Finally we kick off the worker with @async@ and return the number of
187 -- successfully parsed modules.
191 . filterLogger (\_ _ -> confDebug (ideConfiguration env))
192 -- populateStage2 and 3 return Unit for now, so it's fine to discard this
193 -- result. We might want to block on this in a benchmarking situation.
194 _ <- liftIO (async (runLogger (runReaderT (populateStage2 *> populateStage3) env)))
195 pure (TextResult ("Loaded " <> show (length efiles) <> " modules and "
196 <> 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