Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support a callback mechanism from JDT ls extension to third-party vscode-extension and third-party language server #595

Closed
kdvolder opened this issue Mar 20, 2018 · 1 comment

Comments

@kdvolder
Copy link
Contributor

The JDT LS extension mechanic allows a vscode extension to contribute an osgi/eclipse plugin to the language server where it can register a vscode command. When the vscode command is executed code in the extension can be execute to handle the command (via org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler).

This mechanism is suitable to allow for a vscode extension to implement some kind of a 'request -> response' style interaction with the jdt.ls extension. I.e. vscode extension can call on its jdt.ls extension by invoking the command. The command, upon completion returns information as result. The interaction is then ended.

It is not possible via this mechanism alone to implement something akin to a listener. (E.g. in our use case, our vscode extensions requires project classpaths not just once, but wants to receive continual update events whenever the classpath changes).

To facililate a callback / listener type of interaction I propose to add a protocol extension that allows invoking vscode client-side commands from the language server (i.e. this is like LSP workspace/executeCommand, but instead of being a request going from client to server, it goes from server to client.

I beleave this is sufficient as a low-level mechanism to support any callback style interaction between third-party vscode extension and a jdt.ls extensions. I have a working prototype in which our vscode-extension and language server is able to keep its classpath model in synch with the jdt.ls's model.

I'll submit two pull requests for discussion (one to vscode-java and one to eclipse.jdt.ls).

@kdvolder
Copy link
Contributor Author

In mattermost channel @gorkem expressed some concerns around 'missing command'. The PRs in current form do not handle this case explicitly. But I did test what happens in such a case.

This is what happens:

java.util.concurrent.CompletionException: org.eclipse.lsp4j.jsonrpc.ResponseErrorException: Request workspace/executeCommand failed with message: command 'sts4.classpath.KShYryPI' not found
	at java.util.concurrent.CompletableFuture.reportJoin(CompletableFuture.java:375)
	at java.util.concurrent.CompletableFuture.join(CompletableFuture.java:1934)
	at org.eclipse.jdt.ls.core.internal.JavaClientConnection.executeCommand(JavaClientConnection.java:65)
	at org.springframework.tooling.jdt.ls.extension.ClasspathListenerHandler$Subscribptions$2.run(ClasspathListenerHandler.java:73)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:60)
Caused by: org.eclipse.lsp4j.jsonrpc.ResponseErrorException: Request workspace/executeCommand failed with message: command 'sts4.classpath.KShYryPI' not found
	at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.lambda$request$1(RemoteEndpoint.java:121)
	at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.handleResponse(RemoteEndpoint.java:162)
	at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.consume(RemoteEndpoint.java:149)
	at org.eclipse.jdt.ls.core.internal.ParentProcessWatcher.lambda$0(ParentProcessWatcher.java:83)
	at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.handleMessage(StreamMessageProducer.java:149)
	at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:77)
	at org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:84)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
executing callback sts4.classpath.KShYryPI FAILED
java.util.concurrent.CompletionException: org.eclipse.lsp4j.jsonrpc.ResponseErrorException: Request workspace/executeCommand failed with message: command 'sts4.classpath.KShYryPI' not found
	at java.util.concurrent.CompletableFuture.reportJoin(CompletableFuture.java:375)
	at java.util.concurrent.CompletableFuture.join(CompletableFuture.java:1934)
	at org.eclipse.jdt.ls.core.internal.JavaClientConnection.executeCommand(JavaClientConnection.java:65)
	at org.springframework.tooling.jdt.ls.extension.ClasspathListenerHandler$Subscribptions$2.run(ClasspathListenerHandler.java:73)
	at org.eclipse.core.internal.jobs.Worker.run(Worker.java:60)
Caused by: org.eclipse.lsp4j.jsonrpc.ResponseErrorException: Request workspace/executeCommand failed with message: command 'sts4.classpath.KShYryPI' not found
	at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.lambda$request$1(RemoteEndpoint.java:121)
	at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.handleResponse(RemoteEndpoint.java:162)
	at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.consume(RemoteEndpoint.java:149)
	at org.eclipse.jdt.ls.core.internal.ParentProcessWatcher.lambda$0(ParentProcessWatcher.java:83)
	at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.handleMessage(StreamMessageProducer.java:149)
	at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:77)
	at org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:84)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

Do you think we should do something more explicit? I am open to suggestion but decided against it for now. While it is possible to get a list of commands using vscode api, so we could definitely explicitly detect whether the command exist on the client side. I just don't know if this is worth it (the list is likely long and so it will add extra overhead to get the list and iterate to check if a command exist).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants