Add tests
authorLuke Lau <luke_lau@icloud.com>
Sun, 20 May 2018 00:01:15 +0000 (20:01 -0400)
committerLuke Lau <luke_lau@icloud.com>
Sun, 20 May 2018 00:01:15 +0000 (20:01 -0400)
Use new LSP handlers
Fix directory getting changed
Return a success bool

example/Recorded.hs
haskell-lsp-test.cabal
src/Language/Haskell/LSP/Test/Recorded.hs
stack.yaml
test/Test.hs [new file with mode: 0644]
test/recordings/documentSymbolFail/client.log [new file with mode: 0644]
test/recordings/documentSymbolFail/server.log [new file with mode: 0644]
test/recordings/renamePass/client.log [new file with mode: 0644]
test/recordings/renamePass/server.log [new file with mode: 0644]

index e267ff80ee9f9a1d76f45bb7f7953804c2cd1223..470bfdbb7bcba1a8cf1ac8bfba3e2d9937528772 100644 (file)
@@ -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"
index b00d5077ea3df7e45ae0e21ab8aec2819b89f7af..de92fce668b51032a0b669bab8a7c4dce63405d1 100644 (file)
@@ -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
index d109d9a74a9140abfec989b20120b5105db3c21e..746ff0bf7323ca2ce01690d985c569bed481862d 100644 (file)
@@ -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"
index f85170d085e22ec03459fa8308427e4816c883d4..afe56201c3a2cec8ae894bb124dc789928877e2e 100644 (file)
@@ -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 (file)
index 0000000..703265e
--- /dev/null
@@ -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 (file)
index 0000000..4e5566a
--- /dev/null
@@ -0,0 +1,17 @@
+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
diff --git a/test/recordings/documentSymbolFail/server.log b/test/recordings/documentSymbolFail/server.log
new file mode 100644 (file)
index 0000000..d6bf4f8
--- /dev/null
@@ -0,0 +1,19 @@
+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
diff --git a/test/recordings/renamePass/client.log b/test/recordings/renamePass/client.log
new file mode 100644 (file)
index 0000000..2d854f4
--- /dev/null
@@ -0,0 +1,17 @@
+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
diff --git a/test/recordings/renamePass/server.log b/test/recordings/renamePass/server.log
new file mode 100644 (file)
index 0000000..9225b4c
--- /dev/null
@@ -0,0 +1,23 @@
+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