@@ -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 ->  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 ->  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
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. AStringNothing  msg pct
173-       end t =  sendProgressReport t (WorkDoneProgressEnd  L. AStringNothing )
156+         sendProgressReport t $  WorkDoneProgressReport  L. AStringNothing  msg pct
157+     end t =  sendProgressReport t (WorkDoneProgressEnd  L. AStringNothing )
158+ 
159+     begin t =  do 
160+       sendProgressReport t $  WorkDoneProgressBegin  L. AStringNothing  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