@@ -9,27 +9,31 @@ module Development.IDE.Core.ProgressReporting
9
9
, mRunLspTCallback
10
10
-- for tests
11
11
, recordProgress
12
- , InProgress (.. )
12
+ , InProgressState (.. )
13
13
)
14
14
where
15
15
16
16
import Control.Concurrent.Async
17
+ import Control.Concurrent.STM.Stats (STM , TVar , atomically ,
18
+ newTVarIO , readTVar ,
19
+ readTVarIO , writeTVar )
17
20
import Control.Concurrent.Strict
18
21
import Control.Monad.Extra
19
22
import Control.Monad.IO.Class
20
23
import Control.Monad.Trans.Class (lift )
21
24
import Data.Foldable (for_ )
22
25
import Data.Functor (($>) )
23
- import qualified Data.HashMap.Strict as HMap
24
26
import qualified Data.Text as T
25
27
import Data.Unique
26
28
import Development.IDE.GHC.Orphans ()
27
29
import Development.IDE.Graph hiding (ShakeValue )
28
30
import Development.IDE.Types.Location
29
31
import Development.IDE.Types.Options
32
+ import qualified Focus
30
33
import qualified Language.LSP.Server as LSP
31
34
import Language.LSP.Types
32
35
import qualified Language.LSP.Types as LSP
36
+ import qualified StmContainers.Map as STM
33
37
import System.Time.Extra
34
38
import UnliftIO.Exception (bracket_ )
35
39
@@ -69,26 +73,37 @@ updateState _ StopProgress (Running a) = cancel a $> Stopped
69
73
updateState _ StopProgress st = pure st
70
74
71
75
-- | Data structure to track progress across the project
72
- data InProgress = InProgress
73
- { todo :: ! Int -- ^ Number of files to do
74
- , done :: ! Int -- ^ Number of files done
75
- , current :: ! ( HMap. HashMap NormalizedFilePath Int )
76
+ data InProgressState = InProgressState
77
+ { todoVar :: TVar Int -- ^ Number of files to do
78
+ , doneVar :: TVar Int -- ^ Number of files done
79
+ , currentVar :: STM. Map NormalizedFilePath Int
76
80
}
77
81
78
- recordProgress :: NormalizedFilePath -> (Int -> Int ) -> InProgress -> InProgress
79
- recordProgress file shift InProgress {.. } = case HMap. alterF alter file current of
80
- ((prev, new), m') ->
81
- let (done',todo') =
82
- case (prev,new) of
83
- (Nothing ,0 ) -> (done+ 1 , todo+ 1 )
84
- (Nothing ,_) -> (done, todo+ 1 )
85
- (Just 0 , 0 ) -> (done , todo)
86
- (Just 0 , _) -> (done- 1 , todo)
87
- (Just _, 0 ) -> (done+ 1 , todo)
88
- (Just _, _) -> (done , todo)
89
- in InProgress todo' done' m'
82
+ newInProgress :: IO InProgressState
83
+ newInProgress = InProgressState <$> newTVarIO 0 <*> newTVarIO 0 <*> STM. newIO
84
+
85
+ recordProgress :: InProgressState -> NormalizedFilePath -> (Int -> Int ) -> STM ()
86
+ recordProgress InProgressState {.. } file shift = do
87
+ done <- readTVar doneVar
88
+ todo <- readTVar todoVar
89
+ (prev, new) <- STM. focus alterPrevAndNew file currentVar
90
+ let (done',todo') =
91
+ case (prev,new) of
92
+ (Nothing ,0 ) -> (done+ 1 , todo+ 1 )
93
+ (Nothing ,_) -> (done, todo+ 1 )
94
+ (Just 0 , 0 ) -> (done , todo)
95
+ (Just 0 , _) -> (done- 1 , todo)
96
+ (Just _, 0 ) -> (done+ 1 , todo)
97
+ (Just _, _) -> (done , todo)
98
+ writeTVar todoVar todo'
99
+ writeTVar doneVar done'
90
100
where
91
- alter x = let x' = maybe (shift 0 ) shift x in ((x,x'), Just x')
101
+ alterPrevAndNew = do
102
+ prev <- Focus. lookup
103
+ Focus. alter alter
104
+ new <- Focus. lookupWithDefault 0
105
+ return (prev, new)
106
+ alter x = let x' = maybe (shift 0 ) shift x in Just x'
92
107
93
108
-- | A 'ProgressReporting' that enqueues Begin and End notifications in a new
94
109
-- thread, with a grace period (nothing will be sent if 'KickCompleted' arrives
@@ -100,17 +115,16 @@ delayedProgressReporting
100
115
-> ProgressReportingStyle
101
116
-> IO ProgressReporting
102
117
delayedProgressReporting before after lspEnv optProgressStyle = do
103
- inProgressVar <- newVar $ InProgress 0 0 mempty
118
+ inProgressState <- newInProgress
104
119
progressState <- newVar NotStarted
105
120
let progressUpdate event = updateStateVar $ Event event
106
121
progressStop = updateStateVar StopProgress
107
- updateStateVar = modifyVar_ progressState . updateState (mRunLspT lspEnv $ lspShakeProgress inProgressVar )
122
+ updateStateVar = modifyVar_ progressState . updateState (mRunLspT lspEnv $ lspShakeProgress inProgressState )
108
123
109
- inProgress :: NormalizedFilePath -> Action a -> Action a
110
- inProgress = withProgressVar inProgressVar
124
+ inProgress = updateStateForFile inProgressState
111
125
return ProgressReporting {.. }
112
126
where
113
- lspShakeProgress inProgress = do
127
+ lspShakeProgress InProgressState { .. } = do
114
128
-- first sleep a bit, so we only show progress messages if it's going to take
115
129
-- a "noticable amount of time" (we often expect a thread kill to arrive before the sleep finishes)
116
130
liftIO $ sleep before
@@ -143,7 +157,8 @@ delayedProgressReporting before after lspEnv optProgressStyle = do
143
157
loop _ _ | optProgressStyle == NoProgress =
144
158
forever $ liftIO $ threadDelay maxBound
145
159
loop id prev = do
146
- InProgress {.. } <- liftIO $ readVar inProgress
160
+ done <- liftIO $ readTVarIO doneVar
161
+ todo <- liftIO $ readTVarIO todoVar
147
162
liftIO $ sleep after
148
163
if todo == 0 then loop id 0 else do
149
164
let next = 100 * fromIntegral done / fromIntegral todo
@@ -166,12 +181,12 @@ delayedProgressReporting before after lspEnv optProgressStyle = do
166
181
}
167
182
loop id next
168
183
169
- withProgressVar var file = actionBracket (f succ ) (const $ f pred ) . const
184
+ updateStateForFile inProgress file = actionBracket (f succ ) (const $ f pred ) . const
170
185
-- This functions are deliberately eta-expanded to avoid space leaks.
171
186
-- Do not remove the eta-expansion without profiling a session with at
172
187
-- least 1000 modifications.
173
188
where
174
- f shift = modifyVar' var $ recordProgress file shift
189
+ f shift = atomically $ recordProgress inProgress file shift
175
190
176
191
mRunLspT :: Applicative m => Maybe (LSP. LanguageContextEnv c ) -> LSP. LspT c m () -> m ()
177
192
mRunLspT (Just lspEnv) f = LSP. runLspT lspEnv f
0 commit comments