11{-# LANGUAGE OverloadedStrings #-}
2+ {-# LANGUAGE TypeFamilies #-}
23
34module Language.LSP.Server.Control (
45 -- * Running
@@ -11,9 +12,9 @@ module Language.LSP.Server.Control (
1112import Colog.Core (LogAction (.. ), Severity (.. ), WithSeverity (.. ), (<&) )
1213import Colog.Core qualified as L
1314import Control.Applicative ((<|>) )
14- import Control.Concurrent
15+ import Control.Concurrent.Async ( withAsync )
1516import Control.Concurrent.STM.TChan
16- import Control.Monad
17+ import Control.Exception ( catchJust , throwIO )
1718import Control.Monad.IO.Class
1819import Control.Monad.STM
1920import Data.Aeson qualified as J
@@ -33,18 +34,22 @@ import Language.LSP.Server.Processing qualified as Processing
3334import Language.LSP.VFS
3435import Prettyprinter
3536import System.IO
37+ import System.IO.Error (isResourceVanishedError )
3638
3739data LspServerLog
3840 = LspProcessingLog Processing. LspProcessingLog
3941 | DecodeInitializeError String
4042 | HeaderParseFail [String ] String
4143 | EOF
44+ | BrokenPipeWhileSending TL. Text -- truncated outgoing message (including header)
4245 | Starting
46+ | ServerStopped
4347 | ParsedMsg T. Text
4448 | SendMsg TL. Text
4549 deriving (Show )
4650
4751instance Pretty LspServerLog where
52+ pretty ServerStopped = " Server stopped"
4853 pretty (LspProcessingLog l) = pretty l
4954 pretty (DecodeInitializeError err) =
5055 vsep
@@ -57,7 +62,12 @@ instance Pretty LspServerLog where
5762 , pretty (intercalate " > " ctxs) <> " : " <+> pretty err
5863 ]
5964 pretty EOF = " Got EOF"
60- pretty Starting = " Starting server"
65+ pretty (BrokenPipeWhileSending msg) =
66+ vsep
67+ [ " Broken pipe while sending (client likely closed output handle):"
68+ , indent 2 (pretty msg)
69+ ]
70+ pretty Starting = " Server starting"
6171 pretty (ParsedMsg msg) = " ---> " <> pretty msg
6272 pretty (SendMsg msg) = " <--2-- " <> pretty msg
6373
@@ -108,9 +118,15 @@ runServerWithHandles ioLogger logger hin hout serverDefinition = do
108118 let
109119 clientIn = BS. hGetSome hin defaultChunkSize
110120
111- clientOut out = do
112- BSL. hPut hout out
113- hFlush hout
121+ clientOut out =
122+ catchJust
123+ (\ e -> if isResourceVanishedError e then Just e else Nothing )
124+ (BSL. hPut hout out >> hFlush hout)
125+ ( \ e -> do
126+ let txt = TL. toStrict $ TL. take 400 $ TL. decodeUtf8 out -- limit size
127+ ioLogger <& BrokenPipeWhileSending (TL. fromStrict txt) `WithSeverity ` Error
128+ throwIO e
129+ )
114130
115131 runServerWith ioLogger logger clientIn clientOut serverDefinition
116132
@@ -130,15 +146,12 @@ runServerWith ::
130146 IO Int -- exit code
131147runServerWith ioLogger logger clientIn clientOut serverDefinition = do
132148 ioLogger <& Starting `WithSeverity ` Info
133-
134- cout <- atomically newTChan :: IO (TChan J. Value )
135- _rhpid <- forkIO $ sendServer ioLogger cout clientOut
136-
137- let sendMsg msg = atomically $ writeTChan cout $ J. toJSON msg
138-
139- ioLoop ioLogger logger clientIn serverDefinition emptyVFS sendMsg
140-
141- return 1
149+ cout <- atomically newTChan :: IO (TChan FromServerMessage )
150+ withAsync (sendServer ioLogger cout clientOut) $ \ _sendAsync -> do
151+ let sendMsg = atomically . writeTChan cout
152+ ioLoop ioLogger logger clientIn serverDefinition emptyVFS sendMsg
153+ ioLogger <& ServerStopped `WithSeverity ` Info
154+ return 0
142155
143156-- ---------------------------------------------------------------------
144157
@@ -224,9 +237,10 @@ parseOne logger clientIn = go
224237-- ---------------------------------------------------------------------
225238
226239-- | Simple server to make sure all output is serialised
227- sendServer :: LogAction IO (WithSeverity LspServerLog ) -> TChan J. Value -> (BSL. ByteString -> IO () ) -> IO ()
228- sendServer _logger msgChan clientOut = do
229- forever $ do
240+ sendServer :: LogAction IO (WithSeverity LspServerLog ) -> TChan FromServerMessage -> (BSL. ByteString -> IO () ) -> IO ()
241+ sendServer _logger msgChan clientOut = go
242+ where
243+ go = do
230244 msg <- atomically $ readTChan msgChan
231245
232246 -- We need to make sure we only send over the content of the message,
@@ -241,6 +255,10 @@ sendServer _logger msgChan clientOut = do
241255 ]
242256
243257 clientOut out
258+ -- close the client sender when we send out the shutdown request's response
259+ case msg of
260+ FromServerRsp SMethod_Shutdown _ -> pure ()
261+ _ -> go
244262
245263-- TODO: figure out how to re-enable
246264-- This can lead to infinite recursion in logging, see https://github.com/haskell/lsp/issues/447
0 commit comments