@@ -18,6 +18,7 @@ import Control.Concurrent.Extra
18
18
import Control.Exception.Safe
19
19
import Control.Monad.Extra
20
20
import Control.Monad.IO.Class
21
+ import Data.Bifunctor (Bifunctor (second ))
21
22
import Data.Default
22
23
import Data.Either
23
24
import Data.Foldable (for_ )
@@ -122,7 +123,8 @@ main = do
122
123
runLanguageServer options (pluginHandler plugins) onInitialConfiguration onConfigurationChange $ \ getLspId event vfs caps wProg wIndefProg -> do
123
124
t <- t
124
125
hPutStrLn stderr $ " Started LSP server in " ++ showDuration t
125
- let options = (defaultIdeOptions $ loadSessionShake dir)
126
+ sessionLoader <- loadSession dir
127
+ let options = (defaultIdeOptions sessionLoader)
126
128
{ optReportProgress = clientSupportsProgress caps
127
129
, optShakeProfiling = argsShakeProfiling
128
130
, optTesting = IdeTesting argsTesting
@@ -154,7 +156,8 @@ main = do
154
156
vfs <- makeVFSHandle
155
157
debouncer <- newAsyncDebouncer
156
158
let dummyWithProg _ _ f = f (const (pure () ))
157
- ide <- initialise def mainRule (pure $ IdInt 0 ) (showEvent lock) dummyWithProg (const (const id )) (logger minBound ) debouncer (defaultIdeOptions $ loadSessionShake dir) vfs
159
+ sessionLoader <- loadSession dir
160
+ ide <- initialise def mainRule (pure $ IdInt 0 ) (showEvent lock) dummyWithProg (const (const id )) (logger minBound ) debouncer (defaultIdeOptions sessionLoader) vfs
158
161
159
162
putStrLn " \n Step 4/4: Type checking the files"
160
163
setFilesOfInterest ide $ HashSet. fromList $ map toNormalizedFilePath' files
@@ -223,40 +226,43 @@ targetToFile _ (TargetFile f _) = do
223
226
setNameCache :: IORef NameCache -> HscEnv -> HscEnv
224
227
setNameCache nc hsc = hsc { hsc_NC = nc }
225
228
226
- loadSessionShake :: FilePath -> Action (FilePath -> Action (IdeResult HscEnvEq ))
227
- loadSessionShake fp = do
228
- se <- getShakeExtras
229
- IdeOptions {optTesting = IdeTesting ideTesting} <- getIdeOptions
230
- res <- liftIO $ loadSession ideTesting se fp
231
- return (fmap liftIO res)
232
-
233
229
-- | This is the key function which implements multi-component support. All
234
230
-- components mapping to the same hie.yaml file are mapped to the same
235
231
-- HscEnv which is updated as new components are discovered.
236
- loadSession :: Bool -> ShakeExtras -> FilePath -> IO (FilePath -> IO ( IdeResult HscEnvEq ) )
237
- loadSession optTesting ShakeExtras {logger, eventer, restartShakeSession, withIndefiniteProgress} dir = do
232
+ loadSession :: FilePath -> IO (Action IdeGhcSession )
233
+ loadSession dir = do
238
234
-- Mapping from hie.yaml file to HscEnv, one per hie.yaml file
239
235
hscEnvs <- newVar Map. empty :: IO (Var HieMap )
240
236
-- Mapping from a Filepath to HscEnv
241
237
fileToFlags <- newVar Map. empty :: IO (Var FlagsMap )
238
+ -- Version of the mappings above
239
+ version <- newVar 0
240
+ let returnWithVersion fun = IdeGhcSession fun <$> liftIO (readVar version)
241
+ let invalidateShakeCache = do
242
+ modifyVar_ version (return . succ )
243
+ -- This caches the mapping from Mod.hs -> hie.yaml
244
+ cradleLoc <- liftIO $ memoIO $ \ v -> do
245
+ res <- findCradle v
246
+ -- Sometimes we get C:, sometimes we get c:, and sometimes we get a relative path
247
+ -- try and normalise that
248
+ -- e.g. see https://github.com/digital-asset/ghcide/issues/126
249
+ res' <- traverse IO. makeAbsolute res
250
+ return $ normalise <$> res'
242
251
243
252
libdir <- getLibdir
244
253
installationCheck <- ghcVersionChecker libdir
245
254
255
+ dummyAs <- async $ return (error " Uninitialised" )
256
+ runningCradle <- newVar dummyAs :: IO (Var (Async (IdeResult HscEnvEq ,[FilePath ])))
257
+
246
258
case installationCheck of
247
259
InstallationNotFound {.. } ->
248
260
error $ " GHC installation not found in libdir: " <> libdir
249
261
InstallationMismatch {.. } ->
250
- return $ \ fp -> return ([renderPackageSetupException compileTime fp GhcVersionMismatch {.. }], Nothing )
251
- InstallationChecked compileTime ghcLibCheck -> do
252
- -- This caches the mapping from Mod.hs -> hie.yaml
253
- cradleLoc <- memoIO $ \ v -> do
254
- res <- findCradle v
255
- -- Sometimes we get C:, sometimes we get c:, and sometimes we get a relative path
256
- -- try and normalise that
257
- -- e.g. see https://github.com/digital-asset/ghcide/issues/126
258
- res' <- traverse IO. makeAbsolute res
259
- return $ normalise <$> res'
262
+ return $ returnWithVersion $ \ fp -> return (([renderPackageSetupException compileTime fp GhcVersionMismatch {.. }], Nothing ),[] )
263
+ InstallationChecked compileTime ghcLibCheck -> return $ do
264
+ ShakeExtras {logger, eventer, restartShakeSession, withIndefiniteProgress} <- getShakeExtras
265
+ IdeOptions {optTesting = IdeTesting optTesting} <- getIdeOptions
260
266
261
267
-- Create a new HscEnv from a hieYaml root and a set of options
262
268
-- If the hieYaml file already has an HscEnv, the new component is
@@ -269,7 +275,8 @@ loadSession optTesting ShakeExtras{logger, eventer, restartShakeSession, withInd
269
275
hscEnv <- emptyHscEnv
270
276
(df, targets) <- evalGhcEnv hscEnv $
271
277
setOptions opts (hsc_dflags hscEnv)
272
- dep_info <- getDependencyInfo (componentDependencies opts ++ maybeToList hieYaml)
278
+ let deps = componentDependencies opts ++ maybeToList hieYaml
279
+ dep_info <- getDependencyInfo deps
273
280
-- Now lookup to see whether we are combining with an existing HscEnv
274
281
-- or making a new one. The lookup returns the HscEnv and a list of
275
282
-- information about other components loaded into the HscEnv
@@ -329,7 +336,8 @@ loadSession optTesting ShakeExtras{logger, eventer, restartShakeSession, withInd
329
336
-- existing packages
330
337
pure (Map. insert hieYaml (newHscEnv, new_deps) m, (newHscEnv, head new_deps', tail new_deps'))
331
338
332
- let session :: (Maybe FilePath , NormalizedFilePath , ComponentOptions ) -> IO (IdeResult HscEnvEq )
339
+
340
+ let session :: (Maybe FilePath , NormalizedFilePath , ComponentOptions ) -> IO (IdeResult HscEnvEq ,[FilePath ])
333
341
session (hieYaml, cfp, opts) = do
334
342
(hscEnv, new, old_deps) <- packageSetup (hieYaml, cfp, opts)
335
343
-- Make a map from unit-id to DynFlags, this is used when trying to
@@ -350,11 +358,12 @@ loadSession optTesting ShakeExtras{logger, eventer, restartShakeSession, withInd
350
358
pure $ Map. insert hieYaml (HM. fromList (cs ++ cached_targets)) var
351
359
352
360
-- Invalidate all the existing GhcSession build nodes by restarting the Shake session
361
+ invalidateShakeCache
353
362
restartShakeSession [kick]
354
363
355
- return (fst res)
364
+ return (second Map. keys res)
356
365
357
- let consultCradle :: Maybe FilePath -> FilePath -> IO (IdeResult HscEnvEq )
366
+ let consultCradle :: Maybe FilePath -> FilePath -> IO (IdeResult HscEnvEq , [ FilePath ] )
358
367
consultCradle hieYaml cfp = do
359
368
when optTesting $ eventer $ notifyCradleLoaded cfp
360
369
logInfo logger $ T. pack (" Consulting the cradle for " <> show cfp)
@@ -379,10 +388,11 @@ loadSession optTesting ShakeExtras{logger, eventer, restartShakeSession, withInd
379
388
let res = (map (renderCradleError ncfp) err, Nothing )
380
389
modifyVar_ fileToFlags $ \ var -> do
381
390
pure $ Map. insertWith HM. union hieYaml (HM. singleton ncfp (res, dep_info)) var
382
- return res
391
+ return ( res, [] )
383
392
384
393
-- This caches the mapping from hie.yaml + Mod.hs -> [String]
385
- let sessionOpts :: (Maybe FilePath , FilePath ) -> IO (IdeResult HscEnvEq )
394
+ -- Returns the Ghc session and the cradle dependencies
395
+ let sessionOpts :: (Maybe FilePath , FilePath ) -> IO (IdeResult HscEnvEq , [FilePath ])
386
396
sessionOpts (hieYaml, file) = do
387
397
v <- fromMaybe HM. empty . Map. lookup hieYaml <$> readVar fileToFlags
388
398
cfp <- canonicalizePath file
@@ -397,30 +407,26 @@ loadSession optTesting ShakeExtras{logger, eventer, restartShakeSession, withInd
397
407
-- Keep the same name cache
398
408
modifyVar_ hscEnvs (return . Map. adjust (\ (h, _) -> (h, [] )) hieYaml )
399
409
consultCradle hieYaml cfp
400
- else return opts
410
+ else return ( opts, Map. keys old_di)
401
411
Nothing -> consultCradle hieYaml cfp
402
412
403
- dummyAs <- async $ return (error " Uninitialised" )
404
- runningCradle <- newVar dummyAs :: IO (Var (Async (IdeResult HscEnvEq )))
405
413
-- The main function which gets options for a file. We only want one of these running
406
414
-- at a time. Therefore the IORef contains the currently running cradle, if we try
407
415
-- to get some more options then we wait for the currently running action to finish
408
416
-- before attempting to do so.
409
- let getOptions :: FilePath -> IO (IdeResult HscEnvEq )
417
+ let getOptions :: FilePath -> IO (IdeResult HscEnvEq , [ FilePath ] )
410
418
getOptions file = do
411
419
hieYaml <- cradleLoc file
412
420
sessionOpts (hieYaml, file) `catch` \ e ->
413
- return ([renderPackageSetupException compileTime file e], Nothing )
421
+ return (( [renderPackageSetupException compileTime file e], Nothing ), [] )
414
422
415
- return $ \ file -> do
416
- join $ mask_ $ modifyVar runningCradle $ \ as -> do
423
+ returnWithVersion $ \ file -> do
424
+ liftIO $ join $ mask_ $ modifyVar runningCradle $ \ as -> do
417
425
-- If the cradle is not finished, then wait for it to finish.
418
426
void $ wait as
419
427
as <- async $ getOptions file
420
428
return (as, wait as)
421
429
422
-
423
-
424
430
-- | Create a mapping from FilePaths to HscEnvEqs
425
431
newComponentCache
426
432
:: Logger
0 commit comments