@@ -11,6 +11,7 @@ module Development.IDE.LSP.LanguageServer
11
11
, Log (.. )
12
12
, ThreadQueue
13
13
, runWithWorkerThreads
14
+ , Setup (.. )
14
15
) where
15
16
16
17
import Control.Concurrent.STM
@@ -81,6 +82,17 @@ instance Pretty Log where
81
82
LogLspServer msg -> pretty msg
82
83
LogServerShutdownMessage -> " Received shutdown message"
83
84
85
+ data Setup config m a
86
+ = MkSetup
87
+ { doInitialize :: LSP. LanguageContextEnv config -> TRequestMessage Method_Initialize -> IO (Either (TResponseError Method_Initialize ) (LSP. LanguageContextEnv config , a ))
88
+ -- ^ the callback invoked when the language server receives the 'Method_Initialize' request
89
+ , staticHandlers :: LSP. Handlers m
90
+ -- ^ the statically known handlers of the lsp server
91
+ , interpretHandler :: (LanguageContextEnv config , a ) -> m <~> IO
92
+ -- ^ how to interpret @m@ to 'IO' and how to lift 'IO' into @m@
93
+ , onExit :: [IO () ]
94
+ -- ^ a list of 'IO' actions that clean up resources and must be run when the server shuts down
95
+ }
84
96
85
97
runLanguageServer
86
98
:: forall config a m . (Show config )
@@ -90,18 +102,16 @@ runLanguageServer
90
102
-> Handle -- output
91
103
-> config
92
104
-> (config -> Value -> Either T. Text config )
93
- -> (config -> m config () )
94
- -> (MVar ()
95
- -> IO (LSP. LanguageContextEnv config -> TRequestMessage Method_Initialize -> IO (Either (TResponseError Method_Initialize ) (LSP. LanguageContextEnv config , a )),
96
- LSP. Handlers (m config ),
97
- (LanguageContextEnv config , a ) -> m config <~> IO ))
105
+ -> (config -> m () )
106
+ -> (MVar () -> IO (Setup config m a ))
98
107
-> IO ()
99
108
runLanguageServer recorder options inH outH defaultConfig parseConfig onConfigChange setup = do
100
109
-- This MVar becomes full when the server thread exits or we receive exit message from client.
101
110
-- LSP server will be canceled when it's full.
102
111
clientMsgVar <- newEmptyMVar
103
112
104
- (doInitialize, staticHandlers, interpretHandler) <- setup clientMsgVar
113
+ MkSetup
114
+ { doInitialize, staticHandlers, interpretHandler, onExit } <- setup clientMsgVar
105
115
106
116
let serverDefinition = LSP. ServerDefinition
107
117
{ LSP. parseConfig = parseConfig
@@ -115,28 +125,29 @@ runLanguageServer recorder options inH outH defaultConfig parseConfig onConfigCh
115
125
, LSP. options = modifyOptions options
116
126
}
117
127
118
- let lspCologAction :: MonadIO m2 => Colog. LogAction m2 (Colog. WithSeverity LspServerLog )
128
+ let lspCologAction :: forall io . MonadIO io => Colog. LogAction io (Colog. WithSeverity LspServerLog )
119
129
lspCologAction = toCologActionWithPrio (cmapWithPrio LogLspServer recorder)
120
130
121
- void $ untilMVar clientMsgVar $
122
- void $ LSP. runServerWithHandles
131
+ let runServer =
132
+ LSP. runServerWithHandles
123
133
lspCologAction
124
134
lspCologAction
125
135
inH
126
136
outH
127
137
serverDefinition
128
138
139
+ untilMVar clientMsgVar $
140
+ runServer `finally` sequence_ onExit
141
+
129
142
setupLSP ::
130
- forall config err .
143
+ forall config .
131
144
Recorder (WithPriority Log )
132
145
-> FilePath -- ^ root directory, see Note [Root Directory]
133
146
-> (FilePath -> IO FilePath ) -- ^ Map root paths to the location of the hiedb for the project
134
147
-> LSP. Handlers (ServerM config )
135
148
-> (LSP. LanguageContextEnv config -> FilePath -> WithHieDb -> ThreadQueue -> IO IdeState )
136
149
-> MVar ()
137
- -> IO (LSP. LanguageContextEnv config -> TRequestMessage Method_Initialize -> IO (Either err (LSP. LanguageContextEnv config , IdeState )),
138
- LSP. Handlers (ServerM config ),
139
- (LanguageContextEnv config , IdeState ) -> ServerM config <~> IO )
150
+ -> IO (Setup config (ServerM config ) IdeState )
140
151
setupLSP recorder defaultRoot getHieDbLoc userHandlers getIdeState clientMsgVar = do
141
152
-- Send everything over a channel, since you need to wait until after initialise before
142
153
-- LspFuncs is available
@@ -171,7 +182,7 @@ setupLSP recorder defaultRoot getHieDbLoc userHandlers getIdeState clientMsgVar
171
182
cancelled <- readTVar cancelledRequests
172
183
unless (reqId `Set.member` cancelled) retry
173
184
174
- let asyncHandlers = mconcat
185
+ let staticHandlers = mconcat
175
186
[ userHandlers
176
187
, cancelHandler cancelRequest
177
188
, exitHandler exit
@@ -184,7 +195,9 @@ setupLSP recorder defaultRoot getHieDbLoc userHandlers getIdeState clientMsgVar
184
195
185
196
let interpretHandler (env, st) = LSP. Iso (LSP. runLspT env . flip (runReaderT . unServerM) (clientMsgChan,st)) liftIO
186
197
187
- pure (doInitialize, asyncHandlers, interpretHandler)
198
+ let onExit = [stopReactorLoop, exit]
199
+
200
+ pure MkSetup {doInitialize, staticHandlers, interpretHandler, onExit}
188
201
189
202
190
203
handleInit
@@ -266,10 +279,12 @@ runWithWorkerThreads recorder dbLoc f = evalContT $ do
266
279
liftIO $ f hiedb (ThreadQueue threadQueue sessionRestartTQueue sessionLoaderTQueue)
267
280
268
281
-- | Runs the action until it ends or until the given MVar is put.
282
+ -- It is important, that the thread that puts the 'MVar' is not dropped before it puts the 'MVar' i.e. it should
283
+ -- occur as the final action in a 'finally' or 'bracket', because otherwise this thread will finish early (as soon
284
+ -- as the thread receives the BlockedIndefinitelyOnMVar exception)
269
285
-- Rethrows any exceptions.
270
- untilMVar :: MonadUnliftIO m => MVar () -> m () -> m ()
271
- untilMVar mvar io = void $
272
- waitAnyCancel =<< traverse async [ io , readMVar mvar ]
286
+ untilMVar :: MonadUnliftIO m => MVar () -> m a -> m ()
287
+ untilMVar mvar io = race_ (readMVar mvar) io
273
288
274
289
cancelHandler :: (SomeLspId -> IO () ) -> LSP. Handlers (ServerM c )
275
290
cancelHandler cancelRequest = LSP. notificationHandler SMethod_CancelRequest $ \ TNotificationMessage {_params= CancelParams {_id}} ->
0 commit comments