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 #-}
2
9
module Development.IDE.Core.ProgressReporting
3
10
( ProgressEvent (.. )
4
11
, ProgressReporting (.. )
@@ -10,57 +17,63 @@ module Development.IDE.Core.ProgressReporting
10
17
-- for tests
11
18
, recordProgress
12
19
, InProgress (.. )
20
+ , MonadLSP (.. )
21
+ , MonadProgress (.. )
22
+ , MonadUnique (.. )
13
23
)
14
24
where
15
25
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 )
18
30
import 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 )
29
42
import Development.IDE.Types.Location
30
43
import Development.IDE.Types.Options
31
- import qualified Language.LSP.Server as LSP
44
+ import qualified Language.LSP.Server as LSP
32
45
import Language.LSP.Types
33
- import qualified Language.LSP.Types as LSP
46
+ import qualified Language.LSP.Types as LSP
34
47
import System.Time.Extra
35
- import UnliftIO.Exception ( bracket_ , evaluate )
48
+ import UnliftIO ( MonadUnliftIO )
36
49
37
50
data ProgressEvent
38
51
= KickStarted
39
52
| KickCompleted
40
53
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 ()
45
58
}
46
59
47
- noProgressReporting :: IO ProgressReporting
60
+ noProgressReporting :: Monad m => m ( ProgressReporting m n )
48
61
noProgressReporting = return $ ProgressReporting
49
62
{ progressUpdate = const $ pure ()
50
63
, inProgress = const id
51
64
, progressStop = pure ()
52
65
}
53
66
54
67
-- | State used in 'delayedProgressReporting'
55
- data State
68
+ data State m
56
69
= NotStarted
57
70
| Stopped
58
- | Running (Async () )
71
+ | Running (Async m () )
59
72
60
73
-- | State transitions used in 'delayedProgressReporting'
61
74
data Transition = Event ProgressEvent | StopProgress
62
75
63
- updateState :: IO () -> Transition -> State -> IO State
76
+ updateState :: MonadConc m => m () -> Transition -> State m -> m ( State m )
64
77
updateState _ _ Stopped = pure Stopped
65
78
updateState start (Event KickStarted ) NotStarted = Running <$> async start
66
79
updateState 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
91
104
where
92
105
alter x = let x' = maybe (shift 0 ) shift x in ((x,x'), Just x')
93
106
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
+
94
118
-- | A 'ProgressReporting' that enqueues Begin and End notifications in a new
95
119
-- thread, with a grace period (nothing will be sent if 'KickCompleted' arrives
96
120
-- before the end of the grace period).
97
121
delayedProgressReporting
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
99
125
-> Seconds -- ^ sampling delay
100
- -> Maybe (LSP. LanguageContextEnv c )
101
126
-> 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
104
130
inProgressVar <- newMVar $ InProgress 0 0 mempty
105
131
progressState <- newMVar NotStarted
106
132
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)
109
135
110
- inProgress :: NormalizedFilePath -> Action a -> Action a
136
+ -- inProgress :: NormalizedFilePath -> m a -> m a
111
137
inProgress = withProgressVar inProgressVar
112
138
return ProgressReporting {.. }
113
139
where
140
+ lspShakeProgress :: MVar m InProgress -> m ()
114
141
lspShakeProgress inProgress = do
115
142
-- first sleep a bit, so we only show progress messages if it's going to take
116
143
-- 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)
126
157
where
127
- start id = LSP. sendNotification LSP. SProgress $
158
+ start id = runLsp $ sendNotification LSP. SProgress $
128
159
LSP. ProgressParams
129
160
{ _token = id
130
161
, _value = LSP. Begin $ WorkDoneProgressBeginParams
@@ -134,22 +165,22 @@ delayedProgressReporting before after lspEnv optProgressStyle = do
134
165
, _percentage = Nothing
135
166
}
136
167
}
137
- stop id = LSP. sendNotification LSP. SProgress
168
+ stop id = runLsp $ sendNotification LSP. SProgress
138
169
LSP. ProgressParams
139
170
{ _token = id
140
171
, _value = LSP. End WorkDoneProgressEndParams
141
172
{ _message = Nothing
142
173
}
143
174
}
144
175
loop _ _ | optProgressStyle == NoProgress =
145
- forever $ liftIO $ threadDelay maxBound
176
+ forever $ threadDelay maxBound
146
177
loop id prev = do
147
- InProgress {.. } <- liftIO $ readMVar inProgress
148
- liftIO $ sleep after
178
+ InProgress {.. } <- readMVar inProgress
179
+ threadDelay ( floor $ after * 1e9 )
149
180
if todo == 0 then loop id 0 else do
150
181
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 $
153
184
LSP. ProgressParams
154
185
{ _token = id
155
186
, _value = LSP. Report $ case optProgressStyle of
@@ -167,12 +198,19 @@ delayedProgressReporting before after lspEnv optProgressStyle = do
167
198
}
168
199
loop id next
169
200
170
- withProgressVar var file = actionBracket (f succ ) (const $ f pred ) . const
201
+ withProgressVar var file = withProgressV var (f succ ) (f pred )
171
202
-- This functions are deliberately eta-expanded to avoid space leaks.
172
203
-- Do not remove the eta-expansion without profiling a session with at
173
204
-- least 1000 modifications.
174
205
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
176
214
177
215
mRunLspT :: Applicative m => Maybe (LSP. LanguageContextEnv c ) -> LSP. LspT c m () -> m ()
178
216
mRunLspT (Just lspEnv) f = LSP. runLspT lspEnv f
0 commit comments