1
- {-# LANGUAGE LambdaCase #-}
2
1
{-# LANGUAGE OverloadedStrings #-}
3
2
{-# LANGUAGE RankNTypes #-}
4
3
{-# LANGUAGE ScopedTypeVariables #-}
@@ -14,58 +13,39 @@ module Language.LSP.Server.Control (
14
13
LspServerLog (.. ),
15
14
) where
16
15
17
- import Colog.Core (LogAction (.. ), Severity (.. ), WithSeverity (.. ), (<&) )
16
+ import Colog.Core (LogAction (.. ), Severity (.. ), WithSeverity (.. ), cmap , (<&) )
18
17
import Colog.Core qualified as L
19
18
import Control.Applicative ((<|>) )
20
- import Control.Concurrent
19
+ import Control.Concurrent.Async qualified as Async
21
20
import Control.Concurrent.STM.TChan
22
21
import Control.Monad
23
22
import Control.Monad.IO.Class
24
23
import Control.Monad.STM
25
24
import Data.Aeson qualified as J
26
- import Data.Attoparsec.ByteString qualified as Attoparsec
27
- import Data.Attoparsec.ByteString.Char8
28
25
import Data.ByteString qualified as BS
29
26
import Data.ByteString.Builder.Extra (defaultChunkSize )
30
- import Data.ByteString.Lazy qualified as BSL
31
- import Data.List
32
27
import Data.Text qualified as T
33
- import Data.Text.Lazy qualified as TL
34
- import Data.Text.Lazy.Encoding qualified as TL
35
28
import Data.Text.Prettyprint.Doc
36
29
import Language.LSP.Logging (defaultClientLogger )
37
30
import Language.LSP.Protocol.Message
38
31
import Language.LSP.Server.Core
32
+ import Language.LSP.Server.IO qualified as IO
39
33
import Language.LSP.Server.Processing qualified as Processing
40
34
import Language.LSP.VFS
41
35
import System.IO
42
36
43
37
data LspServerLog
44
38
= LspProcessingLog Processing. LspProcessingLog
45
- | DecodeInitializeError String
46
- | HeaderParseFail [String ] String
47
- | EOF
39
+ | LspIoLog IO. LspIoLog
48
40
| Starting
49
- | ParsedMsg T. Text
50
- | SendMsg TL. Text
41
+ | Stopping
51
42
deriving (Show )
52
43
53
44
instance Pretty LspServerLog where
54
45
pretty (LspProcessingLog l) = pretty l
55
- pretty (DecodeInitializeError err) =
56
- vsep
57
- [ " Got error while decoding initialize:"
58
- , pretty err
59
- ]
60
- pretty (HeaderParseFail ctxs err) =
61
- vsep
62
- [ " Failed to parse message header:"
63
- , pretty (intercalate " > " ctxs) <> " : " <+> pretty err
64
- ]
65
- pretty EOF = " Got EOF"
46
+ pretty (LspIoLog l) = pretty l
66
47
pretty Starting = " Starting server"
67
- pretty (ParsedMsg msg) = " ---> " <> pretty msg
68
- pretty (SendMsg msg) = " <--2-- " <> pretty msg
48
+ pretty Stopping = " Stopping server"
69
49
70
50
-- ---------------------------------------------------------------------
71
51
@@ -115,7 +95,7 @@ runServerWithHandles ioLogger logger hin hout serverDefinition = do
115
95
clientIn = BS. hGetSome hin defaultChunkSize
116
96
117
97
clientOut out = do
118
- BSL . hPut hout out
98
+ BS . hPut hout out
119
99
hFlush hout
120
100
121
101
runServerWith ioLogger logger clientIn clientOut serverDefinition
@@ -131,129 +111,32 @@ runServerWith ::
131
111
-- | Client input.
132
112
IO BS. ByteString ->
133
113
-- | Function to provide output to.
134
- (BSL . ByteString -> IO () ) ->
114
+ (BS . ByteString -> IO () ) ->
135
115
ServerDefinition config ->
136
116
IO Int -- exit code
137
117
runServerWith ioLogger logger clientIn clientOut serverDefinition = do
138
118
ioLogger <& Starting `WithSeverity ` Info
139
119
140
- cout <- atomically newTChan :: IO ( TChan J. Value )
141
- _rhpid <- forkIO $ sendServer ioLogger cout clientOut
120
+ cout <- atomically newTChan
121
+ cin <- atomically newTChan
142
122
143
- let sendMsg msg = atomically $ writeTChan cout $ J. toJSON msg
123
+ let serverOut = IO. serverOut (cmap (fmap LspIoLog ) ioLogger) (atomically $ readTChan cout) clientOut
124
+ serverIn = IO. serverIn (cmap (fmap LspIoLog ) ioLogger) (atomically . writeTChan cin) clientIn
144
125
145
- initVFS $ \ vfs -> do
146
- ioLoop ioLogger logger clientIn serverDefinition vfs sendMsg
126
+ sendMsg msg = atomically $ writeTChan cout $ J. toJSON msg
127
+ recvMsg = atomically $ readTChan cin
147
128
148
- return 1
129
+ processingLoop = initVFS $ \ vfs ->
130
+ Processing. processingLoop
131
+ (cmap (fmap LspProcessingLog ) ioLogger)
132
+ (cmap (fmap LspProcessingLog ) logger)
133
+ vfs
134
+ serverDefinition
135
+ sendMsg
136
+ recvMsg
149
137
150
- -- ---------------------------------------------------------------------
151
-
152
- ioLoop ::
153
- forall config .
154
- LogAction IO (WithSeverity LspServerLog ) ->
155
- LogAction (LspM config ) (WithSeverity LspServerLog ) ->
156
- IO BS. ByteString ->
157
- ServerDefinition config ->
158
- VFS ->
159
- (FromServerMessage -> IO () ) ->
160
- IO ()
161
- ioLoop ioLogger logger clientIn serverDefinition vfs sendMsg = do
162
- minitialize <- parseOne ioLogger clientIn (parse parser " " )
163
- case minitialize of
164
- Nothing -> pure ()
165
- Just (msg, remainder) -> do
166
- case J. eitherDecode $ BSL. fromStrict msg of
167
- Left err -> ioLogger <& DecodeInitializeError err `WithSeverity ` Error
168
- Right initialize -> do
169
- mInitResp <- Processing. initializeRequestHandler pioLogger serverDefinition vfs sendMsg initialize
170
- case mInitResp of
171
- Nothing -> pure ()
172
- Just env -> runLspT env $ loop (parse parser remainder)
173
- where
174
- pioLogger = L. cmap (fmap LspProcessingLog ) ioLogger
175
- pLogger = L. cmap (fmap LspProcessingLog ) logger
176
-
177
- loop :: Result BS. ByteString -> LspM config ()
178
- loop = go
179
- where
180
- go r = do
181
- res <- parseOne logger clientIn r
182
- case res of
183
- Nothing -> pure ()
184
- Just (msg, remainder) -> do
185
- Processing. processMessage pLogger $ BSL. fromStrict msg
186
- go (parse parser remainder)
187
-
188
- parser = do
189
- try contentType <|> (return () )
190
- len <- contentLength
191
- try contentType <|> (return () )
192
- _ <- string _ONE_CRLF
193
- Attoparsec. take len
194
-
195
- contentLength = do
196
- _ <- string " Content-Length: "
197
- len <- decimal
198
- _ <- string _ONE_CRLF
199
- return len
200
-
201
- contentType = do
202
- _ <- string " Content-Type: "
203
- skipWhile (/= ' \r ' )
204
- _ <- string _ONE_CRLF
205
- return ()
206
-
207
- parseOne ::
208
- MonadIO m =>
209
- LogAction m (WithSeverity LspServerLog ) ->
210
- IO BS. ByteString ->
211
- Result BS. ByteString ->
212
- m (Maybe (BS. ByteString , BS. ByteString ))
213
- parseOne logger clientIn = go
214
- where
215
- go (Fail _ ctxs err) = do
216
- logger <& HeaderParseFail ctxs err `WithSeverity ` Error
217
- pure Nothing
218
- go (Partial c) = do
219
- bs <- liftIO clientIn
220
- if BS. null bs
221
- then do
222
- logger <& EOF `WithSeverity ` Error
223
- pure Nothing
224
- else go (c bs)
225
- go (Done remainder msg) = do
226
- -- TODO: figure out how to re-enable
227
- -- This can lead to infinite recursion in logging, see https://github.com/haskell/lsp/issues/447
228
- -- logger <& ParsedMsg (T.decodeUtf8 msg) `WithSeverity` Debug
229
- pure $ Just (msg, remainder)
230
-
231
- -- ---------------------------------------------------------------------
232
-
233
- -- | Simple server to make sure all output is serialised
234
- sendServer :: LogAction IO (WithSeverity LspServerLog ) -> TChan J. Value -> (BSL. ByteString -> IO () ) -> IO ()
235
- sendServer _logger msgChan clientOut = do
236
- forever $ do
237
- msg <- atomically $ readTChan msgChan
238
-
239
- -- We need to make sure we only send over the content of the message,
240
- -- and no other tags/wrapper stuff
241
- let str = J. encode msg
242
-
243
- let out =
244
- BSL. concat
245
- [ TL. encodeUtf8 $ TL. pack $ " Content-Length: " ++ show (BSL. length str)
246
- , BSL. fromStrict _TWO_CRLF
247
- , str
248
- ]
249
-
250
- clientOut out
251
-
252
- -- TODO: figure out how to re-enable
253
- -- This can lead to infinite recursion in logging, see https://github.com/haskell/lsp/issues/447
254
- -- logger <& SendMsg (TL.decodeUtf8 str) `WithSeverity` Debug
138
+ -- Bind all the threads together so that any of them terminating will terminate everything
139
+ serverOut `Async.race_` serverIn `Async.race_` processingLoop
255
140
256
- _ONE_CRLF :: BS. ByteString
257
- _ONE_CRLF = " \r\n "
258
- _TWO_CRLF :: BS. ByteString
259
- _TWO_CRLF = " \r\n\r\n "
141
+ ioLogger <& Stopping `WithSeverity ` Info
142
+ return 0
0 commit comments