{- |
This module provides utilities to run an LSP `Session` in `IO`.
-}
module Language.LSP.Client where

import Control.Concurrent.STM
import Control.Monad (forever)
import Control.Monad.IO.Class (MonadIO (liftIO))
import Control.Monad.Reader (asks, runReaderT)
import Data.ByteString.Lazy qualified as LazyByteString
import Data.Dependent.Map qualified as DMap
import Data.Either (fromLeft)
import Data.Generics.Labels ()
import Language.LSP.Client.Decoding
import Language.LSP.Client.Encoding (encode)
import Language.LSP.Client.Session
import Language.LSP.Protocol.Message qualified as LSP
import Language.LSP.VFS (emptyVFS)
import System.IO (Handle)
import UnliftIO (concurrently_, race)
import Prelude

{- | Starts a new session, using the specified handles to communicate with the
server.
-}
runSessionWithHandles
    :: Handle
    -- ^ The input handle: messages sent from the server to the client will be read from here
    -> Handle
    -- ^ The output handle: messages sent by the client will be written here
    -> Session a
    -- ^ Session actions
    -> IO a
runSessionWithHandles :: forall a. Handle -> Handle -> Session a -> IO a
runSessionWithHandles Handle
input Handle
output Session a
action = do
    SessionState
initialState <- VFS -> IO SessionState
defaultSessionState VFS
emptyVFS
    (Session a -> SessionState -> IO a)
-> SessionState -> Session a -> IO a
forall a b c. (a -> b -> c) -> b -> a -> c
flip Session a -> SessionState -> IO a
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT SessionState
initialState (Session a -> IO a) -> Session a -> IO a
forall a b. (a -> b) -> a -> b
$ do
        Either a ()
actionResult <- Session a
-> ReaderT SessionState IO ()
-> ReaderT SessionState IO (Either a ())
forall (m :: * -> *) a b.
MonadUnliftIO m =>
m a -> m b -> m (Either a b)
race Session a
action (ReaderT SessionState IO ()
 -> ReaderT SessionState IO (Either a ()))
-> ReaderT SessionState IO ()
-> ReaderT SessionState IO (Either a ())
forall a b. (a -> b) -> a -> b
$ do
            let send :: ReaderT SessionState IO ()
send = do
                    FromClientMessage
message <- (SessionState -> TQueue FromClientMessage)
-> ReaderT SessionState IO (TQueue FromClientMessage)
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks SessionState -> TQueue FromClientMessage
outgoing ReaderT SessionState IO (TQueue FromClientMessage)
-> (TQueue FromClientMessage
    -> ReaderT SessionState IO FromClientMessage)
-> ReaderT SessionState IO FromClientMessage
forall a b.
ReaderT SessionState IO a
-> (a -> ReaderT SessionState IO b) -> ReaderT SessionState IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= IO FromClientMessage -> ReaderT SessionState IO FromClientMessage
forall a. IO a -> ReaderT SessionState IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO FromClientMessage -> ReaderT SessionState IO FromClientMessage)
-> (TQueue FromClientMessage -> IO FromClientMessage)
-> TQueue FromClientMessage
-> ReaderT SessionState IO FromClientMessage
forall b c a. (b -> c) -> (a -> b) -> a -> c
. STM FromClientMessage -> IO FromClientMessage
forall a. STM a -> IO a
atomically (STM FromClientMessage -> IO FromClientMessage)
-> (TQueue FromClientMessage -> STM FromClientMessage)
-> TQueue FromClientMessage
-> IO FromClientMessage
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TQueue FromClientMessage -> STM FromClientMessage
forall a. TQueue a -> STM a
readTQueue
                    IO () -> ReaderT SessionState IO ()
forall a. IO a -> ReaderT SessionState IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> ReaderT SessionState IO ())
-> IO () -> ReaderT SessionState IO ()
forall a b. (a -> b) -> a -> b
$ Handle -> ByteString -> IO ()
LazyByteString.hPut Handle
output (ByteString -> IO ()) -> ByteString -> IO ()
forall a b. (a -> b) -> a -> b
$ FromClientMessage -> ByteString
forall a. ToJSON a => a -> ByteString
encode FromClientMessage
message
            let receive :: ReaderT SessionState IO ()
receive = do
                    ByteString
serverBytes <- IO ByteString -> ReaderT SessionState IO ByteString
forall a. IO a -> ReaderT SessionState IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO ByteString -> ReaderT SessionState IO ByteString)
-> IO ByteString -> ReaderT SessionState IO ByteString
forall a b. (a -> b) -> a -> b
$ Handle -> IO ByteString
getNextMessage Handle
input
                    (FromServerMessage
serverMessage, IO ()
requestCallback) <-
                        (SessionState -> TVar RequestMap)
-> ReaderT SessionState IO (TVar RequestMap)
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks SessionState -> TVar RequestMap
pendingRequests
                            ReaderT SessionState IO (TVar RequestMap)
-> (TVar RequestMap
    -> ReaderT SessionState IO (FromServerMessage, IO ()))
-> ReaderT SessionState IO (FromServerMessage, IO ())
forall a b.
ReaderT SessionState IO a
-> (a -> ReaderT SessionState IO b) -> ReaderT SessionState IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= IO (FromServerMessage, IO ())
-> ReaderT SessionState IO (FromServerMessage, IO ())
forall a. IO a -> ReaderT SessionState IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO
                                (IO (FromServerMessage, IO ())
 -> ReaderT SessionState IO (FromServerMessage, IO ()))
-> (TVar RequestMap -> IO (FromServerMessage, IO ()))
-> TVar RequestMap
-> ReaderT SessionState IO (FromServerMessage, IO ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. STM (FromServerMessage, IO ()) -> IO (FromServerMessage, IO ())
forall a. STM a -> IO a
atomically
                                (STM (FromServerMessage, IO ()) -> IO (FromServerMessage, IO ()))
-> (TVar RequestMap -> STM (FromServerMessage, IO ()))
-> TVar RequestMap
-> IO (FromServerMessage, IO ())
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (TVar RequestMap
 -> (RequestMap -> ((FromServerMessage, IO ()), RequestMap))
 -> STM (FromServerMessage, IO ()))
-> (RequestMap -> ((FromServerMessage, IO ()), RequestMap))
-> TVar RequestMap
-> STM (FromServerMessage, IO ())
forall a b c. (a -> b -> c) -> b -> a -> c
flip TVar RequestMap
-> (RequestMap -> ((FromServerMessage, IO ()), RequestMap))
-> STM (FromServerMessage, IO ())
forall s a. TVar s -> (s -> (a, s)) -> STM a
stateTVar (ByteString
-> RequestMap -> ((FromServerMessage, IO ()), RequestMap)
decodeFromServerMsg ByteString
serverBytes)
                    FromServerMessage -> ReaderT SessionState IO ()
handleServerMessage FromServerMessage
serverMessage
                    IO () -> ReaderT SessionState IO ()
forall a. IO a -> ReaderT SessionState IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO ()
requestCallback
                    case FromServerMessage
serverMessage of
                        LSP.FromServerMess SMethod m
smethod TMessage m
msg -> case SMethod m -> ServerNotOrReq m
forall {t :: MessageKind} (m :: Method 'ServerToClient t).
SServerMethod m -> ServerNotOrReq m
LSP.splitServerMethod SMethod m
smethod of
                            ServerNotOrReq m
LSP.IsServerNot -> do
                                NotificationMap
handlers :: NotificationMap <- (SessionState -> TVar NotificationMap)
-> ReaderT SessionState IO (TVar NotificationMap)
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks SessionState -> TVar NotificationMap
notificationHandlers ReaderT SessionState IO (TVar NotificationMap)
-> (TVar NotificationMap
    -> ReaderT SessionState IO NotificationMap)
-> ReaderT SessionState IO NotificationMap
forall a b.
ReaderT SessionState IO a
-> (a -> ReaderT SessionState IO b) -> ReaderT SessionState IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= IO NotificationMap -> ReaderT SessionState IO NotificationMap
forall a. IO a -> ReaderT SessionState IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO NotificationMap -> ReaderT SessionState IO NotificationMap)
-> (TVar NotificationMap -> IO NotificationMap)
-> TVar NotificationMap
-> ReaderT SessionState IO NotificationMap
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TVar NotificationMap -> IO NotificationMap
forall a. TVar a -> IO a
readTVarIO
                                let NotificationCallback TNotificationMessage m -> IO ()
cb = NotificationCallback m
-> SMethod m -> NotificationMap -> NotificationCallback m
forall {k1} (k2 :: k1 -> *) (f :: k1 -> *) (v :: k1).
GCompare k2 =>
f v -> k2 v -> DMap k2 f -> f v
DMap.findWithDefault ((TNotificationMessage m -> IO ()) -> NotificationCallback m
forall (m :: Method 'ServerToClient 'Notification).
(TNotificationMessage m -> IO ()) -> NotificationCallback m
NotificationCallback (IO () -> TNotificationMessage m -> IO ()
forall a b. a -> b -> a
const (IO () -> TNotificationMessage m -> IO ())
-> IO () -> TNotificationMessage m -> IO ()
forall a b. (a -> b) -> a -> b
$ () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())) SMethod m
SMethod m
smethod NotificationMap
handlers
                                IO () -> ReaderT SessionState IO ()
forall a. IO a -> ReaderT SessionState IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> ReaderT SessionState IO ())
-> IO () -> ReaderT SessionState IO ()
forall a b. (a -> b) -> a -> b
$ TNotificationMessage m -> IO ()
cb TMessage m
TNotificationMessage m
msg
                            ServerNotOrReq m
_ -> () -> ReaderT SessionState IO ()
forall a. a -> ReaderT SessionState IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
                        FromServerMessage
_ -> () -> ReaderT SessionState IO ()
forall a. a -> ReaderT SessionState IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
            ReaderT SessionState IO Any
-> ReaderT SessionState IO Any -> ReaderT SessionState IO ()
forall (m :: * -> *) a b. MonadUnliftIO m => m a -> m b -> m ()
concurrently_ (ReaderT SessionState IO () -> ReaderT SessionState IO Any
forall (f :: * -> *) a b. Applicative f => f a -> f b
forever ReaderT SessionState IO ()
send) (ReaderT SessionState IO () -> ReaderT SessionState IO Any
forall (f :: * -> *) a b. Applicative f => f a -> f b
forever ReaderT SessionState IO ()
receive)
        pure $ a -> Either a () -> a
forall a b. a -> Either a b -> a
fromLeft ([Char] -> a
forall a. HasCallStack => [Char] -> a
error [Char]
"runSessionWithHandle: send/receive thread should not exit") Either a ()
actionResult