Skip to content

Commit 71ecd10

Browse files
authored
Parameterize the haskell-lsp client config type (#416)
So that haskell-language-server can use its own config And separate it out from the IdeConfiguration which is separately set by the InitializeRequest message.
1 parent d771569 commit 71ecd10

File tree

10 files changed

+73
-52
lines changed

10 files changed

+73
-52
lines changed

exe/Main.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,14 @@ main = do
8989
dir <- getCurrentDirectory
9090

9191
let plugins = Completions.plugin <> CodeAction.plugin
92+
onInitialConfiguration = const $ Right ()
93+
onConfigurationChange = const $ Right ()
9294

9395
if argLSP then do
9496
t <- offsetTime
9597
hPutStrLn stderr "Starting LSP server..."
9698
hPutStrLn stderr "If you are seeing this in a terminal, you probably should have run ghcide WITHOUT the --lsp option!"
97-
runLanguageServer def (pluginHandler plugins) $ \getLspId event vfs caps -> do
99+
runLanguageServer def (pluginHandler plugins) onInitialConfiguration onConfigurationChange $ \getLspId event vfs caps -> do
98100
t <- t
99101
hPutStrLn stderr $ "Started LSP server in " ++ showDuration t
100102
-- very important we only call loadSession once, and it's fast, so just do it before starting

src/Development/IDE/Core/IdeConfiguration.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ getIdeConfiguration :: Action IdeConfiguration
3636
getIdeConfiguration =
3737
getIdeGlobalAction >>= liftIO . readVar . unIdeConfigurationRef
3838

39-
parseConfiguration :: InitializeRequest -> IdeConfiguration
40-
parseConfiguration RequestMessage { _params = InitializeParams {..} } =
39+
parseConfiguration :: InitializeParams -> IdeConfiguration
40+
parseConfiguration InitializeParams {..} =
4141
IdeConfiguration { .. }
4242
where
4343
workspaceFolders =

src/Development/IDE/LSP/HoverDefinition.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ foundHover :: (Maybe Range, [T.Text]) -> Maybe Hover
2929
foundHover (mbRange, contents) =
3030
Just $ Hover (HoverContents $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator contents) mbRange
3131

32-
setHandlersDefinition, setHandlersHover :: PartialHandlers
32+
setHandlersDefinition, setHandlersHover :: PartialHandlers c
3333
setHandlersDefinition = PartialHandlers $ \WithMessage{..} x ->
3434
return x{LSP.definitionHandler = withResponse RspDefinition $ const gotoDefinition}
3535
setHandlersHover = PartialHandlers $ \WithMessage{..} x ->

src/Development/IDE/LSP/LanguageServer.hs

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,14 @@ import Language.Haskell.LSP.Core (LspFuncs(..))
3939
import Language.Haskell.LSP.Messages
4040

4141
runLanguageServer
42-
:: LSP.Options
43-
-> PartialHandlers
42+
:: forall config. (Show config)
43+
=> LSP.Options
44+
-> PartialHandlers config
45+
-> (InitializeRequest -> Either T.Text config)
46+
-> (DidChangeConfigurationNotification -> Either T.Text config)
4447
-> (IO LspId -> (FromServerMessage -> IO ()) -> VFSHandle -> ClientCapabilities -> IO IdeState)
4548
-> IO ()
46-
runLanguageServer options userHandlers getIdeState = do
49+
runLanguageServer options userHandlers onInitialConfig onConfigChange getIdeState = do
4750
-- Move stdout to another file descriptor and duplicate stderr
4851
-- to stdout. This guards against stray prints from corrupting the JSON-RPC
4952
-- message stream.
@@ -60,7 +63,7 @@ runLanguageServer options userHandlers getIdeState = do
6063

6164
-- Send everything over a channel, since you need to wait until after initialise before
6265
-- LspFuncs is available
63-
clientMsgChan :: Chan Message <- newChan
66+
clientMsgChan :: Chan (Message config) <- newChan
6467

6568
-- These barriers are signaled when the threads reading from these chans exit.
6669
-- This should not happen but if it does, we will make sure that the whole server
@@ -79,6 +82,7 @@ runLanguageServer options userHandlers getIdeState = do
7982
let withResponseAndRequest wrap wrapNewReq f = Just $ \r@RequestMessage{_id} -> do
8083
atomically $ modifyTVar pendingRequests (Set.insert _id)
8184
writeChan clientMsgChan $ ResponseAndRequest r wrap wrapNewReq f
85+
let withInitialize f = Just $ \r -> writeChan clientMsgChan $ InitialParams r (\lsp ide x -> f lsp ide x)
8286
let cancelRequest reqId = atomically $ do
8387
queued <- readTVar pendingRequests
8488
-- We want to avoid that the list of cancelled requests
@@ -95,6 +99,7 @@ runLanguageServer options userHandlers getIdeState = do
9599
cancelled <- readTVar cancelledRequests
96100
unless (reqId `Set.member` cancelled) retry
97101
let PartialHandlers parts =
102+
initializeRequestHandler <>
98103
setHandlersIgnore <> -- least important
99104
setHandlersDefinition <> setHandlersHover <>
100105
setHandlersOutline <>
@@ -103,11 +108,11 @@ runLanguageServer options userHandlers getIdeState = do
103108
cancelHandler cancelRequest
104109
-- Cancel requests are special since they need to be handled
105110
-- out of order to be useful. Existing handlers are run afterwards.
106-
handlers <- parts WithMessage{withResponse, withNotification, withResponseAndRequest} def
111+
handlers <- parts WithMessage{withResponse, withNotification, withResponseAndRequest, withInitialize} def
107112

108113
let initializeCallbacks = LSP.InitializeCallbacks
109-
{ LSP.onInitialConfiguration = Right . parseConfiguration
110-
, LSP.onConfigurationChange = const $ Left "Configuration changes not supported yet"
114+
{ LSP.onInitialConfiguration = onInitialConfig
115+
, LSP.onConfigurationChange = onConfigChange
111116
, LSP.onStartup = handleInit (signalBarrier clientMsgBarrier ()) clearReqId waitForCancel clientMsgChan
112117
}
113118

@@ -122,13 +127,11 @@ runLanguageServer options userHandlers getIdeState = do
122127
, void $ waitBarrier clientMsgBarrier
123128
]
124129
where
125-
handleInit :: IO () -> (LspId -> IO ()) -> (LspId -> IO ()) -> Chan Message -> LSP.LspFuncs IdeConfiguration -> IO (Maybe err)
130+
handleInit :: IO () -> (LspId -> IO ()) -> (LspId -> IO ()) -> Chan (Message config) -> LSP.LspFuncs config -> IO (Maybe err)
126131
handleInit exitClientMsg clearReqId waitForCancel clientMsgChan lspFuncs@LSP.LspFuncs{..} = do
127132

128133
ide <- getIdeState getNextReqId sendFunc (makeLSPVFSHandle lspFuncs) clientCapabilities
129134

130-
mapM_ (registerIdeConfiguration (shakeExtras ide)) =<< config
131-
132135
_ <- flip forkFinally (const exitClientMsg) $ forever $ do
133136
msg <- readChan clientMsgChan
134137
case msg of
@@ -152,6 +155,12 @@ runLanguageServer options userHandlers getIdeState = do
152155
Just (rm, newReqParams) -> do
153156
reqId <- getNextReqId
154157
sendFunc $ wrapNewReq $ RequestMessage "2.0" reqId rm newReqParams
158+
InitialParams x@RequestMessage{_id, _params} act -> do
159+
catch (act lspFuncs ide _params) $ \(e :: SomeException) ->
160+
logError (ideLogger ide) $ T.pack $
161+
"Unexpected exception on InitializeRequest handler, please report!\n" ++
162+
"Message: " ++ show x ++ "\n" ++
163+
"Exception: " ++ show e
155164
pure Nothing
156165

157166
checkCancelled ide clearReqId waitForCancel lspFuncs@LSP.LspFuncs{..} wrap act msg _id _params k =
@@ -177,17 +186,28 @@ runLanguageServer options userHandlers getIdeState = do
177186
sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) Nothing $
178187
Just $ ResponseError InternalError (T.pack $ show e) Nothing
179188

189+
initializeRequestHandler :: PartialHandlers config
190+
initializeRequestHandler = PartialHandlers $ \WithMessage{..} x -> return x{
191+
LSP.initializeRequestHandler = withInitialize initHandler
192+
}
193+
194+
initHandler
195+
:: LSP.LspFuncs c
196+
-> IdeState
197+
-> InitializeParams
198+
-> IO ()
199+
initHandler _ ide params = registerIdeConfiguration (shakeExtras ide) (parseConfiguration params)
180200

181201
-- | Things that get sent to us, but we don't deal with.
182202
-- Set them to avoid a warning in VS Code output.
183-
setHandlersIgnore :: PartialHandlers
203+
setHandlersIgnore :: PartialHandlers config
184204
setHandlersIgnore = PartialHandlers $ \_ x -> return x
185205
{LSP.initializedHandler = none
186206
,LSP.responseHandler = none
187207
}
188208
where none = Just $ const $ return ()
189209

190-
cancelHandler :: (LspId -> IO ()) -> PartialHandlers
210+
cancelHandler :: (LspId -> IO ()) -> PartialHandlers config
191211
cancelHandler cancelRequest = PartialHandlers $ \_ x -> return x
192212
{LSP.cancelNotificationHandler = Just $ \msg@NotificationMessage {_params = CancelParams {_id}} -> do
193213
cancelRequest _id
@@ -197,14 +217,15 @@ cancelHandler cancelRequest = PartialHandlers $ \_ x -> return x
197217

198218
-- | A message that we need to deal with - the pieces are split up with existentials to gain additional type safety
199219
-- and defer precise processing until later (allows us to keep at a higher level of abstraction slightly longer)
200-
data Message
201-
= forall m req resp . (Show m, Show req) => Response (RequestMessage m req resp) (ResponseMessage resp -> FromServerMessage) (LSP.LspFuncs IdeConfiguration -> IdeState -> req -> IO (Either ResponseError resp))
220+
data Message c
221+
= forall m req resp . (Show m, Show req) => Response (RequestMessage m req resp) (ResponseMessage resp -> FromServerMessage) (LSP.LspFuncs c -> IdeState -> req -> IO (Either ResponseError resp))
202222
-- | Used for cases in which we need to send not only a response,
203223
-- but also an additional request to the client.
204224
-- For example, 'executeCommand' may generate an 'applyWorkspaceEdit' request.
205-
| forall m rm req resp newReqParams newReqBody . (Show m, Show rm, Show req) => ResponseAndRequest (RequestMessage m req resp) (ResponseMessage resp -> FromServerMessage) (RequestMessage rm newReqParams newReqBody -> FromServerMessage) (LSP.LspFuncs IdeConfiguration -> IdeState -> req -> IO (resp, Maybe (rm, newReqParams)))
206-
| forall m req . (Show m, Show req) => Notification (NotificationMessage m req) (LSP.LspFuncs IdeConfiguration -> IdeState -> req -> IO ())
207-
225+
| forall m rm req resp newReqParams newReqBody . (Show m, Show rm, Show req) => ResponseAndRequest (RequestMessage m req resp) (ResponseMessage resp -> FromServerMessage) (RequestMessage rm newReqParams newReqBody -> FromServerMessage) (LSP.LspFuncs c -> IdeState -> req -> IO (resp, Maybe (rm, newReqParams)))
226+
| forall m req . (Show m, Show req) => Notification (NotificationMessage m req) (LSP.LspFuncs c -> IdeState -> req -> IO ())
227+
-- | Used for the InitializeRequest only, where the response is generated by the LSP core handler.
228+
| InitialParams InitializeRequest (LSP.LspFuncs c -> IdeState -> InitializeParams -> IO ())
208229

209230
modifyOptions :: LSP.Options -> LSP.Options
210231
modifyOptions x = x{ LSP.textDocumentSync = Just $ tweakTDS origTDS

src/Development/IDE/LSP/Notifications.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import Development.IDE.Core.OfInterest
3232
whenUriFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO ()
3333
whenUriFile uri act = whenJust (LSP.uriToFilePath uri) $ act . toNormalizedFilePath
3434

35-
setHandlersNotifications :: PartialHandlers
35+
setHandlersNotifications :: PartialHandlers c
3636
setHandlersNotifications = PartialHandlers $ \WithMessage{..} x -> return x
3737
{LSP.didOpenTextDocumentNotificationHandler = withNotification (LSP.didOpenTextDocumentNotificationHandler x) $
3838
\_ ide (DidOpenTextDocumentParams TextDocumentItem{_uri,_version}) -> do
@@ -70,6 +70,7 @@ setHandlersNotifications = PartialHandlers $ \WithMessage{..} x -> return x
7070
logInfo (ideLogger ide) $ "Files created or deleted: " <> msg
7171
modifyFileExists ide events
7272
setSomethingModified ide
73+
7374
,LSP.didChangeWorkspaceFoldersNotificationHandler = withNotification (LSP.didChangeWorkspaceFoldersNotificationHandler x) $
7475
\_ ide (DidChangeWorkspaceFoldersParams events) -> do
7576
let add = S.union

src/Development/IDE/LSP/Outline.hs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import Data.Text ( Text
1818
)
1919
import qualified Data.Text as T
2020
import Development.IDE.Core.Rules
21-
import Development.IDE.Core.IdeConfiguration
2221
import Development.IDE.Core.Shake
2322
import Development.IDE.GHC.Compat
2423
import Development.IDE.GHC.Error ( srcSpanToRange )
@@ -29,13 +28,13 @@ import Outputable ( Outputable
2928
, showSDocUnsafe
3029
)
3130

32-
setHandlersOutline :: PartialHandlers
31+
setHandlersOutline :: PartialHandlers c
3332
setHandlersOutline = PartialHandlers $ \WithMessage {..} x -> return x
3433
{ LSP.documentSymbolHandler = withResponse RspDocumentSymbols moduleOutline
3534
}
3635

3736
moduleOutline
38-
:: LSP.LspFuncs IdeConfiguration -> IdeState -> DocumentSymbolParams -> IO (Either ResponseError DSResult)
37+
:: LSP.LspFuncs c -> IdeState -> DocumentSymbolParams -> IO (Either ResponseError DSResult)
3938
moduleOutline _lsp ideState DocumentSymbolParams { _textDocument = TextDocumentIdentifier uri }
4039
= case uriToFilePath uri of
4140
Just (toNormalizedFilePath -> fp) -> do

src/Development/IDE/LSP/Server.hs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,34 @@ import Data.Default
1414
import Language.Haskell.LSP.Types
1515
import qualified Language.Haskell.LSP.Core as LSP
1616
import qualified Language.Haskell.LSP.Messages as LSP
17-
import Development.IDE.Core.IdeConfiguration
1817
import Development.IDE.Core.Service
1918

20-
data WithMessage = WithMessage
19+
data WithMessage c = WithMessage
2120
{withResponse :: forall m req resp . (Show m, Show req) =>
2221
(ResponseMessage resp -> LSP.FromServerMessage) -> -- how to wrap a response
23-
(LSP.LspFuncs IdeConfiguration -> IdeState -> req -> IO (Either ResponseError resp)) -> -- actual work
22+
(LSP.LspFuncs c -> IdeState -> req -> IO (Either ResponseError resp)) -> -- actual work
2423
Maybe (LSP.Handler (RequestMessage m req resp))
2524
,withNotification :: forall m req . (Show m, Show req) =>
2625
Maybe (LSP.Handler (NotificationMessage m req)) -> -- old notification handler
27-
(LSP.LspFuncs IdeConfiguration -> IdeState -> req -> IO ()) -> -- actual work
26+
(LSP.LspFuncs c -> IdeState -> req -> IO ()) -> -- actual work
2827
Maybe (LSP.Handler (NotificationMessage m req))
29-
,withResponseAndRequest :: forall m rm req resp newReqParams newReqBody.
28+
,withResponseAndRequest :: forall m rm req resp newReqParams newReqBody .
3029
(Show m, Show rm, Show req, Show newReqParams, Show newReqBody) =>
3130
(ResponseMessage resp -> LSP.FromServerMessage) -> -- how to wrap a response
3231
(RequestMessage rm newReqParams newReqBody -> LSP.FromServerMessage) -> -- how to wrap the additional req
33-
(LSP.LspFuncs IdeConfiguration -> IdeState -> req -> IO (resp, Maybe (rm, newReqParams))) -> -- actual work
32+
(LSP.LspFuncs c -> IdeState -> req -> IO (resp, Maybe (rm, newReqParams))) -> -- actual work
3433
Maybe (LSP.Handler (RequestMessage m req resp))
34+
, withInitialize :: (LSP.LspFuncs c -> IdeState -> InitializeParams -> IO ())
35+
-> Maybe (LSP.Handler InitializeRequest)
3536
}
3637

37-
newtype PartialHandlers = PartialHandlers (WithMessage -> LSP.Handlers -> IO LSP.Handlers)
38+
newtype PartialHandlers c = PartialHandlers (WithMessage c -> LSP.Handlers -> IO LSP.Handlers)
3839

39-
instance Default PartialHandlers where
40+
instance Default (PartialHandlers c) where
4041
def = PartialHandlers $ \_ x -> pure x
4142

42-
instance Semigroup PartialHandlers where
43+
instance Semigroup (PartialHandlers c) where
4344
PartialHandlers a <> PartialHandlers b = PartialHandlers $ \w x -> a w x >>= b w
4445

45-
instance Monoid PartialHandlers where
46+
instance Monoid (PartialHandlers c) where
4647
mempty = def

src/Development/IDE/Plugin.hs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,26 @@ import Development.IDE.LSP.Server
77

88
import Language.Haskell.LSP.Types
99
import Development.IDE.Core.Rules
10-
import Development.IDE.Core.IdeConfiguration
1110
import qualified Language.Haskell.LSP.Core as LSP
1211
import Language.Haskell.LSP.Messages
1312

1413

15-
data Plugin = Plugin
14+
data Plugin c = Plugin
1615
{pluginRules :: Rules ()
17-
,pluginHandler :: PartialHandlers
16+
,pluginHandler :: PartialHandlers c
1817
}
1918

20-
instance Default Plugin where
19+
instance Default (Plugin c) where
2120
def = Plugin mempty def
2221

23-
instance Semigroup Plugin where
22+
instance Semigroup (Plugin c) where
2423
Plugin x1 y1 <> Plugin x2 y2 = Plugin (x1<>x2) (y1<>y2)
2524

26-
instance Monoid Plugin where
25+
instance Monoid (Plugin c) where
2726
mempty = def
2827

2928

30-
codeActionPlugin :: (LSP.LspFuncs IdeConfiguration -> IdeState -> TextDocumentIdentifier -> Range -> CodeActionContext -> IO (Either ResponseError [CAResult])) -> Plugin
29+
codeActionPlugin :: (LSP.LspFuncs c -> IdeState -> TextDocumentIdentifier -> Range -> CodeActionContext -> IO (Either ResponseError [CAResult])) -> Plugin c
3130
codeActionPlugin f = Plugin mempty $ PartialHandlers $ \WithMessage{..} x -> return x{
3231
LSP.codeActionHandler = withResponse RspCodeAction g
3332
}

src/Development/IDE/Plugin/CodeAction.hs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import Language.Haskell.LSP.Types
1212
import Control.Monad (join)
1313
import Development.IDE.Plugin
1414
import Development.IDE.GHC.Compat
15-
import Development.IDE.Core.IdeConfiguration
1615
import Development.IDE.Core.Rules
1716
import Development.IDE.Core.RuleTypes
1817
import Development.IDE.Core.Service
@@ -39,12 +38,12 @@ import Outputable (ppr, showSDocUnsafe)
3938
import DynFlags (xFlags, FlagSpec(..))
4039
import GHC.LanguageExtensions.Type (Extension)
4140

42-
plugin :: Plugin
41+
plugin :: Plugin c
4342
plugin = codeActionPlugin codeAction <> Plugin mempty setHandlersCodeLens
4443

4544
-- | Generate code actions.
4645
codeAction
47-
:: LSP.LspFuncs IdeConfiguration
46+
:: LSP.LspFuncs c
4847
-> IdeState
4948
-> TextDocumentIdentifier
5049
-> Range
@@ -66,7 +65,7 @@ codeAction lsp state (TextDocumentIdentifier uri) _range CodeActionContext{_diag
6665

6766
-- | Generate code lenses.
6867
codeLens
69-
:: LSP.LspFuncs IdeConfiguration
68+
:: LSP.LspFuncs c
7069
-> IdeState
7170
-> CodeLensParams
7271
-> IO (Either ResponseError (List CodeLens))
@@ -87,7 +86,7 @@ codeLens _lsp ideState CodeLensParams{_textDocument=TextDocumentIdentifier uri}
8786

8887
-- | Execute the "typesignature.add" command.
8988
executeAddSignatureCommand
90-
:: LSP.LspFuncs IdeConfiguration
89+
:: LSP.LspFuncs c
9190
-> IdeState
9291
-> ExecuteCommandParams
9392
-> IO (Value, Maybe (ServerMethod, ApplyWorkspaceEditParams))
@@ -445,7 +444,7 @@ matchRegex message regex = case unifySpaces message =~~ regex of
445444
Just (_ :: T.Text, _ :: T.Text, _ :: T.Text, bindings) -> Just bindings
446445
Nothing -> Nothing
447446

448-
setHandlersCodeLens :: PartialHandlers
447+
setHandlersCodeLens :: PartialHandlers c
449448
setHandlersCodeLens = PartialHandlers $ \WithMessage{..} x -> return x{
450449
LSP.codeLensHandler = withResponse RspCodeLens codeLens,
451450
LSP.executeCommandHandler = withResponseAndRequest RspExecuteCommand ReqApplyWorkspaceEdit executeAddSignatureCommand

src/Development/IDE/Plugin/Completions.hs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import Development.IDE.Plugin
1717
import Development.IDE.Core.Service
1818
import Development.IDE.Plugin.Completions.Logic
1919
import Development.IDE.Types.Location
20-
import Development.IDE.Core.IdeConfiguration
2120
import Development.IDE.Core.PositionMapping
2221
import Development.IDE.Core.RuleTypes
2322
import Development.IDE.Core.Shake
@@ -26,7 +25,7 @@ import Development.IDE.LSP.Server
2625
import Development.IDE.Import.DependencyInformation
2726

2827

29-
plugin :: Plugin
28+
plugin :: Plugin c
3029
plugin = Plugin produceCompletions setHandlersCompletion
3130

3231
produceCompletions :: Rules ()
@@ -56,7 +55,7 @@ instance Binary ProduceCompletions
5655

5756
-- | Generate code actions.
5857
getCompletionsLSP
59-
:: LSP.LspFuncs IdeConfiguration
58+
:: LSP.LspFuncs c
6059
-> IdeState
6160
-> CompletionParams
6261
-> IO (Either ResponseError CompletionResponseResult)
@@ -83,7 +82,7 @@ getCompletionsLSP lsp ide
8382
_ -> return (Completions $ List [])
8483
_ -> return (Completions $ List [])
8584

86-
setHandlersCompletion :: PartialHandlers
85+
setHandlersCompletion :: PartialHandlers c
8786
setHandlersCompletion = PartialHandlers $ \WithMessage{..} x -> return x{
8887
LSP.completionHandler = withResponse RspCompletion getCompletionsLSP
8988
}

0 commit comments

Comments
 (0)