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