module StaticLS.IDE.Definition (getDefinition)
where
import Control.Monad (guard, join)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Maybe (MaybeT (..))
import Data.List (isSuffixOf)
import Data.Maybe (fromMaybe, maybeToList)
import Development.IDE.GHC.Error (
srcSpanToFilename,
srcSpanToRange,
)
import qualified GHC.Data.FastString as GHC
import qualified GHC.Iface.Ext.Types as GHC
import qualified GHC.Plugins as GHC
import GHC.Stack (HasCallStack)
import GHC.Utils.Monad (mapMaybeM)
import qualified HieDb
import qualified Language.LSP.Types as LSP
import StaticLS.Except
import StaticLS.HIE
import StaticLS.HIE.File
import StaticLS.Maybe
import StaticLS.StaticEnv
import System.Directory (doesFileExist)
import System.FilePath ((</>))
getDefinition ::
(HasCallStack, HasStaticEnv m, MonadIO m) =>
LSP.TextDocumentIdentifier ->
LSP.Position ->
m [LSP.Location]
getDefinition :: forall (m :: * -> *).
(HasCallStack, HasStaticEnv m, MonadIO m) =>
TextDocumentIdentifier -> Position -> m [Location]
getDefinition TextDocumentIdentifier
tdi Position
pos = do
Maybe [Location]
mLocs <- MaybeT m [Location] -> m (Maybe [Location])
forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
runMaybeT (MaybeT m [Location] -> m (Maybe [Location]))
-> MaybeT m [Location] -> m (Maybe [Location])
forall a b. (a -> b) -> a -> b
$ do
HieFile
hieFile <- TextDocumentIdentifier -> MaybeT m HieFile
forall (m :: * -> *).
(HasStaticEnv m, MonadIO m) =>
TextDocumentIdentifier -> MaybeT m HieFile
getHieFileFromTdi TextDocumentIdentifier
tdi
let identifiersAtPoint :: [Identifier]
identifiersAtPoint =
[[Identifier]] -> [Identifier]
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join ([[Identifier]] -> [Identifier]) -> [[Identifier]] -> [Identifier]
forall a b. (a -> b) -> a -> b
$
HieFile
-> (Int, Int)
-> Maybe (Int, Int)
-> (HieAST Int -> [Identifier])
-> [[Identifier]]
forall a.
HieFile
-> (Int, Int) -> Maybe (Int, Int) -> (HieAST Int -> a) -> [a]
HieDb.pointCommand
HieFile
hieFile
(Position -> (Int, Int)
lspPositionToHieDbCoords Position
pos)
Maybe (Int, Int)
forall a. Maybe a
Nothing
HieAST Int -> [Identifier]
forall a. HieAST a -> [Identifier]
hieAstNodeToIdentifiers
[[Location]] -> [Location]
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join ([[Location]] -> [Location])
-> MaybeT m [[Location]] -> MaybeT m [Location]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Identifier -> MaybeT m [Location])
-> [Identifier] -> MaybeT m [[Location]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM (m [Location] -> MaybeT m [Location]
forall (m :: * -> *) a. Monad m => m a -> MaybeT m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m [Location] -> MaybeT m [Location])
-> (Identifier -> m [Location])
-> Identifier
-> MaybeT m [Location]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Identifier -> m [Location]
forall (m :: * -> *).
(HasStaticEnv m, MonadIO m) =>
Identifier -> m [Location]
identifierToLocation) [Identifier]
identifiersAtPoint
[Location] -> m [Location]
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Location] -> m [Location]) -> [Location] -> m [Location]
forall a b. (a -> b) -> a -> b
$ [Location] -> Maybe [Location] -> [Location]
forall a. a -> Maybe a -> a
fromMaybe [] Maybe [Location]
mLocs
where
identifierToLocation :: (HasStaticEnv m, MonadIO m) => GHC.Identifier -> m [LSP.Location]
identifierToLocation :: forall (m :: * -> *).
(HasStaticEnv m, MonadIO m) =>
Identifier -> m [Location]
identifierToLocation =
(ModuleName -> m [Location])
-> (Name -> m [Location]) -> Identifier -> m [Location]
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either
((Maybe Location -> [Location])
-> m (Maybe Location) -> m [Location]
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Maybe Location -> [Location]
forall a. Maybe a -> [a]
maybeToList (m (Maybe Location) -> m [Location])
-> (ModuleName -> m (Maybe Location)) -> ModuleName -> m [Location]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ModuleName -> m (Maybe Location)
forall (m :: * -> *).
(HasStaticEnv m, MonadIO m) =>
ModuleName -> m (Maybe Location)
modToLocation)
Name -> m [Location]
forall (m :: * -> *).
(HasCallStack, HasStaticEnv m, MonadIO m) =>
Name -> m [Location]
nameToLocation
modToLocation :: (HasStaticEnv m, MonadIO m) => GHC.ModuleName -> m (Maybe LSP.Location)
modToLocation :: forall (m :: * -> *).
(HasStaticEnv m, MonadIO m) =>
ModuleName -> m (Maybe Location)
modToLocation ModuleName
modName =
let zeroPos :: Position
zeroPos = UInt -> UInt -> Position
LSP.Position UInt
0 UInt
0
zeroRange :: Range
zeroRange = Position -> Position -> Range
LSP.Range Position
zeroPos Position
zeroPos
in MaybeT m Location -> m (Maybe Location)
forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
runMaybeT (MaybeT m Location -> m (Maybe Location))
-> MaybeT m Location -> m (Maybe Location)
forall a b. (a -> b) -> a -> b
$ do
SrcFilePath
srcFile <- ModuleName -> MaybeT m SrcFilePath
forall (m :: * -> *).
(HasStaticEnv m, MonadIO m) =>
ModuleName -> MaybeT m SrcFilePath
modToSrcFile ModuleName
modName
Location -> MaybeT m Location
forall a. a -> MaybeT m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Location -> MaybeT m Location) -> Location -> MaybeT m Location
forall a b. (a -> b) -> a -> b
$ Uri -> Range -> Location
LSP.Location (SrcFilePath -> Uri
LSP.filePathToUri SrcFilePath
srcFile) Range
zeroRange
nameToLocation :: (HasCallStack, HasStaticEnv m, MonadIO m) => GHC.Name -> m [LSP.Location]
nameToLocation :: forall (m :: * -> *).
(HasCallStack, HasStaticEnv m, MonadIO m) =>
Name -> m [Location]
nameToLocation Name
name = (Maybe [Location] -> [Location])
-> m (Maybe [Location]) -> m [Location]
forall a b. (a -> b) -> m a -> m b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ([Location] -> Maybe [Location] -> [Location]
forall a. a -> Maybe a -> a
fromMaybe []) (m (Maybe [Location]) -> m [Location])
-> (MaybeT m [Location] -> m (Maybe [Location]))
-> MaybeT m [Location]
-> m [Location]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> MaybeT m [Location] -> m (Maybe [Location])
forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
runMaybeT (MaybeT m [Location] -> m [Location])
-> MaybeT m [Location] -> m [Location]
forall a b. (a -> b) -> a -> b
$
case Name -> SrcSpan
GHC.nameSrcSpan Name
name of
sp :: SrcSpan
sp@(GHC.RealSrcSpan RealSrcSpan
rsp Maybe BufSpan
_)
| SrcFilePath
fs <- FastString -> SrcFilePath
GHC.unpackFS (RealSrcSpan -> FastString
GHC.srcSpanFile RealSrcSpan
rsp)
, Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ SrcFilePath
"boot" SrcFilePath -> SrcFilePath -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` SrcFilePath
fs ->
do
Bool
itExists <- IO Bool -> MaybeT m Bool
forall a. IO a -> MaybeT m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Bool -> MaybeT m Bool) -> IO Bool -> MaybeT m Bool
forall a b. (a -> b) -> a -> b
$ SrcFilePath -> IO Bool
doesFileExist SrcFilePath
fs
if Bool
itExists
then m (Maybe [Location]) -> MaybeT m [Location]
forall (m :: * -> *) a. m (Maybe a) -> MaybeT m a
MaybeT (m (Maybe [Location]) -> MaybeT m [Location])
-> m (Maybe [Location]) -> MaybeT m [Location]
forall a b. (a -> b) -> a -> b
$ [Location] -> Maybe [Location]
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Location] -> Maybe [Location])
-> (Maybe Location -> [Location])
-> Maybe Location
-> Maybe [Location]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe Location -> [Location]
forall a. Maybe a -> [a]
maybeToList (Maybe Location -> Maybe [Location])
-> m (Maybe Location) -> m (Maybe [Location])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (MaybeT m Location -> m (Maybe Location)
forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
runMaybeT (MaybeT m Location -> m (Maybe Location))
-> (SrcSpan -> MaybeT m Location) -> SrcSpan -> m (Maybe Location)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SrcSpan -> MaybeT m Location
forall (m :: * -> *).
(HasCallStack, HasStaticEnv m) =>
SrcSpan -> MaybeT m Location
srcSpanToLocation) SrcSpan
sp
else
SrcSpan -> MaybeT m [Location]
forall (m :: * -> *).
(HasCallStack, HasStaticEnv m, MonadIO m) =>
SrcSpan -> MaybeT m [Location]
fallbackToDb SrcSpan
sp
SrcSpan
sp -> SrcSpan -> MaybeT m [Location]
forall (m :: * -> *).
(HasCallStack, HasStaticEnv m, MonadIO m) =>
SrcSpan -> MaybeT m [Location]
fallbackToDb SrcSpan
sp
where
fallbackToDb :: (HasCallStack, HasStaticEnv m, MonadIO m) => GHC.SrcSpan -> MaybeT m [LSP.Location]
fallbackToDb :: forall (m :: * -> *).
(HasCallStack, HasStaticEnv m, MonadIO m) =>
SrcSpan -> MaybeT m [Location]
fallbackToDb SrcSpan
sp = do
Bool -> MaybeT m ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (SrcSpan
sp SrcSpan -> SrcSpan -> Bool
forall a. Eq a => a -> a -> Bool
/= SrcSpan
GHC.wiredInSrcSpan)
Module
mod' <- m (Maybe Module) -> MaybeT m Module
forall (m :: * -> *) a. m (Maybe a) -> MaybeT m a
MaybeT (m (Maybe Module) -> MaybeT m Module)
-> m (Maybe Module) -> MaybeT m Module
forall a b. (a -> b) -> a -> b
$ Maybe Module -> m (Maybe Module)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Module -> m (Maybe Module))
-> Maybe Module -> m (Maybe Module)
forall a b. (a -> b) -> a -> b
$ Name -> Maybe Module
GHC.nameModule_maybe Name
name
[Res DefRow]
erow <- (HieDb -> IO [Res DefRow]) -> MaybeT m [Res DefRow]
forall (m :: * -> *) a.
(HasStaticEnv m, MonadIO m) =>
(HieDb -> IO a) -> MaybeT m a
runHieDbMaybeT (\HieDb
hieDb -> HieDb
-> OccName -> Maybe ModuleName -> Maybe Unit -> IO [Res DefRow]
HieDb.findDef HieDb
hieDb (Name -> OccName
GHC.nameOccName Name
name) (ModuleName -> Maybe ModuleName
forall a. a -> Maybe a
Just (ModuleName -> Maybe ModuleName) -> ModuleName -> Maybe ModuleName
forall a b. (a -> b) -> a -> b
$ Module -> ModuleName
forall unit. GenModule unit -> ModuleName
GHC.moduleName Module
mod') (Unit -> Maybe Unit
forall a. a -> Maybe a
Just (Unit -> Maybe Unit) -> Unit -> Maybe Unit
forall a b. (a -> b) -> a -> b
$ Module -> Unit
forall unit. GenModule unit -> unit
GHC.moduleUnit Module
mod'))
case [Res DefRow]
erow of
[] -> do
[Res DefRow]
erow' <- (HieDb -> IO [Res DefRow]) -> MaybeT m [Res DefRow]
forall (m :: * -> *) a.
(HasStaticEnv m, MonadIO m) =>
(HieDb -> IO a) -> MaybeT m a
runHieDbMaybeT (\HieDb
hieDb -> HieDb
-> OccName -> Maybe ModuleName -> Maybe Unit -> IO [Res DefRow]
HieDb.findDef HieDb
hieDb (Name -> OccName
GHC.nameOccName Name
name) (ModuleName -> Maybe ModuleName
forall a. a -> Maybe a
Just (ModuleName -> Maybe ModuleName) -> ModuleName -> Maybe ModuleName
forall a b. (a -> b) -> a -> b
$ Module -> ModuleName
forall unit. GenModule unit -> ModuleName
GHC.moduleName Module
mod') Maybe Unit
forall a. Maybe a
Nothing)
case [Res DefRow]
erow' of
[] -> m (Maybe [Location]) -> MaybeT m [Location]
forall (m :: * -> *) a. m (Maybe a) -> MaybeT m a
MaybeT (m (Maybe [Location]) -> MaybeT m [Location])
-> m (Maybe [Location]) -> MaybeT m [Location]
forall a b. (a -> b) -> a -> b
$ Maybe [Location] -> m (Maybe [Location])
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe [Location]
forall a. Maybe a
Nothing
[Res DefRow]
xs -> m [Location] -> MaybeT m [Location]
forall (m :: * -> *) a. Monad m => m a -> MaybeT m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m [Location] -> MaybeT m [Location])
-> m [Location] -> MaybeT m [Location]
forall a b. (a -> b) -> a -> b
$ (Res DefRow -> m (Maybe Location)) -> [Res DefRow] -> m [Location]
forall (m :: * -> *) a b.
Applicative m =>
(a -> m (Maybe b)) -> [a] -> m [b]
mapMaybeM (MaybeT m Location -> m (Maybe Location)
forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
runMaybeT (MaybeT m Location -> m (Maybe Location))
-> (Res DefRow -> MaybeT m Location)
-> Res DefRow
-> m (Maybe Location)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Res DefRow -> MaybeT m Location
forall (m :: * -> *).
(HasCallStack, HasStaticEnv m, MonadIO m) =>
Res DefRow -> MaybeT m Location
defRowToLocation) [Res DefRow]
xs
[Res DefRow]
xs -> m [Location] -> MaybeT m [Location]
forall (m :: * -> *) a. Monad m => m a -> MaybeT m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m [Location] -> MaybeT m [Location])
-> m [Location] -> MaybeT m [Location]
forall a b. (a -> b) -> a -> b
$ (Res DefRow -> m (Maybe Location)) -> [Res DefRow] -> m [Location]
forall (m :: * -> *) a b.
Applicative m =>
(a -> m (Maybe b)) -> [a] -> m [b]
mapMaybeM (MaybeT m Location -> m (Maybe Location)
forall (m :: * -> *) a. MaybeT m a -> m (Maybe a)
runMaybeT (MaybeT m Location -> m (Maybe Location))
-> (Res DefRow -> MaybeT m Location)
-> Res DefRow
-> m (Maybe Location)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Res DefRow -> MaybeT m Location
forall (m :: * -> *).
(HasCallStack, HasStaticEnv m, MonadIO m) =>
Res DefRow -> MaybeT m Location
defRowToLocation) [Res DefRow]
xs
srcSpanToLocation :: (HasCallStack, HasStaticEnv m) => GHC.SrcSpan -> MaybeT m LSP.Location
srcSpanToLocation :: forall (m :: * -> *).
(HasCallStack, HasStaticEnv m) =>
SrcSpan -> MaybeT m Location
srcSpanToLocation SrcSpan
src = do
StaticEnv
staticEnv <- m StaticEnv -> MaybeT m StaticEnv
forall (m :: * -> *) a. Monad m => m a -> MaybeT m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift m StaticEnv
forall (m :: * -> *). HasStaticEnv m => m StaticEnv
getStaticEnv
SrcFilePath
fs <- Maybe SrcFilePath -> MaybeT m SrcFilePath
forall (f :: * -> *) (g :: * -> *) a.
(Functor f, Foldable f, Alternative g) =>
f a -> g a
toAlt (Maybe SrcFilePath -> MaybeT m SrcFilePath)
-> Maybe SrcFilePath -> MaybeT m SrcFilePath
forall a b. (a -> b) -> a -> b
$ (StaticEnv
staticEnv.wsRoot SrcFilePath -> SrcFilePath -> SrcFilePath
</>) (SrcFilePath -> SrcFilePath)
-> Maybe SrcFilePath -> Maybe SrcFilePath
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SrcSpan -> Maybe SrcFilePath
srcSpanToFilename SrcSpan
src
Range
rng <- Maybe Range -> MaybeT m Range
forall (f :: * -> *) (g :: * -> *) a.
(Functor f, Foldable f, Alternative g) =>
f a -> g a
toAlt (Maybe Range -> MaybeT m Range) -> Maybe Range -> MaybeT m Range
forall a b. (a -> b) -> a -> b
$ SrcSpan -> Maybe Range
srcSpanToRange SrcSpan
src
Location -> MaybeT m Location
forall a. a -> MaybeT m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Location -> MaybeT m Location) -> Location -> MaybeT m Location
forall a b. (a -> b) -> a -> b
$ Uri -> Range -> Location
LSP.Location (NormalizedUri -> Uri
LSP.fromNormalizedUri (NormalizedUri -> Uri) -> NormalizedUri -> Uri
forall a b. (a -> b) -> a -> b
$ NormalizedFilePath -> NormalizedUri
LSP.normalizedFilePathToUri (NormalizedFilePath -> NormalizedUri)
-> NormalizedFilePath -> NormalizedUri
forall a b. (a -> b) -> a -> b
$ SrcFilePath -> NormalizedFilePath
LSP.toNormalizedFilePath SrcFilePath
fs) Range
rng
defRowToLocation :: (HasCallStack, HasStaticEnv m, MonadIO m) => HieDb.Res HieDb.DefRow -> MaybeT m LSP.Location
defRowToLocation :: forall (m :: * -> *).
(HasCallStack, HasStaticEnv m, MonadIO m) =>
Res DefRow -> MaybeT m Location
defRowToLocation (DefRow
defRow HieDb.:. ModuleInfo
_) = do
let start :: Maybe Position
start = Except UIntConversionException Position -> Maybe Position
forall a b. Except a b -> Maybe b
exceptToMaybe (Except UIntConversionException Position -> Maybe Position)
-> Except UIntConversionException Position -> Maybe Position
forall a b. (a -> b) -> a -> b
$ (Int, Int) -> Except UIntConversionException Position
forall (m :: * -> *).
Monad m =>
(Int, Int) -> ExceptT UIntConversionException m Position
hiedbCoordsToLspPosition (DefRow
defRow.defSLine, DefRow
defRow.defSCol)
end :: Maybe Position
end = Except UIntConversionException Position -> Maybe Position
forall a b. Except a b -> Maybe b
exceptToMaybe (Except UIntConversionException Position -> Maybe Position)
-> Except UIntConversionException Position -> Maybe Position
forall a b. (a -> b) -> a -> b
$ (Int, Int) -> Except UIntConversionException Position
forall (m :: * -> *).
Monad m =>
(Int, Int) -> ExceptT UIntConversionException m Position
hiedbCoordsToLspPosition (DefRow
defRow.defELine, DefRow
defRow.defECol)
range :: Maybe Range
range = Position -> Position -> Range
LSP.Range (Position -> Position -> Range)
-> Maybe Position -> Maybe (Position -> Range)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Position
start Maybe (Position -> Range) -> Maybe Position -> Maybe Range
forall a b. Maybe (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Maybe Position
end
hieFilePath :: SrcFilePath
hieFilePath = DefRow
defRow.defSrc
SrcFilePath
file <- SrcFilePath -> MaybeT m SrcFilePath
forall (m :: * -> *).
(HasStaticEnv m, MonadIO m) =>
SrcFilePath -> MaybeT m SrcFilePath
hieFilePathToSrcFilePath SrcFilePath
hieFilePath
let lspUri :: Uri
lspUri = SrcFilePath -> Uri
LSP.filePathToUri SrcFilePath
file
m (Maybe Location) -> MaybeT m Location
forall (m :: * -> *) a. m (Maybe a) -> MaybeT m a
MaybeT (m (Maybe Location) -> MaybeT m Location)
-> (Maybe Location -> m (Maybe Location))
-> Maybe Location
-> MaybeT m Location
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe Location -> m (Maybe Location)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe Location -> MaybeT m Location)
-> Maybe Location -> MaybeT m Location
forall a b. (a -> b) -> a -> b
$ Uri -> Range -> Location
LSP.Location Uri
lspUri (Range -> Location) -> Maybe Range -> Maybe Location
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Range
range