git.iko.soy/rowtype-yoga/purescript-native
on golang help sign in
New reexports (#2215)

* [psc-ide] Goto definition * extracts declaration positions from the parsed source files and matches them up with declarations * the definition location for an identifier can be queried by using the type command, which is planned to turn into an :info command * [psc-ide] Refactor Reexport resolving * Gets rid of the old declaration format * Uses the new ReExportRef mechanism to resolve reexports * cleans up module exports

commit95334c8053a160e6f2dbae8034e9ddc0b8d917a0 authorChristoph Hegemann <kRITZCREEK@users.noreply.github.com> dateSat 9 Jul 2016, 20:56 (10 years ago) parent11fdef0
28 files changed+604−454
psc-ide-server/Main.hs+1−2 psc-ide-server/PROTOCOL.md+23−2 purescript.cabal+2 src/Language/PureScript/Ide.hs+14−24 src/Language/PureScript/Ide/CaseSplit.hs−1 src/Language/PureScript/Ide/Command.hs+1−1 src/Language/PureScript/Ide/Completion.hs+13−8 src/Language/PureScript/Ide/Error.hs−1 src/Language/PureScript/Ide/Externs.hs+55−62 src/Language/PureScript/Ide/Filter.hs+4−4 src/Language/PureScript/Ide/Imports.hs+7−12 src/Language/PureScript/Ide/Matcher.hs+20−19 src/Language/PureScript/Ide/Pursuit.hs+4−1 src/Language/PureScript/Ide/Reexports.hs+88−67 src/Language/PureScript/Ide/SourceFile.hs+38−31 src/Language/PureScript/Ide/State.hs+66−63 src/Language/PureScript/Ide/Types.hs+50−52 src/Language/PureScript/Ide/Util.hs+40−14 src/Language/PureScript/Ide/Watcher.hs+4−2 tests/Language/PureScript/Ide/FilterSpec.hs+5−5 tests/Language/PureScript/Ide/ImportsSpec.hs+2−2 tests/Language/PureScript/Ide/Integration.hs+17−6 tests/Language/PureScript/Ide/MatcherSpec.hs+7−7 tests/Language/PureScript/Ide/ReexportsSpec.hs+51−66 tests/Language/PureScript/Ide/SourceFile/IntegrationSpec.hs+36 tests/Language/PureScript/Ide/SourceFileSpec.hs+46 tests/support/pscide/src/RebuildSpecWithForeign.js−2 tests/support/pscide/src/SourceFileSpec.purs+10
psc-ide-server/Main.hs+1−2
@@ -72,7 +72,6 @@ main :: IO ()
7272main = do
7373 Options dir globs outputPath port noWatch debug <- execParser opts
7474 maybe (pure ()) setCurrentDirectory dir
75− serverState <- newTVarIO emptyPscIdeState
7675 ideState <- newTVarIO emptyIdeState
7776 cwd <- getCurrentDirectory
7877 let fullOutputPath = cwd </> outputPath
@@ -87,7 +86,7 @@ main = do
8786 void (forkFinally (watcher ideState fullOutputPath) print)
8887
8988 let conf = Configuration {confDebug = debug, confOutputPath = outputPath, confGlobs = globs}
90− env = IdeEnvironment {envStateVar = serverState, ideStateVar = ideState, ideConfiguration = conf}
89+ env = IdeEnvironment {ideStateVar = ideState, ideConfiguration = conf}
9190 startServer port env
9291 where
9392 parser =
psc-ide-server/PROTOCOL.md+23−2
@@ -30,7 +30,8 @@ to detect all the compiled modules in your project and load them.
3030The Load Command returns a string with a summary about the loading process.
3131
3232### Type
33−The `type` command looks up the type for a given identifier.
33+The `type` command looks up the type for a given identifier. It also returns the
34+definition position, if it can be found in the passed source files.
3435
3536**Params:**
3637 - `search :: String`: The identifier to look for. Only matches on equality.
@@ -50,7 +51,27 @@ The `type` command looks up the type for a given identifier.
5051```
5152
5253**Result:**
53−The possible types are returned in the same format as completions
54+The possible types are returned in the same format as completions + eventual position information
55+```json
56+[
57+ {
58+ "module": "Data.Array",
59+ "identifier": "filter",
60+ "type": "forall a. (a -> Boolean) -> Array a -> Array a"
61+ },
62+ {
63+ "module": "Data.Array",
64+ "identifier": "filter",
65+ "type": "forall a. (a -> Boolean) -> Array a -> Array a",
66+ "definedAt":
67+ {
68+ "name": "/path/to/file",
69+ "start": [1, 3],
70+ "end": [3, 1]
71+ }
72+ }
73+]
74+```
5475
5576### Complete
5677The `complete` command looks up possible completions/corrections.
purescript.cabal+2
@@ -506,5 +506,7 @@ test-suite tests
506506 Language.PureScript.Ide.MatcherSpec
507507 Language.PureScript.Ide.RebuildSpec
508508 Language.PureScript.Ide.ReexportsSpec
509+ Language.PureScript.Ide.SourceFile.IntegrationSpec
510+ Language.PureScript.Ide.SourceFileSpec
509511 buildable: True
510512 hs-source-dirs: tests
src/Language/PureScript/Ide.hs+14−24
@@ -42,6 +42,8 @@ import System.Directory
4242import System.FilePath
4343import System.FilePath.Glob
4444
45+-- | Accepts a Commmand and runs it against psc-ide's State. This is the main
46+-- entry point for the server.
4547handleCommand :: (Ide m, MonadLogger m, MonadError PscIdeError m) =>
4648 Command -> m Success
4749handleCommand c = case c of
@@ -66,7 +68,7 @@ handleCommand c = case c of
6668 CaseSplit l b e wca t ->
6769 caseSplit l b e wca t
6870 AddClause l wca ->
69− addClause l wca
71+ MultilineTextResult <$> CS.addClause l wca
7072 Import fp outfp _ (AddImplicitImport mn) -> do
7173 rs <- addImplicitImport fp mn
7274 answerRequest outfp rs
@@ -86,16 +88,16 @@ handleCommand c = case c of
8688 liftIO exitSuccess
8789
8890findCompletions :: Ide m =>
89− [Filter] -> Matcher -> Maybe P.ModuleName -> m Success
91+ [Filter] -> Matcher IdeDeclaration -> Maybe P.ModuleName -> m Success
9092findCompletions filters matcher currentModule = do
91− modules <- getAllModules2 currentModule
93+ modules <- getAllModules currentModule
9294 pure . CompletionResult . map completionFromMatch . getCompletions filters matcher $ modules
9395
9496findType :: Ide m =>
9597 Text -> [Filter] -> Maybe P.ModuleName -> m Success
9698findType search filters currentModule = do
97− modules <- getAllModules2 currentModule
98− pure . CompletionResult . map completionFromMatch . getExactMatches search filters $ modules
99+ modules <- getAllModules currentModule
100+ pure . InfoResult . map infoFromMatch . getExactMatches search filters $ modules
99101
100102findPursuitCompletions :: MonadIO m =>
101103 PursuitQuery -> m Success
@@ -130,13 +132,6 @@ caseSplit l b e csa t = do
130132 patterns <- CS.makePattern l b e csa <$> CS.caseSplit t
131133 pure (MultilineTextResult patterns)
132134
133−addClause
134− :: (MonadError PscIdeError m)
135− => Text
136− -> CS.WildcardAnnotations
137− -> m Success
138−addClause t wca = MultilineTextResult <$> CS.addClause t wca
139−
140135-- | Finds all the externs.json files inside the output folder and returns the
141136-- corresponding Modulenames
142137findAvailableExterns :: (Ide m, MonadError PscIdeError m) => m [P.ModuleName]
@@ -146,13 +141,13 @@ findAvailableExterns = do
146141 (throwError (GeneralError "Couldn't locate your output directory."))
147142 liftIO $ do
148143 directories <- getDirectoryContents oDir
149− moduleNames <- filterM (checkExternsPath oDir) directories
144+ moduleNames <- filterM (containsExterns oDir) directories
150145 pure (P.moduleNameFromString <$> moduleNames)
151146 where
152− -- | Takes the output directory and a filepath like "Monad.Control.Eff" and
147+ -- Takes the output directory and a filepath like "Monad.Control.Eff" and
153148 -- looks up, whether that folder contains an externs.json
154− checkExternsPath :: FilePath -> FilePath -> IO Bool
155− checkExternsPath oDir d
149+ containsExterns :: FilePath -> FilePath -> IO Bool
150+ containsExterns oDir d
156151 | d `elem` [".", ".."] = pure False
157152 | otherwise = do
158153 let file = oDir </> d </> "externs.json"
@@ -188,19 +183,14 @@ loadModules moduleNames = do
188183 $(logDebug) ("Failed to parse: " <> show failures)
189184 traverse_ insertModule allModules
190185
191− -- Because we still need the "old" module format to resolve reexports in the
192− -- worker thread, we insert it into the state aswell.
193− -- TODO Get rid of this once ModuleOld is gone
194− traverse_ insertModuleOld efiles
195−
196186 -- Finally we kick off the worker with @async@ and return the number of
197187 -- successfully parsed modules.
198188 env <- ask
199189 let runLogger =
200190 runStdoutLoggingT
201191 . filterLogger (\_ _ -> confDebug (ideConfiguration env))
202− -- populateStage2 returns Unit for now, so it's fine to discard this result.
203− -- We might want to block on this in a benchmarking situation.
204− _ <- liftIO (async (runLogger (runReaderT populateStage2 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)))
205195 pure (TextResult ("Loaded " <> show (length efiles) <> " modules and "
206196 <> show (length allModules) <> " source files."))
src/Language/PureScript/Ide/CaseSplit.hs−1
@@ -30,7 +30,6 @@ import qualified Language.PureScript as P
3030
3131import Language.PureScript.Externs
3232import Language.PureScript.Ide.Error
33−import Language.PureScript.Ide.Externs (unwrapPositioned)
3433import Language.PureScript.Ide.State
3534import Language.PureScript.Ide.Types
3635import Language.PureScript.Ide.Util
src/Language/PureScript/Ide/Command.hs+1−1
@@ -35,7 +35,7 @@ data Command
3535 }
3636 | Complete
3737 { completeFilters :: [Filter]
38− , completeMatcher :: Matcher
38+ , completeMatcher :: Matcher IdeDeclaration
3939 , completeCurrentModule :: Maybe P.ModuleName
4040 }
4141 | Pursuit
src/Language/PureScript/Ide/Completion.hs+13−8
@@ -9,19 +9,24 @@ import Protolude
99import Language.PureScript.Ide.Filter
1010import Language.PureScript.Ide.Matcher
1111import Language.PureScript.Ide.Types
12+import Language.PureScript.Ide.Util
1213
1314-- | Applies the CompletionFilters and the Matcher to the given Modules
1415-- and sorts the found Completions according to the Matching Score
15−getCompletions :: [Filter] -> Matcher -> [Module] -> [Match]
16+getCompletions
17+ :: [Filter]
18+ -> Matcher IdeDeclaration
19+ -> [Module]
20+ -> [Match IdeDeclaration]
1621getCompletions filters matcher modules =
17− runMatcher matcher (completionsFromModules (applyFilters filters modules))
22+ runMatcher matcher (completionsFromModules discardAnn (applyFilters filters modules))
1823
19−getExactMatches :: Text -> [Filter] -> [Module] -> [Match]
24+getExactMatches :: Text -> [Filter] -> [Module] -> [Match IdeDeclarationAnn]
2025getExactMatches search filters modules =
21− completionsFromModules (applyFilters (equalityFilter search : filters) modules)
26+ completionsFromModules identity (applyFilters (equalityFilter search : filters) modules)
2227
23−completionsFromModules :: [Module] -> [Match]
24−completionsFromModules = foldMap completionFromModule
28+completionsFromModules :: (IdeDeclarationAnn -> a) -> [Module] -> [Match a]
29+completionsFromModules f = foldMap completionFromModule
2530 where
26− completionFromModule :: Module -> [Match]
27− completionFromModule (moduleName, decls) = map (Match moduleName) decls
31+ completionFromModule (moduleName, decls) =
32+ map (\x -> Match (moduleName, f x)) decls
src/Language/PureScript/Ide/Error.hs−1
@@ -15,7 +15,6 @@
1515{-# LANGUAGE OverloadedStrings #-}
1616module Language.PureScript.Ide.Error
1717 ( PscIdeError(..)
18− , textError
1918 ) where
2019
2120import Protolude
src/Language/PureScript/Ide/Externs.hs+55−62
@@ -17,27 +17,23 @@
1717{-# LANGUAGE FlexibleContexts #-}
1818
1919module Language.PureScript.Ide.Externs
20− ( ExternDecl(..),
21− ModuleIdent,
22− readExternFile,
20+ ( readExternFile,
2321 convertExterns,
24− convertModule,
25− unwrapPositioned,
26− unwrapPositionedRef
22+ annotateLocations
2723 ) where
2824
2925import Protolude
3026
3127import Data.Aeson (decodeStrict)
3228import Data.List (nub)
29+import qualified Data.Map as Map
3330import qualified Data.ByteString as BS
3431import Language.PureScript.Ide.Error (PscIdeError (..))
3532import Language.PureScript.Ide.Types
3633import Language.PureScript.Ide.Util
3734
3835import qualified Language.PureScript as P
39−
40−import System.FilePath
36+import System.FilePath
4137
4238readExternFile :: (MonadIO m, MonadError PscIdeError m) =>
4339 FilePath -> m P.ExternsFile
@@ -47,86 +43,83 @@ readExternFile fp = do
4743 Nothing -> throwError . GeneralError $ "Parsing the extern at: " <> toS fp <> " failed"
4844 Just externs -> pure externs
4945
50−convertExterns :: P.ExternsFile -> ModuleOld
51−convertExterns ef = (runModuleNameT moduleName, exportDecls ++ importDecls ++ decls ++ operatorDecls ++ tyOperatorDecls)
46+convertExterns :: P.ExternsFile -> (Module, [(P.ModuleName, P.DeclarationRef)])
47+convertExterns ef =
48+ ((P.efModuleName ef, decls), exportDecls)
5249 where
53− moduleName = P.efModuleName ef
54− importDecls = convertImport <$> P.efImports ef
50+ decls = map
51+ (IdeDeclarationAnn emptyAnn)
52+ (cleanDeclarations ++ operatorDecls ++ tyOperatorDecls)
5553 exportDecls = mapMaybe (convertExport . unwrapPositionedRef) (P.efExports ef)
5654 operatorDecls = convertOperator <$> P.efFixities ef
5755 tyOperatorDecls = convertTypeOperator <$> P.efTypeFixities ef
58− otherDecls = mapMaybe convertDecl (P.efDeclarations ef)
56+ declarations = mapMaybe convertDecl (P.efDeclarations ef)
5957
60− typeClassFilter = foldMap removeTypeDeclarationsForClass (filter isTypeClassDeclaration otherDecls)
61− decls = nub $ appEndo typeClassFilter otherDecls
58+ typeClassFilter = foldMap removeTypeDeclarationsForClass (filter isTypeClassDeclaration declarations)
59+ cleanDeclarations = nub $ appEndo typeClassFilter declarations
6260
63−removeTypeDeclarationsForClass :: ExternDecl -> Endo [ExternDecl]
64−removeTypeDeclarationsForClass (TypeClassDeclaration n) = Endo (filter notDuplicate)
65− where notDuplicate (TypeDeclaration n' _) = runProperNameT n /= runProperNameT n'
66− notDuplicate (TypeSynonymDeclaration n' _) = runProperNameT n /= runProperNameT n'
61+removeTypeDeclarationsForClass :: IdeDeclaration -> Endo [IdeDeclaration]
62+removeTypeDeclarationsForClass (IdeTypeClass n) = Endo (filter notDuplicate)
63+ where notDuplicate (IdeType n' _) = runProperNameT n /= runProperNameT n'
64+ notDuplicate (IdeTypeSynonym n' _) = runProperNameT n /= runProperNameT n'
6765 notDuplicate _ = True
6866removeTypeDeclarationsForClass _ = mempty
6967
70−isTypeClassDeclaration :: ExternDecl -> Bool
71−isTypeClassDeclaration TypeClassDeclaration{} = True
68+isTypeClassDeclaration :: IdeDeclaration -> Bool
69+isTypeClassDeclaration IdeTypeClass{} = True
7270isTypeClassDeclaration _ = False
7371
74−convertImport :: P.ExternsImport -> ExternDecl
75−convertImport ei = Dependency
76− (runModuleNameT (P.eiModule ei))
77− []
78− (runModuleNameT <$> P.eiImportedAs ei)
79−
80−convertExport :: P.DeclarationRef -> Maybe ExternDecl
81−convertExport (P.ModuleRef mn) = Just (Export (runModuleNameT mn))
72+convertExport :: P.DeclarationRef -> Maybe (P.ModuleName, P.DeclarationRef)
73+convertExport (P.ReExportRef m r) = Just (m, r)
8274convertExport _ = Nothing
8375
84−convertDecl :: P.ExternsDeclaration -> Maybe ExternDecl
85−convertDecl P.EDType{..} = Just $ TypeDeclaration edTypeName edTypeKind
86−convertDecl P.EDTypeSynonym{..} = Just $
87− TypeSynonymDeclaration edTypeSynonymName edTypeSynonymType
76+convertDecl :: P.ExternsDeclaration -> Maybe IdeDeclaration
77+convertDecl P.EDType{..} = Just (IdeType edTypeName edTypeKind)
78+convertDecl P.EDTypeSynonym{..} =
79+ Just (IdeTypeSynonym edTypeSynonymName edTypeSynonymType)
8880convertDecl P.EDDataConstructor{..} = Just $
89− DataConstructor (runProperNameT edDataCtorName) edDataCtorTypeCtor edDataCtorType
81+ IdeDataConstructor edDataCtorName edDataCtorTypeCtor edDataCtorType
9082convertDecl P.EDValue{..} = Just $
91− ValueDeclaration (runIdentT edValueName) edValueType
92−convertDecl P.EDClass{..} = Just $ TypeClassDeclaration edClassName
83+ IdeValue edValueName edValueType
84+convertDecl P.EDClass{..} = Just (IdeTypeClass edClassName)
9385convertDecl P.EDInstance{} = Nothing
9486
95−convertOperator :: P.ExternsFixity -> ExternDecl
87+convertOperator :: P.ExternsFixity -> IdeDeclaration
9688convertOperator P.ExternsFixity{..} =
97− ValueOperator
89+ IdeValueOperator
9890 efOperator
9991 (toS (P.showQualified (either P.runIdent P.runProperName) efAlias))
10092 efPrecedence
10193 efAssociativity
10294
103−convertTypeOperator :: P.ExternsTypeFixity -> ExternDecl
95+convertTypeOperator :: P.ExternsTypeFixity -> IdeDeclaration
10496convertTypeOperator P.ExternsTypeFixity{..} =
105− TypeOperator
97+ IdeTypeOperator
10698 efTypeOperator
10799 (toS (P.showQualified P.runProperName efTypeAlias))
108100 efTypePrecedence
109101 efTypeAssociativity
110102
111−unwrapPositioned :: P.Declaration -> P.Declaration
112−unwrapPositioned (P.PositionedDeclaration _ _ x) = x
113−unwrapPositioned x = x
114−
115−unwrapPositionedRef :: P.DeclarationRef -> P.DeclarationRef
116−unwrapPositionedRef (P.PositionedDeclarationRef _ _ x) = x
117−unwrapPositionedRef x = x
118−
119−convertModule :: ModuleOld -> Module
120−convertModule (mn, decls) = (P.moduleNameFromString (toS mn), mapMaybe convertDeclaration decls)
121− where convertDeclaration :: ExternDecl -> Maybe IdeDeclaration
122− convertDeclaration d = case d of
123− ValueDeclaration i t -> Just (IdeValue i t)
124− TypeDeclaration i k -> Just (IdeType i k)
125− TypeSynonymDeclaration i t -> Just (IdeTypeSynonym i t)
126− DataConstructor i tn t -> Just (IdeDataConstructor i tn t)
127− TypeClassDeclaration i -> Just (IdeTypeClass i)
128− ValueOperator n i p a -> Just (IdeValueOperator n i p a)
129− TypeOperator n i p a -> Just (IdeTypeOperator n i p a)
130− Dependency{} -> Nothing
131− ModuleDecl _ _ -> Nothing
132− Export _ -> Nothing
103+annotateLocations :: Map (Either Text Text) P.SourceSpan -> Module -> Module
104+annotateLocations ast (moduleName, decls) =
105+ (moduleName, map convertDeclaration decls)
106+ where
107+ convertDeclaration :: IdeDeclarationAnn -> IdeDeclarationAnn
108+ convertDeclaration (IdeDeclarationAnn ann d) = case d of
109+ IdeValue i t ->
110+ annotateValue (runIdentT i) (IdeValue i t)
111+ IdeType i k ->
112+ annotateType (runProperNameT i) (IdeType i k)
113+ IdeTypeSynonym i t ->
114+ annotateType (runProperNameT i) (IdeTypeSynonym i t)
115+ IdeDataConstructor i tn t ->
116+ annotateValue (runProperNameT i) (IdeDataConstructor i tn t)
117+ IdeTypeClass i ->
118+ annotateType (runProperNameT i) (IdeTypeClass i)
119+ IdeValueOperator n i p a ->
120+ annotateValue i (IdeValueOperator n i p a)
121+ IdeTypeOperator n i p a ->
122+ annotateType i (IdeTypeOperator n i p a)
123+ where
124+ annotateValue x = IdeDeclarationAnn (ann {annLocation = Map.lookup (Left x) ast})
125+ annotateType x = IdeDeclarationAnn (ann {annLocation = Map.lookup (Right x) ast})
src/Language/PureScript/Ide/Filter.hs+4−4
@@ -20,7 +20,6 @@ module Language.PureScript.Ide.Filter
2020 , moduleFilter
2121 , prefixFilter
2222 , equalityFilter
23− , runFilter
2423 , applyFilters
2524 ) where
2625
@@ -65,11 +64,12 @@ identFilter predicate search =
6564 filter (not . null . snd) . fmap filterModuleDecls
6665 where
6766 filterModuleDecls :: Module -> Module
68− filterModuleDecls (moduleIdent,decls) =
69− (moduleIdent, filter (`predicate` search) decls)
67+ filterModuleDecls (moduleIdent, decls) =
68+ (moduleIdent, filter (flip predicate search . getDeclaration) decls)
69+ getDeclaration (IdeDeclarationAnn _ d) = d
7070
7171runFilter :: Filter -> [Module] -> [Module]
72−runFilter (Filter f)= appEndo f
72+runFilter (Filter f) = appEndo f
7373
7474applyFilters :: [Filter] -> [Module] -> [Module]
7575applyFilters = runFilter . fold
src/Language/PureScript/Ide/Imports.hs+7−12
@@ -36,8 +36,6 @@ import qualified Data.Text.IO as TIO
3636import qualified Language.PureScript as P
3737import Language.PureScript.Ide.Completion
3838import Language.PureScript.Ide.Error
39−import Language.PureScript.Ide.Externs (unwrapPositioned,
40− unwrapPositionedRef)
4139import Language.PureScript.Ide.Filter
4240import Language.PureScript.Ide.State
4341import Language.PureScript.Ide.Types
@@ -203,7 +201,7 @@ addExplicitImport' decl moduleName imports =
203201 refFromDeclaration (IdeTypeClass n) =
204202 P.TypeClassRef n
205203 refFromDeclaration (IdeDataConstructor n tn _) =
206− P.TypeRef tn (Just [P.ProperName (T.unpack n)])
204+ P.TypeRef tn (Just [n])
207205 refFromDeclaration (IdeType n _) =
208206 P.TypeRef n (Just [])
209207 refFromDeclaration (IdeValueOperator op _ _ _) =
@@ -222,10 +220,7 @@ addExplicitImport' decl moduleName imports =
222220
223221 insertDeclIntoRefs :: IdeDeclaration -> [P.DeclarationRef] -> [P.DeclarationRef]
224222 insertDeclIntoRefs (IdeDataConstructor dtor tn _) refs =
225− let
226− dtor' = P.ProperName (T.unpack dtor)
227− in
228− updateAtFirstOrPrepend (matchType tn) (insertDtor dtor') (P.TypeRef tn (Just [dtor'])) refs
223+ updateAtFirstOrPrepend (matchType tn) (insertDtor dtor) (P.TypeRef tn (Just [dtor])) refs
229224 insertDeclIntoRefs dr refs = nubBy ((==) `on` P.prettyPrintRef) (refFromDeclaration dr : refs)
230225
231226 insertDtor dtor (P.TypeRef tn' dtors) =
@@ -261,17 +256,17 @@ addImportForIdentifier :: (Ide m, MonadError PscIdeError m)
261256 -> Text -- ^ The identifier to import
262257 -> [Filter] -- ^ Filters to apply before searching for
263258 -- the identifier
264− -> m (Either [Match] [Text])
259+ -> m (Either [Match IdeDeclaration] [Text])
265260addImportForIdentifier fp ident filters = do
266− modules <- getAllModules2 Nothing
267− case getExactMatches ident filters modules of
261+ modules <- getAllModules Nothing
262+ case map (fmap discardAnn) (getExactMatches ident filters modules) of
268263 [] ->
269264 throwError (NotFound "Couldn't find the given identifier. \
270265 \Have you loaded the corresponding module?")
271266
272267 -- Only one match was found for the given identifier, so we can insert it
273268 -- right away
274− [Match m decl] ->
269+ [Match (m, decl)] ->
275270 Right <$> addExplicitImport fp decl m
276271
277272 -- This case comes up for newtypes and dataconstructors. Because values and
@@ -279,7 +274,7 @@ addImportForIdentifier fp ident filters = do
279274 -- module. This also happens for parameterized types, as these generate both
280275 -- a type aswell as a type synonym.
281276
282− ms@[Match m1 d1, Match m2 d2] ->
277+ ms@[Match (m1, d1), Match (m2, d2)] ->
283278 if m1 /= m2
284279 -- If the modules don't line up we just ask the user to specify the
285280 -- module
src/Language/PureScript/Ide/Matcher.hs+20−19
@@ -14,11 +14,13 @@
1414
1515{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1616{-# LANGUAGE OverloadedStrings #-}
17+{-# LANGUAGE FlexibleInstances #-}
1718
1819module Language.PureScript.Ide.Matcher
1920 ( Matcher
20− , flexMatcher
2121 , runMatcher
22+ -- for tests
23+ , flexMatcher
2224 ) where
2325
2426import Protolude
@@ -32,23 +34,22 @@ import Text.EditDistance
3234import Text.Regex.TDFA ((=~))
3335
3436
35−type ScoredMatch = (Match, Double)
37+type ScoredMatch a = (Match a, Double)
3638
37−newtype Matcher = Matcher (Endo [Match]) deriving (Monoid)
39+newtype Matcher a = Matcher (Endo [Match a]) deriving (Monoid)
3840
39−instance FromJSON Matcher where
41+instance FromJSON (Matcher IdeDeclaration) where
4042 parseJSON = withObject "matcher" $ \o -> do
4143 (matcher :: Maybe Text) <- o .:? "matcher"
4244 case matcher of
4345 Just "flex" -> do
4446 params <- o .: "params"
45− search <- params .: "search"
46− pure $ flexMatcher search
47+ flexMatcher <$> params .: "search"
4748 Just "distance" -> do
4849 params <- o .: "params"
49− search <- params .: "search"
50− maxDist <- params .: "maximumDistance"
51− pure $ distanceMatcher search maxDist
50+ distanceMatcher
51+ <$> params .: "search"
52+ <*> params .: "maximumDistance"
5253 Just _ -> mzero
5354 Nothing -> return mempty
5455
@@ -59,37 +60,37 @@ instance FromJSON Matcher where
5960-- Examples:
6061-- flMa matches flexMatcher. Score: 14.28
6162-- sons matches sortCompletions. Score: 6.25
62−flexMatcher :: Text -> Matcher
63+flexMatcher :: Text -> Matcher IdeDeclaration
6364flexMatcher p = mkMatcher (flexMatch p)
6465
65−distanceMatcher :: Text -> Int -> Matcher
66+distanceMatcher :: Text -> Int -> Matcher IdeDeclaration
6667distanceMatcher q maxDist = mkMatcher (distanceMatcher' q maxDist)
6768
68−distanceMatcher' :: Text -> Int -> [Match] -> [ScoredMatch]
69+distanceMatcher' :: Text -> Int -> [Match IdeDeclaration] -> [ScoredMatch IdeDeclaration]
6970distanceMatcher' q maxDist = mapMaybe go
7071 where
7172 go m = let d = dist (T.unpack y)
72− y = identifierFromMatch m
73+ y = identifierFromIdeDeclaration (unwrapMatch m)
7374 in if d <= maxDist
7475 then Just (m, 1 / fromIntegral d)
7576 else Nothing
7677 dist = levenshteinDistance defaultEditCosts (T.unpack q)
7778
78−mkMatcher :: ([Match] -> [ScoredMatch]) -> Matcher
79+mkMatcher :: ([Match a] -> [ScoredMatch a]) -> Matcher a
7980mkMatcher matcher = Matcher . Endo $ fmap fst . sortCompletions . matcher
8081
81−runMatcher :: Matcher -> [Match] -> [Match]
82+runMatcher :: Matcher a -> [Match a] -> [Match a]
8283runMatcher (Matcher m)= appEndo m
8384
84−sortCompletions :: [ScoredMatch] -> [ScoredMatch]
85+sortCompletions :: [ScoredMatch a] -> [ScoredMatch a]
8586sortCompletions = sortBy (flip compare `on` snd)
8687
87−flexMatch :: Text -> [Match] -> [ScoredMatch]
88+flexMatch :: Text -> [Match IdeDeclaration] -> [ScoredMatch IdeDeclaration]
8889flexMatch = mapMaybe . flexRate
8990
90−flexRate :: Text -> Match -> Maybe ScoredMatch
91+flexRate :: Text -> Match IdeDeclaration -> Maybe (ScoredMatch IdeDeclaration)
9192flexRate p c = do
92− score <- flexScore p (identifierFromMatch c)
93+ score <- flexScore p (identifierFromIdeDeclaration (unwrapMatch c))
9394 return (c, score)
9495
9596-- FlexMatching ala Sublime.
src/Language/PureScript/Ide/Pursuit.hs+4−1
@@ -14,7 +14,10 @@
1414
1515{-# LANGUAGE OverloadedStrings #-}
1616
17−module Language.PureScript.Ide.Pursuit where
17+module Language.PureScript.Ide.Pursuit
18+ ( searchPursuitForDeclarations
19+ , findPackagesForModuleIdent
20+ ) where
1821
1922import Protolude
2023
src/Language/PureScript/Ide/Reexports.hs+88−67
@@ -14,84 +14,105 @@
1414-----------------------------------------------------------------------------
1515
1616{-# LANGUAGE OverloadedStrings #-}
17+{-# LANGUAGE PackageImports #-}
18+{-# LANGUAGE TemplateHaskell #-}
19+{-# LANGUAGE RecordWildCards #-}
1720
1821module Language.PureScript.Ide.Reexports
19− ( resolveReexports2
20− -- for tests
21− , getReexports
22− , replaceReexport
23− , replaceReexports
22+ ( resolveReexports
23+ , prettyPrintReexportResult
24+ , reexportHasFailures
25+ , ReexportResult(..)
2426 ) where
2527
26−
2728import Protolude
2829
29−import Data.List (union)
3030import qualified Data.Map as Map
3131import Language.PureScript.Ide.Types
32−import Language.PureScript.Ide.Externs
32+import Language.PureScript.Ide.Util
3333import qualified Language.PureScript as P
3434
35−getReexports :: ModuleOld -> [ExternDecl]
36−getReexports (mn, decls)= concatMap getExport decls
37− where getExport d
38− | (Export mn') <- d
39− , mn /= mn' = replaceExportWithAliases decls mn'
40− | otherwise = []
41−
42−dependencyToExport :: ExternDecl -> ExternDecl
43−dependencyToExport (Dependency m _ _) = Export m
44−dependencyToExport decl = decl
45−
46−replaceExportWithAliases :: [ExternDecl] -> ModuleIdent -> [ExternDecl]
47−replaceExportWithAliases decls ident =
48− case filter isMatch decls of
49− [] -> [Export ident]
50− aliases -> map dependencyToExport aliases
51− where isMatch d
52− | Dependency _ _ (Just alias) <- d
53− , alias == ident = True
54− | otherwise = False
55−
56−replaceReexport :: ExternDecl -> ModuleOld -> ModuleOld -> ModuleOld
57−replaceReexport e@(Export _) (m, decls) (_, newDecls) =
58− (m, filter (/= e) decls `union` newDecls)
59−replaceReexport _ _ _ = P.internalError "Should only get Exports here"
60−
61−emptyModule :: ModuleOld
62−emptyModule = ("Empty", [])
63−
64−isExport :: ExternDecl -> Bool
65−isExport (Export _) = True
66−isExport _ = False
67−
68−removeExportDecls :: ModuleOld -> ModuleOld
69−removeExportDecls = fmap (filter (not . isExport))
70−
71−replaceReexports :: ModuleOld -> Map ModuleIdent [ExternDecl] -> ModuleOld
72−replaceReexports m db = result
35+-- | Contains the module with resolved reexports, and eventual failures
36+data ReexportResult a
37+ = ReexportResult
38+ { reResolved :: a
39+ , reFailed :: [(P.ModuleName, P.DeclarationRef)]
40+ } deriving (Show, Eq, Functor)
41+
42+-- | Uses the passed formatter to format the resolved module, and adds eventual
43+-- failures
44+prettyPrintReexportResult
45+ :: (a -> Text)
46+ -- ^ Formatter for the resolved result
47+ -> ReexportResult a
48+ -- ^ The Result to be pretty printed
49+ -> Text
50+prettyPrintReexportResult f ReexportResult{..}
51+ | null reFailed =
52+ "Successfully resolved reexports for " <> f reResolved
53+ | otherwise =
54+ "Failed to resolve reexports for "
55+ <> f reResolved
56+ <> foldMap (\(mn, ref) -> runModuleNameT mn <> show ref) reFailed
57+
58+-- | Whether any Refs couldn't be resolved
59+reexportHasFailures :: ReexportResult a -> Bool
60+reexportHasFailures = not . null . reFailed
61+
62+-- | Resolves Reexports for a given Module, by looking up the reexported values
63+-- from the passed in Map
64+resolveReexports
65+ :: Map P.ModuleName [IdeDeclarationAnn]
66+ -- ^ Modules to search for the reexported declarations
67+ -> (Module, [(P.ModuleName, P.DeclarationRef)])
68+ -- ^ The module to resolve reexports for, aswell as the references to resolve
69+ -> ReexportResult Module
70+resolveReexports modules ((moduleName, decls), refs) =
71+ ReexportResult (moduleName, decls <> concat resolvedRefs) failedRefs
72+ where
73+ (failedRefs, resolvedRefs) = partitionEithers (resolveRef' <$> refs)
74+ resolveRef' x@(mn, r) = case Map.lookup mn modules of
75+ Nothing -> Left x
76+ Just decls' -> first (mn,) (resolveRef decls' r)
77+
78+resolveRef
79+ :: [IdeDeclarationAnn]
80+ -> P.DeclarationRef
81+ -> Either P.DeclarationRef [IdeDeclarationAnn]
82+resolveRef decls ref = case ref of
83+ P.TypeRef tn mdtors ->
84+ case findRef (\case IdeType name _ -> name == tn; _ -> False) of
85+ Nothing -> Left ref
86+ Just d -> Right $ d : case mdtors of
87+ Nothing ->
88+ -- If the dataconstructor field inside the TypeRef is Nothing, that
89+ -- means that all data constructors are exported, so we need to look
90+ -- those up ourselfes
91+ findDtors tn
92+ Just dtors -> mapMaybe lookupDtor dtors
93+ P.ValueRef i ->
94+ findWrapped (\case IdeValue i' _ -> i' == i; _ -> False)
95+ P.TypeOpRef name ->
96+ findWrapped (\case IdeTypeOperator n _ _ _ -> n == name; _ -> False)
97+ P.ValueOpRef name ->
98+ findWrapped (\case IdeValueOperator n _ _ _ -> n == name; _ -> False)
99+ P.TypeClassRef name ->
100+ findWrapped (\case IdeTypeClass n -> n == name; _ -> False)
101+ _ ->
102+ Left ref
73103 where
74− reexports = getReexports m
75− result = foldl go (removeExportDecls m) reexports
104+ findWrapped = wrapSingle . findRef
105+ wrapSingle = maybe (Left ref) (Right . pure)
106+ findRef f = find (f . discardAnn) decls
76107
77− go :: ModuleOld -> ExternDecl -> ModuleOld
78− go m' re@(Export name) = replaceReexport re m' (getModule name)
79− go _ _ = P.internalError "Should only get Exports here"
108+ lookupDtor name =
109+ findRef (\case IdeDataConstructor name' _ _ -> name == name'
110+ _ -> False)
80111
81− getModule :: ModuleIdent -> ModuleOld
82− getModule name = clean res
112+ findDtors tn = filter (f . discardAnn) decls
83113 where
84− res = fromMaybe emptyModule $ (name , ) <$> Map.lookup name db
85− -- we have to do this because keeping self exports in will result in
86− -- infinite loops
87− clean (mn, decls) = (mn,) (filter (/= Export mn) decls)
88−
89−resolveReexports :: Map ModuleIdent [ExternDecl] -> ModuleOld -> ModuleOld
90−resolveReexports modules m =
91− let replaced = replaceReexports m modules
92− in if null (getReexports replaced)
93− then replaced
94− else resolveReexports modules replaced
95−
96−resolveReexports2 :: Map Text [ExternDecl] -> ModuleOld -> Module
97−resolveReexports2 decls = convertModule . resolveReexports decls
114+ f :: IdeDeclaration -> Bool
115+ f decl
116+ | (IdeDataConstructor _ tn' _) <- decl
117+ , tn == tn' = True
118+ | otherwise = False
src/Language/PureScript/Ide/SourceFile.hs+38−31
@@ -17,8 +17,7 @@
1717module Language.PureScript.Ide.SourceFile
1818 ( parseModule
1919 , getImportsForFile
20− -- SOON...
21− , getDeclPosition
20+ , extractSpans
2221 ) where
2322
2423import Protolude
@@ -26,8 +25,6 @@ import Protolude
2625import qualified Language.PureScript as P
2726import Language.PureScript.Ide.Error
2827import Language.PureScript.Ide.Util
29−import Language.PureScript.Ide.Externs (unwrapPositioned,
30− unwrapPositionedRef)
3128import Language.PureScript.Ide.Types
3229import System.FilePath
3330import System.IO.UTF8 (readUTF8File)
@@ -68,31 +65,41 @@ getImportsForFile fp = do
6865 unwrapImportType (P.Hiding decls) = P.Hiding (map unwrapPositionedRef decls)
6966 unwrapImportType P.Implicit = P.Implicit
7067
71−getDeclPosition :: P.Module -> Text -> Maybe P.SourceSpan
72−getDeclPosition m ident = getFirst (foldMap (match ident) decls)
68+-- | Given a surrounding Sourcespan and a Declaration from the PS AST, extracts
69+-- definition sites inside that Declaration.
70+extractSpans
71+ :: P.SourceSpan
72+ -- ^ The surrounding span
73+ -> P.Declaration
74+ -- ^ The declaration to extract spans from
75+ -> [(Either Text Text, P.SourceSpan)]
76+ -- ^ A @Right@ corresponds to a type level declaration, and a @Left@ to a
77+ -- value level one
78+extractSpans ss d = case d of
79+ P.PositionedDeclaration ss' _ d' ->
80+ extractSpans ss' d'
81+ P.ValueDeclaration i _ _ _ ->
82+ [(Left (runIdentT i), ss)]
83+ P.TypeSynonymDeclaration name _ _ ->
84+ [(Right (runProperNameT name), ss)]
85+ P.TypeClassDeclaration name _ _ members ->
86+ (Right (runProperNameT name), ss) : concatMap (extractSpans' ss) members
87+ P.DataDeclaration _ name _ ctors ->
88+ (Right (runProperNameT name), ss)
89+ : map (\(cname, _) -> (Left (runProperNameT cname), ss)) ctors
90+ P.ExternDeclaration ident _ ->
91+ [(Left (runIdentT ident), ss)]
92+ P.ExternDataDeclaration name _ ->
93+ [(Right (runProperNameT name), ss)]
94+ _ -> []
7395 where
74− decls = getDeclarations m
75− match q (P.PositionedDeclaration ss _ decl) = First (if go q decl
76− then Just ss
77− else Nothing)
78− match _ _ = First Nothing
79−
80− go q (P.DataDeclaration _ name _ constructors) =
81− properEqual name q || any (\(x,_) -> properEqual x q) constructors
82− go q (P.DataBindingGroupDeclaration decls') = any (go q) decls'
83− go q (P.TypeSynonymDeclaration name _ _) = properEqual name q
84− go q (P.TypeDeclaration ident' _) = identEqual ident' q
85− go q (P.ValueDeclaration ident' _ _ _) = identEqual ident' q
86− go q (P.ExternDeclaration ident' _) = identEqual ident' q
87− go q (P.ExternDataDeclaration name _) = properEqual name q
88− go q (P.TypeClassDeclaration name _ _ members) =
89− properEqual name q || any (go q . unwrapPositioned) members
90− go q (P.TypeInstanceDeclaration ident' _ _ _ _) =
91− identEqual ident' q
92− go _ _ = False
93−
94− properEqual x q = runProperNameT x == q
95− identEqual x q = runIdentT x == q
96−
97− getDeclarations :: P.Module -> [P.Declaration]
98− getDeclarations (P.Module _ _ _ declarations _) = declarations
96+ -- We need this special case to be able to also get the position info for
97+ -- typeclass member functions. Typedeclaratations would clash with value
98+ -- declarations for non-typeclass members, which is why we can't handle them
99+ -- in extractSpans.
100+ extractSpans' ssP dP = case dP of
101+ P.PositionedDeclaration ssP' _ dP' ->
102+ extractSpans' ssP' dP'
103+ P.TypeDeclaration ident _ ->
104+ [(Left (runIdentT ident), ssP)]
105+ _ -> []
src/Language/PureScript/Ide/State.hs+66−63
@@ -19,20 +19,15 @@
1919module Language.PureScript.Ide.State
2020 ( getLoadedModulenames
2121 , getExternFiles
22− , insertModuleOld
2322 , resetIdeState
2423 , cacheRebuild
2524 , insertExterns
2625 , insertModule
27− , insertModuleSTM
2826 , insertExternsSTM
29− , getAllModules2
30− , getStage1
31− , setStage1
32− , getStage2
33− , setStage2
27+ , getAllModules
3428 , populateStage2
35− , populateStage2STM
29+ , populateStage3
30+ , populateStage3STM
3631 ) where
3732
3833import Protolude
@@ -44,6 +39,7 @@ import qualified Data.Map.Lazy as M
4439import Language.PureScript.Externs
4540import Language.PureScript.Ide.Externs
4641import Language.PureScript.Ide.Reexports
42+import Language.PureScript.Ide.SourceFile
4743import Language.PureScript.Ide.Types
4844import Language.PureScript.Ide.Util
4945import qualified Language.PureScript as P
@@ -53,12 +49,10 @@ import System.FilePath
5349-- | Resets all State inside psc-ide
5450resetIdeState :: Ide m => m ()
5551resetIdeState = do
56− stateVar <- envStateVar <$> ask
5752 ideVar <- ideStateVar <$> ask
5853 liftIO . atomically $ do
59− writeTVar stateVar emptyPscIdeState
6054 writeTVar ideVar emptyIdeState
61− setStage2STM ideVar emptyStage2
55+ setStage3STM ideVar emptyStage3
6256
6357-- | Gets the loaded Modulenames
6458getLoadedModulenames :: Ide m => m [P.ModuleName]
@@ -68,26 +62,6 @@ getLoadedModulenames = M.keys <$> getExternFiles
6862getExternFiles :: Ide m => m (M.Map P.ModuleName ExternsFile)
6963getExternFiles = s1Externs <$> getStage1
7064
71−-- | Inserts an @ExternsFile@ into the PscIdeState. Also converts the
72−-- ExternsFile into psc-ide's internal Declaration format
73−-- TODO: should be removed when the "old" Declaration format gets removed
74−insertModuleOld :: Ide m => ExternsFile -> m ()
75−insertModuleOld externsFile = do
76− stateVar <- envStateVar <$> ask
77− liftIO . atomically $ insertModuleOldSTM stateVar externsFile
78−
79−-- | STM version of insertModuleOld
80−insertModuleOldSTM :: TVar PscIdeState -> ExternsFile -> STM ()
81−insertModuleOldSTM st ef = modifyTVar st (insertModule' ef)
82−
83−-- | Pure version of insertModuleOld
84−insertModule' :: ExternsFile -> PscIdeState -> PscIdeState
85−insertModule' ef state =
86− state
87− { pscIdeStateModules = let (mn, decls) = convertExterns ef
88− in M.insert mn decls (pscIdeStateModules state)
89− }
90−
9165-- | Insert a Module into Stage1 of the State
9266insertModule :: Ide m => (FilePath, P.Module) -> m ()
9367insertModule module' = do
@@ -106,7 +80,6 @@ insertModuleSTM ref (fp, module') =
10680
10781-- | Retrieves Stage1 from the State.
10882-- This includes loaded Externfiles
109−-- (TODO: as soon as we actually parse the modules) aswell as the parsed modules
11083getStage1 :: Ide m => m Stage1
11184getStage1 = do
11285 st <- ideStateVar <$> ask
@@ -116,27 +89,14 @@ getStage1 = do
11689getStage1STM :: TVar IdeState -> STM Stage1
11790getStage1STM ref = ideStage1 <$> readTVar ref
11891
119−-- | Sets Stage1 inside the compiler
120−setStage1 :: Ide m => Stage1 -> m ()
121−setStage1 s1 = do
122− st <- ideStateVar <$> ask
123− liftIO . atomically . modifyTVar st $ \x ->
124− x {ideStage1 = s1}
125− pure ()
126−
127−-- TODO: Soon to be Stage3
12892-- | Retrieves Stage2 from the State.
129−-- This includes the denormalized Declarations and cached rebuilds
13093getStage2 :: Ide m => m Stage2
13194getStage2 = do
13295 st <- ideStateVar <$> ask
133− fmap ideStage2 . liftIO . readTVarIO $ st
96+ liftIO (atomically (getStage2STM st))
13497
135−-- | Sets Stage2 inside the compiler
136−setStage2 :: Ide m => Stage2 -> m ()
137−setStage2 s2 = do
138− st <- ideStateVar <$> ask
139− liftIO . atomically $ setStage2STM st s2
98+getStage2STM :: TVar IdeState -> STM Stage2
99+getStage2STM ref = ideStage2 <$> readTVar ref
140100
141101-- | STM version of setStage2
142102setStage2STM :: TVar IdeState -> Stage2 -> STM ()
@@ -145,21 +105,38 @@ setStage2STM ref s2 = do
145105 x {ideStage2 = s2}
146106 pure ()
147107
108+-- | Retrieves Stage3 from the State.
109+-- This includes the denormalized Declarations and cached rebuilds
110+getStage3 :: Ide m => m Stage3
111+getStage3 = do
112+ st <- ideStateVar <$> ask
113+ fmap ideStage3 . liftIO . readTVarIO $ st
114+
115+-- | Sets Stage3 inside the compiler
116+setStage3STM :: TVar IdeState -> Stage3 -> STM ()
117+setStage3STM ref s3 = do
118+ modifyTVar ref $ \x ->
119+ x {ideStage3 = s3}
120+ pure ()
121+
148122-- | Checks if the given ModuleName matches the last rebuild cache and if it
149123-- does returns all loaded definitions + the definitions inside the rebuild
150124-- cache
151−getAllModules2 :: Ide m => Maybe P.ModuleName -> m [(P.ModuleName, [IdeDeclaration])]
152−getAllModules2 mmoduleName = do
153− declarations <- s2Declarations <$> getStage2
125+getAllModules :: Ide m => Maybe P.ModuleName -> m [Module]
126+getAllModules mmoduleName = do
127+ declarations <- s3Declarations <$> getStage3
154128 rebuild <- cachedRebuild
155129 case mmoduleName of
156130 Nothing -> pure (M.toList declarations)
157131 Just moduleName ->
158132 case rebuild of
159133 Just (cachedModulename, ef)
160− | cachedModulename == moduleName ->
161− pure . M.toList $
162− M.insert moduleName (snd . convertModule . convertExterns $ ef) declarations
134+ | cachedModulename == moduleName -> do
135+ (AstData asts) <- s2AstData <$> getStage2
136+ let ast = fromMaybe M.empty (M.lookup moduleName asts)
137+ pure . M.toList $
138+ M.insert moduleName
139+ (snd . annotateLocations ast . fst . convertExterns $ ef) declarations
163140 _ -> pure (M.toList declarations)
164141
165142-- | Adds an ExternsFile into psc-ide's State Stage1. This does not populate the
@@ -182,14 +159,14 @@ cacheRebuild :: Ide m => ExternsFile -> m ()
182159cacheRebuild ef = do
183160 st <- ideStateVar <$> ask
184161 liftIO . atomically . modifyTVar st $ \x ->
185− x { ideStage2 = (ideStage2 x) {
186− s2CachedRebuild = Just (efModuleName ef, ef)}}
162+ x { ideStage3 = (ideStage3 x) {
163+ s3CachedRebuild = Just (efModuleName ef, ef)}}
187164
188165-- | Retrieves the rebuild cache
189166cachedRebuild :: Ide m => m (Maybe (P.ModuleName, ExternsFile))
190−cachedRebuild = s2CachedRebuild <$> getStage2
167+cachedRebuild = s3CachedRebuild <$> getStage3
191168
192−-- | Resolves reexports and populates Stage2 with data to be used in queries.
169+-- | Extracts source spans from the parsed ASTs
193170populateStage2 :: (Ide m, MonadLogger m) => m ()
194171populateStage2 = do
195172 st <- ideStateVar <$> ask
@@ -203,9 +180,35 @@ populateStage2 = do
203180-- | STM version of populateStage2
204181populateStage2STM :: TVar IdeState -> STM ()
205182populateStage2STM ref = do
183+ modules <- s1Modules <$> getStage1STM ref
184+ let spans = map (\((P.Module ss _ _ decls _), _) -> M.fromList (concatMap (extractSpans ss) decls)) modules
185+ setStage2STM ref (Stage2 (AstData spans))
186+
187+-- | Resolves reexports and populates Stage3 with data to be used in queries.
188+populateStage3 :: (Ide m, MonadLogger m) => m ()
189+populateStage3 = do
190+ st <- ideStateVar <$> ask
191+ (duration, results) <- liftIO $ do
192+ start <- getTime Monotonic
193+ results <- atomically (populateStage3STM st)
194+ end <- getTime Monotonic
195+ pure (Prelude.show (diffTimeSpec start end), results)
196+ traverse_
197+ (logWarnN . prettyPrintReexportResult (runModuleNameT . fst))
198+ (filter reexportHasFailures results)
199+ $(logDebug) $ "Finished populating Stage3 in " <> toS duration
200+
201+-- | STM version of populateStage3
202+populateStage3STM :: TVar IdeState -> STM [ReexportResult Module]
203+populateStage3STM ref = do
206204 externs <- s1Externs <$> getStage1STM ref
207− -- Build the "old" ExternDecl format
208− let modules = M.mapKeys runModuleNameT (M.map (snd . convertExterns) externs)
209− -- Convert ExternDecl into IdeDeclaration
210− declarations = resolveReexports2 modules <$> M.toList modules
211− setStage2STM ref (Stage2 (M.fromList declarations) Nothing)
205+ (AstData asts) <- s2AstData <$> getStage2STM ref
206+ let modules = M.map convertExterns externs
207+ nModules :: Map P.ModuleName (Module, [(P.ModuleName, P.DeclarationRef)])
208+ nModules = M.mapWithKey
209+ (\moduleName (m, refs) ->
210+ (fromMaybe m $ annotateLocations <$> M.lookup moduleName asts <*> pure m, refs)) modules
211+ -- resolves reexports and discards load failures for now
212+ result = resolveReexports (M.map (snd . fst) nModules) <$> M.elems nModules
213+ setStage3STM ref (Stage3 (M.fromList (map reResolved result)) Nothing)
214+ pure result
src/Language/PureScript/Ide/Types.hs+50−52
@@ -13,6 +13,7 @@
1313-----------------------------------------------------------------------------
1414
1515{-# LANGUAGE OverloadedStrings #-}
16+{-# LANGUAGE DeriveFoldable #-}
1617
1718module Language.PureScript.Ide.Types where
1819
@@ -28,47 +29,35 @@ import System.FilePath
2829import Text.Parsec as Parsec
2930import Text.Parsec.Text
3031
31−type Ident = Text
3232type ModuleIdent = Text
3333
34−data ExternDecl
35− -- | A function/value declaration
36− = ValueDeclaration Ident P.Type
37− | TypeDeclaration (P.ProperName 'P.TypeName) P.Kind
38− | TypeSynonymDeclaration (P.ProperName 'P.TypeName) P.Type
39− -- | A Dependency onto another Module
40− | Dependency
41− ModuleIdent -- name of the dependency
42− [Text] -- explicit imports
43− (Maybe Text) -- An eventual qualifier
44− -- | A module declaration
45− | ModuleDecl
46− ModuleIdent -- The modules name
47− [Ident] -- The exported identifiers
48− -- | A data/newtype declaration
49− | DataConstructor
50− Ident -- The type name
51− (P.ProperName 'P.TypeName)
52− P.Type -- The "type"
53− -- | An exported module
54− | TypeClassDeclaration (P.ProperName 'P.ClassName)
55− | ValueOperator (P.OpName 'P.ValueOpName) Ident P.Precedence P.Associativity
56− | TypeOperator (P.OpName 'P.TypeOpName) Ident P.Precedence P.Associativity
57− | Export ModuleIdent -- The exported Modules name
58− deriving (Show,Eq,Ord)
59−
6034data IdeDeclaration
61− = IdeValue Ident P.Type
35+ = IdeValue P.Ident P.Type
6236 | IdeType (P.ProperName 'P.TypeName) P.Kind
6337 | IdeTypeSynonym (P.ProperName 'P.TypeName) P.Type
64− | IdeDataConstructor Ident (P.ProperName 'P.TypeName) P.Type
38+ | IdeDataConstructor (P.ProperName 'P.ConstructorName) (P.ProperName 'P.TypeName) P.Type
6539 | IdeTypeClass (P.ProperName 'P.ClassName)
66− | IdeValueOperator (P.OpName 'P.ValueOpName) Ident P.Precedence P.Associativity
67− | IdeTypeOperator (P.OpName 'P.TypeOpName) Ident P.Precedence P.Associativity
40+ | IdeValueOperator (P.OpName 'P.ValueOpName) Text P.Precedence P.Associativity
41+ | IdeTypeOperator (P.OpName 'P.TypeOpName) Text P.Precedence P.Associativity
42+ deriving (Show, Eq, Ord)
43+
44+data IdeDeclarationAnn = IdeDeclarationAnn Annotation IdeDeclaration
6845 deriving (Show, Eq, Ord)
6946
70−type Module = (P.ModuleName, [IdeDeclaration])
71−type ModuleOld = (Text, [ExternDecl])
47+data Annotation
48+ = Annotation
49+ { annLocation :: Maybe P.SourceSpan
50+ , annExportedFrom :: Maybe P.ModuleName
51+ } deriving (Show, Eq, Ord)
52+
53+emptyAnn :: Annotation
54+emptyAnn = Annotation Nothing Nothing
55+
56+type Module = (P.ModuleName, [IdeDeclarationAnn])
57+
58+newtype AstData a =
59+ AstData (Map P.ModuleName (Map (Either Text Text) a))
60+ deriving (Show, Eq, Ord, Functor, Foldable)
7261
7362data Configuration =
7463 Configuration
@@ -79,34 +68,29 @@ data Configuration =
7968
8069data IdeEnvironment =
8170 IdeEnvironment
82− { envStateVar :: TVar PscIdeState
83− , ideStateVar :: TVar IdeState
71+ { ideStateVar :: TVar IdeState
8472 , ideConfiguration :: Configuration
8573 }
8674
8775type Ide m = (MonadIO m, MonadReader IdeEnvironment m)
8876
89−data PscIdeState =
90− PscIdeState
91− { pscIdeStateModules :: M.Map Text [ExternDecl]
92− } deriving Show
93−
94−emptyPscIdeState :: PscIdeState
95−emptyPscIdeState = PscIdeState M.empty
96−
9777data IdeState = IdeState
9878 { ideStage1 :: Stage1
9979 , ideStage2 :: Stage2
80+ , ideStage3 :: Stage3
10081 }
10182
10283emptyIdeState :: IdeState
103−emptyIdeState = IdeState emptyStage1 emptyStage2
84+emptyIdeState = IdeState emptyStage1 emptyStage2 emptyStage3
10485
10586emptyStage1 :: Stage1
10687emptyStage1 = Stage1 M.empty M.empty
10788
10889emptyStage2 :: Stage2
109−emptyStage2 = Stage2 M.empty Nothing
90+emptyStage2 = Stage2 (AstData M.empty)
91+
92+emptyStage3 :: Stage3
93+emptyStage3 = Stage3 M.empty Nothing
11094
11195data Stage1 = Stage1
11296 { s1Externs :: M.Map P.ModuleName P.ExternsFile
@@ -114,19 +98,31 @@ data Stage1 = Stage1
11498 }
11599
116100data Stage2 = Stage2
117− { s2Declarations :: M.Map P.ModuleName [IdeDeclaration]
118− , s2CachedRebuild :: Maybe (P.ModuleName, P.ExternsFile)
101+ { s2AstData :: AstData P.SourceSpan
102+ }
103+
104+data Stage3 = Stage3
105+ { s3Declarations :: M.Map P.ModuleName [IdeDeclarationAnn]
106+ , s3CachedRebuild :: Maybe (P.ModuleName, P.ExternsFile)
119107 }
120108
121−data Match = Match P.ModuleName IdeDeclaration
122− deriving (Show, Eq)
109+newtype Match a = Match (P.ModuleName, a)
110+ deriving (Show, Eq, Functor)
123111
124112newtype Completion =
125− Completion (ModuleIdent, Ident, Text)
113+ Completion (Text, Text, Text)
126114 deriving (Show,Eq)
127115
116+newtype Info =
117+ Info (Text, Text, Text, Maybe P.SourceSpan)
118+ deriving (Show,Eq)
119+
120+instance ToJSON Info where
121+ toJSON (Info (m, d, t, sourceSpan)) =
122+ object ["module" .= m, "identifier" .= d, "type" .= t, "definedAt" .= sourceSpan]
123+
128124instance ToJSON Completion where
129− toJSON (Completion (m,d,t)) =
125+ toJSON (Completion (m, d, t)) =
130126 object ["module" .= m, "identifier" .= d, "type" .= t]
131127
132128data ModuleImport =
@@ -165,6 +161,7 @@ identifierFromDeclarationRef _ = ""
165161
166162data Success =
167163 CompletionResult [Completion]
164+ | InfoResult [Info]
168165 | TextResult Text
169166 | MultilineTextResult [Text]
170167 | PursuitResult [PursuitResponse]
@@ -179,6 +176,7 @@ encodeSuccess res =
179176
180177instance ToJSON Success where
181178 toJSON (CompletionResult cs) = encodeSuccess cs
179+ toJSON (InfoResult i) = encodeSuccess i
182180 toJSON (TextResult t) = encodeSuccess t
183181 toJSON (MultilineTextResult ts) = encodeSuccess ts
184182 toJSON (PursuitResult resp) = encodeSuccess resp
@@ -208,7 +206,7 @@ data PursuitResponse =
208206 ModuleResponse ModuleIdent Text
209207 -- | A Pursuit Response for a declaration. Consist of the declarations type,
210208 -- module, name and package
211− | DeclarationResponse Text ModuleIdent Ident Text
209+ | DeclarationResponse Text ModuleIdent Text Text
212210 deriving (Show,Eq)
213211
214212instance FromJSON PursuitResponse where
src/Language/PureScript/Ide/Util.hs+40−14
@@ -16,10 +16,14 @@
1616
1717module Language.PureScript.Ide.Util
1818 ( identifierFromIdeDeclaration
19− , identifierFromMatch
19+ , unwrapMatch
20+ , unwrapPositioned
21+ , unwrapPositionedRef
2022 , completionFromMatch
23+ , infoFromMatch
2124 , encodeT
2225 , decodeT
26+ , discardAnn
2327 , module Language.PureScript.Ide.Conversions
2428 ) where
2529
@@ -33,26 +37,34 @@ import Language.PureScript.Ide.Conversions
3337
3438identifierFromIdeDeclaration :: IdeDeclaration -> Text
3539identifierFromIdeDeclaration d = case d of
36− IdeValue name _ -> name
40+ IdeValue name _ -> runIdentT name
3741 IdeType name _ -> runProperNameT name
3842 IdeTypeSynonym name _ -> runProperNameT name
39− IdeDataConstructor name _ _ -> name
43+ IdeDataConstructor name _ _ -> runProperNameT name
4044 IdeTypeClass name -> runProperNameT name
4145 IdeValueOperator op _ _ _ -> runOpNameT op
4246 IdeTypeOperator op _ _ _ -> runOpNameT op
4347
44−identifierFromMatch :: Match -> Text
45−identifierFromMatch (Match _ ed) = identifierFromIdeDeclaration ed
48+discardAnn :: IdeDeclarationAnn -> IdeDeclaration
49+discardAnn (IdeDeclarationAnn _ d) = d
4650
47−completionFromMatch :: Match -> Completion
48−completionFromMatch (Match m' d) = case d of
49− IdeValue name type' -> Completion (m, name, prettyTypeT type')
50− IdeType name kind -> Completion (m, runProperNameT name, toS (P.prettyPrintKind kind))
51− IdeTypeSynonym name kind -> Completion (m, runProperNameT name, prettyTypeT kind)
52− IdeDataConstructor name _ type' -> Completion (m, name, prettyTypeT type')
53− IdeTypeClass name -> Completion (m, runProperNameT name, "class")
54− IdeValueOperator op ref precedence associativity -> Completion (m, runOpNameT op, showFixity precedence associativity ref op)
55− IdeTypeOperator op ref precedence associativity -> Completion (m, runOpNameT op, showFixity precedence associativity ref op)
51+unwrapMatch :: Match a -> a
52+unwrapMatch (Match (_, ed)) = ed
53+
54+completionFromMatch :: Match IdeDeclaration -> Completion
55+completionFromMatch = Completion . completionFromMatch'
56+
57+completionFromMatch' :: Match IdeDeclaration -> (Text, Text, Text)
58+completionFromMatch' (Match (m', d)) = case d of
59+ IdeValue name type' -> (m, runIdentT name, prettyTypeT type')
60+ IdeType name kind -> (m, runProperNameT name, toS (P.prettyPrintKind kind))
61+ IdeTypeSynonym name kind -> (m, runProperNameT name, prettyTypeT kind)
62+ IdeDataConstructor name _ type' -> (m, runProperNameT name, prettyTypeT type')
63+ IdeTypeClass name -> (m, runProperNameT name, "class")
64+ IdeValueOperator op ref precedence associativity ->
65+ (m, runOpNameT op, showFixity precedence associativity ref op)
66+ IdeTypeOperator op ref precedence associativity ->
67+ (m, runOpNameT op, showFixity precedence associativity ref op)
5668 where
5769 m = runModuleNameT m'
5870 showFixity p a r o =
@@ -62,8 +74,22 @@ completionFromMatch (Match m' d) = case d of
6274 P.Infixr -> "infixr"
6375 in T.unwords [asso, show p, r, "as", runOpNameT o]
6476
77+infoFromMatch :: Match IdeDeclarationAnn -> Info
78+infoFromMatch (Match (m, (IdeDeclarationAnn ann d))) =
79+ Info (a, b, c, annLocation ann)
80+ where
81+ (a, b, c) = completionFromMatch' (Match (m, d))
82+
6583encodeT :: (ToJSON a) => a -> Text
6684encodeT = toS . decodeUtf8 . encode
6785
6886decodeT :: (FromJSON a) => Text -> Maybe a
6987decodeT = decode . encodeUtf8 . toS
88+
89+unwrapPositioned :: P.Declaration -> P.Declaration
90+unwrapPositioned (P.PositionedDeclaration _ _ x) = x
91+unwrapPositioned x = x
92+
93+unwrapPositionedRef :: P.DeclarationRef -> P.DeclarationRef
94+unwrapPositionedRef (P.PositionedDeclarationRef _ _ x) = x
95+unwrapPositionedRef x = x
src/Language/PureScript/Ide/Watcher.hs+4−2
@@ -12,7 +12,9 @@
1212-- File watcher for externs files
1313-----------------------------------------------------------------------------
1414
15−module Language.PureScript.Ide.Watcher where
15+module Language.PureScript.Ide.Watcher
16+ ( watcher
17+ ) where
1618
1719import Protolude
1820
@@ -33,7 +35,7 @@ reloadFile ref ev = do
3335 case ef' of
3436 Left _ -> pure ()
3537 Right ef -> do
36− atomically (insertExternsSTM ref ef *> populateStage2STM ref)
38+ void $ atomically (insertExternsSTM ref ef *> populateStage3STM ref)
3739 putStrLn ("Reloaded File at: " ++ fp)
3840
3941-- | Installs filewatchers for the given directory and reloads ExternsFiles when
tests/Language/PureScript/Ide/FilterSpec.hs+5−5
@@ -8,8 +8,8 @@ import Language.PureScript.Ide.Types
88import qualified Language.PureScript as P
99import Test.Hspec
1010
11−value :: Text -> IdeDeclaration
12−value s = IdeValue s $ P.TypeWildcard $ P.SourceSpan "" (P.SourcePos 0 0) (P.SourcePos 0 0)
11+value :: Text -> IdeDeclarationAnn
12+value s = IdeDeclarationAnn emptyAnn (IdeValue (P.Ident (toS s)) P.REmpty)
1313
1414moduleA, moduleB :: Module
1515moduleA = (P.moduleNameFromString "Module.A", [value "function1"])
@@ -19,13 +19,13 @@ modules :: [Module]
1919modules = [moduleA, moduleB]
2020
2121runEq :: Text -> [Module]
22−runEq s = runFilter (equalityFilter s) modules
22+runEq s = applyFilters [equalityFilter s] modules
2323
2424runPrefix :: Text -> [Module]
25−runPrefix s = runFilter (prefixFilter s) modules
25+runPrefix s = applyFilters [prefixFilter s] modules
2626
2727runModule :: [P.ModuleName] -> [Module]
28−runModule ms = runFilter (moduleFilter ms) modules
28+runModule ms = applyFilters [moduleFilter ms] modules
2929
3030spec :: Spec
3131spec = do
tests/Language/PureScript/Ide/ImportsSpec.hs+2−2
@@ -68,11 +68,11 @@ spec = do
6868 describe "import commands" $ do
6969 let simpleFileImports = let (_, _, i, _) = splitSimpleFile in i
7070 addValueImport i mn is =
71− prettyPrintImportSection (addExplicitImport' (IdeValue i wildcard) mn is)
71+ prettyPrintImportSection (addExplicitImport' (IdeValue (P.Ident i) wildcard) mn is)
7272 addOpImport op mn is =
7373 prettyPrintImportSection (addExplicitImport' (IdeValueOperator op "" 2 P.Infix) mn is)
7474 addDtorImport i t mn is =
75− prettyPrintImportSection (addExplicitImport' (IdeDataConstructor i t wildcard) mn is)
75+ prettyPrintImportSection (addExplicitImport' (IdeDataConstructor (P.ProperName i) t wildcard) mn is)
7676 it "adds an implicit unqualified import" $
7777 shouldBe
7878 (addImplicitImport' simpleFileImports (P.moduleNameFromString "Data.Map"))
tests/Language/PureScript/Ide/Integration.hs+17−6
@@ -37,11 +37,13 @@ module Language.PureScript.Ide.Integration
3737 , getFlexCompletions
3838 , getFlexCompletionsInModule
3939 , getType
40+ , getInfo
4041 , rebuildModule
4142 , reset
4243 -- checking results
4344 , resultIsSuccess
4445 , parseCompletions
46+ , parseInfo
4547 , parseTextResult
4648 ) where
4749
@@ -53,6 +55,7 @@ import Data.Aeson.Types
5355import qualified Data.Text as T
5456import qualified Data.Vector as V
5557import Language.PureScript.Ide.Util
58+import qualified Language.PureScript as P
5659import System.Directory
5760import System.FilePath
5861import System.IO.Error (mkIOError, userErrorType)
@@ -68,8 +71,8 @@ startServer = do
6871 pdir <- projectDirectory
6972 -- Turn off filewatching since it creates race condition in a testing environment
7073 (_, _, _, procHandle) <- createProcess $
71− (shell "psc-ide-server --no-watch") {cwd = Just pdir}
72− threadDelay 500000 -- give the server 500ms to start up
74+ (shell "psc-ide-server --no-watch src/*.purs") {cwd = Just pdir}
75+ threadDelay 2000000 -- give the server 2s to start up
7376 return procHandle
7477
7578stopServer :: ProcessHandle -> IO ()
@@ -91,10 +94,7 @@ compileTestProject :: IO Bool
9194compileTestProject = do
9295 pdir <- projectDirectory
9396 (_, _, _, procHandle) <- createProcess $
94− (shell . toS $ "psc " <> fileGlob) { cwd = Just pdir
95− , std_out = CreatePipe
96− , std_err = CreatePipe
97− }
97+ (shell . toS $ "psc " <> fileGlob) { cwd = Just pdir }
9898 r <- tryNTimes 5 (getProcessExitCode procHandle)
9999 pure (fromMaybe False (isSuccess <$> r))
100100
@@ -167,6 +167,9 @@ getFlexCompletionsInModule q m = parseCompletions <$> sendCommand (completion []
167167getType :: Text -> IO [(Text, Text, Text)]
168168getType q = parseCompletions <$> sendCommand (typeC q [])
169169
170+getInfo :: Text -> IO [P.SourceSpan]
171+getInfo q = parseInfo <$> sendCommand (typeC q [])
172+
170173addImport :: Text -> FilePath -> FilePath -> IO Text
171174addImport identifier fp outfp = sendCommand (addImportC identifier fp outfp)
172175
@@ -259,6 +262,10 @@ completionParser = withArray "res" $ \cs ->
259262 ty <- o .: "type"
260263 pure (module', ident, ty)) (V.toList cs)
261264
265+infoParser :: Value -> Parser [P.SourceSpan]
266+infoParser = withArray "res" $ \cs ->
267+ mapM (withObject "info" $ \o -> o .: "definedAt") (V.toList cs)
268+
262269valueFromText :: Text -> Value
263270valueFromText = fromJust . decode . toS
264271
@@ -269,6 +276,10 @@ parseCompletions :: Text -> [(Text, Text, Text)]
269276parseCompletions s =
270277 fromJust $ join (rightToMaybe <$> parseMaybe (withResult completionParser) (valueFromText s))
271278
279+parseInfo :: Text -> [P.SourceSpan]
280+parseInfo s =
281+ fromJust $ join (rightToMaybe <$> parseMaybe (withResult infoParser) (valueFromText s))
282+
272283parseTextResult :: Text -> Text
273284parseTextResult s =
274285 fromJust $ join (rightToMaybe <$> parseMaybe (withResult (withText "tr" pure)) (valueFromText s))
tests/Language/PureScript/Ide/MatcherSpec.hs+7−7
@@ -12,17 +12,17 @@ import Language.PureScript.Ide.Types
1212import Test.Hspec
1313
1414value :: Text -> IdeDeclaration
15−value s = IdeValue s $ P.TypeWildcard $ P.SourceSpan "" (P.SourcePos 0 0) (P.SourcePos 0 0)
15+value s = IdeValue (P.Ident (toS s)) P.REmpty
1616
17−firstResult, secondResult, fiult :: Match
18−firstResult = Match (P.moduleNameFromString "Match") (value "firstResult")
19−secondResult = Match (P.moduleNameFromString "Match") (value "secondResult")
20−fiult = Match (P.moduleNameFromString "Match") (value "fiult")
17+firstResult, secondResult, fiult :: Match IdeDeclaration
18+firstResult = Match (P.moduleNameFromString "Match", value "firstResult")
19+secondResult = Match (P.moduleNameFromString "Match", value "secondResult")
20+fiult = Match (P.moduleNameFromString "Match", value "fiult")
2121
22−completions :: [Match]
22+completions :: [Match IdeDeclaration]
2323completions = [firstResult, secondResult, fiult]
2424
25−runFlex :: Text -> [Match]
25+runFlex :: Text -> [Match IdeDeclaration]
2626runFlex s = runMatcher (flexMatcher s) completions
2727
2828setup :: IO ()
tests/Language/PureScript/Ide/ReexportsSpec.hs+51−66
@@ -2,78 +2,63 @@
22{-# LANGUAGE NoImplicitPrelude #-}
33module Language.PureScript.Ide.ReexportsSpec where
44
5+import qualified Prelude as Prelude
56import Protolude
67
7−import qualified Data.Map as Map
8+import qualified Data.Map as Map
89import Language.PureScript.Ide.Reexports
910import Language.PureScript.Ide.Types
1011import qualified Language.PureScript as P
1112import Test.Hspec
1213
13−wildcard :: P.Type
14−wildcard = P.TypeWildcard $ P.SourceSpan "" (P.SourcePos 0 0) (P.SourcePos 0 0)
15−
16−decl1 :: ExternDecl
17−decl1 = ValueDeclaration "filter" wildcard
18−decl2 :: ExternDecl
19−decl2 = ValueDeclaration "map" wildcard
20−decl3 :: ExternDecl
21−decl3 = ValueDeclaration "catMaybe" wildcard
22−dep1 :: ExternDecl
23−dep1 = Dependency "Test.Foo" [] (Just "T")
24−dep2 :: ExternDecl
25−dep2 = Dependency "Test.Bar" [] (Just "T")
26−
27−circularModule :: ModuleOld
28−circularModule = ("Circular", [Export "Circular"])
29−
30−module1 :: ModuleOld
31−module1 = ("Module1", [Export "Module2", Export "Module3", decl1])
32−
33−module2 :: ModuleOld
34−module2 = ("Module2", [decl2])
35−
36−module3 :: ModuleOld
37−module3 = ("Module3", [decl3])
38−
39−module4 :: ModuleOld
40−module4 = ("Module4", [Export "T", decl1, dep1, dep2])
41−
42−result :: ModuleOld
43−result = ("Module1", [decl1, decl2, Export "Module3"])
44−
45−db :: Map ModuleIdent [ExternDecl]
46−db = Map.fromList [module1, module2, module3]
47−
48−shouldBeEqualSorted :: ModuleOld -> ModuleOld -> Expectation
49−shouldBeEqualSorted (n1, d1) (n2, d2) = (n1, sort d1) `shouldBe` (n2, sort d2)
14+m :: Prelude.String -> P.ModuleName
15+m = P.moduleNameFromString
16+
17+d :: IdeDeclaration -> IdeDeclarationAnn
18+d = IdeDeclarationAnn emptyAnn
19+
20+valueA, typeA, classA, dtorA1, dtorA2 :: IdeDeclarationAnn
21+valueA = d (IdeValue (P.Ident "valueA") P.REmpty)
22+typeA = d (IdeType (P.ProperName "TypeA") P.Star)
23+classA = d (IdeTypeClass (P.ProperName "ClassA"))
24+dtorA1 = d (IdeDataConstructor (P.ProperName "DtorA1") (P.ProperName "TypeA") P.REmpty)
25+dtorA2 = d (IdeDataConstructor (P.ProperName "DtorA2") (P.ProperName "TypeA") P.REmpty)
26+
27+env :: Map P.ModuleName [IdeDeclarationAnn]
28+env = Map.fromList
29+ [ (m "A", [valueA, typeA, classA, dtorA1, dtorA2])
30+ ]
31+
32+type Refs = [(P.ModuleName, P.DeclarationRef)]
33+
34+succTestCases :: [(Text, Module, Refs, Module)]
35+succTestCases =
36+ [ ("resolves a value reexport", (m "C", []), [(m "A", P.ValueRef (P.Ident "valueA"))], (m "C", [valueA]))
37+ , ("resolves a type reexport with explicit data constructors"
38+ , (m "C", []), [(m "A", P.TypeRef (P.ProperName "TypeA") (Just [P.ProperName "DtorA1"]))], (m "C", [typeA, dtorA1]))
39+ , ("resolves a type reexport with implicit data constructors"
40+ , (m "C", []), [(m "A", P.TypeRef (P.ProperName "TypeA") Nothing)], (m "C", [typeA, dtorA1, dtorA2]))
41+ , ("resolves a class reexport", (m "C", []), [(m "A", P.TypeClassRef (P.ProperName "ClassA"))], (m "C", [classA]))
42+ ]
43+
44+failTestCases :: [(Text, Module, Refs)]
45+failTestCases =
46+ [ ("fails to resolve a non existing value", (m "C", []), [(m "A", P.ValueRef (P.Ident "valueB"))])
47+ , ("fails to resolve a non existing type reexport" , (m "C", []), [(m "A", P.TypeRef (P.ProperName "TypeB") Nothing)])
48+ , ("fails to resolve a non existing class reexport", (m "C", []), [(m "A", P.TypeClassRef (P.ProperName "ClassB"))])
49+ ]
5050
5151spec :: Spec
52−spec =
53− describe "Reexports" $ do
54− it "finds all reexports" $
55− getReexports module1 `shouldBe` [Export "Module2", Export "Module3"]
56−
57− it "replaces a reexport with another module" $
58− replaceReexport (Export "Module2") module1 module2 `shouldBeEqualSorted` result
59−
60− it "adds another module even if there is no export statement" $
61− replaceReexport (Export "Module2") ("Module1", [decl1, Export "Module3"]) module2
62− `shouldBeEqualSorted` result
63−
64− it "only adds a declaration once" $
65− let replaced = replaceReexport (Export "Module2") module1 module2
66− in replaceReexport (Export "Module2") replaced module2 `shouldBeEqualSorted` result
67−
68− it "replaces all Exports with their corresponding declarations" $
69− replaceReexports module1 db `shouldBe` ("Module1", [decl1, decl2, decl3])
70−
71− it "does not list itself as a reexport" $
72− getReexports circularModule `shouldBe` []
73−
74− it "does not include circular references when replacing reexports" $
75− replaceReexports circularModule (uncurry Map.singleton circularModule )
76− `shouldBe` ("Circular", [])
77−
78− it "replaces exported aliases with imported module" $
79− getReexports module4 `shouldBe` [Export "Test.Foo", Export "Test.Bar"]
52+spec = do
53+ describe "Successful Reexports" $
54+ for_ succTestCases $ \(desc, initial, refs, result) ->
55+ it (toS desc) $ do
56+ let reResult = resolveReexports env (initial, refs)
57+ reResolved reResult `shouldBe` result
58+ reResult `shouldSatisfy` not . reexportHasFailures
59+ describe "Failed Reexports" $
60+ for_ failTestCases $ \(desc, initial, refs) ->
61+ it (toS desc) $ do
62+ let reResult = resolveReexports env (initial, refs)
63+ reFailed reResult `shouldBe` refs
64+ reResult `shouldSatisfy` reexportHasFailures
tests/Language/PureScript/Ide/SourceFile/IntegrationSpec.hsnew file+36
@@ -0,0 +1,36 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE NoImplicitPrelude #-}
3+module Language.PureScript.Ide.SourceFile.IntegrationSpec where
4+
5+
6+import Protolude
7+
8+import qualified Data.Text as T
9+import qualified Language.PureScript.Ide.Integration as Integration
10+import qualified Language.PureScript as P
11+import Test.Hspec
12+
13+setup :: IO ()
14+setup = void (Integration.reset *> Integration.loadAll)
15+
16+spec :: Spec
17+spec = beforeAll_ setup $ do
18+ describe "Sourcefile Integration" $ do
19+ it "finds a value declaration" $ do
20+ testCase "sfValue" (3, 1)
21+ it "finds a type declaration" $ do
22+ testCase "SFType" (5, 1)
23+ it "finds a data declaration" $ do
24+ testCase "SFData" (7, 1)
25+ it "finds a data constructor" $ do
26+ testCase "SFOne" (7, 1)
27+ it "finds a typeclass" $ do
28+ testCase "SFClass" (9, 1)
29+ it "finds a typeclass member" $ do
30+ testCase "sfShow" (10, 3)
31+
32+testCase :: Text -> (Int, Int) -> IO ()
33+testCase s (x, y) = do
34+ (P.SourceSpan f (P.SourcePos l c) _):_ <- Integration.getInfo s
35+ toS f `shouldSatisfy` T.isSuffixOf "SourceFileSpec.purs"
36+ (l, c) `shouldBe` (x, y)
tests/Language/PureScript/Ide/SourceFileSpec.hsnew file+46
@@ -0,0 +1,46 @@
1+{-# LANGUAGE NoImplicitPrelude #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+module Language.PureScript.Ide.SourceFileSpec where
4+
5+import Protolude
6+
7+import qualified Language.PureScript as P
8+import Language.PureScript.Ide.SourceFile
9+import Test.Hspec
10+
11+span0, span1, span2 :: P.SourceSpan
12+span0 = P.SourceSpan "ModuleLevel" (P.SourcePos 0 0) (P.SourcePos 1 1)
13+span1 = P.SourceSpan "" (P.SourcePos 1 1) (P.SourcePos 2 2)
14+span2 = P.SourceSpan "" (P.SourcePos 2 2) (P.SourcePos 3 3)
15+
16+value1, synonym1, class1, class2, data1, data2, foreign1, foreign2, member1 :: P.Declaration
17+value1 = P.ValueDeclaration (P.Ident "value1") P.Public [] (Left [])
18+synonym1 = P.TypeSynonymDeclaration (P.ProperName "Synonym1") [] P.REmpty
19+class1 = P.TypeClassDeclaration (P.ProperName "Class1") [] [] []
20+class2 = P.TypeClassDeclaration (P.ProperName "Class2") [] []
21+ [P.PositionedDeclaration span2 [] member1]
22+data1 = P.DataDeclaration P.Newtype (P.ProperName "Data1") [] []
23+data2 = P.DataDeclaration P.Data (P.ProperName "Data2") [] [(P.ProperName "Cons1", [])]
24+foreign1 = P.ExternDeclaration (P.Ident "foreign1") P.REmpty
25+foreign2 = P.ExternDataDeclaration (P.ProperName "Foreign2") P.Star
26+member1 = P.TypeDeclaration (P.Ident "member1") P.REmpty
27+
28+spec :: Spec
29+spec = do
30+ describe "Extracting Spans" $ do
31+ it "extracts a span for a value declaration" $
32+ extractSpans span0 (P.PositionedDeclaration span1 [] value1) `shouldBe` [(Left "value1", span1)]
33+ it "extracts a span for a type synonym declaration" $
34+ extractSpans span0 (P.PositionedDeclaration span1 [] synonym1) `shouldBe` [(Right "Synonym1", span1)]
35+ it "extracts a span for a typeclass declaration" $
36+ extractSpans span0 (P.PositionedDeclaration span1 [] class1) `shouldBe` [(Right "Class1", span1)]
37+ it "extracts spans for a typeclass declaration and its members" $
38+ extractSpans span0 (P.PositionedDeclaration span1 [] class2) `shouldBe` [(Right "Class2", span1), (Left "member1", span2)]
39+ it "extracts a span for a data declaration" $
40+ extractSpans span0 (P.PositionedDeclaration span1 [] data1) `shouldBe` [(Right "Data1", span1)]
41+ it "extracts spans for a data declaration and its constructors" $
42+ extractSpans span0 (P.PositionedDeclaration span1 [] data2) `shouldBe` [(Right "Data2", span1), (Left "Cons1", span1)]
43+ it "extracts a span for a foreign declaration" $
44+ extractSpans span0 (P.PositionedDeclaration span1 [] foreign1) `shouldBe` [(Left "foreign1", span1)]
45+ it "extracts a span for a data foreign declaration" $
46+ extractSpans span0 (P.PositionedDeclaration span1 [] foreign2) `shouldBe` [(Right "Foreign2", span1)]
tests/support/pscide/src/RebuildSpecWithForeign.js−2
@@ -1,3 +1 @@
1−// module RebuildSpecWithForeign
2−
31exports.f = 5;
tests/support/pscide/src/SourceFileSpec.pursnew file+10
@@ -0,0 +1,10 @@
1+module SourceFileSpec where
2+
3+sfValue = "sfValue"
4+
5+type SFType = String
6+
7+data SFData = SFOne | SFTwo | SFThree
8+
9+class SFClass a where
10+ sfShow :: a -> String