From 35925b4a5a5e24e1611431106fd8ddd7c89a4436 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Sat, 19 May 2018 20:01:15 -0400 Subject: [PATCH] Add tests Use new LSP handlers Fix directory getting changed Return a success bool --- example/Recorded.hs | 5 +- haskell-lsp-test.cabal | 11 ++ src/Language/Haskell/LSP/Test/Recorded.hs | 123 +++++++++++------- stack.yaml | 7 +- test/Test.hs | 13 ++ test/recordings/documentSymbolFail/client.log | 17 +++ test/recordings/documentSymbolFail/server.log | 19 +++ test/recordings/renamePass/client.log | 17 +++ test/recordings/renamePass/server.log | 23 ++++ 9 files changed, 186 insertions(+), 49 deletions(-) create mode 100644 test/Test.hs create mode 100644 test/recordings/documentSymbolFail/client.log create mode 100644 test/recordings/documentSymbolFail/server.log create mode 100644 test/recordings/renamePass/client.log create mode 100644 test/recordings/renamePass/server.log diff --git a/example/Recorded.hs b/example/Recorded.hs index e267ff8..470bfdb 100644 --- a/example/Recorded.hs +++ b/example/Recorded.hs @@ -3,5 +3,6 @@ import System.Directory 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" diff --git a/haskell-lsp-test.cabal b/haskell-lsp-test.cabal index b00d507..de92fce 100644 --- a/haskell-lsp-test.cabal +++ b/haskell-lsp-test.cabal @@ -29,6 +29,7 @@ library , text , transformers , process + , directory if os(windows) build-depends: win32 else @@ -37,6 +38,16 @@ library 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 diff --git a/src/Language/Haskell/LSP/Test/Recorded.hs b/src/Language/Haskell/LSP/Test/Recorded.hs index d109d9a..746ff0b 100644 --- a/src/Language/Haskell/LSP/Test/Recorded.hs +++ b/src/Language/Haskell/LSP/Test/Recorded.hs @@ -1,5 +1,4 @@ {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE FlexibleContexts #-} module Language.Haskell.LSP.Test.Recorded ( replay ) @@ -17,16 +16,21 @@ import Data.Maybe 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 } @@ -41,6 +45,8 @@ replay cfp sfp = do 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 @@ -50,17 +56,10 @@ replay cfp sfp = do 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) @@ -68,44 +67,68 @@ replay cfp sfp = do 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 @@ -120,12 +143,12 @@ whenRequest msg = forM_ (decode msg :: (Maybe (LSP.RequestMessage Value Value Va 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] @@ -170,6 +193,7 @@ handlers serverH (reqSema, rspSema) = def , documentLinkHandler = Just request , documentLinkResolveHandler = Just request , executeCommandHandler = Just request + , initializeRequestHandler = Just request -- Notifications , didChangeConfigurationParamsHandler = Just notification , didOpenTextDocumentNotificationHandler = Just notification @@ -180,23 +204,32 @@ handlers serverH (reqSema, rspSema) = def , 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" diff --git a/stack.yaml b/stack.yaml index f85170d..afe5620 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,13 +1,16 @@ 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 diff --git a/test/Test.hs b/test/Test.hs new file mode 100644 index 0000000..703265e --- /dev/null +++ b/test/Test.hs @@ -0,0 +1,13 @@ +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 diff --git a/test/recordings/documentSymbolFail/client.log b/test/recordings/documentSymbolFail/client.log new file mode 100644 index 0000000..4e5566a --- /dev/null +++ b/test/recordings/documentSymbolFail/client.log @@ -0,0 +1,17 @@ +Content-Length: 400 + +{"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 + +{"jsonrpc":"2.0","method":"initialized","params":{}}Content-Length: 5528 + +{"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 + +{"jsonrpc":"2.0","result":null,"id":0}Content-Length: 160 + +{"jsonrpc":"2.0","method":"textDocument/documentSymbol","params":{"textDocument":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs"}},"id":25}Content-Length: 160 + +{"jsonrpc":"2.0","method":"textDocument/documentSymbol","params":{"textDocument":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs"}},"id":30}Content-Length: 208 + +{"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 + +{"jsonrpc":"2.0","method":"exit","params":null} \ No newline at end of file diff --git a/test/recordings/documentSymbolFail/server.log b/test/recordings/documentSymbolFail/server.log new file mode 100644 index 0000000..d6bf4f8 --- /dev/null +++ b/test/recordings/documentSymbolFail/server.log @@ -0,0 +1,19 @@ +Content-Length: 579 + +{"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 + +{"jsonrpc":"2.0","id":0,"method":"client/registerCapability","params":{"registrations":[{"registerOptions":{"documentSelector":{"language":"haskell"}},"method":"workspace/executeCommand","id":"hare:demote"}]}}Content-Length: 208 + +{"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 + +{"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 + +{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","diagnostics":[]}}Content-Length: 156 + +{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","diagnostics":[]}}Content-Length: 3454 + +{"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 + +{"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 + +{"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 diff --git a/test/recordings/renamePass/client.log b/test/recordings/renamePass/client.log new file mode 100644 index 0000000..2d854f4 --- /dev/null +++ b/test/recordings/renamePass/client.log @@ -0,0 +1,17 @@ +Content-Length: 400 + +{"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 + +{"jsonrpc":"2.0","method":"initialized","params":{}}Content-Length: 5528 + +{"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 + +{"jsonrpc":"2.0","result":null,"id":0}Content-Length: 5560 + +{"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 + +{"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 + +{"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 + +{"jsonrpc":"2.0","method":"exit","params":null} \ No newline at end of file diff --git a/test/recordings/renamePass/server.log b/test/recordings/renamePass/server.log new file mode 100644 index 0000000..9225b4c --- /dev/null +++ b/test/recordings/renamePass/server.log @@ -0,0 +1,23 @@ +Content-Length: 579 + +{"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 + +{"jsonrpc":"2.0","id":0,"method":"client/registerCapability","params":{"registrations":[{"registerOptions":{"documentSelector":{"language":"haskell"}},"method":"workspace/executeCommand","id":"hare:demote"}]}}Content-Length: 208 + +{"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 + +{"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 + +{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","diagnostics":[]}}Content-Length: 156 + +{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","diagnostics":[]}}Content-Length: 156 + +{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","diagnostics":[]}}Content-Length: 366 + +{"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 + +{"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 + +{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:///Users/luke/Repos/haskell-lsp-client/example/Main.hs","diagnostics":[]}}Content-Length: 628 + +{"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 -- 2.30.2