1-- Copyright (c) 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
2-- SPDX-License-Identifier: Apache-2.0
4{-# LANGUAGE OverloadedStrings #-}
5{-# LANGUAGE ExistentialQuantification #-}
7-- WARNING: A copy of DA.Service.Daml.LanguageServer, try to keep them in sync
8-- This version removes the daml: handling
9module Development.IDE.LSP.LanguageServer
13import Language.Haskell.LSP.Types
14import Development.IDE.LSP.Server
15import qualified Language.Haskell.LSP.Control as LSP
16import qualified Language.Haskell.LSP.Core as LSP
17import Control.Concurrent.Chan
18import Control.Concurrent.Extra
19import Control.Concurrent.Async
20import Control.Exception.Safe
23import qualified Data.Text as T
24import GHC.IO.Handle (hDuplicate, hDuplicateTo)
26import Control.Monad.Extra
28import Development.IDE.LSP.Definition
29import Development.IDE.LSP.Hover
30import Development.IDE.LSP.Notifications
31import Development.IDE.Core.Service
32import Development.IDE.Types.Logger
33import Development.IDE.Core.FileStore
34import Language.Haskell.LSP.Core (LspFuncs(..))
35import Language.Haskell.LSP.Messages
41 -> ((FromServerMessage -> IO ()) -> VFSHandle -> IO IdeState)
43runLanguageServer options userHandlers getIdeState = do
44 -- Move stdout to another file descriptor and duplicate stderr
45 -- to stdout. This guards against stray prints from corrupting the JSON-RPC
47 newStdout <- hDuplicate stdout
48 stderr `hDuplicateTo` stdout
49 hSetBuffering stderr NoBuffering
50 hSetBuffering stdout NoBuffering
52 -- Print out a single space to assert that the above redirection works.
53 -- This is interleaved with the logger, hence we just print a space here in
54 -- order not to mess up the output too much. Verified that this breaks
55 -- the language server tests without the redirection.
56 putStr " " >> hFlush stdout
58 -- Send everything over a channel, since you need to wait until after initialise before
59 -- LspFuncs is available
60 clientMsgChan :: Chan Message <- newChan
62 -- These barriers are signaled when the threads reading from these chans exit.
63 -- This should not happen but if it does, we will make sure that the whole server
64 -- dies and can be restarted instead of losing threads silently.
65 clientMsgBarrier <- newBarrier
67 let withResponse wrap f = Just $ \r -> writeChan clientMsgChan $ Response r wrap f
68 let withNotification old f = Just $ \r -> writeChan clientMsgChan $ Notification r (\ide x -> f ide x >> whenJust old ($ r))
69 let PartialHandlers parts =
70 setHandlersIgnore <> -- least important
71 setHandlersDefinition <> setHandlersHover <> -- useful features someone may override
73 setHandlersNotifications -- absolutely critical, join them with user notifications
74 handlers <- parts WithMessage{withResponse, withNotification} def
76 void $ waitAnyCancel =<< traverse async
77 [ void $ LSP.runWithHandles
81 , handleInit (signalBarrier clientMsgBarrier ()) clientMsgChan
84 (modifyOptions options)
86 , void $ waitBarrier clientMsgBarrier
89 handleInit :: IO () -> Chan Message -> LSP.LspFuncs () -> IO (Maybe err)
90 handleInit exitClientMsg clientMsgChan lspFuncs@LSP.LspFuncs{..} = do
91 ide <- getIdeState sendFunc (makeLSPVFSHandle lspFuncs)
92 _ <- flip forkFinally (const exitClientMsg) $ forever $ do
93 msg <- readChan clientMsgChan
95 Notification x@NotificationMessage{_params} act -> do
96 catch (act ide _params) $ \(e :: SomeException) ->
97 logError (ideLogger ide) $ T.pack $
98 "Unexpected exception on notification, please report!\n" ++
99 "Message: " ++ show x ++ "\n" ++
100 "Exception: " ++ show e
101 Response x@RequestMessage{_id, _params} wrap act ->
103 res <- act ide _params
104 sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) (Just res) Nothing
105 ) $ \(e :: SomeException) -> do
106 logError (ideLogger ide) $ T.pack $
107 "Unexpected exception on request, please report!\n" ++
108 "Message: " ++ show x ++ "\n" ++
109 "Exception: " ++ show e
110 sendFunc $ wrap $ ResponseMessage "2.0" (responseId _id) Nothing $
111 Just $ ResponseError InternalError (T.pack $ show e) Nothing
115-- | Things that get sent to us, but we don't deal with.
116-- Set them to avoid a warning in VS Code output.
117setHandlersIgnore :: PartialHandlers
118setHandlersIgnore = PartialHandlers $ \_ x -> return x
119 {LSP.cancelNotificationHandler = none
120 ,LSP.initializedHandler = none
122 where none = Just $ const $ return ()
125-- | A message that we need to deal with - the pieces are split up with existentials to gain additional type safety
126-- and defer precise processing until later (allows us to keep at a higher level of abstraction slightly longer)
128 = forall m req resp . (Show m, Show req) => Response (RequestMessage m req resp) (ResponseMessage resp -> FromServerMessage) (IdeState -> req -> IO resp)
129 | forall m req . (Show m, Show req) => Notification (NotificationMessage m req) (IdeState -> req -> IO ())
132modifyOptions :: LSP.Options -> LSP.Options
133modifyOptions x = x{LSP.textDocumentSync = Just $ tweak orig}
135 tweak x = x{_openClose=Just True, _change=Just TdSyncIncremental, _save=Just $ SaveOptions Nothing}
136 orig = fromMaybe tdsDefault $ LSP.textDocumentSync x
137 tdsDefault = TextDocumentSyncOptions Nothing Nothing Nothing Nothing Nothing
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list