haskell / lsp

Haskell library for the Microsoft Language Server Protocol
366 stars 92 forks source link

No easy way to wait for a response to a request #405

Open michaelpj opened 2 years ago

michaelpj commented 2 years ago

sendRequest gives you an LspId, but you can't say "wait until this is responded to". Instead you have a callback-based interface, which isn't easy for clients to use (empirically, see e.g. https://github.com/haskell/lsp/blob/master/lsp/src/Language/LSP/Server/Core.hs#L619).

heitor-lassarote commented 2 years ago

Today I was attempting to use sendRequest but got some surprising behavior. I was attempting to read a user response from SWindowShowMessageRequest before continuing by using putMVar inside the response handler, and a readMVar outside, but turns out it just blocks indefinitely as the handler doesn't get called. I later thought that this is not done concurrently in the manner I thought and was left clueless on how to handle this, and I see now that this is a known problem, but what workarounds are there for this? Or, is there anything lsp users can do while this isn't resolved?

michaelpj commented 2 years ago

Hm, I would expect the MVar-based solution to work, but I admit I'm not 100% clear on the mechnaics myself. I suspect that indeed your entire server is running single-threaded, so the response cannot be processed while you're sitting waiting on the MVar.

HLS avoids this by using the reactor pattern, but I wonder if we should build something like that in, since I think basically everyone will want concurrent processing of messages. And indeed, the bit that I linked to which handles progress reporting is arguably broken for this reason.

expipiplus1 commented 1 month ago

@heitor-lassarote is it possible that you were waiting on the MVar in inside a handler, in which case and no responses from the client can be observed because the outer handler never returns

Something like this

myHandler _ = do
  res <- newEmptyMVar
  sendRequest LSP.SMethod_WindowShowMessageRequest _ (putMVar res)
  takeMVar res >>= handleMyRequest 

One easy solution is to wait on the mvar in another thread so that the outer handler (myHandler) can finish

myHandler _ = do
  res <- newEmptyMVar
  sendRequest LSP.SMethod_WindowShowMessageRequest _ (putMVar res)
  forkIO $ takeMVar res >>= handleMyRequest