1- {-# LANGUAGE RankNTypes #-}
1+ {-# LANGUAGE ConstraintKinds #-}
2+ {-# LANGUAGE FlexibleInstances #-}
3+ {-# LANGUAGE FunctionalDependencies #-}
4+ {-# LANGUAGE MultiParamTypeClasses #-}
5+ {-# LANGUAGE PolyKinds #-}
6+ {-# LANGUAGE QuantifiedConstraints #-}
7+ {-# LANGUAGE RankNTypes #-}
8+ {-# LANGUAGE ScopedTypeVariables #-}
29module Development.IDE.Core.ProgressReporting
310 ( ProgressEvent (.. )
411 , ProgressReporting (.. )
@@ -10,57 +17,63 @@ module Development.IDE.Core.ProgressReporting
1017 -- for tests
1118 , recordProgress
1219 , InProgress (.. )
20+ , MonadLSP (.. )
21+ , MonadProgress (.. )
22+ , MonadUnique (.. )
1323 )
1424 where
1525
16- import Control.Concurrent.Async
17- import Control.Concurrent.Strict
26+ import Control.Concurrent.Classy
27+ import Control.Concurrent.Classy.Async
28+ import Control.Exception (evaluate )
29+ import Control.Monad.Catch (bracket_ , finally )
1830import Control.Monad.Extra
19- import Control.Monad.IO.Class
20- import Control.Monad.Trans.Class (lift )
21- import Data.Foldable (for_ )
22- import Data.Functor (($>) )
23- import qualified Data.HashMap.Strict as HMap
24- import qualified Data.Text as T
25- import Data.Tuple.Extra (dupe )
26- import Data.Unique
27- import Development.IDE.GHC.Orphans ()
28- import Development.IDE.Graph hiding (ShakeValue )
31+ import Control.Monad.Trans
32+ import Data.Bifunctor (bimap )
33+ import Data.Foldable (for_ )
34+ import Data.Functor (($>) )
35+ import qualified Data.HashMap.Strict as HMap
36+ import qualified Data.Text as T
37+ import Data.Tuple.Extra (dupe )
38+ import Data.Unique (hashUnique )
39+ import qualified Data.Unique as IO
40+ import Development.IDE.GHC.Orphans ()
41+ import Development.IDE.Graph (Action , actionBracket )
2942import Development.IDE.Types.Location
3043import Development.IDE.Types.Options
31- import qualified Language.LSP.Server as LSP
44+ import qualified Language.LSP.Server as LSP
3245import Language.LSP.Types
33- import qualified Language.LSP.Types as LSP
46+ import qualified Language.LSP.Types as LSP
3447import System.Time.Extra
35- import UnliftIO.Exception ( bracket_ , evaluate )
48+ import UnliftIO ( MonadUnliftIO )
3649
3750data ProgressEvent
3851 = KickStarted
3952 | KickCompleted
4053
41- data ProgressReporting = ProgressReporting
42- { progressUpdate :: ProgressEvent -> IO ()
43- , inProgress :: forall a . NormalizedFilePath -> Action a -> Action a
44- , progressStop :: IO ()
54+ data ProgressReporting io m = ProgressReporting
55+ { progressUpdate :: ProgressEvent -> io ()
56+ , inProgress :: forall a . NormalizedFilePath -> m a -> m a
57+ , progressStop :: io ()
4558 }
4659
47- noProgressReporting :: IO ProgressReporting
60+ noProgressReporting :: Monad m => m ( ProgressReporting m n )
4861noProgressReporting = return $ ProgressReporting
4962 { progressUpdate = const $ pure ()
5063 , inProgress = const id
5164 , progressStop = pure ()
5265 }
5366
5467-- | State used in 'delayedProgressReporting'
55- data State
68+ data State m
5669 = NotStarted
5770 | Stopped
58- | Running (Async () )
71+ | Running (Async m () )
5972
6073-- | State transitions used in 'delayedProgressReporting'
6174data Transition = Event ProgressEvent | StopProgress
6275
63- updateState :: IO () -> Transition -> State -> IO State
76+ updateState :: MonadConc m => m () -> Transition -> State m -> m ( State m )
6477updateState _ _ Stopped = pure Stopped
6578updateState start (Event KickStarted ) NotStarted = Running <$> async start
6679updateState start (Event KickStarted ) (Running a) = cancel a >> Running <$> async start
@@ -91,40 +104,58 @@ recordProgress file shift InProgress{..} = case HMap.alterF alter file current o
91104 where
92105 alter x = let x' = maybe (shift 0 ) shift x in ((x,x'), Just x')
93106
107+ class (forall n . Functor n => Functor (m n )) => MonadLSP c m | m -> c where
108+ sendNotification :: forall n (meth :: Method 'FromServer 'Notification) . c n => SServerMethod meth -> MessageParams meth -> m n ()
109+ sendRequest :: forall n (meth :: Method 'FromServer 'Request) . c n => SServerMethod meth -> MessageParams meth -> (Either () () -> n () ) -> m n ()
110+
111+ instance MonadLSP MonadUnliftIO (LSP. LspT c ) where
112+ sendNotification m p = LSP. sendNotification m p
113+ sendRequest m p k = void $ LSP. sendRequest m p (lift . k . bimap (const () ) (const () ))
114+
115+ class MonadUnique m where newUnique :: m Int
116+ instance MonadUnique IO where newUnique = hashUnique <$> IO. newUnique
117+
94118-- | A 'ProgressReporting' that enqueues Begin and End notifications in a new
95119-- thread, with a grace period (nothing will be sent if 'KickCompleted' arrives
96120-- before the end of the grace period).
97121delayedProgressReporting
98- :: Seconds -- ^ Grace period before starting
122+ :: forall c lsp m action
123+ . (c m , MonadProgress m action , MonadConc m , MonadUnique m , MonadLSP c lsp )
124+ => Seconds -- ^ Grace period before starting
99125 -> Seconds -- ^ sampling delay
100- -> Maybe (LSP. LanguageContextEnv c )
101126 -> ProgressReportingStyle
102- -> IO ProgressReporting
103- delayedProgressReporting before after lspEnv optProgressStyle = do
127+ -> (lsp m () -> m () )
128+ -> m (ProgressReporting m action )
129+ delayedProgressReporting before after optProgressStyle runLsp = do
104130 inProgressVar <- newMVar $ InProgress 0 0 mempty
105131 progressState <- newMVar NotStarted
106132 let progressUpdate event = updateStateVar $ Event event
107- progressStop = updateStateVar StopProgress
108- updateStateVar = modifyMVar_ progressState . updateState (mRunLspT lspEnv $ lspShakeProgress inProgressVar)
133+ progressStop = updateStateVar StopProgress
134+ updateStateVar = modifyMVar_ progressState . updateState (lspShakeProgress inProgressVar)
109135
110- inProgress :: NormalizedFilePath -> Action a -> Action a
136+ -- inProgress :: NormalizedFilePath -> m a -> m a
111137 inProgress = withProgressVar inProgressVar
112138 return ProgressReporting {.. }
113139 where
140+ lspShakeProgress :: MVar m InProgress -> m ()
114141 lspShakeProgress inProgress = do
115142 -- first sleep a bit, so we only show progress messages if it's going to take
116143 -- a "noticable amount of time" (we often expect a thread kill to arrive before the sleep finishes)
117- liftIO $ sleep before
118- u <- ProgressTextToken . T. pack . show . hashUnique <$> liftIO newUnique
119-
120- b <- liftIO newEmptyMVar
121- void $ LSP. sendRequest LSP. SWindowWorkDoneProgressCreate
122- LSP. WorkDoneProgressCreateParams { _token = u } $ liftIO . putMVar b
123- ready <- liftIO $ takeMVar b
124-
125- for_ ready $ const $ bracket_ (start u) (stop u) (loop u 0 )
144+ -- threadDelay (floor $ before * 1e9)
145+ u <- ProgressTextToken . T. pack . show <$> newUnique
146+
147+ b <- newEmptyMVar
148+ runLsp $ sendRequest LSP. SWindowWorkDoneProgressCreate
149+ LSP. WorkDoneProgressCreateParams { _token = u } (putMVar b)
150+ ready <- takeMVar b
151+
152+ for_ ready $ const $ uninterruptibleMask $ \ unmask -> do
153+ start u
154+ -- stop u
155+ unmask (loop u 0 ) `finally` stop u
156+ -- bracket_ (start u) (stop u) (loop u 0)
126157 where
127- start id = LSP. sendNotification LSP. SProgress $
158+ start id = runLsp $ sendNotification LSP. SProgress $
128159 LSP. ProgressParams
129160 { _token = id
130161 , _value = LSP. Begin $ WorkDoneProgressBeginParams
@@ -134,22 +165,22 @@ delayedProgressReporting before after lspEnv optProgressStyle = do
134165 , _percentage = Nothing
135166 }
136167 }
137- stop id = LSP. sendNotification LSP. SProgress
168+ stop id = runLsp $ sendNotification LSP. SProgress
138169 LSP. ProgressParams
139170 { _token = id
140171 , _value = LSP. End WorkDoneProgressEndParams
141172 { _message = Nothing
142173 }
143174 }
144175 loop _ _ | optProgressStyle == NoProgress =
145- forever $ liftIO $ threadDelay maxBound
176+ forever $ threadDelay maxBound
146177 loop id prev = do
147- InProgress {.. } <- liftIO $ readMVar inProgress
148- liftIO $ sleep after
178+ InProgress {.. } <- readMVar inProgress
179+ threadDelay ( floor $ after * 1e9 )
149180 if todo == 0 then loop id 0 else do
150181 let next = 100 * fromIntegral done / fromIntegral todo
151- when (next /= prev) $
152- LSP. sendNotification LSP. SProgress $
182+ when (next /= prev) $ runLsp $
183+ sendNotification LSP. SProgress $
153184 LSP. ProgressParams
154185 { _token = id
155186 , _value = LSP. Report $ case optProgressStyle of
@@ -167,12 +198,19 @@ delayedProgressReporting before after lspEnv optProgressStyle = do
167198 }
168199 loop id next
169200
170- withProgressVar var file = actionBracket (f succ ) (const $ f pred ) . const
201+ withProgressVar var file = withProgressV var (f succ ) (f pred )
171202 -- This functions are deliberately eta-expanded to avoid space leaks.
172203 -- Do not remove the eta-expansion without profiling a session with at
173204 -- least 1000 modifications.
174205 where
175- f shift = modifyMVar var $ evaluate . dupe . recordProgress file shift
206+ f shift = recordProgress file shift
207+
208+ class MonadProgress io m | m -> io where
209+ withProgressV :: MVar io a -> (a -> a ) -> (a -> a ) -> m b -> m b
210+
211+ instance MonadProgress IO Action where
212+ withProgressV var succ pred =
213+ actionBracket (modifyMVar var (evaluate. dupe. succ )) (const $ modifyMVar var (evaluate. dupe. pred )) . const
176214
177215mRunLspT :: Applicative m => Maybe (LSP. LanguageContextEnv c ) -> LSP. LspT c m () -> m ()
178216mRunLspT (Just lspEnv) f = LSP. runLspT lspEnv f
0 commit comments