@@ -17,16 +17,15 @@ import Control.Monad
1717import Control.Monad.IO.Class
1818import Control.Monad.IO.Unlift
1919import Data.Aeson qualified as J
20- import Data.Foldable
2120import Data.Map.Strict qualified as Map
2221import Data.Maybe
2322import Data.Text (Text )
23+ import Data.Text qualified as T
2424import Language.LSP.Protocol.Lens qualified as L
2525import Language.LSP.Protocol.Message
2626import Language.LSP.Protocol.Types
2727import Language.LSP.Protocol.Types qualified as L
2828import Language.LSP.Server.Core
29- import UnliftIO qualified as U
3029import UnliftIO.Exception qualified as UE
3130
3231{- | A package indicating the percentage of progress complete and a
@@ -53,16 +52,16 @@ instance E.Exception ProgressCancelledException
5352data ProgressCancellable = Cancellable | NotCancellable
5453
5554-- Get a new id for the progress session and make a new one
56- getNewProgressId :: MonadLsp config m = > m ProgressToken
57- getNewProgressId = do
55+ getNewProgressId :: ( MonadLsp config m ) => Text - > m ProgressToken
56+ getNewProgressId title = do
5857 stateState (progressNextId . resProgressData) $ \ cur ->
5958 let ! next = cur + 1
60- in (L. ProgressToken $ L. InL cur, next)
59+ in (L. ProgressToken $ L. InR (title <> T. pack ( show cur)) , next)
6160{-# INLINE getNewProgressId #-}
6261
6362withProgressBase ::
6463 forall c m a .
65- MonadLsp c m =>
64+ ( MonadLsp c m ) =>
6665 Bool ->
6766 Text ->
6867 Maybe ProgressToken ->
@@ -102,12 +101,10 @@ withProgressBase indefinite title clientToken cancellable f = do
102101
103102 -- Deregister our 'ProgressToken', specifically its cancellation handler. It is important
104103 -- to do this reliably or else we will leak handlers.
105- unregisterToken :: m ()
106- unregisterToken = do
104+ unregisterToken :: ProgressToken -> m ()
105+ unregisterToken token = do
107106 handlers <- getProgressCancellationHandlers
108- liftIO $ atomically $ do
109- mt <- tryReadTMVar tokenVar
110- for_ mt $ \ t -> modifyTVar handlers (Map. delete t)
107+ liftIO $ atomically $ modifyTVar handlers (Map. delete token)
111108
112109 -- Find and register our 'ProgressToken', asking the client for it if necessary.
113110 -- Note that this computation may terminate before we get the token, we need to wait
@@ -120,14 +117,14 @@ withProgressBase indefinite title clientToken cancellable f = do
120117 -- the title/initial percentage aren't given until the 'begin' mesage. However,
121118 -- it's neater not to create tokens that we won't use, and clients may find it
122119 -- easier to clean them up if they receive begin/end reports for them.
123- liftIO $ threadDelay startDelay
120+ when (startDelay > 0 ) $ liftIO $ threadDelay startDelay
124121 case clientToken of
125122 -- See Note [Client- versus server-initiated progress]
126123 -- Client-initiated progress
127124 Just t -> registerToken t
128125 -- Try server-initiated progress
129126 Nothing -> do
130- t <- getNewProgressId
127+ t <- getNewProgressId title
131128 clientCaps <- getClientCapabilities
132129
133130 -- If we don't have a progress token from the client and
@@ -145,43 +142,54 @@ withProgressBase indefinite title clientToken cancellable f = do
145142 -- Successfully registered the token, we can now use it.
146143 -- So we go ahead and start. We do this as soon as we get the
147144 -- token back so the client gets feedback ASAP
148- Right _ -> registerToken t
145+ Right _ -> do
146+ registerToken t
149147 -- The client sent us an error, we can't use the token.
150- Left _err -> pure ()
151-
152- -- Actually send the progress reports.
153- sendReports :: m ()
154- sendReports = do
155- t <- liftIO $ atomically $ readTMVar tokenVar
156- begin t
157- -- Once we are sending updates, if we get interrupted we should send
158- -- the end notification
159- update t `UE.finally` end t
160- where
161- cancellable' = case cancellable of
162- Cancellable -> Just True
163- NotCancellable -> Just False
164- begin t = do
148+ Left _err -> do
149+ pure ()
150+
151+ update t = do
152+ forever $ do
153+ -- See Note [Delayed progress reporting]
154+ when (updateDelay > 0 ) $ liftIO $ threadDelay updateDelay
165155 (ProgressAmount pct msg) <- liftIO $ atomically $ takeTMVar reportVar
166- sendProgressReport t $ WorkDoneProgressBegin L. AString title cancellable' msg pct
167- update t =
168- forever $ do
169- -- See Note [Delayed progress reporting]
170- liftIO $ threadDelay updateDelay
171- (ProgressAmount pct msg) <- liftIO $ atomically $ takeTMVar reportVar
172- sendProgressReport t $ WorkDoneProgressReport L. AString Nothing msg pct
173- end t = sendProgressReport t (WorkDoneProgressEnd L. AString Nothing )
156+ sendProgressReport t $ WorkDoneProgressReport L. AString Nothing msg pct
157+ end t = sendProgressReport t (WorkDoneProgressEnd L. AString Nothing )
158+
159+ begin t = do
160+ sendProgressReport t $ WorkDoneProgressBegin L. AString title cancellable' Nothing Nothing
161+ return t
162+
163+ cancellable' = case cancellable of
164+ Cancellable -> Just True
165+ NotCancellable -> Just False
166+
167+ -- if we have no delays then we can use uninterruptibleMask_ to create the token
168+ -- to ensure we always get begin and end messages
169+ maskTokenCreation =
170+ if startDelay == 0 && updateDelay == 0
171+ then UE. uninterruptibleMask_
172+ else id
174173
175174 -- Create the token and then start sending reports; all of which races with the check for the
176175 -- progress having ended. In all cases, make sure to unregister the token at the end.
177- progressThreads :: m ()
178- progressThreads =
179- ((createToken >> sendReports) `UE.finally` unregisterToken) `U.race_` liftIO progressEnded
180-
176+ progressThreads runInBase =
177+ runInBase
178+ ( UE. bracket
179+ ( maskTokenCreation $
180+ createToken
181+ >> liftIO (atomically $ readTMVar tokenVar)
182+ >>= begin
183+ )
184+ ( \ t -> end t >> unregisterToken t
185+ )
186+ update
187+ )
188+ `race_` progressEnded
181189 withRunInIO $ \ runInBase -> do
182190 withAsync (runInBase $ f updater) $ \ mainAct ->
183191 -- If the progress gets cancelled then we need to get cancelled too
184- withAsync (runInBase progressThreads) $ \ pthreads -> do
192+ withAsync (progressThreads runInBase ) $ \ pthreads -> do
185193 r <- waitEither mainAct pthreads
186194 -- TODO: is this weird? I can't see how else to gracefully use the ending barrier
187195 -- as a guard to cancel the other async
0 commit comments