Skip to content

Commit f7fca52

Browse files
committed
[chore] apply suggestions from code review by @fendor
1 parent 6696657 commit f7fca52

File tree

1 file changed

+29
-22
lines changed

1 file changed

+29
-22
lines changed

ghcide/src/Development/IDE/LSP/LanguageServer.hs

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import UnliftIO.Directory
3434
import UnliftIO.Exception
3535

3636
import qualified Colog.Core as Colog
37-
import Control.Exception (BlockedIndefinitelyOnMVar (..))
3837
import Control.Monad.IO.Unlift (MonadUnliftIO)
3938
import Control.Monad.Trans.Cont (evalContT)
4039
import 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

8696
runLanguageServer
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 ()
100107
runLanguageServer 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

131141
setupLSP ::
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)
143150
setupLSP 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

195202
handleInit
@@ -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.
278285
untilMVar :: MonadUnliftIO m => MVar () -> m a -> m ()
279286
untilMVar mvar io = race_ (readMVar mvar) io

0 commit comments

Comments
 (0)