{-# LANGUAGE CPP             #-}
{-# LANGUAGE PatternSynonyms #-}

-- | Compat module for 'UnitState' and 'UnitInfo'.
module Development.IDE.GHC.Compat.Units (
    -- * UnitState
    UnitState,
#if MIN_VERSION_ghc(9,3,0)
    initUnits,
#endif
    oldInitUnits,
    unitState,
    getUnitName,
    explicitUnits,
    preloadClosureUs,
    listVisibleModuleNames,
    LookupResult(..),
    lookupModuleWithSuggestions,
    -- * UnitInfoMap
    UnitInfoMap,
    getUnitInfoMap,
    lookupUnit,
    lookupUnit',
    -- * UnitInfo
    UnitInfo,
    unitExposedModules,
    unitDepends,
    unitHaddockInterfaces,
    mkUnit,
    unitPackageNameString,
    unitPackageVersion,
    -- * UnitId helpers
    UnitId,
    Unit,
    unitString,
    stringToUnit,
    definiteUnitId,
    defUnitId,
    installedModule,
    -- * Module
    toUnitId,
    Development.IDE.GHC.Compat.Units.moduleUnitId,
    moduleUnit,
    -- * ExternalPackageState
    ExternalPackageState(..),
    -- * Utils
    filterInplaceUnits,
    FinderCache,
    showSDocForUser',
    findImportedModule,
    ) where

import           Data.Either
import           Development.IDE.GHC.Compat.Core
import           Development.IDE.GHC.Compat.Env
import           Development.IDE.GHC.Compat.Outputable
import           Prelude                               hiding (mod)

-- See Note [Guidelines For Using CPP In GHCIDE Import Statements]

import           GHC.Types.Unique.Set
import qualified GHC.Unit.Info                         as UnitInfo
import           GHC.Unit.State                        (LookupResult, UnitInfo,
                                                        UnitInfoMap,
                                                        UnitState (unitInfoMap),
                                                        lookupUnit', mkUnit,
                                                        unitDepends,
                                                        unitExposedModules,
                                                        unitPackageNameString,
                                                        unitPackageVersion)
import qualified GHC.Unit.State                        as State
import           GHC.Unit.Types
import qualified GHC.Unit.Types                        as Unit


#if !MIN_VERSION_ghc(9,3,0)
import           GHC.Data.FastString

#endif

import qualified GHC.Data.ShortText                    as ST
import           GHC.Unit.External
import qualified GHC.Unit.Finder                       as GHC

#if !MIN_VERSION_ghc(9,3,0)
import           GHC.Unit.Env
import           GHC.Unit.Finder                       hiding
                                                       (findImportedModule)
#endif

#if MIN_VERSION_ghc(9,3,0)
import           Control.Monad
import qualified Data.List.NonEmpty                    as NE
import qualified Data.Map.Strict                       as Map
import qualified GHC
import qualified GHC.Driver.Session                    as DynFlags
import           GHC.Types.PkgQual                     (PkgQual (NoPkgQual))
import           GHC.Unit.Home.ModInfo
#endif


type PreloadUnitClosure = UniqSet UnitId

unitState :: HscEnv -> UnitState
unitState :: HscEnv -> UnitState
unitState = UnitEnv -> UnitState
ue_units forall b c a. (b -> c) -> (a -> b) -> a -> c
. HscEnv -> UnitEnv
hsc_unit_env

#if MIN_VERSION_ghc(9,3,0)
createUnitEnvFromFlags :: NE.NonEmpty DynFlags -> HomeUnitGraph
createUnitEnvFromFlags unitDflags =
  let
    newInternalUnitEnv dflags = mkHomeUnitEnv dflags emptyHomePackageTable Nothing
    unitEnvList = NE.map (\dflags -> (homeUnitId_ dflags, newInternalUnitEnv dflags)) unitDflags
  in
    unitEnv_new (Map.fromList (NE.toList (unitEnvList)))

initUnits :: [DynFlags] -> HscEnv -> IO HscEnv
initUnits unitDflags env = do
  let dflags0         = hsc_dflags env
  -- additionally, set checked dflags so we don't lose fixes
  let initial_home_graph = createUnitEnvFromFlags (dflags0 NE.:| unitDflags)
      home_units = unitEnv_keys initial_home_graph
  home_unit_graph <- forM initial_home_graph $ \homeUnitEnv -> do
    let cached_unit_dbs = homeUnitEnv_unit_dbs homeUnitEnv
        dflags = homeUnitEnv_dflags homeUnitEnv
        old_hpt = homeUnitEnv_hpt homeUnitEnv

    (dbs,unit_state,home_unit,mconstants) <- State.initUnits (hsc_logger env) dflags cached_unit_dbs home_units

    updated_dflags <- DynFlags.updatePlatformConstants dflags mconstants
    pure HomeUnitEnv
      { homeUnitEnv_units = unit_state
      , homeUnitEnv_unit_dbs = Just dbs
      , homeUnitEnv_dflags = updated_dflags
      , homeUnitEnv_hpt = old_hpt
      , homeUnitEnv_home_unit = Just home_unit
      }

  let dflags1 = homeUnitEnv_dflags $ unitEnv_lookup (homeUnitId_ dflags0) home_unit_graph
  let unit_env = UnitEnv
        { ue_platform        = targetPlatform dflags1
        , ue_namever         = GHC.ghcNameVersion dflags1
        , ue_home_unit_graph = home_unit_graph
        , ue_current_unit    = homeUnitId_ dflags0
        , ue_eps             = ue_eps (hsc_unit_env env)
        }
  pure $ hscSetFlags dflags1 $ hscSetUnitEnv unit_env env
#endif

-- | oldInitUnits only needs to modify DynFlags for GHC <9.2
-- For GHC >= 9.2, we need to set the hsc_unit_env also, that is
-- done later by initUnits
oldInitUnits :: DynFlags -> IO DynFlags
oldInitUnits :: DynFlags -> IO DynFlags
oldInitUnits = forall (f :: * -> *) a. Applicative f => a -> f a
pure

explicitUnits :: UnitState -> [Unit]
explicitUnits :: UnitState -> [Unit]
explicitUnits UnitState
ue =
#if MIN_VERSION_ghc(9,3,0)
  map fst $ State.explicitUnits ue
#else
  UnitState -> [Unit]
State.explicitUnits UnitState
ue
#endif

listVisibleModuleNames :: HscEnv -> [ModuleName]
listVisibleModuleNames :: HscEnv -> [ModuleName]
listVisibleModuleNames HscEnv
env =
  UnitState -> [ModuleName]
State.listVisibleModuleNames forall a b. (a -> b) -> a -> b
$ HscEnv -> UnitState
unitState HscEnv
env

getUnitName :: HscEnv -> UnitId -> Maybe PackageName
getUnitName :: HscEnv -> UnitId -> Maybe PackageName
getUnitName HscEnv
env UnitId
i =
  forall compid srcpkgid srcpkgname uid modulename mod.
GenericUnitInfo compid srcpkgid srcpkgname uid modulename mod
-> srcpkgname
State.unitPackageName forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> UnitState -> UnitId -> Maybe UnitInfo
State.lookupUnitId (HscEnv -> UnitState
unitState HscEnv
env) UnitId
i

lookupModuleWithSuggestions
  :: HscEnv
  -> ModuleName
#if MIN_VERSION_ghc(9,3,0)
  -> GHC.PkgQual
#else
  -> Maybe FastString
#endif
  -> LookupResult
lookupModuleWithSuggestions :: HscEnv -> ModuleName -> Maybe FastString -> LookupResult
lookupModuleWithSuggestions HscEnv
env ModuleName
modname Maybe FastString
mpkg =
  UnitState -> ModuleName -> Maybe FastString -> LookupResult
State.lookupModuleWithSuggestions (HscEnv -> UnitState
unitState HscEnv
env) ModuleName
modname Maybe FastString
mpkg

getUnitInfoMap :: HscEnv -> UnitInfoMap
getUnitInfoMap :: HscEnv -> UnitInfoMap
getUnitInfoMap =
  UnitState -> UnitInfoMap
unitInfoMap forall b c a. (b -> c) -> (a -> b) -> a -> c
. UnitEnv -> UnitState
ue_units forall b c a. (b -> c) -> (a -> b) -> a -> c
. HscEnv -> UnitEnv
hsc_unit_env

lookupUnit :: HscEnv -> Unit -> Maybe UnitInfo
lookupUnit :: HscEnv -> Unit -> Maybe UnitInfo
lookupUnit HscEnv
env Unit
pid = UnitState -> Unit -> Maybe UnitInfo
State.lookupUnit (HscEnv -> UnitState
unitState HscEnv
env) Unit
pid

preloadClosureUs :: HscEnv -> PreloadUnitClosure
preloadClosureUs :: HscEnv -> PreloadUnitClosure
preloadClosureUs = UnitState -> PreloadUnitClosure
State.preloadClosure forall b c a. (b -> c) -> (a -> b) -> a -> c
. HscEnv -> UnitState
unitState

unitHaddockInterfaces :: UnitInfo -> [FilePath]
unitHaddockInterfaces :: UnitInfo -> [FilePath]
unitHaddockInterfaces =
  forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ShortText -> FilePath
ST.unpack forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall compid srcpkgid srcpkgname uid modulename mod.
GenericUnitInfo compid srcpkgid srcpkgname uid modulename mod
-> [ShortText]
UnitInfo.unitHaddockInterfaces

-- ------------------------------------------------------------------
-- Backwards Compatible UnitState
-- ------------------------------------------------------------------

-- ------------------------------------------------------------------
-- Patterns and helpful definitions
-- ------------------------------------------------------------------

definiteUnitId :: Definite uid -> GenUnit uid
definiteUnitId :: forall uid. Definite uid -> GenUnit uid
definiteUnitId         = forall uid. Definite uid -> GenUnit uid
RealUnit
defUnitId :: unit -> Definite unit
defUnitId :: forall unit. unit -> Definite unit
defUnitId              = forall unit. unit -> Definite unit
Definite
installedModule :: unit -> ModuleName -> GenModule unit
installedModule :: forall unit. unit -> ModuleName -> GenModule unit
installedModule        = forall unit. unit -> ModuleName -> GenModule unit
Module


moduleUnitId :: Module -> UnitId
moduleUnitId :: Module -> UnitId
moduleUnitId =
    Unit -> UnitId
Unit.toUnitId forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall unit. GenModule unit -> unit
Unit.moduleUnit

filterInplaceUnits :: [UnitId] -> [PackageFlag] -> ([UnitId], [PackageFlag])
filterInplaceUnits :: [UnitId] -> [PackageFlag] -> ([UnitId], [PackageFlag])
filterInplaceUnits [UnitId]
us [PackageFlag]
packageFlags =
  forall a b. [Either a b] -> ([a], [b])
partitionEithers (forall a b. (a -> b) -> [a] -> [b]
map PackageFlag -> Either UnitId PackageFlag
isInplace [PackageFlag]
packageFlags)
  where
    isInplace :: PackageFlag -> Either UnitId PackageFlag
    isInplace :: PackageFlag -> Either UnitId PackageFlag
isInplace p :: PackageFlag
p@(ExposePackage FilePath
_ (UnitIdArg Unit
u) ModRenaming
_) =
      if Unit -> UnitId
toUnitId Unit
u forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [UnitId]
us
        then forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ Unit -> UnitId
toUnitId  Unit
u
        else forall a b. b -> Either a b
Right PackageFlag
p
    isInplace PackageFlag
p = forall a b. b -> Either a b
Right PackageFlag
p

showSDocForUser' :: HscEnv -> PrintUnqualified -> SDoc -> String
showSDocForUser' :: HscEnv -> PrintUnqualified -> SDoc -> FilePath
showSDocForUser' HscEnv
env = DynFlags -> UnitState -> PrintUnqualified -> SDoc -> FilePath
showSDocForUser (HscEnv -> DynFlags
hsc_dflags HscEnv
env) (HscEnv -> UnitState
unitState HscEnv
env)

findImportedModule :: HscEnv -> ModuleName -> IO (Maybe Module)
findImportedModule :: HscEnv -> ModuleName -> IO (Maybe Module)
findImportedModule HscEnv
env ModuleName
mn = do
#if MIN_VERSION_ghc(9,3,0)
    res <- GHC.findImportedModule env mn NoPkgQual
#else
    FindResult
res <- HscEnv -> ModuleName -> Maybe FastString -> IO FindResult
GHC.findImportedModule HscEnv
env ModuleName
mn forall a. Maybe a
Nothing
#endif
    case FindResult
res of
        Found ModLocation
_ Module
mod -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ Module
mod
        FindResult
_           -> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing