-
-
Notifications
You must be signed in to change notification settings - Fork 391
Goto dependency definition #3749
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
8d356b4
59502a6
36d3a91
dc6fc8f
19bf2b8
727a044
a1a70b7
3b9470e
d6d245e
95afde5
377e6c1
e2578a0
ac6d4b9
71f94c6
74fd8cc
1add24f
0b674d0
5a8abcc
1fea4fd
f6dd201
fbcb22e
34df92c
d61b290
fbb3b57
ec22375
666afb1
34f2307
c23139d
9902ab7
b1f0af3
2329109
192446b
60264bc
092b5d7
004568f
9b90f0a
e9ea310
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,13 +10,20 @@ module Development.IDE.Core.Actions | |
, lookupMod | ||
) where | ||
|
||
import Control.Concurrent.MVar (MVar, newEmptyMVar, | ||
putMVar, readMVar) | ||
import Control.Concurrent.STM (atomically) | ||
import Control.Concurrent.STM.TQueue (unGetTQueue) | ||
import Control.Monad.Extra (mapMaybeM) | ||
import Control.Monad.Reader | ||
import Control.Monad.Trans.Maybe | ||
import qualified Data.ByteString as BS | ||
import Data.Function ((&)) | ||
import qualified Data.HashMap.Strict as HM | ||
import Data.Maybe | ||
import qualified Data.Text as T | ||
import Data.Tuple.Extra | ||
import Development.IDE.Core.Compile (loadHieFile) | ||
import Development.IDE.Core.OfInterest | ||
import Development.IDE.Core.PluginUtils | ||
import Development.IDE.Core.PositionMapping | ||
|
@@ -29,22 +36,98 @@ import qualified Development.IDE.Spans.AtPoint as AtPoint | |
import Development.IDE.Types.HscEnvEq (hscEnv) | ||
import Development.IDE.Types.Location | ||
import qualified HieDb | ||
import Ide.Types (dependenciesDirectory, | ||
hlsDirectory) | ||
import Language.LSP.Protocol.Types (DocumentHighlight (..), | ||
SymbolInformation (..), | ||
normalizedFilePathToUri, | ||
uriToNormalizedFilePath) | ||
import Language.LSP.Server (resRootPath) | ||
import System.Directory (createDirectoryIfMissing, | ||
doesFileExist, | ||
getPermissions, | ||
setOwnerExecutable, | ||
setOwnerWritable, | ||
setPermissions) | ||
import System.FilePath (takeDirectory, (<.>), | ||
(</>)) | ||
|
||
|
||
-- | Eventually this will lookup/generate URIs for files in dependencies, but not in the | ||
-- project. Right now, this is just a stub. | ||
-- | Generates URIs for files in dependencies, but not in the | ||
-- project. Dependency files are produced from an HIE file and | ||
-- placed in the .hls/dependencies directory. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this in the project root? I guess maybe it needs to be so the LSP server thinks it's part of the project. It's a bit awkward to introduce a new directory just for this. Can we not put it in one of the existing places, like somewhere in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please elaborate a little about what you find awkward about generating a new directory? I don't think putting things in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's just One More Thing. At the moment users of HLS need to worry about:
Plus, it's in the project directory, so everyone will have to add a new thing to their Does it need to be in the project root? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it needs to be in the project root to prevent vscode from spawning another HLS instance. |
||
lookupMod | ||
:: HieDbWriter -- ^ access the database | ||
-> FilePath -- ^ The `.hie` file we got from the database | ||
-> ModuleName | ||
-> Unit | ||
-> Bool -- ^ Is this file a boot file? | ||
-> MaybeT IdeAction Uri | ||
lookupMod _dbchan _hie_f _mod _uid _boot = MaybeT $ pure Nothing | ||
lookupMod HieDbWriter{indexQueue} hieFile moduleName uid _boot = MaybeT $ do | ||
-- We need the project root directory to determine where to put | ||
-- the .hls directory. | ||
mProjectRoot <- (resRootPath =<<) <$> asks lspEnv | ||
case mProjectRoot of | ||
Nothing -> pure Nothing | ||
Just projectRoot -> do | ||
-- Database writes happen asynchronously. We use an MVar to mark | ||
-- completion of the database update. | ||
completionToken <- liftIO newEmptyMVar | ||
-- Write out the contents of the dependency source to the | ||
-- .hls/dependencies directory, generate a URI for that | ||
-- location, and update the HieDb database with the source | ||
-- file location. | ||
moduleUri <- writeAndIndexSource projectRoot completionToken | ||
-- Wait for the database update to be completed. | ||
-- Reading the completionToken is blocked until it has | ||
-- a value. | ||
liftIO $ readMVar completionToken | ||
pure $ Just moduleUri | ||
where | ||
writeAndIndexSource :: FilePath -> MVar () -> IdeAction Uri | ||
writeAndIndexSource projectRoot completionToken = do | ||
fileExists <- liftIO $ doesFileExist writeOutPath | ||
-- No need to write out the file if it already exists. | ||
if fileExists then pure () else do | ||
nc <- asks ideNc | ||
liftIO $ do | ||
-- Create the directory where we will put the source. | ||
createDirectoryIfMissing True $ takeDirectory writeOutPath | ||
-- Load a raw Bytestring of the source from the HIE file. | ||
moduleSource <- hie_hs_src <$> loadHieFile (mkUpdater nc) hieFile | ||
-- Write the source into the .hls/dependencies directory. | ||
BS.writeFile writeOutPath moduleSource | ||
fileDefaultPermissions <- getPermissions writeOutPath | ||
let filePermissions = fileDefaultPermissions | ||
& setOwnerWritable False | ||
& setOwnerExecutable False | ||
-- Set the source file to readonly permissions. | ||
setPermissions writeOutPath filePermissions | ||
liftIO $ atomically $ | ||
unGetTQueue indexQueue $ \withHieDb -> do | ||
withHieDb $ \db -> | ||
-- Add a source file to the database row for | ||
-- the HIE file. | ||
HieDb.addSrcFile db hieFile writeOutPath False | ||
-- Mark completion of the database update. | ||
putMVar completionToken () | ||
pure moduleUri | ||
where | ||
-- The source will be written out in a directory from the | ||
-- name and hash of the package the dependency module is | ||
-- found in. The name and hash are both parts of the UnitId. | ||
writeOutDir :: FilePath | ||
writeOutDir = projectRoot </> hlsDirectory </> dependenciesDirectory </> show uid | ||
-- The module name is separated into directories, with the | ||
-- last part of the module name giving the name of the | ||
-- haskell file with a .hs extension. | ||
writeOutFile :: FilePath | ||
writeOutFile = moduleNameSlashes moduleName <.> "hs" | ||
writeOutPath :: FilePath | ||
writeOutPath = writeOutDir </> writeOutFile | ||
moduleUri :: Uri | ||
moduleUri = AtPoint.toUri writeOutPath | ||
|
||
|
||
|
||
-- IMPORTANT NOTE : make sure all rules `useWithStaleFastMT`d by these have a "Persistent Stale" rule defined, | ||
|
@@ -61,11 +144,21 @@ getAtPoint file pos = runMaybeT $ do | |
opts <- liftIO $ getIdeOptionsIO ide | ||
|
||
(hf, mapping) <- useWithStaleFastMT GetHieAst file | ||
env <- hscEnv . fst <$> useWithStaleFastMT GhcSession file | ||
dkMap <- lift $ maybe (DKMap mempty mempty) fst <$> runMaybeT (useWithStaleFastMT GetDocMap file) | ||
-- The HscEnv and DKMap are not strictly necessary for hover | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if we don't have them? The consequences of this are still unclear to me. |
||
-- to work, so we only calculate them for project files, not | ||
-- for dependency files. They provide information that will | ||
-- not be displayed in dependency files. See the atPoint | ||
-- function in ghcide/src/Development/IDE/Spans/AtPoint.hs | ||
-- for the specifics of how they are used. | ||
(mEnv, mDkMap) <- case getSourceFileOrigin file of | ||
FromDependency -> pure (Nothing, Nothing) | ||
FromProject -> do | ||
env <- hscEnv . fst <$> useWithStaleFastMT GhcSession file | ||
dkMap <- lift $ maybe (DKMap mempty mempty) fst <$> runMaybeT (useWithStaleFastMT GetDocMap file) | ||
pure (Just env, Just dkMap) | ||
|
||
!pos' <- MaybeT (return $ fromCurrentPosition mapping pos) | ||
MaybeT $ liftIO $ fmap (first (toCurrentRange mapping =<<)) <$> AtPoint.atPoint opts hf dkMap env pos' | ||
MaybeT $ liftIO $ fmap (first (toCurrentRange mapping =<<)) <$> AtPoint.atPoint opts hf mDkMap mEnv pos' | ||
|
||
-- | For each Location, determine if we have the PositionMapping | ||
-- for the correct file. If not, get the correct position mapping | ||
|
@@ -104,7 +197,9 @@ getDefinition file pos = runMaybeT $ do | |
ide@ShakeExtras{ withHieDb, hiedbWriter } <- ask | ||
opts <- liftIO $ getIdeOptionsIO ide | ||
(HAR _ hf _ _ _, mapping) <- useWithStaleFastMT GetHieAst file | ||
(ImportMap imports, _) <- useWithStaleFastMT GetImportMap file | ||
(ImportMap imports, _) <- case getSourceFileOrigin file of | ||
FromProject -> useWithStaleFastMT GetImportMap file | ||
FromDependency -> pure (ImportMap mempty, PositionMapping idDelta) | ||
!pos' <- MaybeT (pure $ fromCurrentPosition mapping pos) | ||
locations <- AtPoint.gotoDefinition withHieDb (lookupMod hiedbWriter) opts imports hf pos' | ||
MaybeT $ Just <$> toCurrentLocations mapping file locations | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to leave this here? Will we want to turn it back to a released version at some point? Write it down!