{-# LANGUAGE LambdaCase #-}
module Language.LSP.Server.Progress (
withProgress,
withIndefiniteProgress,
ProgressAmount (..),
ProgressCancellable (..),
ProgressCancelledException,
) where
import Control.Concurrent.Async
import Control.Concurrent.Extra as C
import Control.Concurrent.STM
import Control.Exception qualified as E
import Control.Lens hiding (Empty)
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.IO.Unlift
import Data.Aeson qualified as J
import Data.Foldable
import Data.Map.Strict qualified as Map
import Data.Maybe
import Data.Text (Text)
import Language.LSP.Protocol.Lens qualified as L
import Language.LSP.Protocol.Message
import Language.LSP.Protocol.Types
import Language.LSP.Protocol.Types qualified as L
import Language.LSP.Server.Core
import UnliftIO qualified as U
import UnliftIO.Exception qualified as UE
data ProgressAmount = ProgressAmount (Maybe UInt) (Maybe Text)
data ProgressCancelledException = ProgressCancelledException
deriving (Int -> ProgressCancelledException -> ShowS
[ProgressCancelledException] -> ShowS
ProgressCancelledException -> String
(Int -> ProgressCancelledException -> ShowS)
-> (ProgressCancelledException -> String)
-> ([ProgressCancelledException] -> ShowS)
-> Show ProgressCancelledException
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ProgressCancelledException -> ShowS
showsPrec :: Int -> ProgressCancelledException -> ShowS
$cshow :: ProgressCancelledException -> String
show :: ProgressCancelledException -> String
$cshowList :: [ProgressCancelledException] -> ShowS
showList :: [ProgressCancelledException] -> ShowS
Show)
instance E.Exception ProgressCancelledException
data ProgressCancellable = Cancellable | NotCancellable
getNewProgressId :: MonadLsp config m => m ProgressToken
getNewProgressId :: forall config (m :: * -> *). MonadLsp config m => m ProgressToken
getNewProgressId = do
(LanguageContextState config -> TVar Int32)
-> (Int32 -> (ProgressToken, Int32)) -> m ProgressToken
forall config (m :: * -> *) s a.
MonadLsp config m =>
(LanguageContextState config -> TVar s) -> (s -> (a, s)) -> m a
stateState (ProgressData -> TVar Int32
progressNextId (ProgressData -> TVar Int32)
-> (LanguageContextState config -> ProgressData)
-> LanguageContextState config
-> TVar Int32
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LanguageContextState config -> ProgressData
forall config. LanguageContextState config -> ProgressData
resProgressData) ((Int32 -> (ProgressToken, Int32)) -> m ProgressToken)
-> (Int32 -> (ProgressToken, Int32)) -> m ProgressToken
forall a b. (a -> b) -> a -> b
$ \Int32
cur ->
let !next :: Int32
next = Int32
cur Int32 -> Int32 -> Int32
forall a. Num a => a -> a -> a
+ Int32
1
in ((Int32 |? Text) -> ProgressToken
L.ProgressToken ((Int32 |? Text) -> ProgressToken)
-> (Int32 |? Text) -> ProgressToken
forall a b. (a -> b) -> a -> b
$ Int32 -> Int32 |? Text
forall a b. a -> a |? b
L.InL Int32
cur, Int32
next)
{-# INLINE getNewProgressId #-}
withProgressBase ::
forall c m a.
MonadLsp c m =>
Bool ->
Text ->
Maybe ProgressToken ->
ProgressCancellable ->
((ProgressAmount -> m ()) -> m a) ->
m a
withProgressBase :: forall c (m :: * -> *) a.
MonadLsp c m =>
Bool
-> Text
-> Maybe ProgressToken
-> ProgressCancellable
-> ((ProgressAmount -> m ()) -> m a)
-> m a
withProgressBase Bool
indefinite Text
title Maybe ProgressToken
clientToken ProgressCancellable
cancellable (ProgressAmount -> m ()) -> m a
f = do
let initialProgress :: ProgressAmount
initialProgress = Maybe UInt -> Maybe Text -> ProgressAmount
ProgressAmount (if Bool
indefinite then Maybe UInt
forall a. Maybe a
Nothing else UInt -> Maybe UInt
forall a. a -> Maybe a
Just UInt
0) Maybe Text
forall a. Maybe a
Nothing
LanguageContextEnv{resProgressStartDelay = startDelay, resProgressUpdateDelay = updateDelay} <- m (LanguageContextEnv c)
forall config (m :: * -> *).
MonadLsp config m =>
m (LanguageContextEnv config)
getLspEnv
tokenVar <- liftIO newEmptyTMVarIO
reportVar <- liftIO $ newTMVarIO initialProgress
endBarrier <- liftIO newEmptyMVar
let
updater :: ProgressAmount -> m ()
updater ProgressAmount
pa = IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
_ <- TMVar ProgressAmount -> STM (Maybe ProgressAmount)
forall a. TMVar a -> STM (Maybe a)
tryTakeTMVar TMVar ProgressAmount
reportVar
putTMVar reportVar pa
progressEnded :: IO ()
progressEnded = MVar () -> IO ()
forall a. MVar a -> IO a
readMVar MVar ()
endBarrier
endProgress :: IO ()
endProgress = IO Bool -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO Bool -> IO ()) -> IO Bool -> IO ()
forall a b. (a -> b) -> a -> b
$ MVar () -> () -> IO Bool
forall a. MVar a -> a -> IO Bool
tryPutMVar MVar ()
endBarrier ()
registerToken :: ProgressToken -> m ()
registerToken ProgressToken
t = do
handlers <- m (TVar (Map ProgressToken (IO ())))
getProgressCancellationHandlers
liftIO $ atomically $ do
putTMVar tokenVar t
modifyTVar handlers (Map.insert t endProgress)
unregisterToken :: m ()
unregisterToken = do
handlers <- m (TVar (Map ProgressToken (IO ())))
getProgressCancellationHandlers
liftIO $ atomically $ do
mt <- tryReadTMVar tokenVar
for_ mt $ \ProgressToken
t -> TVar (Map ProgressToken (IO ()))
-> (Map ProgressToken (IO ()) -> Map ProgressToken (IO ()))
-> STM ()
forall a. TVar a -> (a -> a) -> STM ()
modifyTVar TVar (Map ProgressToken (IO ()))
handlers (ProgressToken
-> Map ProgressToken (IO ()) -> Map ProgressToken (IO ())
forall k a. Ord k => k -> Map k a -> Map k a
Map.delete ProgressToken
t)
createToken :: m ()
createToken = do
IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ Int -> IO ()
threadDelay Int
startDelay
case Maybe ProgressToken
clientToken of
Just ProgressToken
t -> ProgressToken -> m ()
registerToken ProgressToken
t
Maybe ProgressToken
Nothing -> do
t <- m ProgressToken
forall config (m :: * -> *). MonadLsp config m => m ProgressToken
getNewProgressId
clientCaps <- getClientCapabilities
when (clientSupportsServerInitiatedProgress clientCaps)
$ void
$
sendRequest
SMethod_WindowWorkDoneProgressCreate
(WorkDoneProgressCreateParams t)
$ \case
Right MessageResult
@'ServerToClient @'Request 'Method_WindowWorkDoneProgressCreate
_ -> ProgressToken -> m ()
registerToken ProgressToken
t
Left TResponseError
@'ServerToClient 'Method_WindowWorkDoneProgressCreate
_err -> () -> m ()
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
sendReports :: m ()
sendReports = do
t <- IO ProgressToken -> m ProgressToken
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO ProgressToken -> m ProgressToken)
-> IO ProgressToken -> m ProgressToken
forall a b. (a -> b) -> a -> b
$ STM ProgressToken -> IO ProgressToken
forall a. STM a -> IO a
atomically (STM ProgressToken -> IO ProgressToken)
-> STM ProgressToken -> IO ProgressToken
forall a b. (a -> b) -> a -> b
$ TMVar ProgressToken -> STM ProgressToken
forall a. TMVar a -> STM a
readTMVar TMVar ProgressToken
tokenVar
begin t
update t `UE.finally` end t
where
cancellable' :: Maybe Bool
cancellable' = case ProgressCancellable
cancellable of
ProgressCancellable
Cancellable -> Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
True
ProgressCancellable
NotCancellable -> Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
False
begin :: ProgressToken -> m ()
begin ProgressToken
t = do
(ProgressAmount pct msg) <- IO ProgressAmount -> m ProgressAmount
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO ProgressAmount -> m ProgressAmount)
-> IO ProgressAmount -> m ProgressAmount
forall a b. (a -> b) -> a -> b
$ STM ProgressAmount -> IO ProgressAmount
forall a. STM a -> IO a
atomically (STM ProgressAmount -> IO ProgressAmount)
-> STM ProgressAmount -> IO ProgressAmount
forall a b. (a -> b) -> a -> b
$ TMVar ProgressAmount -> STM ProgressAmount
forall a. TMVar a -> STM a
takeTMVar TMVar ProgressAmount
reportVar
sendProgressReport t $ WorkDoneProgressBegin L.AString title cancellable' msg pct
update :: ProgressToken -> m b
update ProgressToken
t =
m () -> m b
forall (f :: * -> *) a b. Applicative f => f a -> f b
forever (m () -> m b) -> m () -> m b
forall a b. (a -> b) -> a -> b
$ do
IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> IO () -> m ()
forall a b. (a -> b) -> a -> b
$ Int -> IO ()
threadDelay Int
updateDelay
(ProgressAmount pct msg) <- IO ProgressAmount -> m ProgressAmount
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO ProgressAmount -> m ProgressAmount)
-> IO ProgressAmount -> m ProgressAmount
forall a b. (a -> b) -> a -> b
$ STM ProgressAmount -> IO ProgressAmount
forall a. STM a -> IO a
atomically (STM ProgressAmount -> IO ProgressAmount)
-> STM ProgressAmount -> IO ProgressAmount
forall a b. (a -> b) -> a -> b
$ TMVar ProgressAmount -> STM ProgressAmount
forall a. TMVar a -> STM a
takeTMVar TMVar ProgressAmount
reportVar
sendProgressReport t $ WorkDoneProgressReport L.AString Nothing msg pct
end :: ProgressToken -> m ()
end ProgressToken
t = ProgressToken -> WorkDoneProgressEnd -> m ()
forall r. ToJSON r => ProgressToken -> r -> m ()
sendProgressReport ProgressToken
t (AString "end" -> Maybe Text -> WorkDoneProgressEnd
WorkDoneProgressEnd AString "end"
forall (s :: Symbol). KnownSymbol s => AString s
L.AString Maybe Text
forall a. Maybe a
Nothing)
progressThreads :: m ()
progressThreads =
((m ()
createToken m () -> m () -> m ()
forall a b. m a -> m b -> m b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> m ()
sendReports) m () -> m () -> m ()
forall (m :: * -> *) a b. MonadUnliftIO m => m a -> m b -> m a
`UE.finally` m ()
unregisterToken) m () -> m () -> m ()
forall (m :: * -> *) a b. MonadUnliftIO m => m a -> m b -> m ()
`U.race_` IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
progressEnded
withRunInIO $ \forall a. m a -> IO a
runInBase -> do
IO a -> (Async a -> IO a) -> IO a
forall a b. IO a -> (Async a -> IO b) -> IO b
withAsync (m a -> IO a
forall a. m a -> IO a
runInBase (m a -> IO a) -> m a -> IO a
forall a b. (a -> b) -> a -> b
$ (ProgressAmount -> m ()) -> m a
f ProgressAmount -> m ()
updater) ((Async a -> IO a) -> IO a) -> (Async a -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ \Async a
mainAct ->
IO () -> (Async () -> IO a) -> IO a
forall a b. IO a -> (Async a -> IO b) -> IO b
withAsync (m () -> IO ()
forall a. m a -> IO a
runInBase m ()
progressThreads) ((Async () -> IO a) -> IO a) -> (Async () -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ \Async ()
pthreads -> do
r <- Async a -> Async () -> IO (Either a ())
forall a b. Async a -> Async b -> IO (Either a b)
waitEither Async a
mainAct Async ()
pthreads
case r of
Left a
a -> a -> IO a
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
a
Right ()
_ -> Async a -> ProgressCancelledException -> IO ()
forall e a. Exception e => Async a -> e -> IO ()
cancelWith Async a
mainAct ProgressCancelledException
ProgressCancelledException IO () -> IO a -> IO a
forall a b. IO a -> IO b -> IO b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Async a -> IO a
forall a. Async a -> IO a
wait Async a
mainAct
where
sendProgressReport :: (J.ToJSON r) => ProgressToken -> r -> m ()
sendProgressReport :: forall r. ToJSON r => ProgressToken -> r -> m ()
sendProgressReport ProgressToken
token r
report = SServerMethod @'Notification ('Method_Progress @'ServerToClient)
-> MessageParams
@'ServerToClient @'Notification ('Method_Progress @'ServerToClient)
-> m ()
forall (m :: Method 'ServerToClient 'Notification) (f :: * -> *)
config.
MonadLsp config f =>
SServerMethod @'Notification m
-> MessageParams @'ServerToClient @'Notification m -> f ()
sendNotification SServerMethod @'Notification ('Method_Progress @'ServerToClient)
forall {f :: MessageDirection}.
SMethod @f @'Notification ('Method_Progress @f)
SMethod_Progress (MessageParams
@'ServerToClient @'Notification ('Method_Progress @'ServerToClient)
-> m ())
-> MessageParams
@'ServerToClient @'Notification ('Method_Progress @'ServerToClient)
-> m ()
forall a b. (a -> b) -> a -> b
$ ProgressToken -> Value -> ProgressParams
ProgressParams ProgressToken
token (Value -> ProgressParams) -> Value -> ProgressParams
forall a b. (a -> b) -> a -> b
$ r -> Value
forall a. ToJSON a => a -> Value
J.toJSON r
report
getProgressCancellationHandlers :: m (TVar (Map.Map ProgressToken (IO ())))
getProgressCancellationHandlers :: m (TVar (Map ProgressToken (IO ())))
getProgressCancellationHandlers = (LanguageContextState c -> TVar (Map ProgressToken (IO ())))
-> m (TVar (Map ProgressToken (IO ())))
forall config (m :: * -> *) a.
MonadLsp config m =>
(LanguageContextState config -> TVar a) -> m (TVar a)
getStateVar (ProgressData -> TVar (Map ProgressToken (IO ()))
progressCancel (ProgressData -> TVar (Map ProgressToken (IO ())))
-> (LanguageContextState c -> ProgressData)
-> LanguageContextState c
-> TVar (Map ProgressToken (IO ()))
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LanguageContextState c -> ProgressData
forall config. LanguageContextState config -> ProgressData
resProgressData)
clientSupportsServerInitiatedProgress :: L.ClientCapabilities -> Bool
clientSupportsServerInitiatedProgress :: ClientCapabilities -> Bool
clientSupportsServerInitiatedProgress ClientCapabilities
caps = Bool -> Maybe Bool -> Bool
forall a. a -> Maybe a -> a
fromMaybe Bool
False (Maybe Bool -> Bool) -> Maybe Bool -> Bool
forall a b. (a -> b) -> a -> b
$ ClientCapabilities
caps ClientCapabilities
-> Getting (First Bool) ClientCapabilities Bool -> Maybe Bool
forall s a. s -> Getting (First a) s a -> Maybe a
^? (Maybe WindowClientCapabilities
-> Const @(*) (First Bool) (Maybe WindowClientCapabilities))
-> ClientCapabilities -> Const @(*) (First Bool) ClientCapabilities
forall s a. HasWindow s a => Lens' s a
Lens' ClientCapabilities (Maybe WindowClientCapabilities)
L.window ((Maybe WindowClientCapabilities
-> Const @(*) (First Bool) (Maybe WindowClientCapabilities))
-> ClientCapabilities
-> Const @(*) (First Bool) ClientCapabilities)
-> ((Bool -> Const @(*) (First Bool) Bool)
-> Maybe WindowClientCapabilities
-> Const @(*) (First Bool) (Maybe WindowClientCapabilities))
-> Getting (First Bool) ClientCapabilities Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (WindowClientCapabilities
-> Const @(*) (First Bool) WindowClientCapabilities)
-> Maybe WindowClientCapabilities
-> Const @(*) (First Bool) (Maybe WindowClientCapabilities)
forall a b (p :: * -> * -> *) (f :: * -> *).
(Choice p, Applicative f) =>
p a (f b) -> p (Maybe a) (f (Maybe b))
_Just ((WindowClientCapabilities
-> Const @(*) (First Bool) WindowClientCapabilities)
-> Maybe WindowClientCapabilities
-> Const @(*) (First Bool) (Maybe WindowClientCapabilities))
-> ((Bool -> Const @(*) (First Bool) Bool)
-> WindowClientCapabilities
-> Const @(*) (First Bool) WindowClientCapabilities)
-> (Bool -> Const @(*) (First Bool) Bool)
-> Maybe WindowClientCapabilities
-> Const @(*) (First Bool) (Maybe WindowClientCapabilities)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe Bool -> Const @(*) (First Bool) (Maybe Bool))
-> WindowClientCapabilities
-> Const @(*) (First Bool) WindowClientCapabilities
forall s a. HasWorkDoneProgress s a => Lens' s a
Lens' WindowClientCapabilities (Maybe Bool)
L.workDoneProgress ((Maybe Bool -> Const @(*) (First Bool) (Maybe Bool))
-> WindowClientCapabilities
-> Const @(*) (First Bool) WindowClientCapabilities)
-> ((Bool -> Const @(*) (First Bool) Bool)
-> Maybe Bool -> Const @(*) (First Bool) (Maybe Bool))
-> (Bool -> Const @(*) (First Bool) Bool)
-> WindowClientCapabilities
-> Const @(*) (First Bool) WindowClientCapabilities
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Bool -> Const @(*) (First Bool) Bool)
-> Maybe Bool -> Const @(*) (First Bool) (Maybe Bool)
forall a b (p :: * -> * -> *) (f :: * -> *).
(Choice p, Applicative f) =>
p a (f b) -> p (Maybe a) (f (Maybe b))
_Just
{-# INLINE clientSupportsServerInitiatedProgress #-}
withProgress ::
MonadLsp c m =>
Text ->
Maybe ProgressToken ->
ProgressCancellable ->
((ProgressAmount -> m ()) -> m a) ->
m a
withProgress :: forall c (m :: * -> *) a.
MonadLsp c m =>
Text
-> Maybe ProgressToken
-> ProgressCancellable
-> ((ProgressAmount -> m ()) -> m a)
-> m a
withProgress Text
title Maybe ProgressToken
clientToken ProgressCancellable
cancellable (ProgressAmount -> m ()) -> m a
f = Bool
-> Text
-> Maybe ProgressToken
-> ProgressCancellable
-> ((ProgressAmount -> m ()) -> m a)
-> m a
forall c (m :: * -> *) a.
MonadLsp c m =>
Bool
-> Text
-> Maybe ProgressToken
-> ProgressCancellable
-> ((ProgressAmount -> m ()) -> m a)
-> m a
withProgressBase Bool
False Text
title Maybe ProgressToken
clientToken ProgressCancellable
cancellable (ProgressAmount -> m ()) -> m a
f
withIndefiniteProgress ::
MonadLsp c m =>
Text ->
Maybe ProgressToken ->
ProgressCancellable ->
((Text -> m ()) -> m a) ->
m a
withIndefiniteProgress :: forall c (m :: * -> *) a.
MonadLsp c m =>
Text
-> Maybe ProgressToken
-> ProgressCancellable
-> ((Text -> m ()) -> m a)
-> m a
withIndefiniteProgress Text
title Maybe ProgressToken
clientToken ProgressCancellable
cancellable (Text -> m ()) -> m a
f =
Bool
-> Text
-> Maybe ProgressToken
-> ProgressCancellable
-> ((ProgressAmount -> m ()) -> m a)
-> m a
forall c (m :: * -> *) a.
MonadLsp c m =>
Bool
-> Text
-> Maybe ProgressToken
-> ProgressCancellable
-> ((ProgressAmount -> m ()) -> m a)
-> m a
withProgressBase Bool
True Text
title Maybe ProgressToken
clientToken ProgressCancellable
cancellable (\ProgressAmount -> m ()
update -> (Text -> m ()) -> m a
f (\Text
msg -> ProgressAmount -> m ()
update (Maybe UInt -> Maybe Text -> ProgressAmount
ProgressAmount Maybe UInt
forall a. Maybe a
Nothing (Text -> Maybe Text
forall a. a -> Maybe a
Just Text
msg))))