@@ -34,7 +34,6 @@ import UnliftIO.Directory
34
34
import UnliftIO.Exception
35
35
36
36
import qualified Colog.Core as Colog
37
- import Control.Exception (BlockedIndefinitelyOnMVar (.. ))
38
37
import Control.Monad.IO.Unlift (MonadUnliftIO )
39
38
import Control.Monad.Trans.Cont (evalContT )
40
39
import Development.IDE.Core.IdeConfiguration
@@ -82,6 +81,17 @@ instance Pretty Log where
82
81
LogLspServer msg -> pretty msg
83
82
LogServerShutdownMessage -> " Received shutdown message"
84
83
84
+ data Setup config m a
85
+ = MkSetup
86
+ { doInitialize :: LSP. LanguageContextEnv config -> TRequestMessage Method_Initialize -> IO (Either (TResponseError Method_Initialize ) (LSP. LanguageContextEnv config , a ))
87
+ -- ^ the callback invoked when the language server receives the 'Method_Initialize' request
88
+ , staticHandlers :: LSP. Handlers m
89
+ -- ^ the statically known handlers of the lsp server
90
+ , interpretHandler :: (LanguageContextEnv config , a ) -> m <~> IO
91
+ -- ^ how to interpret @m@ to 'IO' and how to lift 'IO' into @m@
92
+ , onExit :: [IO () ]
93
+ -- ^ a list of 'IO' actions that clean up resources and must be run when the server shuts down
94
+ }
85
95
86
96
runLanguageServer
87
97
:: forall config a m . (Show config )
@@ -91,18 +101,16 @@ runLanguageServer
91
101
-> Handle -- output
92
102
-> config
93
103
-> (config -> Value -> Either T. Text config )
94
- -> (config -> m config () )
95
- -> (MVar ()
96
- -> IO (LSP. LanguageContextEnv config -> TRequestMessage Method_Initialize -> IO (Either (TResponseError Method_Initialize ) (LSP. LanguageContextEnv config , a )),
97
- LSP. Handlers (m config ),
98
- (LanguageContextEnv config , a ) -> m config <~> IO , [IO () ]))
104
+ -> (config -> m () )
105
+ -> (MVar () -> IO (Setup config m a ))
99
106
-> IO ()
100
107
runLanguageServer recorder options inH outH defaultConfig parseConfig onConfigChange setup = do
101
108
-- This MVar becomes full when the server thread exits or we receive exit message from client.
102
109
-- LSP server will be canceled when it's full.
103
110
clientMsgVar <- newEmptyMVar
104
111
105
- (doInitialize, staticHandlers, interpretHandler, onExit) <- setup clientMsgVar
112
+ MkSetup
113
+ { doInitialize, staticHandlers, interpretHandler, onExit } <- setup clientMsgVar
106
114
107
115
let serverDefinition = LSP. ServerDefinition
108
116
{ LSP. parseConfig = parseConfig
@@ -116,30 +124,29 @@ runLanguageServer recorder options inH outH defaultConfig parseConfig onConfigCh
116
124
, LSP. options = modifyOptions options
117
125
}
118
126
119
- let lspCologAction :: MonadIO m2 => Colog. LogAction m2 (Colog. WithSeverity LspServerLog )
127
+ let lspCologAction :: forall io . MonadIO io => Colog. LogAction io (Colog. WithSeverity LspServerLog )
120
128
lspCologAction = toCologActionWithPrio (cmapWithPrio LogLspServer recorder)
121
129
122
- untilMVar clientMsgVar $
130
+ let runServer =
123
131
LSP. runServerWithHandles
124
132
lspCologAction
125
133
lspCologAction
126
134
inH
127
135
outH
128
136
serverDefinition
129
- `finally` sequence_ onExit
137
+
138
+ untilMVar clientMsgVar $
139
+ runServer `finally` sequence_ onExit
130
140
131
141
setupLSP ::
132
- forall config err .
142
+ forall config .
133
143
Recorder (WithPriority Log )
134
144
-> FilePath -- ^ root directory, see Note [Root Directory]
135
145
-> (FilePath -> IO FilePath ) -- ^ Map root paths to the location of the hiedb for the project
136
146
-> LSP. Handlers (ServerM config )
137
147
-> (LSP. LanguageContextEnv config -> FilePath -> WithHieDb -> ThreadQueue -> IO IdeState )
138
148
-> MVar ()
139
- -> IO (LSP. LanguageContextEnv config -> TRequestMessage Method_Initialize -> IO (Either err (LSP. LanguageContextEnv config , IdeState )),
140
- LSP. Handlers (ServerM config ),
141
- (LanguageContextEnv config , IdeState ) -> ServerM config <~> IO ,
142
- [IO () ])
149
+ -> IO (Setup config (ServerM config ) IdeState )
143
150
setupLSP recorder defaultRoot getHieDbLoc userHandlers getIdeState clientMsgVar = do
144
151
-- Send everything over a channel, since you need to wait until after initialise before
145
152
-- LspFuncs is available
@@ -174,7 +181,7 @@ setupLSP recorder defaultRoot getHieDbLoc userHandlers getIdeState clientMsgVar
174
181
cancelled <- readTVar cancelledRequests
175
182
unless (reqId `Set.member` cancelled) retry
176
183
177
- let asyncHandlers = mconcat
184
+ let staticHandlers = mconcat
178
185
[ userHandlers
179
186
, cancelHandler cancelRequest
180
187
, exitHandler exit
@@ -187,9 +194,9 @@ setupLSP recorder defaultRoot getHieDbLoc userHandlers getIdeState clientMsgVar
187
194
188
195
let interpretHandler (env, st) = LSP. Iso (LSP. runLspT env . flip (runReaderT . unServerM) (clientMsgChan,st)) liftIO
189
196
190
- let finalHandlers = [stopReactorLoop, exit]
197
+ let onExit = [stopReactorLoop, exit]
191
198
192
- pure ( doInitialize, asyncHandlers , interpretHandler, finalHandlers)
199
+ pure MkSetup { doInitialize, staticHandlers , interpretHandler, onExit}
193
200
194
201
195
202
handleInit
@@ -270,10 +277,10 @@ runWithWorkerThreads recorder dbLoc f = evalContT $ do
270
277
(WithHieDbShield hiedb, threadQueue) <- runWithDb recorder dbLoc
271
278
liftIO $ f hiedb (ThreadQueue threadQueue sessionRestartTQueue sessionLoaderTQueue)
272
279
273
- -- | Runs the action until it ends or until the given MVar is put or the thread to fill the mvar is dropped, in which case the MVar will never be filled .
274
- -- This happens when the thread that handles the shutdown notification dies. Ideally, this should not rely on the RTS detecting the blocked MVar
275
- -- and instead *also* run the shutdown inf a finally block enclosing the handlers. In which case the BlockedIndefinitelyOnMVar Exception also wouldn't
276
- -- be thrown.
280
+ -- | Runs the action until it ends or until the given MVar is put.
281
+ -- It is important, that the thread that puts the 'MVar' is not dropped before it puts the 'MVar' i.e. it should
282
+ -- occur as the final action in a ' finally' or 'bracket', because otherwise this thread will finish early (as soon
283
+ -- as the thread receives the BlockedIndefinitelyOnMVar exception)
277
284
-- Rethrows any exceptions.
278
285
untilMVar :: MonadUnliftIO m => MVar () -> m a -> m ()
279
286
untilMVar mvar io = race_ (readMVar mvar) io
0 commit comments