on master help sign in
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.

commit71ecd105d91833910812ca7fad73f9ad94c477aa authorAlan Zimmerman <alan.zimm@gmail.com> dateFri 14 Feb 2020, 12:21 (7 years ago) parentd771569
10 files changed+73−52
exe/Main.hs+3−1 src/Development/IDE/Core/IdeConfiguration.hs+2−2 src/Development/IDE/LSP/HoverDefinition.hs+1−1 src/Development/IDE/LSP/LanguageServer.hs+38−17 src/Development/IDE/LSP/Notifications.hs+2−1 src/Development/IDE/LSP/Outline.hs+2−3 src/Development/IDE/LSP/Server.hs+11−10 src/Development/IDE/Plugin.hs+6−7 src/Development/IDE/Plugin/CodeAction.hs+5−6 src/Development/IDE/Plugin/Completions.hs+3−4
exe/Main.hs+3−1
@@ -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+2−2
@@ -36,8 +36,8 @@ getIdeConfiguration :: Action IdeConfiguration
3636getIdeConfiguration =
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+1−1
@@ -29,7 +29,7 @@ foundHover :: (Maybe Range, [T.Text]) -> Maybe Hover
2929foundHover (mbRange, contents) =
3030 Just $ Hover (HoverContents $ MarkupContent MkMarkdown $ T.intercalate sectionSeparator contents) mbRange
3131
32−setHandlersDefinition, setHandlersHover :: PartialHandlers
32+setHandlersDefinition, setHandlersHover :: PartialHandlers c
3333setHandlersDefinition = PartialHandlers $ \WithMessage{..} x ->
3434 return x{LSP.definitionHandler = withResponse RspDefinition $ const gotoDefinition}
3535setHandlersHover = PartialHandlers $ \WithMessage{..} x ->
src/Development/IDE/LSP/LanguageServer.hs+38−17
@@ -39,11 +39,14 @@ import Language.Haskell.LSP.Core (LspFuncs(..))
3939import Language.Haskell.LSP.Messages
4040
4141runLanguageServer
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
184204setHandlersIgnore = 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
191211cancelHandler 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
209230modifyOptions :: LSP.Options -> LSP.Options
210231modifyOptions x = x{ LSP.textDocumentSync = Just $ tweakTDS origTDS
src/Development/IDE/LSP/Notifications.hs+2−1
@@ -32,7 +32,7 @@ import Development.IDE.Core.OfInterest
3232whenUriFile :: Uri -> (NormalizedFilePath -> IO ()) -> IO ()
3333whenUriFile uri act = whenJust (LSP.uriToFilePath uri) $ act . toNormalizedFilePath
3434
35−setHandlersNotifications :: PartialHandlers
35+setHandlersNotifications :: PartialHandlers c
3636setHandlersNotifications = 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+2−3
@@ -18,7 +18,6 @@ import Data.Text ( Text
1818 )
1919import qualified Data.Text as T
2020import Development.IDE.Core.Rules
21−import Development.IDE.Core.IdeConfiguration
2221import Development.IDE.Core.Shake
2322import Development.IDE.GHC.Compat
2423import Development.IDE.GHC.Error ( srcSpanToRange )
@@ -29,13 +28,13 @@ import Outputable ( Outputable
2928 , showSDocUnsafe
3029 )
3130
32−setHandlersOutline :: PartialHandlers
31+setHandlersOutline :: PartialHandlers c
3332setHandlersOutline = PartialHandlers $ \WithMessage {..} x -> return x
3433 { LSP.documentSymbolHandler = withResponse RspDocumentSymbols moduleOutline
3534 }
3635
3736moduleOutline
38− :: LSP.LspFuncs IdeConfiguration -> IdeState -> DocumentSymbolParams -> IO (Either ResponseError DSResult)
37+ :: LSP.LspFuncs c -> IdeState -> DocumentSymbolParams -> IO (Either ResponseError DSResult)
3938moduleOutline _lsp ideState DocumentSymbolParams { _textDocument = TextDocumentIdentifier uri }
4039 = case uriToFilePath uri of
4140 Just (toNormalizedFilePath -> fp) -> do
src/Development/IDE/LSP/Server.hs+11−10
@@ -14,33 +14,34 @@ import Data.Default
1414import Language.Haskell.LSP.Types
1515import qualified Language.Haskell.LSP.Core as LSP
1616import qualified Language.Haskell.LSP.Messages as LSP
17−import Development.IDE.Core.IdeConfiguration
1817import 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+6−7
@@ -7,27 +7,26 @@ import Development.IDE.LSP.Server
77
88import Language.Haskell.LSP.Types
99import Development.IDE.Core.Rules
10−import Development.IDE.Core.IdeConfiguration
1110import qualified Language.Haskell.LSP.Core as LSP
1211import 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
3130codeActionPlugin f = Plugin mempty $ PartialHandlers $ \WithMessage{..} x -> return x{
3231 LSP.codeActionHandler = withResponse RspCodeAction g
3332 }
src/Development/IDE/Plugin/CodeAction.hs+5−6
@@ -12,7 +12,6 @@ import Language.Haskell.LSP.Types
1212import Control.Monad (join)
1313import Development.IDE.Plugin
1414import Development.IDE.GHC.Compat
15−import Development.IDE.Core.IdeConfiguration
1615import Development.IDE.Core.Rules
1716import Development.IDE.Core.RuleTypes
1817import Development.IDE.Core.Service
@@ -39,12 +38,12 @@ import Outputable (ppr, showSDocUnsafe)
3938import DynFlags (xFlags, FlagSpec(..))
4039import GHC.LanguageExtensions.Type (Extension)
4140
42−plugin :: Plugin
41+plugin :: Plugin c
4342plugin = codeActionPlugin codeAction <> Plugin mempty setHandlersCodeLens
4443
4544-- | Generate code actions.
4645codeAction
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.
6867codeLens
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.
8988executeAddSignatureCommand
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
449448setHandlersCodeLens = PartialHandlers $ \WithMessage{..} x -> return x{
450449 LSP.codeLensHandler = withResponse RspCodeLens codeLens,
451450 LSP.executeCommandHandler = withResponseAndRequest RspExecuteCommand ReqApplyWorkspaceEdit executeAddSignatureCommand
src/Development/IDE/Plugin/Completions.hs+3−4
@@ -17,7 +17,6 @@ import Development.IDE.Plugin
1717import Development.IDE.Core.Service
1818import Development.IDE.Plugin.Completions.Logic
1919import Development.IDE.Types.Location
20−import Development.IDE.Core.IdeConfiguration
2120import Development.IDE.Core.PositionMapping
2221import Development.IDE.Core.RuleTypes
2322import Development.IDE.Core.Shake
@@ -26,7 +25,7 @@ import Development.IDE.LSP.Server
2625import Development.IDE.Import.DependencyInformation
2726
2827
29−plugin :: Plugin
28+plugin :: Plugin c
3029plugin = Plugin produceCompletions setHandlersCompletion
3130
3231produceCompletions :: Rules ()
@@ -56,7 +55,7 @@ instance Binary ProduceCompletions
5655
5756-- | Generate code actions.
5857getCompletionsLSP
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
8786setHandlersCompletion = PartialHandlers $ \WithMessage{..} x -> return x{
8887 LSP.completionHandler = withResponse RspCompletion getCompletionsLSP
8988 }