-
-
Notifications
You must be signed in to change notification settings - Fork 396
Synchronous progress reporting in tests #1770
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
Closed
Closed
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
4809271
factor out progress reporting
pepeiborra 3d9fbdd
extract out progress reporting
pepeiborra 9850aa3
direct progress reporting
pepeiborra 4725002
hlint
pepeiborra c969332
compat with 8.6
pepeiborra a02f495
apply feedbacks
pepeiborra be621ee
Fix benchmarks
pepeiborra 794a48f
Fix splice plugin tests
pepeiborra 5c2cf2f
fix client settings test
pepeiborra 47bf559
Sacrifice delayedProgressReporting
pepeiborra 7ae8b16
round 2
pepeiborra 58a6a57
Avoid empty report messages in the NoProgress style
pepeiborra File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
{-# LANGUAGE RankNTypes #-} | ||
module Development.IDE.Core.ProgressReporting | ||
( ProgressEvent(..) | ||
, ProgressReporting(..) | ||
, noProgressReporting | ||
, makeProgressReporting | ||
-- utilities, reexported for use in Core.Shake | ||
, mRunLspT | ||
, mRunLspTCallback | ||
) where | ||
|
||
import Control.Concurrent.Async | ||
import Control.Concurrent.Strict | ||
import Control.Monad.Extra | ||
import Control.Monad.IO.Class | ||
import Control.Monad.Trans.Class (lift) | ||
import Data.Foldable (for_, traverse_) | ||
import Data.HashMap.Strict (HashMap) | ||
import qualified Data.HashMap.Strict as HMap | ||
import Data.IORef | ||
import Data.IORef.Extra (atomicModifyIORef'_) | ||
import qualified Data.Text as T | ||
import Data.Unique | ||
import Development.IDE.GHC.Orphans () | ||
import Development.IDE.Graph hiding (ShakeValue) | ||
import Development.IDE.Types.Location | ||
import Development.IDE.Types.Options | ||
import qualified Language.LSP.Server as LSP | ||
import Language.LSP.Types | ||
import qualified Language.LSP.Types as LSP | ||
import System.Time.Extra | ||
|
||
data ProgressEvent | ||
= KickStarted | ||
| KickCompleted | ||
|
||
data ProgressReporting = ProgressReporting | ||
{ progressUpdate :: ProgressEvent -> IO () | ||
, inProgress :: forall a. NormalizedFilePath -> Action a -> Action a | ||
, progressStop :: IO () | ||
} | ||
|
||
noProgressReporting :: IO ProgressReporting | ||
noProgressReporting = return $ ProgressReporting | ||
{ progressUpdate = const $ pure () | ||
, inProgress = const id | ||
, progressStop = pure () | ||
} | ||
|
||
-- | A 'ProgressReporting' that sends the WorkDone Begin and End notifications | ||
-- synchronously. Progress notifications are sent from a sampling thread. | ||
pepeiborra marked this conversation as resolved.
Show resolved
Hide resolved
|
||
-- | ||
-- This 'ProgressReporting' is currently used only in tests. | ||
makeProgressReporting | ||
:: Seconds -- ^ sleep before reporting | ||
-> Seconds -- ^ sleep after reporting | ||
-> Maybe (LSP.LanguageContextEnv config) | ||
-> ProgressReportingStyle | ||
-> IO ProgressReporting | ||
makeProgressReporting before after env style = do | ||
st <- newIORef Nothing | ||
inProgressVar <- newIORef (HMap.empty @NormalizedFilePath @Int) | ||
|
||
let progressUpdate KickStarted = do | ||
readIORef st >>= traverse_ (mRunLspT env . stop) | ||
u <- newProgressToken | ||
mRunLspT env $ do | ||
ready <- create u | ||
for_ ready $ \_ -> do | ||
start u | ||
liftIO $ writeIORef st (Just u) | ||
progressUpdate KickCompleted = do | ||
mbToken <- atomicModifyIORef st (Nothing,) | ||
for_ mbToken $ \u -> | ||
mRunLspT env $ stop u | ||
|
||
inProgress file = actionBracket (f file succ) (const $ f file pred) . const | ||
-- This function is deliberately eta-expanded to avoid space leaks. | ||
-- Do not remove the eta-expansion without profiling a session with at | ||
-- least 1000 modifications. | ||
f file shift = atomicModifyIORef'_ inProgressVar $ | ||
HMap.insertWith (\_ x -> shift x) file (shift 0) | ||
|
||
progressLoop :: Seconds -> LSP.LspM a () | ||
progressLoop prev = do | ||
liftIO $ sleep before | ||
mbToken <- liftIO $ readIORef st | ||
next <- case mbToken of | ||
Nothing -> | ||
pure 0 | ||
Just t -> do | ||
current <- liftIO $ readIORef inProgressVar | ||
progress style prev current t | ||
liftIO $ sleep after | ||
progressLoop next | ||
|
||
progressThread <- async $ mRunLspT env $ progressLoop 0 | ||
let progressStop = cancel progressThread | ||
|
||
pure ProgressReporting {..} | ||
|
||
newProgressToken :: IO ProgressToken | ||
newProgressToken = ProgressTextToken . T.pack . show . hashUnique <$> liftIO newUnique | ||
|
||
create | ||
:: LSP.MonadLsp config f | ||
=> ProgressToken | ||
-> f (Either ResponseError Empty) | ||
create u = do | ||
b <- liftIO newBarrier | ||
_ <- LSP.sendRequest LSP.SWindowWorkDoneProgressCreate | ||
LSP.WorkDoneProgressCreateParams { _token = u } | ||
(liftIO . signalBarrier b) | ||
liftIO $ waitBarrier b | ||
|
||
start :: LSP.MonadLsp config f => ProgressToken -> f () | ||
start id = LSP.sendNotification LSP.SProgress $ | ||
LSP.ProgressParams | ||
{ _token = id | ||
, _value = LSP.Begin $ WorkDoneProgressBeginParams | ||
{ _title = "Processing" | ||
, _cancellable = Nothing | ||
, _message = Nothing | ||
, _percentage = Nothing | ||
} | ||
} | ||
stop :: LSP.MonadLsp config f => ProgressToken -> f () | ||
stop id = LSP.sendNotification LSP.SProgress | ||
LSP.ProgressParams | ||
{ _token = id | ||
, _value = LSP.End WorkDoneProgressEndParams | ||
{ _message = Nothing | ||
} | ||
} | ||
|
||
progress :: (LSP.MonadLsp config f) => | ||
ProgressReportingStyle -> Seconds -> HashMap NormalizedFilePath Int -> ProgressToken -> f Seconds | ||
progress NoProgress _ _ _ = return 0 | ||
progress style prev current id = do | ||
let done = length $ filter (== 0) $ HMap.elems current | ||
let todo = HMap.size current | ||
let next = 100 * fromIntegral done / fromIntegral todo | ||
when (next /= prev) $ LSP.sendNotification LSP.SProgress $ LSP.ProgressParams | ||
{ _token = id | ||
, _value = LSP.Report $ case style of | ||
Explicit -> LSP.WorkDoneProgressReportParams | ||
{ _cancellable = Nothing | ||
, _message = Just $ T.pack $ show done <> "/" <> show todo | ||
, _percentage = Nothing | ||
} | ||
Percentage -> LSP.WorkDoneProgressReportParams | ||
{ _cancellable = Nothing | ||
, _message = Nothing | ||
, _percentage = Just next | ||
} | ||
NoProgress -> LSP.WorkDoneProgressReportParams | ||
{ _cancellable = Nothing | ||
, _message = Nothing | ||
, _percentage = Nothing | ||
} | ||
} | ||
return next | ||
|
||
mRunLspT :: Applicative m => Maybe (LSP.LanguageContextEnv c ) -> LSP.LspT c m () -> m () | ||
mRunLspT (Just lspEnv) f = LSP.runLspT lspEnv f | ||
mRunLspT Nothing _ = pure () | ||
|
||
mRunLspTCallback :: Monad m | ||
=> Maybe (LSP.LanguageContextEnv c) | ||
-> (LSP.LspT c m a -> LSP.LspT c m a) | ||
-> m a | ||
-> m a | ||
mRunLspTCallback (Just lspEnv) f g = LSP.runLspT lspEnv $ f (lift g) | ||
mRunLspTCallback Nothing _ g = g |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.