Skip to content

Commit

Permalink
Various minor changes
Browse files Browse the repository at this point in the history
Signed-off-by: Matthew Brown <[email protected]>
  • Loading branch information
Matthew Brown authored and Matthew Brown committed May 11, 2022
1 parent 1661556 commit f7248fa
Show file tree
Hide file tree
Showing 27 changed files with 670 additions and 728 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ Breaking API changes:

* Method `LanguageClient.setTrace` moved to `LanguageServer`, where it should
have been according to the specification
* Removed `RenameOptions.id` as it was never specified for `StaticRegistrationOptions`
* Removed `RenameOptions.id` as it was already deprecated and never specified for `StaticRegistrationOptions`
* Removed `SemanticTokenTypes.Member` as it was already deprecated and not specified
* Removed `TraceValue.Message` as it was already deprecated and not specified
* Changed `TraceValue` to be `final` matching similar classes
* Removed duplicate `ResponseErrorCode` as it has been deprecated for several versions
* Removed `ResponseErrorCode.serverErrorStart` and `ResponseErrorCode.serverErrorEnd` as they were
already deprecated and just boundaries not actual error codes
* Return type of `workspace/symbol` changed from `List<? extends SymbolInformation>` to
`Either<List<? extends SymbolInformation>, List<? extends WorkspaceSymbol>>`
* Type of `FileSystemWatcher.globPattern` changed from `String` to `Either<String, RelativePattern>`
Expand All @@ -25,6 +31,7 @@ Breaking Beta API changes:
Deprecated API changes:

* `SymbolInformation` is deprecated in favor of `DocumentSymbol` or `WorkspaceSymbol`
* `ResponseErrorCode.serverNotInitialized` deprecated in favor of `ResponseErrorCode.ServerNotInitialized`

### v0.12.0 (Apr. 2021)

Expand Down
89 changes: 43 additions & 46 deletions documentation/jsonrpc.md
Original file line number Diff line number Diff line change
@@ -1,69 +1,68 @@
# Core Concepts
# JSON-RPC Core Concepts

The LSP is based on an extended version of [JSON RPC v2.0](http://www.jsonrpc.org/specification), for which LSP4J provides a Java implementation. There are basically three levels of interaction:
The LSP is based on an extended version of [JSON-RPC v2.0](http://www.jsonrpc.org/specification), for which LSP4J provides a Java implementation. There are basically three levels of interaction:

# Basic Message Sending
## Basic Message Sending

On the lowest Level JSON RPC just sends messages from a client to a server. Those messages can be notifications, requests or responses. The relation between an incoming request and a sent response is done through a request id. As a user you usually don't want to do the wiring yourself, but want to work at least with an `Endpoint`.
On the lowest level, JSON-RPC just sends messages from a client to a server. Those messages can be notifications, requests, or responses. The relation between an incoming request and a sent response is done through a request `id`. As a user, you usually don't want to do the wiring yourself, but want to work at least with an `Endpoint`.

# Endpoint
## Endpoint

LSP4J provides the notion of an [Endpoint](../org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/Endpoint.java) that takes care of the connecting a request messages with responses. The interface defines two methods
LSP4J provides the notion of an [Endpoint](../org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/Endpoint.java) that takes care of the connecting a request messages with responses. The interface defines two methods:

``` java
/**
* An endpoint is a generic interface that accepts jsonrpc requests and notifications.
*/
public interface Endpoint {

CompletableFuture<?> request(String method, Object parameter);
void notify(String method, Object parameter);
CompletableFuture<?> request(String method, Object parameter);

void notify(String method, Object parameter);

}
```

# Notifications
## Notifications

You always work with two `Endpoints`. Usually one of the endpoints, a `RemoteEndpoint`, sits on some remote communication channel, like a socket and receives and sends json messages. A local `Endpoint` implementation is connected bidirectionally such that it can receive and send messages. For instance, when a notification messages comes in the `RemoteEndpoint` simply translates it to a call on your local Endpoint implementation. This simple approach works nicely in both directions.
You always work with two `Endpoints`. Usually one of the endpoints, a `RemoteEndpoint`, sits on some remote communication channel, like a socket and receives and sends json messages. A local `Endpoint` implementation is connected bidirectionally such that it can receive and send messages. For instance, when a notification messages comes in the `RemoteEndpoint` simply translates it to a call on your local `Endpoint` implementation. This simple approach works nicely in both directions.

# Requests
## Requests

For requests, the story is slightly more complicated. When a request message comes in, the `RemoteEndpoint` tracks the request `id` and invokes `request` on the local endpoint. In addition it adds completion stage to the returned `CompletableFuture`, that translates the result into a JSON RPC response message.
For requests, the story is slightly more complicated. When a request message comes in, the `RemoteEndpoint` tracks the request `id` and invokes `request` on the local endpoint. In addition, it adds completion stage to the returned `CompletableFuture`, that translates the result into a JSON-RPC response message.

For the other direction, if the implementation calls request on the RemoteEndpoint, the message is send and tracked locally.
The returned `CompletableFuture` will complete once a corresponsing result message is received.
For the other direction, if the implementation calls request on the RemoteEndpoint, the message is sent and tracked locally. The returned `CompletableFuture` will complete once a corresponding result message is received.

## Response Errors

The receiver of a request always needs to return a response message to conform to the JSON RPC specification. In case the result value cannot be provided in a response because of an error, the `error` property of the `ResponseMessage` must be set to a `ResponseError` describing the failure.
The receiver of a request always needs to return a response message to conform to the JSON-RPC specification. In case the result value cannot be provided in a response because of an error, the `error` property of the `ResponseMessage` must be set to a `ResponseError` describing the failure.

This can be done by returning a `CompletableFuture` completed exceptionally with a `ResponseErrorException` from the request message handler in a local endpoint. The exception carries a `ResponseError` to attach to the response. The `RemoteEndpoint` will handle the exceptionally completed future and send a response message with the attached error object.

For example:

```java
@Override
public CompletableFuture<Object> shutdown() {
if (!isInitialized()) {
CompletableFuture<Object> exceptionalResult = new CompletableFuture<>();
ResponseError error = new ResponseError(ResponseErrorCode.serverNotInitialized, "Server was not initialized", null);
ResponseError error = new ResponseError(ResponseErrorCode.ServerNotInitialized, "Server was not initialized", null);
exceptionalResult.completeExceptionally(new ResponseErrorException(error));
return exceptionalResult;
return exceptionalResult;
}
return doShutdown();
}
```

# Cancelling Requests
## Cancelling Requests

The LSP defines an extension to the JSON RPC, that allows to cancel requests. It is done through a special notification message, that contains the request id that should be cancelled. If you want to cancel a pending request in LSP4J, you can simply call `cancel(true)` on the returned `CompletableFuture`. The `RemoteEndpoint` will send the cancellation notification. If you are implementing a request message, you should return a `CompletableFuture` created through [`CompletebleFutures.computeAsync`] (../org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/CompletableFutures.java#L24). It accepts a lambda that is provided with a `CancelChecker`, which you need to ask `checkCanceled` and which will throw a `CancellationException` in case the request got canceled.
The LSP defines an extension to the JSON-RPC, that allows to cancel requests. It is done through a special notification message, which contains the request `id` that should be cancelled. If you want to cancel a pending request in LSP4J, you can simply call `cancel(true)` on the returned `CompletableFuture`. The `RemoteEndpoint` will send the cancellation notification. If you are implementing a request message, you should return a `CompletableFuture` created through [`CompletableFutures.computeAsync`](../org.eclipse.lsp4j.jsonrpc/src/main/java/org/eclipse/lsp4j/jsonrpc/CompletableFutures.java#L24). It accepts a lambda that is provided with a `CancelChecker`, which you need to ask `checkCanceled` and which will throw a `CancellationException` in case the request got canceled.

``` java
@JsonRequest
public CompletableFuture<CompletionList> completion(
TextDocumentPositionParams position) {
public CompletableFuture<CompletionList> completion(TextDocumentPositionParams position) {
return CompletableFutures.computeAsync(cancelToken -> {
// the actual implementation should check for
// the actual implementation should check for
// cancellation like this
cancelToken.checkCanceled();
// more code... and more cancel checking
Expand All @@ -72,64 +71,62 @@ public CompletableFuture<CompletionList> completion(
}
```

# Static Typing through Service Layer
## Static Typing through Service Layer

So far with `Endpoint` and `Object` as parameter and result the API is quite generic. In order to leverage Java's type system and tool support, the JSON RPC module supports the notion of service objects.
So far with `Endpoint` and `Object` as parameter and result the API is quite generic. In order to leverage Java's type system and tool support, the JSON-RPC module supports the notion of service objects.

# Service Objects
## Service Objects

A service object provides methods that are annotated with either `@JsonNotification` or `@JsonRequest`. A `GenericEndpoint` is a reflective implementation of an Endpoint, that simply delegates any calls to `request` or `notify` to the corresponding method in the service object. Here is a simple example:
A service object provides methods that are annotated with either `@JsonNotification` or `@JsonRequest`. A `GenericEndpoint` is a reflective implementation of an Endpoint that simply delegates any calls to `request` or `notify` to the corresponding method in the service object. Here is a simple example:

``` java
public class MyService {
@JsonNotification public void sayHello(HelloParam param) {
... do stuff
// do stuff
}
}

// turn it into an Endpoint

MyService service = new MyService();
Endpoint serviceAsEndpoint = ServiceEndpoints.toEndpoint(service);

```

If in turn you want to talk to an Endpoint in a more statically typed fashion, the `EndpointProxy` comes in handy. It is a dynamic proxy for a given service interface with annotated @JsonRequest and @JsonNotification methods. You can create one like this:
If in turn you want to talk to an Endpoint in a more statically typed fashion, the `EndpointProxy` comes in handy. It is a dynamic proxy for a given service interface with annotated `@JsonRequest` and `@JsonNotification` methods. You can create one like this:

``` java
public interface MyService {
@JsonNotification public void sayHello(HelloParam param);
}


Endpoint endpoint = ...
MyService proxy = ServiceEndpoints.toProxy(endpoint, MyService.class);
```

Of course you can use the same interface, as is done with the [interfaces](../org.eclipse.lsp4j/src/main/java/org/eclipse/lsp4j/services/LanguageServer.java) defining the messages of the LSP.

# Naming of JSON RPC Request and Notifications
## Naming of JSON-RPC Request and Notifications

When annotated with @JsonRequest or @JsonNotification LSP4J will use the name of the annotated method to create the JSON RPC method name. This naming can be customized by using segments and providing explicit names in the annotations. Here are some examples of method naming options:
When annotated with `@JsonRequest` or `@JsonNotification` LSP4J will use the name of the annotated method to create the JSON-RPC method name. This naming can be customized by using segments and providing explicit names in the annotations. Here are some examples of method naming options:

```java
@JsonSegment("mysegment")
public interface NamingExample {

// The JSON RPC method name will be "mysegment/myrequest"
@JsonRequest
CompletableFuture<?> myrequest();
// The JSON-RPC method name will be "mysegment/myrequest"
@JsonRequest
CompletableFuture<?> myrequest();

// The JSON RPC method name will be "myotherrequest"
@JsonRequest(useSegment = false)
CompletableFuture<?> myotherrequest();
// The JSON-RPC method name will be "myotherrequest"
@JsonRequest(useSegment = false)
CompletableFuture<?> myotherrequest();

// The JSON RPC method name will be "mysegment/somethirdrequest"
@JsonRequest(value="somethirdrequest")
CompletableFuture<?> notthesamenameasvalue();
// The JSON-RPC method name will be "mysegment/somethirdrequest"
@JsonRequest(value="somethirdrequest")
CompletableFuture<?> notthesamenameasvalue();

// The JSON RPC method name will be "call/it/what/you/want"
@JsonRequest(value="call/it/what/you/want", useSegment = false)
CompletableFuture<?> yetanothername();
// The JSON-RPC method name will be "call/it/what/you/want"
@JsonRequest(value="call/it/what/you/want", useSegment = false)
CompletableFuture<?> yetanothername();
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -15,54 +15,66 @@
* A number indicating the error type that occurred.
*/
public enum ResponseErrorCode {


/**
* Invalid JSON was received by the server. An error occurred on
* the server while parsing the JSON text.
*/
ParseError(-32700),


/**
* The JSON sent is not a valid Request object.
*/
InvalidRequest(-32600),


/**
* The method does not exist / is not available.
*/
MethodNotFound(-32601),


/**
* Invalid method parameter(s).
*/
InvalidParams(-32602),


/**
* Internal JSON-RPC error.
*/
InternalError(-32603),

/**
* This is the start range of JSON RPC reserved error codes.
* It doesn't denote a real error code. No LSP error codes should
* be defined between the start and end range. For backwards
* compatibility the {@link #serverNotInitialized} and the
* compatibility the {@link #ServerNotInitialized} and the
* {@link #UnknownErrorCode} are left in the range.
* <p>
* Since 3.16.0
*/
jsonrpcReservedErrorRangeStart(-32099),

/** @deprecated use {@link #jsonrpcReservedErrorRangeStart} */

/**
* @deprecated Use {@link #ServerNotInitialized}
*/
@Deprecated
serverErrorStart(-32099),
serverNotInitialized(-32002),

/**
* Error code indicating that a server received a notification or
* request before the server has received the {@code initialize} request.
* <p>
* Should be {@code ServerNotInitialized}
*/
serverNotInitialized(-32002),
ServerNotInitialized(-32002),

UnknownErrorCode(-32001),

/**
* This is the end range of JSON RPC reserved error codes.
* It doesn't denote a real error code.
* <p>
* Since 3.16.0
*/
jsonrpcReservedErrorRangeEnd(-32000),

/** @deprecated use {@link #jsonrpcReservedErrorRangeEnd} */
@Deprecated
serverErrorEnd(-32000),


/**
* This is the start range of LSP reserved error codes.
* It doesn't denote a real error code.
Expand All @@ -80,7 +92,7 @@ public enum ResponseErrorCode {
* Since 3.17.0
*/
RequestFailed(-32803),

/**
* The server cancelled the request. This error code should
* only be used for requests that explicitly support being
Expand All @@ -89,7 +101,7 @@ public enum ResponseErrorCode {
* Since 3.17.0
*/
ServerCancelled(-32802),

/**
* The server detected that the content of a document got
* modified outside normal conditions. A server should
Expand All @@ -101,27 +113,27 @@ public enum ResponseErrorCode {
* the client should cancel the request.
*/
ContentModified(-32801),

/**
* The client has canceled a request and a server as detected
* the cancel.
*/
RequestCancelled(-32800),

/**
* This is the end range of LSP reserved error codes.
* It doesn't denote a real error code.
* <p>
* Since 3.16.0
*/
lspReservedErrorRangeEnd(-32800);

private final int value;

ResponseErrorCode(int value) {
this.value = value;
}

public int getValue() {
return value;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,30 @@
/**
* Completion item tags are extra annotations that tweak the rendering of a completion
* item.
*
* <p>
* Since 3.15.0
*/
public enum CompletionItemTag {

/**
* Render a completion as obsolete, usually using a strike-out.
*/
Deprecated(1);
/**
* Render a completion as obsolete, usually using a strike-out.
*/
Deprecated(1);

private final int value;
private final int value;

CompletionItemTag(int value) {
this.value = value;
}
CompletionItemTag(int value) {
this.value = value;
}

public int getValue() {
return value;
}
public int getValue() {
return value;
}

public static CompletionItemTag forValue(int value) {
CompletionItemTag[] allValues = CompletionItemTag.values();
if (value < 1 || value > allValues.length)
throw new IllegalArgumentException("Illegal enum value: " + value);
return allValues[value - 1];
}
public static CompletionItemTag forValue(int value) {
CompletionItemTag[] allValues = CompletionItemTag.values();
if (value < 1 || value > allValues.length)
throw new IllegalArgumentException("Illegal enum value: " + value);
return allValues[value - 1];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public enum CompletionTriggerKind {

/**
* Completion was re-triggered as the current completion list is incomplete.
* <p>
* Since 3.6.0
*/
TriggerForIncompleteCompletions(3);

Expand Down
Loading

0 comments on commit f7248fa

Please sign in to comment.