@@ -25,7 +25,7 @@ module Development.IDE.Core.Shake(
25
25
IdeRule , IdeResult , GetModificationTime (.. ),
26
26
shakeOpen , shakeShut ,
27
27
shakeRun ,
28
- shakeRunGently ,
28
+ shakeEnqueue ,
29
29
shakeProfile ,
30
30
use , useWithStale , useNoFile , uses , usesWithStale ,
31
31
use_ , useNoFile_ , uses_ ,
@@ -213,10 +213,13 @@ type IdeRule k v =
213
213
, NFData v
214
214
)
215
215
216
+ -- | A live Shake session with the ability to enqueue Actions for running.
217
+ -- Keeps the 'ShakeDatabase' open, so at most one 'ShakeSession' per database.
216
218
data ShakeSession = ShakeSession
217
219
{ cancelShakeSession :: ! (IO () )
218
220
-- ^ Close the Shake session
219
221
, runInShakeSession :: ! (forall a . Action a -> IO (IO a ))
222
+ -- ^ Enqueue an action in the Shake session.
220
223
}
221
224
222
225
nilShakeSession :: ShakeSession
@@ -395,7 +398,11 @@ withMVar' var unmasked masked = mask $ \restore -> do
395
398
putMVar var a'
396
399
pure c
397
400
398
- -- | Spawn immediately. If you are already inside a call to shakeRun that will be aborted with an exception.
401
+ -- | Spawn immediately, restarting the current 'ShakeSession' and 'ShakeDatabase',
402
+ -- and implicily typechecking all the modules before running the actions.
403
+ -- Any computation running in the current session will be aborted with an exception.
404
+ --
405
+ -- Appropriate for file system events.
399
406
shakeRun :: IdeState -> [Action a ] -> IO (IO [a ])
400
407
shakeRun it@ IdeState {shakeExtras= ShakeExtras {logger}, .. } acts =
401
408
withMVar'
@@ -407,70 +414,66 @@ shakeRun it@IdeState{shakeExtras=ShakeExtras{logger}, ..} acts =
407
414
-- It is crucial to be masked here, otherwise we can get killed
408
415
-- between spawning the new thread and updating shakeSession.
409
416
-- See https://github.com/digital-asset/ghcide/issues/79
410
- (\ () -> realShakeRun it acts)
417
+ (\ () -> newSession it acts)
411
418
412
- -- | Append an action to the existing 'ShakeSession'.
413
- -- Assumes an existing 'ShakeSession' is available.
414
- shakeRunGently :: IdeState -> Action a -> IO (IO a )
415
- shakeRunGently IdeState {shakeExtras= ShakeExtras {.. }, .. } act =
419
+ -- | Enqueue an action in the existing 'ShakeSession'.
420
+ -- Returns a computation to block until the action is run.
421
+ -- Assumes a 'ShakeSession' is available.
422
+ --
423
+ -- Appropriate for user actions other than edits.
424
+ shakeEnqueue :: IdeState -> Action a -> IO (IO a )
425
+ shakeEnqueue IdeState {shakeExtras= ShakeExtras {.. }, .. } act =
416
426
withMVar shakeSession $ \ s -> runInShakeSession s act
417
427
418
- realShakeRun :: IdeState -> [Action a ] -> IO (ShakeSession , IO [a ])
419
- realShakeRun IdeState {shakeExtras= ShakeExtras {.. }, .. } acts = do
420
- actionQueue :: TQueue (Action () ) <- newTQueueIO
421
- start <- offsetTime
422
-
423
- let
424
- -- A daemon-like action used to inject additional work
425
- pumpAction =
426
- forever $ join $ liftIO $ atomically $ readTQueue actionQueue
427
-
428
- workThread restore = do
429
- let acts' = (Nothing <$ pumpAction) : (fmap Just <$> acts)
430
- res <- try (restore $ shakeRunDatabaseProfile shakeProfileDir shakeDb acts')
431
- runTime <- start
432
- let res' = case res of
433
- Left e -> " exception: " <> displayException e
434
- Right _ -> " completed"
435
- profile = case res of
436
- Right (_, Just fp) ->
437
- let link = case filePathToUri' $ toNormalizedFilePath' fp of
438
- NormalizedUri _ x -> x
439
- in " , profile saved at " <> T. unpack link
440
- _ -> " "
441
-
442
- -- Wrap up in a thread to avoid calling interruptible
443
- -- operations inside the masked section
444
- let wrapUp = do
445
- logDebug logger $ T. pack $
446
- " Finishing shakeRun (took " ++ showDuration runTime ++ " , " ++ res' ++ profile ++ " )"
447
-
448
- return (fst <$> res, wrapUp)
449
-
450
- -- Do the work in a background thread
451
- aThread <- asyncWithUnmask workThread
452
-
453
- -- run the wrap up unmasked
454
- _ <- async $ do
455
- (_, wrapUp) <- wait aThread
456
- wrapUp
457
-
458
- -- 'runInShakeSession' is used to append work in this Shake session
459
- -- The session stays open until 'cancelShakeSession' is called
460
- -- This should only be necessary iff the (virtual) filesystem has changed
461
- let runInShakeSession :: forall a . Action a -> IO (IO a )
462
- runInShakeSession act = do
463
- res <- newEmptyMVar
464
- atomically $ writeTQueue actionQueue (act >>= liftIO . putMVar res)
465
- return (takeMVar res)
466
-
467
- cancelShakeSession = cancel aThread
468
-
469
- initialResult = do
470
- (res,_) <- wait aThread
471
- either (throwIO @ SomeException ) (return . catMaybes) res
472
-
473
- pure (ShakeSession {.. }, initialResult)
428
+ -- Set up a new 'ShakeSession' with a set of initial actions.
429
+ -- Only valid after aborting the previous one
430
+ newSession :: IdeState -> [Action a ] -> IO (ShakeSession , IO [a ])
431
+ newSession IdeState {shakeExtras= ShakeExtras {.. }, .. } acts = do
432
+ actionQueue :: TQueue (Action () ) <- newTQueueIO
433
+ start <- offsetTime
434
+ let
435
+ -- A daemon-like action used to inject additional work
436
+ -- Only one
437
+ pumpAction =
438
+ forever $ join $ liftIO $ atomically $ readTQueue actionQueue
439
+ workThread restore = do
440
+ let acts' = (Nothing <$ pumpAction) : (fmap Just <$> acts)
441
+ res <- try (restore $ shakeRunDatabaseProfile shakeProfileDir shakeDb acts')
442
+ runTime <- start
443
+ let res' = case res of
444
+ Left e -> " exception: " <> displayException e
445
+ Right _ -> " completed"
446
+ profile = case res of
447
+ Right (_, Just fp) ->
448
+ let link = case filePathToUri' $ toNormalizedFilePath' fp of
449
+ NormalizedUri _ x -> x
450
+ in " , profile saved at " <> T. unpack link
451
+ _ -> " "
452
+ -- Wrap up in a thread to avoid calling interruptible
453
+ -- operations inside the masked section
454
+ let wrapUp = do
455
+ logDebug logger $ T. pack $
456
+ " Finishing shakeRun (took " ++ showDuration runTime ++ " , " ++ res' ++ profile ++ " )"
457
+ return (fst <$> res, wrapUp)
458
+ -- Do the work in a background thread
459
+ aThread <- asyncWithUnmask workThread
460
+ -- run the wrap up unmasked
461
+ _ <- async $ do
462
+ (_, wrapUp) <- wait aThread
463
+ wrapUp
464
+ -- 'runInShakeSession' is used to append work in this Shake session
465
+ -- The session stays open until 'cancelShakeSession' is called
466
+ -- This should only be necessary iff the (virtual) filesystem has changed
467
+ let runInShakeSession :: forall a . Action a -> IO (IO a )
468
+ runInShakeSession act = do
469
+ res <- newEmptyMVar
470
+ atomically $ writeTQueue actionQueue (act >>= liftIO . putMVar res)
471
+ return (takeMVar res)
472
+ cancelShakeSession = cancel aThread
473
+ initialResult = do
474
+ (res,_) <- wait aThread
475
+ either (throwIO @ SomeException ) (return . catMaybes) res
476
+ pure (ShakeSession {.. }, initialResult)
474
477
475
478
getDiagnostics :: IdeState -> IO [FileDiagnostic ]
476
479
getDiagnostics IdeState {shakeExtras = ShakeExtras {diagnostics}} = do
0 commit comments