@@ -9,6 +9,7 @@ module Development.IDE.Import.DependencyInformation
9 , ModuleParseError(..)
9 , ModuleParseError(..)
10 , TransitiveDependencies(..)
10 , TransitiveDependencies(..)
11 , FilePathId(..)
11 , FilePathId(..)
13 , PathIdMap
14 , PathIdMap
14 , emptyPathIdMap
15 , emptyPathIdMap
@@ -17,7 +18,7 @@ module Development.IDE.Import.DependencyInformation
17 , pathToId
18 , pathToId
18 , idToPath
19 , idToPath
19 , reachableModules
20 , reachableModules
20−
21+ , modLocationToNormalizedFilePath
21 , processDependencyInformation
22 , processDependencyInformation
22 , transitiveDeps
23 , transitiveDeps
@@ -46,6 +47,7 @@ import GHC.Generics (Generic)
47import Development.IDE.Types.Diagnostics
48import Development.IDE.Types.Diagnostics
48import Development.IDE.Types.Location
49import Development.IDE.Types.Location
50+import Development.IDE.Import.FindImports (ArtifactsLocation(..))
51import Module
53import Module
@@ -67,27 +69,34 @@ newtype FilePathId = FilePathId { getFilePathId :: Int }
67 deriving (Show, NFData, Eq, Ord)
69 deriving (Show, NFData, Eq, Ord)
69data PathIdMap = PathIdMap
71data PathIdMap = PathIdMap
70− { idToPathMap :: !(IntMap NormalizedFilePath)
72+ { idToPathMap :: !(IntMap ArtifactsLocation)
71 , pathToIdMap :: !(HashMap NormalizedFilePath FilePathId)
73 , pathToIdMap :: !(HashMap NormalizedFilePath FilePathId)
73 deriving (Show, Generic)
75 deriving (Show, Generic)
75instance NFData PathIdMap
77instance NFData PathIdMap
79+modLocationToNormalizedFilePath :: ArtifactsLocation -> NormalizedFilePath
80+modLocationToNormalizedFilePath (ArtifactsLocation loc) =
81+ case ml_hs_file loc of
82+ Just filePath -> toNormalizedFilePath filePath
83+ -- Since we craete all 'ModLocation' values via 'mkHomeModLocation'
84+ Nothing -> error "Has something changed in mkHomeModLocation?"
77emptyPathIdMap :: PathIdMap
86emptyPathIdMap :: PathIdMap
78emptyPathIdMap = PathIdMap IntMap.empty HMS.empty
87emptyPathIdMap = PathIdMap IntMap.empty HMS.empty
80−getPathId :: NormalizedFilePath -> PathIdMap -> (FilePathId, PathIdMap)
89+getPathId :: ArtifactsLocation -> PathIdMap -> (FilePathId, PathIdMap)
81getPathId path m@PathIdMap{..} =
90getPathId path m@PathIdMap{..} =
82− case HMS.lookup path pathToIdMap of
91+ case HMS.lookup (modLocationToNormalizedFilePath path) pathToIdMap of
83 Nothing ->
92 Nothing ->
84 let !newId = FilePathId $ HMS.size pathToIdMap
93 let !newId = FilePathId $ HMS.size pathToIdMap
85 in (newId, insertPathId path newId m)
94 in (newId, insertPathId path newId m)
86 Just id -> (id, m)
95 Just id -> (id, m)
88−insertPathId :: NormalizedFilePath -> FilePathId -> PathIdMap -> PathIdMap
97+insertPathId :: ArtifactsLocation -> FilePathId -> PathIdMap -> PathIdMap
89insertPathId path id PathIdMap{..} =
98insertPathId path id PathIdMap{..} =
90− PathIdMap (IntMap.insert (getFilePathId id) path idToPathMap) (HMS.insert path id pathToIdMap)
99+ PathIdMap (IntMap.insert (getFilePathId id) path idToPathMap) (HMS.insert (modLocationToNormalizedFilePath path) id pathToIdMap)
92insertImport :: FilePathId -> Either ModuleParseError ModuleImports -> RawDependencyInformation -> RawDependencyInformation
101insertImport :: FilePathId -> Either ModuleParseError ModuleImports -> RawDependencyInformation -> RawDependencyInformation
93insertImport (FilePathId k) v rawDepInfo = rawDepInfo { rawImports = IntMap.insert k v (rawImports rawDepInfo) }
102insertImport (FilePathId k) v rawDepInfo = rawDepInfo { rawImports = IntMap.insert k v (rawImports rawDepInfo) }
@@ -96,7 +105,11 @@ pathToId :: PathIdMap -> NormalizedFilePath -> FilePathId
96pathToId PathIdMap{pathToIdMap} path = pathToIdMap HMS.! path
105pathToId PathIdMap{pathToIdMap} path = pathToIdMap HMS.! path
98idToPath :: PathIdMap -> FilePathId -> NormalizedFilePath
107idToPath :: PathIdMap -> FilePathId -> NormalizedFilePath
108+idToPath pathIdMap filePathId = modLocationToNormalizedFilePath $ idToModLocation pathIdMap filePathId
110+idToModLocation :: PathIdMap -> FilePathId -> ArtifactsLocation
99−idToPath PathIdMap{idToPathMap} (FilePathId id) = idToPathMap IntMap.! id
111+idToModLocation PathIdMap{idToPathMap} (FilePathId id) = idToPathMap IntMap.! id
101-- | Unprocessed results that we find by following imports recursively.
114-- | Unprocessed results that we find by following imports recursively.
102data RawDependencyInformation = RawDependencyInformation
115data RawDependencyInformation = RawDependencyInformation
@@ -112,6 +125,7 @@ data DependencyInformation =
112 DependencyInformation
125 DependencyInformation
113 { depErrorNodes :: !(IntMap (NonEmpty NodeError))
126 { depErrorNodes :: !(IntMap (NonEmpty NodeError))
114 -- ^ Nodes that cannot be processed correctly.
127 -- ^ Nodes that cannot be processed correctly.
128+ , depModuleNames :: !(IntMap ShowableModuleName)
115 , depModuleDeps :: !(IntMap IntSet)
129 , depModuleDeps :: !(IntMap IntSet)
116 -- ^ For a non-error node, this contains the set of module immediate dependencies
130 -- ^ For a non-error node, this contains the set of module immediate dependencies
117 -- in the same package.
131 -- in the same package.
@@ -120,6 +134,12 @@ data DependencyInformation =
120 , depPathIdMap :: !PathIdMap
134 , depPathIdMap :: !PathIdMap
121 } deriving (Show, Generic)
135 } deriving (Show, Generic)
137+newtype ShowableModuleName =
138+ ShowableModuleName {showableModuleName :: ModuleName}
141+instance Show ShowableModuleName where show = moduleNameString . showableModuleName
123reachableModules :: DependencyInformation -> [NormalizedFilePath]
143reachableModules :: DependencyInformation -> [NormalizedFilePath]
124reachableModules DependencyInformation{..} =
144reachableModules DependencyInformation{..} =
125 map (idToPath depPathIdMap . FilePathId) $ IntMap.keys depErrorNodes <> IntMap.keys depModuleDeps
145 map (idToPath depPathIdMap . FilePathId) $ IntMap.keys depErrorNodes <> IntMap.keys depModuleDeps
@@ -186,16 +206,24 @@ processDependencyInformation rawDepInfo@RawDependencyInformation{..} =
186 DependencyInformation
206 DependencyInformation
187 { depErrorNodes = IntMap.fromList errorNodes
207 { depErrorNodes = IntMap.fromList errorNodes
188 , depModuleDeps = moduleDeps
208 , depModuleDeps = moduleDeps
209+ , depModuleNames = IntMap.fromList $ coerce moduleNames
189 , depPkgDeps = pkgDependencies rawDepInfo
210 , depPkgDeps = pkgDependencies rawDepInfo
190 , depPathIdMap = rawPathIdMap
211 , depPathIdMap = rawPathIdMap
192 where resultGraph = buildResultGraph rawImports
213 where resultGraph = buildResultGraph rawImports
193 (errorNodes, successNodes) = partitionNodeResults $ IntMap.toList resultGraph
214 (errorNodes, successNodes) = partitionNodeResults $ IntMap.toList resultGraph
215+ moduleNames :: [(FilePathId, ModuleName)]
217+ [ (fId, modName) | (_, imports) <- successNodes, (L _ modName, fId) <- imports]
194 successEdges :: [(FilePathId, FilePathId, [FilePathId])]
218 successEdges :: [(FilePathId, FilePathId, [FilePathId])]
195 successEdges =
219 successEdges =
196− map (\(file, imports) -> (FilePathId file, FilePathId file, map snd imports)) successNodes
221+ (\(file, imports) -> (FilePathId file, FilePathId file, map snd imports))
197 moduleDeps =
223 moduleDeps =
198− IntMap.fromList $ map (\(_, FilePathId v, vs) -> (v, IntSet.fromList $ coerce vs)) successEdges
225+ map (\(_, FilePathId v, vs) -> (v, IntSet.fromList $ coerce vs))
200-- | Given a dependency graph, buildResultGraph detects and propagates errors in that graph as follows:
228-- | Given a dependency graph, buildResultGraph detects and propagates errors in that graph as follows:
201-- 1. Mark each node that is part of an import cycle as an error node.
229-- 1. Mark each node that is part of an import cycle as an error node.
@@ -268,18 +296,27 @@ transitiveDeps DependencyInformation{..} file = do
268 IntSet.delete (getFilePathId fileId) .
296 IntSet.delete (getFilePathId fileId) .
269 IntSet.fromList . map (fst3 . fromVertex) .
297 IntSet.fromList . map (fst3 . fromVertex) .
270 reachable g <$> toVertex (getFilePathId fileId)
298 reachable g <$> toVertex (getFilePathId fileId)
299+ let transitiveModuleDepIds =
271− let transitiveModuleDepIds = filter (\v -> v `IntSet.member` reachableVs) $ map (fst3 . fromVertex) vs
300+ filter (\v -> v `IntSet.member` reachableVs) $ map (fst3 . fromVertex) vs
272 let transitivePkgDeps =
301 let transitivePkgDeps =
273 Set.toList $ Set.unions $
302 Set.toList $ Set.unions $
274 map (\f -> IntMap.findWithDefault Set.empty f depPkgDeps) $
303 map (\f -> IntMap.findWithDefault Set.empty f depPkgDeps) $
275 getFilePathId fileId : transitiveModuleDepIds
304 getFilePathId fileId : transitiveModuleDepIds
305+ let transitiveModuleDeps =
276− let transitiveModuleDeps = map (idToPath depPathIdMap . FilePathId) transitiveModuleDepIds
306+ map (idToPath depPathIdMap . FilePathId) transitiveModuleDepIds
307+ let transitiveNamedModuleDeps =
308+ [ NamedModuleDep (idToPath depPathIdMap (FilePathId fid)) mn ml
309+ | (fid, ShowableModuleName mn) <- IntMap.toList depModuleNames
310+ , let ArtifactsLocation ml = idToPathMap depPathIdMap IntMap.! fid
277 pure TransitiveDependencies {..}
312 pure TransitiveDependencies {..}
278− where (g, fromVertex, toVertex) = graphFromEdges (map (\(f, fs) -> (f, f, IntSet.toList fs)) $ IntMap.toList depModuleDeps)
314+ (g, fromVertex, toVertex) = graphFromEdges (map (\(f, fs) -> (f, f, IntSet.toList fs)) $ IntMap.toList depModuleDeps)
279− vs = topSort g
315+ vs = topSort g
281data TransitiveDependencies = TransitiveDependencies
317data TransitiveDependencies = TransitiveDependencies
282 { transitiveModuleDeps :: [NormalizedFilePath]
318 { transitiveModuleDeps :: [NormalizedFilePath]
319+ , transitiveNamedModuleDeps :: [NamedModuleDep]
283 -- ^ Transitive module dependencies in topological order.
320 -- ^ Transitive module dependencies in topological order.
284 -- The module itself is not included.
321 -- The module itself is not included.
285 , transitivePkgDeps :: [InstalledUnitId]
322 , transitivePkgDeps :: [InstalledUnitId]
@@ -287,3 +324,24 @@ data TransitiveDependencies = TransitiveDependencies
287 } deriving (Eq, Show, Generic)
324 } deriving (Eq, Show, Generic)
289instance NFData TransitiveDependencies
326instance NFData TransitiveDependencies
328+data NamedModuleDep = NamedModuleDep {
329+ nmdFilePath :: !NormalizedFilePath,
330+ nmdModuleName :: !ModuleName,
331+ nmdModLocation :: !ModLocation
335+instance Eq NamedModuleDep where
336+ a == b = nmdFilePath a == nmdFilePath b
338+instance NFData NamedModuleDep where
339+ rnf NamedModuleDep{..} =
340+ rnf nmdFilePath `seq`
341+ rnf nmdModuleName `seq`
342+ -- 'ModLocation' lacks an 'NFData' instance
343+ rwhnf nmdModLocation
345+instance Show NamedModuleDep where
346+ show NamedModuleDep{..} = show nmdFilePath