@@ -34,7 +34,6 @@ import UnliftIO.Directory
3434import UnliftIO.Exception
3535
3636import qualified Colog.Core as Colog
37- import Control.Exception (BlockedIndefinitelyOnMVar (.. ))
3837import Control.Monad.IO.Unlift (MonadUnliftIO )
3938import Control.Monad.Trans.Cont (evalContT )
4039import Development.IDE.Core.IdeConfiguration
@@ -82,6 +81,17 @@ instance Pretty Log where
8281 LogLspServer msg -> pretty msg
8382 LogServerShutdownMessage -> " Received shutdown message"
8483
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+ }
8595
8696runLanguageServer
8797 :: forall config a m . (Show config )
@@ -91,18 +101,16 @@ runLanguageServer
91101 -> Handle -- output
92102 -> config
93103 -> (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 ))
99106 -> IO ()
100107runLanguageServer recorder options inH outH defaultConfig parseConfig onConfigChange setup = do
101108 -- This MVar becomes full when the server thread exits or we receive exit message from client.
102109 -- LSP server will be canceled when it's full.
103110 clientMsgVar <- newEmptyMVar
104111
105- (doInitialize, staticHandlers, interpretHandler, onExit) <- setup clientMsgVar
112+ MkSetup
113+ { doInitialize, staticHandlers, interpretHandler, onExit } <- setup clientMsgVar
106114
107115 let serverDefinition = LSP. ServerDefinition
108116 { LSP. parseConfig = parseConfig
@@ -116,30 +124,29 @@ runLanguageServer recorder options inH outH defaultConfig parseConfig onConfigCh
116124 , LSP. options = modifyOptions options
117125 }
118126
119- let lspCologAction :: MonadIO m2 => Colog. LogAction m2 (Colog. WithSeverity LspServerLog )
127+ let lspCologAction :: forall io . MonadIO io => Colog. LogAction io (Colog. WithSeverity LspServerLog )
120128 lspCologAction = toCologActionWithPrio (cmapWithPrio LogLspServer recorder)
121129
122- untilMVar clientMsgVar $
130+ let runServer =
123131 LSP. runServerWithHandles
124132 lspCologAction
125133 lspCologAction
126134 inH
127135 outH
128136 serverDefinition
129- `finally` sequence_ onExit
137+
138+ untilMVar clientMsgVar $
139+ runServer `finally` sequence_ onExit
130140
131141setupLSP ::
132- forall config err .
142+ forall config .
133143 Recorder (WithPriority Log )
134144 -> FilePath -- ^ root directory, see Note [Root Directory]
135145 -> (FilePath -> IO FilePath ) -- ^ Map root paths to the location of the hiedb for the project
136146 -> LSP. Handlers (ServerM config )
137147 -> (LSP. LanguageContextEnv config -> FilePath -> WithHieDb -> ThreadQueue -> IO IdeState )
138148 -> 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 )
143150setupLSP recorder defaultRoot getHieDbLoc userHandlers getIdeState clientMsgVar = do
144151 -- Send everything over a channel, since you need to wait until after initialise before
145152 -- LspFuncs is available
@@ -174,7 +181,7 @@ setupLSP recorder defaultRoot getHieDbLoc userHandlers getIdeState clientMsgVar
174181 cancelled <- readTVar cancelledRequests
175182 unless (reqId `Set.member` cancelled) retry
176183
177- let asyncHandlers = mconcat
184+ let staticHandlers = mconcat
178185 [ userHandlers
179186 , cancelHandler cancelRequest
180187 , exitHandler exit
@@ -187,9 +194,9 @@ setupLSP recorder defaultRoot getHieDbLoc userHandlers getIdeState clientMsgVar
187194
188195 let interpretHandler (env, st) = LSP. Iso (LSP. runLspT env . flip (runReaderT . unServerM) (clientMsgChan,st)) liftIO
189196
190- let finalHandlers = [stopReactorLoop, exit]
197+ let onExit = [stopReactorLoop, exit]
191198
192- pure ( doInitialize, asyncHandlers , interpretHandler, finalHandlers)
199+ pure MkSetup { doInitialize, staticHandlers , interpretHandler, onExit}
193200
194201
195202handleInit
@@ -270,10 +277,10 @@ runWithWorkerThreads recorder dbLoc f = evalContT $ do
270277 (WithHieDbShield hiedb, threadQueue) <- runWithDb recorder dbLoc
271278 liftIO $ f hiedb (ThreadQueue threadQueue sessionRestartTQueue sessionLoaderTQueue)
272279
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)
277284-- Rethrows any exceptions.
278285untilMVar :: MonadUnliftIO m => MVar () -> m a -> m ()
279286untilMVar mvar io = race_ (readMVar mvar) io
0 commit comments