import System.Environment
main = do
- [client, server] <- ((take 2) <$> getArgs) >>= mapM canonicalizePath
- replay client server
+ [client, server] <- (take 2 <$> getArgs) >>= mapM canonicalizePath
+ passed <- replay client server
+ putStrLn $ if passed then "Passed" else "Failed"
, text
, transformers
, process
+ , directory
if os(windows)
build-depends: win32
else
Capabilities
ghc-options: -W
+test-suite tests
+ type: exitcode-stdio-1.0
+ main-is: Test.hs
+ hs-source-dirs: test
+ ghc-options: -W
+ build-depends: base >= 4.7 && < 5
+ , hspec
+ , haskell-lsp-test
+ default-language: Haskell2010
+
executable example
hs-source-dirs: example
main-is: Main.hs
{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE FlexibleContexts #-}
module Language.Haskell.LSP.Test.Recorded
( replay
)
import Control.Lens
import Control.Monad
import System.IO
+import System.Directory
import System.Process
-- | Replays a recorded client output and
-- makes sure it matches up with an expected response.
-replay :: FilePath -- ^ The client output to replay to the server.
+replay
+ :: FilePath -- ^ The client output to replay to the server.
-> FilePath -- ^ The expected response from the server.
- -> IO Int
+ -> IO Bool
replay cfp sfp = do
- (Just serverIn, Just serverOut, _, _) <- createProcess
+ -- need to keep hold of current directory since haskell-lsp changes it
+ prevDir <- getCurrentDirectory
+
+ (Just serverIn, Just serverOut, _, serverProc) <- createProcess
(proc "hie" ["--lsp", "-l", "/tmp/hie.log", "-d"]) { std_in = CreatePipe
, std_out = CreatePipe
}
rspSema <- newEmptyMVar :: IO (MVar LSP.LspId)
let semas = (reqSema, rspSema)
+ didPass <- newEmptyMVar
+
-- the recorded client input to the server
clientRecIn <- openFile cfp ReadMode
serverRecIn <- openFile sfp ReadMode
expectedMsgs <- getAllMessages serverRecIn
-- listen to server
- forkIO $ listenServer expectedMsgs serverOut semas
-
- -- send initialize request ourselves since haskell-lsp consumes it
- -- rest are handled via `handlers`
- sendInitialize clientRecIn serverIn
-
- -- wait for initialize response
- putStrLn "Waiting for initialzie response"
- takeMVar reqSema
- putStrLn "Got initialize response"
+ forkIO $ listenServer expectedMsgs serverOut semas didPass
+ -- start client replay
+ forkIO $ do
Control.runWithHandles clientRecIn
null
(const $ Right (), const $ return Nothing)
def
Nothing
Nothing
- where
- listenServer :: [B.ByteString] -> Handle -> (MVar LSP.LspIdRsp, MVar LSP.LspId) -> IO ()
- listenServer expectedMsgs h semas@(reqSema, rspSema) = do
+
+ -- todo: we shouldn't do this, we should check all notifications were delivered first
+ putMVar didPass True
+
+ result <- takeMVar didPass
+ terminateProcess serverProc
+
+ -- restore directory
+ setCurrentDirectory prevDir
+
+ return result
+
+-- todo: Maybe make a reader monad and a fail function for it?
+listenServer
+ :: [B.ByteString]
+ -> Handle
+ -> (MVar LSP.LspIdRsp, MVar LSP.LspId)
+ -> MVar Bool
+ -> IO ()
+listenServer [] _ _ passVar = putMVar passVar True
+listenServer expectedMsgs h semas@(reqSema, rspSema) passVar = do
msg <- getNextMessage h
putStrLn $ "Remaining messages " ++ show (length expectedMsgs)
if inRightOrder msg expectedMsgs
then do
- -- if we got a request response unblock the replay waiting for a response
whenResponse msg $ \res -> do
- putStrLn ("Got response for id " ++ show (res ^. LSP.id))
- putMVar reqSema (res ^. LSP.id)
+ putStrLn $ "Got response for id " ++ show (res ^. LSP.id)
+ putMVar reqSema (res ^. LSP.id) -- unblock the handler waiting to send a request
whenRequest msg $ \req -> do
- putStrLn ("Got request for id " ++ show (req ^. LSP.id) ++ " " ++ show (req ^. LSP.method))
- putMVar rspSema (req ^. LSP.id)
+ putStrLn $ "Got request for id " ++ show (req ^. LSP.id) ++ " " ++ show (req ^. LSP.method)
+ putMVar rspSema (req ^. LSP.id) -- unblock the handler waiting for a response
+
+ whenNotification msg $ \n -> putStrLn $ "Got notification " ++ (show (n ^. LSP.method))
- listenServer (delete msg expectedMsgs) h semas
- else error $ "Got: " ++ show msg ++ "\n Expected: " ++ show (head (filter (not . isNotification) expectedMsgs))
+ when (not (msg `elem` expectedMsgs)) $ do
+ putStrLn "Got an unexpected message"
+ putMVar passVar False
- sendInitialize recH serverH = do
- message <- getNextMessage recH
- B.hPut serverH (addHeader message)
- putStrLn $ "Sent initialize response " ++ show message
- -- bring the file back to the start for haskell-lsp
- hSeek recH AbsoluteSeek 0
+ listenServer (delete msg expectedMsgs) h semas passVar
+ else do
+ putStrLn $ "Got: " ++ show msg ++ "\n Expected: " ++ show
+ (head (filter (not . isNotification) expectedMsgs))
+ putMVar passVar False
isNotification :: B.ByteString -> Bool
-isNotification msg = isJust (decode msg :: Maybe (LSP.NotificationMessage Value Value))
+isNotification msg =
+ isJust (decode msg :: Maybe (LSP.NotificationMessage Value Value))
whenResponse :: B.ByteString -> (LSP.ResponseMessage Value -> IO ()) -> IO ()
-whenResponse msg f =
- case decode msg :: Maybe (LSP.ResponseMessage Value) of
+whenResponse msg f = case decode msg :: Maybe (LSP.ResponseMessage Value) of
Just msg' -> when (isJust (msg' ^. LSP.result)) (f msg')
_ -> return ()
-whenRequest :: B.ByteString -> (LSP.RequestMessage Value Value Value -> IO ()) -> IO ()
-whenRequest msg = forM_ (decode msg :: (Maybe (LSP.RequestMessage Value Value Value)))
+whenRequest
+ :: B.ByteString -> (LSP.RequestMessage Value Value Value -> IO ()) -> IO ()
+whenRequest msg =
+ forM_ (decode msg :: (Maybe (LSP.RequestMessage Value Value Value)))
+
+whenNotification :: B.ByteString -> (LSP.NotificationMessage Value Value -> IO ()) -> IO ()
+whenNotification msg = forM_ (decode msg :: (Maybe (LSP.NotificationMessage Value Value)))
-- TODO: QuickCheck tests?
-- | Checks wether or not the message appears in the right order
inRightOrder :: B.ByteString -> [B.ByteString] -> Bool
inRightOrder _ [] = error "why is this empty"
inRightOrder received msgs = received `elem` valid
- where valid = takeWhile canSkip msgs ++ firstNonSkip
+ where
+ valid = takeWhile canSkip msgs ++ firstNonSkip
-- we don't care about the order of notifications
canSkip = isNotification
nonSkip = dropWhile canSkip msgs
- firstNonSkip
- | null nonSkip = []
+ firstNonSkip | null nonSkip = []
| otherwise = [head nonSkip]
getAllMessages :: Handle -> IO [B.ByteString]
, documentLinkHandler = Just request
, documentLinkResolveHandler = Just request
, executeCommandHandler = Just request
+ , initializeRequestHandler = Just request
-- Notifications
, didChangeConfigurationParamsHandler = Just notification
, didOpenTextDocumentNotificationHandler = Just notification
, initializedHandler = Just notification
, willSaveTextDocumentNotificationHandler = Just notification
, cancelNotificationHandler = Just notification
+ , exitNotificationHandler = Just notification
-- Responses
, responseHandler = Just response
}
where
- notification m = do
- B.hPut serverH $ addHeader (encode m)
- putStrLn "Sent a notification"
+ -- TODO: May need to prevent premature exit notification being sent
+ -- notification msg@(LSP.NotificationMessage _ LSP.Exit _) = do
+ -- putStrLn "Will send exit notification soon"
+ -- threadDelay 10000000
+ -- B.hPut serverH $ addHeader (encode msg)
+ notification msg @(LSP.NotificationMessage _ m _) = do
+ B.hPut serverH $ addHeader (encode msg)
- request msg@(LSP.RequestMessage _ id m _) = do
+ putStrLn $ "Sent a notification " ++ show m
+ request msg@(LSP.RequestMessage _ id m _) = do
B.hPut serverH $ addHeader (encode msg)
putStrLn $ "Sent a request id " ++ show id ++ ": " ++ show m ++ "\nWaiting for a response"
rspId <- takeMVar reqSema
- if LSP.responseId id /= rspId
- then error $ "Expected id " ++ show id ++ ", got " ++ show rspId
- else putStrLn $ "Got a response for request id " ++ show id ++ ": " ++ show m
+ when (LSP.responseId id /= rspId)
+ $ error
+ $ "Expected id "
+ ++ show id
+ ++ ", got "
+ ++ show rspId
response msg@(LSP.ResponseMessage _ id _ _) = do
putStrLn $ "Waiting for request id " ++ show id ++ " from the server"
resolver: nightly-2018-04-24
packages:
- .
+ - location:
+ ../haskell-lsp
+ extra-dep: true
extra-deps:
- github: Bubba/haskell-lsp-client
commit: b7cf14eb48837a73032e867dab90db1708220c66
- haskell-lsp-types-0.2.2.0
- - github: Bubba/haskell-lsp
- commit: d21ebcfbe8c2c09ac92e33fa18be0a2ce098b8bb
+ # - github: Bubba/haskell-lsp
+ # commit: 5bfaf0318e8a7700e1235cc139fdf894c052b8b0
- sorted-list-0.2.1.0
- github: yi-editor/yi-rope
commit: 7867909f4f20952be051fd4252cca5bbfc80cf41
--- /dev/null
+import Test.Hspec
+import Language.Haskell.LSP.Test.Recorded
+
+main = hspec $ do
+ describe "Replay" $ do
+ it "passes a test" $ do
+ replay "test/recordings/renamePass/client.log"
+ "test/recordings/renamePass/server.log"
+ `shouldReturn` True
+ it "fails a test" $
+ replay "test/recordings/documentSymbolFail/client.log"
+ "test/recordings/documentSymbolFail/server.log"
+ `shouldReturn` False
--- /dev/null
+Content-Length: 400\r
+\r
+{"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{"textDocument":{"completion":{"completionItem":{"commitCharactersSupport":null,"documentationFormat":null,"snippetSupport":false},"dynamicRegistration":null}}},"initializationOptions":null,"processId":37483,"rootPath":"/Users/luke/Repos/haskell-lsp-client","rootUri":"file:///Users/luke/Repos/haskell-lsp-client","trace":"off"},"id":9}Content-Length: 52\r
+\r
+{"jsonrpc":"2.0","method":"initialized","params":{}}Content-Length: 5528\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"languageId":"haskell","text":"{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE LambdaCase #-}\nmodule Main where\n\nimport qualified Language.Haskell.LSP.TH.DataTypesJSON as LSP\nimport qualified Language.Haskell.LSP.TH.ClientCapabilities as LSP\nimport qualified LSP.Client as Client\nimport Data.Proxy\nimport qualified Data.Text.IO as T\nimport Control.Concurrent\nimport System.Process\nimport Control.Lens\nimport System.IO\nimport System.Exit\nimport System.Environment\nimport System.Directory\nimport Control.Monad\n\nimport qualified Compat\n\nmain :: IO ()\nmain = do\n progName <- getProgName\n args <- getArgs\n\n when (length args /= 1) $ do\n hPutStrLn stderr (\"This program expects one argument: \" ++ progName ++ \" FILEPATH\")\n exitFailure\n\n let [path] = args\n\n exists <- doesFileExist path\n unless exists $ do\n hPutStrLn stderr (\"File does not exist: \" ++ path)\n exitFailure\n\n file <- canonicalizePath path\n\n pid <- Compat.getPID\n\n let caps = LSP.ClientCapabilities (Just workspaceCaps) (Just textDocumentCaps) Nothing\n workspaceCaps = LSP.WorkspaceClientCapabilities\n (Just False)\n (Just (LSP.WorkspaceEditClientCapabilities (Just False)))\n (Just (LSP.DidChangeConfigurationClientCapabilities (Just False)))\n (Just (LSP.DidChangeWatchedFilesClientCapabilities (Just False)))\n (Just (LSP.SymbolClientCapabilities (Just False)))\n (Just (LSP.ExecuteClientCapabilities (Just False)))\n textDocumentCaps = LSP.TextDocumentClientCapabilities\n (Just (LSP.SynchronizationTextDocumentClientCapabilities\n (Just False)\n (Just False)\n (Just False)\n (Just False)))\n (Just (LSP.CompletionClientCapabilities\n (Just False)\n (Just (LSP.CompletionItemClientCapabilities (Just False)))))\n (Just (LSP.HoverClientCapabilities (Just False)))\n (Just (LSP.SignatureHelpClientCapabilities (Just False)))\n (Just (LSP.ReferencesClientCapabilities (Just False)))\n (Just (LSP.DocumentHighlightClientCapabilities (Just False)))\n (Just (LSP.DocumentSymbolClientCapabilities (Just False)))\n (Just (LSP.FormattingClientCapabilities (Just False)))\n (Just (LSP.RangeFormattingClientCapabilities (Just False)))\n (Just (LSP.OnTypeFormattingClientCapabilities (Just False)))\n (Just (LSP.DefinitionClientCapabilities (Just False)))\n (Just (LSP.CodeActionClientCapabilities (Just False)))\n (Just (LSP.CodeLensClientCapabilities (Just False)))\n (Just (LSP.DocumentLinkClientCapabilities (Just False)))\n (Just (LSP.RenameClientCapabilities (Just False)))\n\n initializeParams :: LSP.InitializeParams\n initializeParams = LSP.InitializeParams (Just pid) Nothing Nothing Nothing caps Nothing\n\n\n (Just inp, Just out, _, _) <- createProcess (proc \"hie\" [\"--lsp\", \"-l\", \"/tmp/hie.log\", \"--debug\"])\n {std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe}\n\n client <- Client.start (Client.Config inp out testNotificationMessageHandler testRequestMessageHandler)\n\n Client.sendClientRequest client (Proxy :: Proxy LSP.InitializeRequest) LSP.Initialize initializeParams\n\n Client.sendClientNotification client LSP.Initialized (Just LSP.InitializedParams)\n\n txt <- T.readFile file\n\n let uri = LSP.filePathToUri file\n\n Client.sendClientNotification client LSP.TextDocumentDidOpen (Just (LSP.DidOpenTextDocumentParams (LSP.TextDocumentItem uri \"haskell\" 1 txt)))\n\n Client.sendClientRequest\n client\n (Proxy :: Proxy LSP.DefinitionRequest)\n LSP.TextDocumentDefinition\n (LSP.TextDocumentPositionParams (LSP.TextDocumentIdentifier uri) (LSP.Position 88 36)) >>= \\case\n Just (Right pos) -> print pos\n _ -> putStrLn \"Server couldn't give us defnition position\"\n\n Client.sendClientRequest client (Proxy :: Proxy LSP.DocumentSymbolRequest) LSP.TextDocumentDocumentSymbol (LSP.DocumentSymbolParams (LSP.TextDocumentIdentifier uri))\n >>= \\case\n Just (Right as) -> mapM_ T.putStrLn (as ^.. traverse . LSP.name)\n _ -> putStrLn \"Server couldn't give us document symbol information\"\n\n Client.sendClientRequest client (Proxy :: Proxy LSP.ShutdownRequest) LSP.Shutdown Nothing\n Client.sendClientNotification client LSP.Exit (Just LSP.ExitParams)\n\n Client.stop client\n\ntestRequestMessageHandler :: Client.RequestMessageHandler\ntestRequestMessageHandler = Client.RequestMessageHandler\n (\\m -> emptyResponse m <$ print m)\n (\\m -> emptyResponse m <$ print m)\n (\\m -> emptyResponse m <$ print m)\n (\\m -> emptyResponse m <$ print m)\n where\n toRspId (LSP.IdInt i) = LSP.IdRspInt i\n toRspId (LSP.IdString t) = LSP.IdRspString t\n\n emptyResponse :: LSP.RequestMessage m req resp -> LSP.ResponseMessage a\n emptyResponse m = LSP.ResponseMessage (m ^. LSP.jsonrpc) (toRspId (m ^. LSP.id)) Nothing Nothing\n\ntestNotificationMessageHandler :: Client.NotificationMessageHandler\ntestNotificationMessageHandler = Client.NotificationMessageHandler\n (T.putStrLn . view (LSP.params . LSP.message))\n (T.putStrLn . view (LSP.params . LSP.message))\n (print . view LSP.params)\n (mapM_ T.putStrLn . (^.. LSP.params . LSP.diagnostics . traverse . LSP.message))\n","uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","version":0}}}Content-Length: 38\r
+\r
+{"jsonrpc":"2.0","result":null,"id":0}Content-Length: 160\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/documentSymbol","params":{"textDocument":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs"}},"id":25}Content-Length: 160\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/documentSymbol","params":{"textDocument":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs"}},"id":30}Content-Length: 208\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/rename","params":{"newName":"pathh","position":{"character":30,"line":36},"textDocument":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs"}},"id":37}Content-Length: 47\r
+\r
+{"jsonrpc":"2.0","method":"exit","params":null}
\ No newline at end of file
--- /dev/null
+Content-Length: 579\r
+\r
+{"result":{"capabilities":{"textDocumentSync":{"openClose":true,"change":2,"willSave":false,"willSaveWaitUntil":false,"save":{"includeText":false}},"hoverProvider":true,"completionProvider":{"resolveProvider":true,"triggerCharacters":["."]},"definitionProvider":true,"referencesProvider":true,"documentHighlightProvider":true,"documentSymbolProvider":true,"codeActionProvider":true,"documentFormattingProvider":true,"documentRangeFormattingProvider":true,"renameProvider":true,"executeCommandProvider":{"commands":["applyrefact:applyOne","hare:demote"]}}},"jsonrpc":"2.0","id":9}Content-Length: 209\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"client/registerCapability","params":{"registrations":[{"registerOptions":{"documentSelector":{"language":"haskell"}},"method":"workspace/executeCommand","id":"hare:demote"}]}}Content-Length: 208\r
+\r
+{"jsonrpc":"2.0","method":"window/logMessage","params":{"type":4,"message":"Using ghc version: Version 0.2.0.0, Git revision 73fc39b23129436626232230e0f79a391120db5a (dirty) (1350 commits) x86_64 ghc-8.4.2"}}Content-Length: 145\r
+\r
+{"jsonrpc":"2.0","method":"window/logMessage","params":{"type":4,"message":"Using hoogle db at: /Users/luke/.hoogle/default-haskell-5.0.17.hoo"}}Content-Length: 156\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","diagnostics":[]}}Content-Length: 156\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","diagnostics":[]}}Content-Length: 3454\r
+\r
+{"result":[{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":4,"character":17},"end":{"line":4,"character":54}}},"kind":3,"containerName":"LSP","name":"Language.Haskell.LSP.TH.DataTypesJSON"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":5,"character":17},"end":{"line":5,"character":59}}},"kind":3,"containerName":"LSP","name":"Language.Haskell.LSP.TH.ClientCapabilities"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":6,"character":17},"end":{"line":6,"character":27}}},"kind":3,"containerName":"Client","name":"LSP.Client"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":7,"character":7},"end":{"line":7,"character":17}}},"kind":2,"name":"Data.Proxy"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":8,"character":17},"end":{"line":8,"character":29}}},"kind":3,"containerName":"T","name":"Data.Text.IO"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":9,"character":7},"end":{"line":9,"character":25}}},"kind":2,"name":"Control.Concurrent"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":10,"character":7},"end":{"line":10,"character":21}}},"kind":2,"name":"System.Process"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":11,"character":7},"end":{"line":11,"character":19}}},"kind":2,"name":"Control.Lens"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":12,"character":7},"end":{"line":12,"character":16}}},"kind":2,"name":"System.IO"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":13,"character":7},"end":{"line":13,"character":18}}},"kind":2,"name":"System.Exit"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":14,"character":7},"end":{"line":14,"character":25}}},"kind":2,"name":"System.Environment"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":15,"character":7},"end":{"line":15,"character":23}}},"kind":2,"name":"System.Directory"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":16,"character":7},"end":{"line":16,"character":20}}},"kind":2,"name":"Control.Monad"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":18,"character":17},"end":{"line":18,"character":23}}},"kind":2,"name":"Compat"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":21,"character":0},"end":{"line":21,"character":4}}},"kind":12,"name":"main"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":109,"character":0},"end":{"line":109,"character":25}}},"kind":12,"name":"testRequestMessageHandler"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":122,"character":0},"end":{"line":122,"character":30}}},"kind":12,"name":"testNotificationMessageHandler"}],"jsonrpc":"2.0","id":25}Content-Length: 3454\r
+\r
+{"result":[{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":4,"character":17},"end":{"line":4,"character":54}}},"kind":3,"containerName":"LSP","name":"Language.Haskell.LSP.TH.DataTypesJSON"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":5,"character":17},"end":{"line":5,"character":59}}},"kind":3,"containerName":"LSP","name":"Language.Haskell.LSP.TH.ClientCapabilities"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":6,"character":17},"end":{"line":6,"character":27}}},"kind":3,"containerName":"Client","name":"LSP.Client"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":7,"character":7},"end":{"line":7,"character":17}}},"kind":2,"name":"Data.Proxy"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":8,"character":17},"end":{"line":8,"character":29}}},"kind":3,"containerName":"T","name":"Data.Text.IO"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":9,"character":7},"end":{"line":9,"character":25}}},"kind":2,"name":"Control.Concurrent"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":10,"character":7},"end":{"line":10,"character":21}}},"kind":2,"name":"System.Process"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":11,"character":7},"end":{"line":11,"character":19}}},"kind":2,"name":"Control.Lens"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":12,"character":7},"end":{"line":12,"character":16}}},"kind":2,"name":"System.IO"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":13,"character":7},"end":{"line":13,"character":18}}},"kind":2,"name":"System.Exit"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":14,"character":7},"end":{"line":14,"character":25}}},"kind":2,"name":"System.Environment"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":15,"character":7},"end":{"line":15,"character":23}}},"kind":2,"name":"System.Directory"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":16,"character":7},"end":{"line":16,"character":20}}},"kind":2,"name":"Control.Monad"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":18,"character":17},"end":{"line":18,"character":23}}},"kind":2,"name":"Compat"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":21,"character":0},"end":{"line":21,"character":4}}},"kind":12,"name":"main"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":109,"character":0},"end":{"line":109,"character":25}}},"kind":12,"name":"testRequestMessageHandler"},{"location":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","range":{"start":{"line":122,"character":0},"end":{"line":122,"character":30}}},"kind":12,"name":"testNotificationMessageHandler"}],"jsonrpc":"2.0","id":30}Content-Length: 620\r
+\r
+{"result":{"changes":{"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs":[{"range":{"start":{"line":36,"character":0},"end":{"line":36,"character":31}},"newText":" file <- canonicalizePath pathh"},{"range":{"start":{"line":33,"character":0},"end":{"line":33,"character":54}},"newText":" hPutStrLn stderr (\"File does not exist: \" ++ pathh)"},{"range":{"start":{"line":31,"character":0},"end":{"line":31,"character":30}},"newText":" exists <- doesFileExist pathh"},{"range":{"start":{"line":29,"character":0},"end":{"line":29,"character":19}},"newText":" let [pathh] = args"}]}},"jsonrpc":"2.0","id":37}
\ No newline at end of file
--- /dev/null
+Content-Length: 400\r
+\r
+{"jsonrpc":"2.0","method":"initialize","params":{"capabilities":{"textDocument":{"completion":{"completionItem":{"commitCharactersSupport":null,"documentationFormat":null,"snippetSupport":false},"dynamicRegistration":null}}},"initializationOptions":null,"processId":60645,"rootPath":"/Users/luke/Repos/haskell-lsp-client","rootUri":"file:///Users/luke/Repos/haskell-lsp-client","trace":"off"},"id":9}Content-Length: 52\r
+\r
+{"jsonrpc":"2.0","method":"initialized","params":{}}Content-Length: 5528\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"languageId":"haskell","text":"{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE LambdaCase #-}\nmodule Main where\n\nimport qualified Language.Haskell.LSP.TH.DataTypesJSON as LSP\nimport qualified Language.Haskell.LSP.TH.ClientCapabilities as LSP\nimport qualified LSP.Client as Client\nimport Data.Proxy\nimport qualified Data.Text.IO as T\nimport Control.Concurrent\nimport System.Process\nimport Control.Lens\nimport System.IO\nimport System.Exit\nimport System.Environment\nimport System.Directory\nimport Control.Monad\n\nimport qualified Compat\n\nmain :: IO ()\nmain = do\n progName <- getProgName\n args <- getArgs\n\n when (length args /= 1) $ do\n hPutStrLn stderr (\"This program expects one argument: \" ++ progName ++ \" FILEPATH\")\n exitFailure\n\n let [path] = args\n\n exists <- doesFileExist path\n unless exists $ do\n hPutStrLn stderr (\"File does not exist: \" ++ path)\n exitFailure\n\n file <- canonicalizePath path\n\n pid <- Compat.getPID\n\n let caps = LSP.ClientCapabilities (Just workspaceCaps) (Just textDocumentCaps) Nothing\n workspaceCaps = LSP.WorkspaceClientCapabilities\n (Just False)\n (Just (LSP.WorkspaceEditClientCapabilities (Just False)))\n (Just (LSP.DidChangeConfigurationClientCapabilities (Just False)))\n (Just (LSP.DidChangeWatchedFilesClientCapabilities (Just False)))\n (Just (LSP.SymbolClientCapabilities (Just False)))\n (Just (LSP.ExecuteClientCapabilities (Just False)))\n textDocumentCaps = LSP.TextDocumentClientCapabilities\n (Just (LSP.SynchronizationTextDocumentClientCapabilities\n (Just False)\n (Just False)\n (Just False)\n (Just False)))\n (Just (LSP.CompletionClientCapabilities\n (Just False)\n (Just (LSP.CompletionItemClientCapabilities (Just False)))))\n (Just (LSP.HoverClientCapabilities (Just False)))\n (Just (LSP.SignatureHelpClientCapabilities (Just False)))\n (Just (LSP.ReferencesClientCapabilities (Just False)))\n (Just (LSP.DocumentHighlightClientCapabilities (Just False)))\n (Just (LSP.DocumentSymbolClientCapabilities (Just False)))\n (Just (LSP.FormattingClientCapabilities (Just False)))\n (Just (LSP.RangeFormattingClientCapabilities (Just False)))\n (Just (LSP.OnTypeFormattingClientCapabilities (Just False)))\n (Just (LSP.DefinitionClientCapabilities (Just False)))\n (Just (LSP.CodeActionClientCapabilities (Just False)))\n (Just (LSP.CodeLensClientCapabilities (Just False)))\n (Just (LSP.DocumentLinkClientCapabilities (Just False)))\n (Just (LSP.RenameClientCapabilities (Just False)))\n\n initializeParams :: LSP.InitializeParams\n initializeParams = LSP.InitializeParams (Just pid) Nothing Nothing Nothing caps Nothing\n\n\n (Just inp, Just out, _, _) <- createProcess (proc \"hie\" [\"--lsp\", \"-l\", \"/tmp/hie.log\", \"--debug\"])\n {std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe}\n\n client <- Client.start (Client.Config inp out testNotificationMessageHandler testRequestMessageHandler)\n\n Client.sendClientRequest client (Proxy :: Proxy LSP.InitializeRequest) LSP.Initialize initializeParams\n\n Client.sendClientNotification client LSP.Initialized (Just LSP.InitializedParams)\n\n txt <- T.readFile file\n\n let uri = LSP.filePathToUri file\n\n Client.sendClientNotification client LSP.TextDocumentDidOpen (Just (LSP.DidOpenTextDocumentParams (LSP.TextDocumentItem uri \"haskell\" 1 txt)))\n\n Client.sendClientRequest\n client\n (Proxy :: Proxy LSP.DefinitionRequest)\n LSP.TextDocumentDefinition\n (LSP.TextDocumentPositionParams (LSP.TextDocumentIdentifier uri) (LSP.Position 88 36)) >>= \\case\n Just (Right pos) -> print pos\n _ -> putStrLn \"Server couldn't give us defnition position\"\n\n Client.sendClientRequest client (Proxy :: Proxy LSP.DocumentSymbolRequest) LSP.TextDocumentDocumentSymbol (LSP.DocumentSymbolParams (LSP.TextDocumentIdentifier uri))\n >>= \\case\n Just (Right as) -> mapM_ T.putStrLn (as ^.. traverse . LSP.name)\n _ -> putStrLn \"Server couldn't give us document symbol information\"\n\n Client.sendClientRequest client (Proxy :: Proxy LSP.ShutdownRequest) LSP.Shutdown Nothing\n Client.sendClientNotification client LSP.Exit (Just LSP.ExitParams)\n\n Client.stop client\n\ntestRequestMessageHandler :: Client.RequestMessageHandler\ntestRequestMessageHandler = Client.RequestMessageHandler\n (\\m -> emptyResponse m <$ print m)\n (\\m -> emptyResponse m <$ print m)\n (\\m -> emptyResponse m <$ print m)\n (\\m -> emptyResponse m <$ print m)\n where\n toRspId (LSP.IdInt i) = LSP.IdRspInt i\n toRspId (LSP.IdString t) = LSP.IdRspString t\n\n emptyResponse :: LSP.RequestMessage m req resp -> LSP.ResponseMessage a\n emptyResponse m = LSP.ResponseMessage (m ^. LSP.jsonrpc) (toRspId (m ^. LSP.id)) Nothing Nothing\n\ntestNotificationMessageHandler :: Client.NotificationMessageHandler\ntestNotificationMessageHandler = Client.NotificationMessageHandler\n (T.putStrLn . view (LSP.params . LSP.message))\n (T.putStrLn . view (LSP.params . LSP.message))\n (print . view LSP.params)\n (mapM_ T.putStrLn . (^.. LSP.params . LSP.diagnostics . traverse . LSP.message))\n","uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","version":0}}}Content-Length: 38\r
+\r
+{"jsonrpc":"2.0","result":null,"id":0}Content-Length: 5560\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"contentChanges":[{"range":null,"rangeLength":null,"text":"{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE LambdaCase #-}\nmodule Main where\n\nimport qualified Language.Haskell.LSP.TH.DataTypesJSON as LSP\nimport qualified Language.Haskell.LSP.TH.ClientCapabilities as LSP\nimport qualified LSP.Client as Client\nimport Data.Proxy\nimport qualified Data.Text.IO as T\nimport Control.Concurrent\nimport System.Process\nimport Control.Lens\nimport System.IO\nimport System.Exit\nimport System.Environment\nimport System.Directory\nimport Control.Monad\n\nimport qualified Compat\n\nmain :: IO ()\nmain = do\n progName <- getProgName\n args <- getArgs\n\n when (length args /= 1) $ do\n hPutStrLn stderr (\"This program expects one argument: \" ++ progName ++ \" FILEPATH\")\n exitFailure\n\n let [path] = args\n\n exists <- doesFileExist path\n unless exists $ do\n hPutStrLn stderr (\"File does not exist: \" ++ path)\n exitFailure\n\n file <- canonicalizePath patj\n\n pid <- Compat.getPID\n\n let caps = LSP.ClientCapabilities (Just workspaceCaps) (Just textDocumentCaps) Nothing\n workspaceCaps = LSP.WorkspaceClientCapabilities\n (Just False)\n (Just (LSP.WorkspaceEditClientCapabilities (Just False)))\n (Just (LSP.DidChangeConfigurationClientCapabilities (Just False)))\n (Just (LSP.DidChangeWatchedFilesClientCapabilities (Just False)))\n (Just (LSP.SymbolClientCapabilities (Just False)))\n (Just (LSP.ExecuteClientCapabilities (Just False)))\n textDocumentCaps = LSP.TextDocumentClientCapabilities\n (Just (LSP.SynchronizationTextDocumentClientCapabilities\n (Just False)\n (Just False)\n (Just False)\n (Just False)))\n (Just (LSP.CompletionClientCapabilities\n (Just False)\n (Just (LSP.CompletionItemClientCapabilities (Just False)))))\n (Just (LSP.HoverClientCapabilities (Just False)))\n (Just (LSP.SignatureHelpClientCapabilities (Just False)))\n (Just (LSP.ReferencesClientCapabilities (Just False)))\n (Just (LSP.DocumentHighlightClientCapabilities (Just False)))\n (Just (LSP.DocumentSymbolClientCapabilities (Just False)))\n (Just (LSP.FormattingClientCapabilities (Just False)))\n (Just (LSP.RangeFormattingClientCapabilities (Just False)))\n (Just (LSP.OnTypeFormattingClientCapabilities (Just False)))\n (Just (LSP.DefinitionClientCapabilities (Just False)))\n (Just (LSP.CodeActionClientCapabilities (Just False)))\n (Just (LSP.CodeLensClientCapabilities (Just False)))\n (Just (LSP.DocumentLinkClientCapabilities (Just False)))\n (Just (LSP.RenameClientCapabilities (Just False)))\n\n initializeParams :: LSP.InitializeParams\n initializeParams = LSP.InitializeParams (Just pid) Nothing Nothing Nothing caps Nothing\n\n\n (Just inp, Just out, _, _) <- createProcess (proc \"hie\" [\"--lsp\", \"-l\", \"/tmp/hie.log\", \"--debug\"])\n {std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe}\n\n client <- Client.start (Client.Config inp out testNotificationMessageHandler testRequestMessageHandler)\n\n Client.sendClientRequest client (Proxy :: Proxy LSP.InitializeRequest) LSP.Initialize initializeParams\n\n Client.sendClientNotification client LSP.Initialized (Just LSP.InitializedParams)\n\n txt <- T.readFile file\n\n let uri = LSP.filePathToUri file\n\n Client.sendClientNotification client LSP.TextDocumentDidOpen (Just (LSP.DidOpenTextDocumentParams (LSP.TextDocumentItem uri \"haskell\" 1 txt)))\n\n Client.sendClientRequest\n client\n (Proxy :: Proxy LSP.DefinitionRequest)\n LSP.TextDocumentDefinition\n (LSP.TextDocumentPositionParams (LSP.TextDocumentIdentifier uri) (LSP.Position 88 36)) >>= \\case\n Just (Right pos) -> print pos\n _ -> putStrLn \"Server couldn't give us defnition position\"\n\n Client.sendClientRequest client (Proxy :: Proxy LSP.DocumentSymbolRequest) LSP.TextDocumentDocumentSymbol (LSP.DocumentSymbolParams (LSP.TextDocumentIdentifier uri))\n >>= \\case\n Just (Right as) -> mapM_ T.putStrLn (as ^.. traverse . LSP.name)\n _ -> putStrLn \"Server couldn't give us document symbol information\"\n\n Client.sendClientRequest client (Proxy :: Proxy LSP.ShutdownRequest) LSP.Shutdown Nothing\n Client.sendClientNotification client LSP.Exit (Just LSP.ExitParams)\n\n Client.stop client\n\ntestRequestMessageHandler :: Client.RequestMessageHandler\ntestRequestMessageHandler = Client.RequestMessageHandler\n (\\m -> emptyResponse m <$ print m)\n (\\m -> emptyResponse m <$ print m)\n (\\m -> emptyResponse m <$ print m)\n (\\m -> emptyResponse m <$ print m)\n where\n toRspId (LSP.IdInt i) = LSP.IdRspInt i\n toRspId (LSP.IdString t) = LSP.IdRspString t\n\n emptyResponse :: LSP.RequestMessage m req resp -> LSP.ResponseMessage a\n emptyResponse m = LSP.ResponseMessage (m ^. LSP.jsonrpc) (toRspId (m ^. LSP.id)) Nothing Nothing\n\ntestNotificationMessageHandler :: Client.NotificationMessageHandler\ntestNotificationMessageHandler = Client.NotificationMessageHandler\n (T.putStrLn . view (LSP.params . LSP.message))\n (T.putStrLn . view (LSP.params . LSP.message))\n (print . view LSP.params)\n (mapM_ T.putStrLn . (^.. LSP.params . LSP.diagnostics . traverse . LSP.message))\n"}],"textDocument":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","version":1}}}Content-Length: 5560\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"contentChanges":[{"range":null,"rangeLength":null,"text":"{-# LANGUAGE OverloadedStrings #-}\n{-# LANGUAGE LambdaCase #-}\nmodule Main where\n\nimport qualified Language.Haskell.LSP.TH.DataTypesJSON as LSP\nimport qualified Language.Haskell.LSP.TH.ClientCapabilities as LSP\nimport qualified LSP.Client as Client\nimport Data.Proxy\nimport qualified Data.Text.IO as T\nimport Control.Concurrent\nimport System.Process\nimport Control.Lens\nimport System.IO\nimport System.Exit\nimport System.Environment\nimport System.Directory\nimport Control.Monad\n\nimport qualified Compat\n\nmain :: IO ()\nmain = do\n progName <- getProgName\n args <- getArgs\n\n when (length args /= 1) $ do\n hPutStrLn stderr (\"This program expects one argument: \" ++ progName ++ \" FILEPATH\")\n exitFailure\n\n let [path] = args\n\n exists <- doesFileExist path\n unless exists $ do\n hPutStrLn stderr (\"File does not exist: \" ++ path)\n exitFailure\n\n file <- canonicalizePath path\n\n pid <- Compat.getPID\n\n let caps = LSP.ClientCapabilities (Just workspaceCaps) (Just textDocumentCaps) Nothing\n workspaceCaps = LSP.WorkspaceClientCapabilities\n (Just False)\n (Just (LSP.WorkspaceEditClientCapabilities (Just False)))\n (Just (LSP.DidChangeConfigurationClientCapabilities (Just False)))\n (Just (LSP.DidChangeWatchedFilesClientCapabilities (Just False)))\n (Just (LSP.SymbolClientCapabilities (Just False)))\n (Just (LSP.ExecuteClientCapabilities (Just False)))\n textDocumentCaps = LSP.TextDocumentClientCapabilities\n (Just (LSP.SynchronizationTextDocumentClientCapabilities\n (Just False)\n (Just False)\n (Just False)\n (Just False)))\n (Just (LSP.CompletionClientCapabilities\n (Just False)\n (Just (LSP.CompletionItemClientCapabilities (Just False)))))\n (Just (LSP.HoverClientCapabilities (Just False)))\n (Just (LSP.SignatureHelpClientCapabilities (Just False)))\n (Just (LSP.ReferencesClientCapabilities (Just False)))\n (Just (LSP.DocumentHighlightClientCapabilities (Just False)))\n (Just (LSP.DocumentSymbolClientCapabilities (Just False)))\n (Just (LSP.FormattingClientCapabilities (Just False)))\n (Just (LSP.RangeFormattingClientCapabilities (Just False)))\n (Just (LSP.OnTypeFormattingClientCapabilities (Just False)))\n (Just (LSP.DefinitionClientCapabilities (Just False)))\n (Just (LSP.CodeActionClientCapabilities (Just False)))\n (Just (LSP.CodeLensClientCapabilities (Just False)))\n (Just (LSP.DocumentLinkClientCapabilities (Just False)))\n (Just (LSP.RenameClientCapabilities (Just False)))\n\n initializeParams :: LSP.InitializeParams\n initializeParams = LSP.InitializeParams (Just pid) Nothing Nothing Nothing caps Nothing\n\n\n (Just inp, Just out, _, _) <- createProcess (proc \"hie\" [\"--lsp\", \"-l\", \"/tmp/hie.log\", \"--debug\"])\n {std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe}\n\n client <- Client.start (Client.Config inp out testNotificationMessageHandler testRequestMessageHandler)\n\n Client.sendClientRequest client (Proxy :: Proxy LSP.InitializeRequest) LSP.Initialize initializeParams\n\n Client.sendClientNotification client LSP.Initialized (Just LSP.InitializedParams)\n\n txt <- T.readFile file\n\n let uri = LSP.filePathToUri file\n\n Client.sendClientNotification client LSP.TextDocumentDidOpen (Just (LSP.DidOpenTextDocumentParams (LSP.TextDocumentItem uri \"haskell\" 1 txt)))\n\n Client.sendClientRequest\n client\n (Proxy :: Proxy LSP.DefinitionRequest)\n LSP.TextDocumentDefinition\n (LSP.TextDocumentPositionParams (LSP.TextDocumentIdentifier uri) (LSP.Position 88 36)) >>= \\case\n Just (Right pos) -> print pos\n _ -> putStrLn \"Server couldn't give us defnition position\"\n\n Client.sendClientRequest client (Proxy :: Proxy LSP.DocumentSymbolRequest) LSP.TextDocumentDocumentSymbol (LSP.DocumentSymbolParams (LSP.TextDocumentIdentifier uri))\n >>= \\case\n Just (Right as) -> mapM_ T.putStrLn (as ^.. traverse . LSP.name)\n _ -> putStrLn \"Server couldn't give us document symbol information\"\n\n Client.sendClientRequest client (Proxy :: Proxy LSP.ShutdownRequest) LSP.Shutdown Nothing\n Client.sendClientNotification client LSP.Exit (Just LSP.ExitParams)\n\n Client.stop client\n\ntestRequestMessageHandler :: Client.RequestMessageHandler\ntestRequestMessageHandler = Client.RequestMessageHandler\n (\\m -> emptyResponse m <$ print m)\n (\\m -> emptyResponse m <$ print m)\n (\\m -> emptyResponse m <$ print m)\n (\\m -> emptyResponse m <$ print m)\n where\n toRspId (LSP.IdInt i) = LSP.IdRspInt i\n toRspId (LSP.IdString t) = LSP.IdRspString t\n\n emptyResponse :: LSP.RequestMessage m req resp -> LSP.ResponseMessage a\n emptyResponse m = LSP.ResponseMessage (m ^. LSP.jsonrpc) (toRspId (m ^. LSP.id)) Nothing Nothing\n\ntestNotificationMessageHandler :: Client.NotificationMessageHandler\ntestNotificationMessageHandler = Client.NotificationMessageHandler\n (T.putStrLn . view (LSP.params . LSP.message))\n (T.putStrLn . view (LSP.params . LSP.message))\n (print . view LSP.params)\n (mapM_ T.putStrLn . (^.. LSP.params . LSP.diagnostics . traverse . LSP.message))\n"}],"textDocument":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","version":2}}}Content-Length: 210\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/rename","params":{"newName":"notPath","position":{"character":30,"line":36},"textDocument":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs"}},"id":45}Content-Length: 47\r
+\r
+{"jsonrpc":"2.0","method":"exit","params":null}
\ No newline at end of file
--- /dev/null
+Content-Length: 579\r
+\r
+{"result":{"capabilities":{"textDocumentSync":{"openClose":true,"change":2,"willSave":false,"willSaveWaitUntil":false,"save":{"includeText":false}},"hoverProvider":true,"completionProvider":{"resolveProvider":true,"triggerCharacters":["."]},"definitionProvider":true,"referencesProvider":true,"documentHighlightProvider":true,"documentSymbolProvider":true,"codeActionProvider":true,"documentFormattingProvider":true,"documentRangeFormattingProvider":true,"renameProvider":true,"executeCommandProvider":{"commands":["applyrefact:applyOne","hare:demote"]}}},"jsonrpc":"2.0","id":9}Content-Length: 209\r
+\r
+{"jsonrpc":"2.0","id":0,"method":"client/registerCapability","params":{"registrations":[{"registerOptions":{"documentSelector":{"language":"haskell"}},"method":"workspace/executeCommand","id":"hare:demote"}]}}Content-Length: 208\r
+\r
+{"jsonrpc":"2.0","method":"window/logMessage","params":{"type":4,"message":"Using ghc version: Version 0.2.0.0, Git revision 73fc39b23129436626232230e0f79a391120db5a (dirty) (1350 commits) x86_64 ghc-8.4.2"}}Content-Length: 145\r
+\r
+{"jsonrpc":"2.0","method":"window/logMessage","params":{"type":4,"message":"Using hoogle db at: /Users/luke/.hoogle/default-haskell-5.0.17.hoo"}}Content-Length: 156\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","diagnostics":[]}}Content-Length: 156\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","diagnostics":[]}}Content-Length: 156\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","diagnostics":[]}}Content-Length: 366\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","diagnostics":[{"severity":1,"range":{"start":{"line":36,"character":27},"end":{"line":36,"character":31}},"source":"ghcmod","message":"• Variable not in scope: patj :: FilePath\n• Perhaps you meant ‘path’ (line 30)"}]}}Content-Length: 366\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","diagnostics":[{"severity":1,"range":{"start":{"line":36,"character":27},"end":{"line":36,"character":31}},"source":"ghcmod","message":"• Variable not in scope: patj :: FilePath\n• Perhaps you meant ‘path’ (line 30)"}]}}Content-Length: 156\r
+\r
+{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","diagnostics":[]}}Content-Length: 628\r
+\r
+{"result":{"changes":{"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs":[{"range":{"start":{"line":36,"character":0},"end":{"line":36,"character":31}},"newText":" file <- canonicalizePath notPath"},{"range":{"start":{"line":33,"character":0},"end":{"line":33,"character":54}},"newText":" hPutStrLn stderr (\"File does not exist: \" ++ notPath)"},{"range":{"start":{"line":31,"character":0},"end":{"line":31,"character":30}},"newText":" exists <- doesFileExist notPath"},{"range":{"start":{"line":29,"character":0},"end":{"line":29,"character":19}},"newText":" let [notPath] = args"}]}},"jsonrpc":"2.0","id":45}
\ No newline at end of file