packages feed

static-ls-0.1.2: src/StaticLS/HI/File.hs

{-# LANGUAGE ScopedTypeVariables #-}

module StaticLS.HI.File (
    readHiFile,
    srcFilePathToHiFilePath,
    getModIfaceFromTdi,
    tdiToHiFilePath,
    modToHiFile,
) where

import Control.Exception
import Control.Monad
import Control.Monad.IO.Unlift (MonadIO, liftIO)
import Control.Monad.Trans.Maybe (MaybeT (..))
import qualified Data.Set as Set
import qualified GHC
import qualified GHC.Iface.Binary as GHC
import qualified GHC.Platform as GHC
import qualified GHC.Platform.Profile as GHC
import qualified GHC.Types.Name.Cache as GHC
import qualified Language.LSP.Protocol.Types as LSP
import StaticLS.FilePath
import StaticLS.SrcFiles
import StaticLS.StaticEnv
import System.FilePath ((</>))

getModIfaceFromTdi :: (HasStaticEnv m, MonadIO m) => LSP.TextDocumentIdentifier -> MaybeT m GHC.ModIface
getModIfaceFromTdi = MaybeT . readHiFile <=< tdiToHiFilePath

tdiToHiFilePath :: (HasStaticEnv m, MonadIO m) => LSP.TextDocumentIdentifier -> MaybeT m HiFilePath
tdiToHiFilePath = srcFilePathToHiFilePath <=< (MaybeT . pure . LSP.uriToFilePath . (._uri))

modToHiFile :: (HasStaticEnv m, MonadIO m) => GHC.ModuleName -> MaybeT m HiFilePath
modToHiFile modName = do
    staticEnv <- getStaticEnv
    let hiFiles = staticEnv.hiFilesPath
    pure $ staticEnv.wsRoot </> hiFiles </> modToFilePath modName ".hi"

-- | Only supports 64 bit platforms
readHiFile :: (MonadIO m) => FilePath -> m (Maybe GHC.ModIface)
readHiFile filePath = do
    nameCache <- liftIO $ GHC.initNameCache 'a' []
    liftIO $
        ( Just
            <$> GHC.readBinIface
                GHC.Profile
                    { GHC.profilePlatform = GHC.genericPlatform
                    , GHC.profileWays = Set.empty
                    }
                nameCache
                GHC.IgnoreHiWay
                GHC.QuietBinIFace
                filePath
        )
            `catch` (\(_ :: IOException) -> pure Nothing)
            `catch` (\(_ :: GHC.GhcException) -> pure Nothing)
            `catch` (\(_ :: SomeException) -> pure Nothing)

srcFilePathToHiFilePath :: (HasStaticEnv m, MonadIO m) => SrcFilePath -> MaybeT m HiFilePath
srcFilePathToHiFilePath srcPath = do
    staticEnv <- getStaticEnv
    let hiFiles = staticEnv.hiFilesPath
        hiDir = staticEnv.wsRoot </> hiFiles
    subRootExtensionFilepath staticEnv.wsRoot hiDir ".hi" srcPath