module Haskell.Ide.Engine.ArtifactMap where import Data.Maybe import qualified Data.IntervalMap.FingerTree as IM import qualified Data.Generics as SYB import qualified GHC import GHC (TypecheckedModule) import qualified SrcLoc as GHC import qualified Var import Haskell.Ide.Engine.GhcCompat import Language.Haskell.LSP.Types -- --------------------------------------------------------------------- type SourceMap a = IM.IntervalMap Position a type LocMap = SourceMap GHC.Name type TypeMap = SourceMap GHC.Type type ModuleMap = SourceMap GHC.ModuleName type DefMap = SourceMap GHC.RdrName -- TODO: Maybe look at using ranges instead of positions -- --------------------------------------------------------------------- genIntervalMap :: [(Position,Position,a)] -> SourceMap a genIntervalMap ts = foldr go IM.empty ts where go (l,h,x) im = IM.insert (IM.Interval l h) x im -- --------------------------------------------------------------------- -- | Generates a LocMap from a TypecheckedModule, -- which allows fast queries for all the symbols -- located at a particular point in the source genLocMap :: TypecheckedModule -> LocMap genLocMap tm = names where typechecked = GHC.tm_typechecked_source tm renamed = fromJust $ GHC.tm_renamed_source tm names = IM.union names2 $ SYB.everything IM.union (IM.empty `SYB.mkQ` hsRecFieldT) typechecked names2 = SYB.everything IM.union (IM.empty `SYB.mkQ` fieldOcc `SYB.extQ` hsRecFieldN `SYB.extQ` checker) renamed checker (GHC.L (GHC.RealSrcSpan r) x) = IM.singleton (rspToInt r) x checker _ = IM.empty fieldOcc :: GHC.FieldOcc GhcRn -> LocMap fieldOcc (FieldOccCompat n (GHC.L (GHC.RealSrcSpan r) _)) = IM.singleton (rspToInt r) n fieldOcc _ = IM.empty hsRecFieldN :: GHC.LHsExpr GhcRn -> LocMap hsRecFieldN (GHC.L _ (HsRecFldCompat (UnambiguousCompat n (GHC.L (GHC.RealSrcSpan r) _)) )) = IM.singleton (rspToInt r) n hsRecFieldN _ = IM.empty hsRecFieldT :: GHC.LHsExpr GhcTc -> LocMap hsRecFieldT (GHC.L _ (HsRecFldCompat (AmbiguousCompat n (GHC.L (GHC.RealSrcSpan r) _)) )) = IM.singleton (rspToInt r) (Var.varName n) hsRecFieldT _ = IM.empty -- | Generates a ModuleMap of imported and exported modules names, -- and the locations that they were imported/exported at. genImportMap :: TypecheckedModule -> ModuleMap genImportMap tm = moduleMap where (lImports, mlies) = fromJust $ exportedSymbols tm lies = fromMaybe [] mlies moduleMap :: ModuleMap moduleMap = foldl goImp IM.empty lImports `IM.union` foldl goExp IM.empty lies goImp :: ModuleMap -> GHC.LImportDecl a -> ModuleMap goImp acc (GHC.L (GHC.RealSrcSpan r) i) = IM.insert (rspToInt r) (GHC.unLoc $ GHC.ideclName i) acc goImp acc _ = acc goExp :: ModuleMap -> GHC.LIE name -> ModuleMap goExp acc (GHC.L (GHC.RealSrcSpan r) (IEModuleContentsCompat lmn)) = IM.insert (rspToInt r) (GHC.unLoc lmn) acc goExp acc _ = acc -- | Generates a map of function definitions and types -- i.e. top-level bindings and their `where` clauses genDefMap :: TypecheckedModule -> DefMap genDefMap tm = mconcat $ map (go . GHC.unLoc) decls where go :: GHC.HsDecl GhcPs -> DefMap -- Type signatures go (SigDCompat (TypeSigCompat lns _)) = foldl IM.union mempty $ fmap go' lns where go' (GHC.L (GHC.RealSrcSpan r) n) = IM.singleton (rspToInt r) n go' _ = mempty -- Definitions go (ValDCompat (FunBindCompat (GHC.L (GHC.RealSrcSpan r) n) (GHC.MG { GHC.mg_alts = llms }))) = IM.insert (rspToInt r) n wheres where wheres = mconcat $ fmap (gomatch . GHC.unLoc) (GHC.unLoc llms) gomatch (MatchCompat lbs) = golbs (GHC.unLoc lbs) golbs (HsValBindsCompat (ValBindsCompat lhsbs lsigs)) = foldl (\acc x -> IM.union acc (go $ ValDCompat $ GHC.unLoc x)) mempty lhsbs `mappend` foldl IM.union mempty (fmap (go . SigDCompat . GHC.unLoc) lsigs) golbs _ = mempty go _ = mempty decls = GHC.hsmodDecls $ GHC.unLoc $ GHC.pm_parsed_source $ GHC.tm_parsed_module tm -- | Converts a RealSrcSpan to an interval for an IntervalMap. rspToInt :: GHC.RealSrcSpan -> IM.Interval Position rspToInt = uncurry IM.Interval . unpackRealSrcSpan -- -- | Seaches for all the symbols at a point in the -- -- given LocMap -- getNamesAtPos :: Position -> LocMap -> [((Position,Position), GhcRn)] -- getNamesAtPos p im = map f $ IM.search p im getArtifactsAtPos :: Position -> SourceMap a -> [(Range, a)] getArtifactsAtPos p im = map f $ IM.search p im where f (IM.Interval a b, x) = (Range a b, x) unpackRealSrcSpan :: GHC.RealSrcSpan -> (Position, Position) unpackRealSrcSpan rspan = (toPos (l1,c1),toPos (l2,c2)) where s = GHC.realSrcSpanStart rspan l1 = GHC.srcLocLine s c1 = GHC.srcLocCol s e = GHC.realSrcSpanEnd rspan l2 = GHC.srcLocLine e c2 = GHC.srcLocCol e -- | Converts to one based tuple unPos :: Position -> (Int,Int) unPos (Position l c) = (l+1,c+1) -- | Converts from one based tuple toPos :: (Int,Int) -> Position toPos (l,c) = Position (l-1) (c-1) -- --------------------------------------------------------------------- -- ---------------------------------------------------------------------