A brief description for using LSP4J to develop a language server.
The first thing you should do is to implement your language server. To do so just implement the interface org.eclipse.lsp4j.services.LanguageServer
.
If you are implementing a client (e.g. an editor) you simply need to implement org.eclipse.lsp4j.services.LanguageClient
instead.
Now that you have an actual implementation you can connect it with a remote client. Let's assume you have an Inputstream
and an Outputstream
, over which you want to communicate with a language client.
The utility class LSPLauncher
does most of the wiring for you. Here is the code needed.
LanguageServer server = ... ;
Launcher<LanguageClient> launcher =
LSPLauncher.createServerLauncher(server,
inputstream,
outputstream);
With this we have a Launcher object on which we can obtain the remote proxy. Usually a language server should also implement LanguageClientAware
, which defines a single method connect(LanguageClient)
over which you can pass the remote proxy to the language server.
if (myImpl instanceof LanguageClientAware) {
LanguageClient client = launcher.getRemoteProxy();
((LanguageClientAware)myImpl).connect(client);
}
Now your language server is not only able to receive messages from the other side, but can send messages back as well.
The final thing you need to to do in order to start listening on the given inputstream, is this:
launcher.startListening();
This will start the listening process in a new thread.
As explained in the JSON-RPC implementation documentation, protocol messages can be specified through methods annotated with @JsonNotification
or @JsonRequest
. If you would like to add more client-to-server messages, just pass a LanguageServer
implementation with additional annotated methods to the LSPLauncher
. In case you need to change the protocol so it is no longer compatible with the LanguageServer
interface, or you would like to add or change some server-to-client messages, use the utility methods in Launcher
instead of LSPLauncher
. Those allow you to use arbitrary local service objects and arbitrary remote service interfaces, and you can even combine multiple service objects or interfaces.
The LSP has extended JSON RPC with support for request cancellation. LSP4J supports this through the cancellation of CompletableFuture
s. To use it a request method needs to be implemented like this:
@Override
public CompletableFuture<CompletionList> completion(
TextDocumentPositionParams position) {
return CompletableFutures.computeAsync(cancelToken -> {
// the actual implementation should check for
// cancellation like this
cancelToken.checkCanceled();
// more code... and more cancel checking
});
}
The method checkCanceled
will throw a CancellationException
in case the request was cancelled. So make sure you don't catch it accidentally.
If you are on the other side and want to cancel a request you made, you need to call cancel on the returned future :
CompletableFuture<CompletionList> result = languageServer
.getTextDocumentService().completion(myparams);
// cancel the request
result.cancel(true);
For more information about the underlying json rpc framework see JSON RPC Documentation.