# gen-lsp-types
Programmatically generated Rust type definitions for the
[Language Server Protocol](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/).
## Features
### 100% Accurate to the protocol*
**All** types are generated by the LSP Metamodel, and will be up-to-date on the
latest LSP changes.
\* If you notice that this is not the case, please
[file an issue](https://github.com/ribru17/gen-lsp-types/issues/new)!
### Comprehensive `derive` usage
Every type implements `serde`'s `Serialize` and `Deserialize` traits adhering to
the protocol, as well as `Debug`, `Clone`, and `PartialEq`. Additionally, the
generator will derive all of the following traits on every `enum`/`struct` that
supports them:
- `Copy`
- `Default`
- `Eq`
- `Hash`
Special types `Position` and `Range` also derive `Ord` and `PartialOrd`, and
string enumerations derive `From<String>` and `Display`.
All structures generate `::new()` constructors for convenience. For the same
reason, string enumerations supporting custom values will also provide `::new()`
constructors, taking a `&'static str`.
All "or" types in the spec (e.g. `bar: string | integer | null`) are represented
as untagged `enum`s, with intuitive naming based on the context that the "or"
type is defined in. Every "or" type `enum` implements `From` so it can be
seamlessly converted from its member types. E.g., for the above example, you can
do:
```rust
// This...
let bar: Bar = 123.into();
// ...or this...
let bar: Bar = String::from("baz").into();
// ...or this.
let bar: Bar = ().into();
```
### Distinguishes between `null` and "not present" properties
Unlike the original [`lsp-types`](https://github.com/gluon-lang/lsp-types)
crate, this one is able to encode properties as `null` **or** `undefined` (not
present). This is important because the spec **does differentiate the two from
each other**.
For example, for the following property which can be `null` **or** `undefined`:
```ts
interface InitializeParams extends WorkDoneProgressParams {
// ...
/**
* The workspace folders configured in the client when the server starts.
* This property is only available if the client supports workspace folders.
* It can be `null` if the client supports workspace folders but none are
* configured.
*
* @since 3.6.0
*/
workspaceFolders?: WorkspaceFolder[] | null;
}
```
The generated Rust struct will be:
```rust
pub struct InitializeParams {
// ...
/// The workspace folders configured in the client when the server starts.
///
/// This property is only available if the client supports workspace folders.
/// It can be `null` if the client supports workspace folders but none are
/// configured.
///
/// @since 3.6.0
#[serde(default, deserialize_with = "deserialize_some")]
#[serde(skip_serializing_if = "Option::is_none")]
pub workspace_folders: Option<WorkspaceFolders>,
}
```
...where `WorkspaceFolders` is an `enum`:
```rust
pub enum WorkspaceFolders {
WorkspaceFolderList(Vec<WorkspaceFolder>),
#[serde(rename = "null")]
Null,
}
```
So `undefined` is represented as `workspace_folders = None`, and `null` is
represented as `workspace_folders = Some(WorkspaceFolders::Null)`.
> [!NOTE]
> If a property can only be one of `undefined` or `null`, not both, `None` will
> be used in both cases to denote those values, for convenience.
### Support for string literal properties
Structures with string literal properties will have those properties omitted
from their generated Rust types. E.g., for the following `DeleteFile`
definition:
```ts
/**
* Delete file operation
*/
export interface DeleteFile {
/**
* This is a delete operation.
*/
kind: 'delete';
/**
* The file to delete.
*/
uri: DocumentUri;
/**
* Delete options.
*/
options?: DeleteFileOptions;
/**
* An optional annotation identifier describing the operation.
*
* @since 3.16.0
*/
annotationId?: ChangeAnnotationIdentifier;
}
```
The generated Rust struct looks like the following:
```rust
/// Delete file operation
pub struct DeleteFile {
/// The file to delete.
pub uri: Uri,
/// Delete options.
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<DeleteFileOptions>,
/// An optional annotation identifier describing the operation.
///
/// @since 3.16.0
#[serde(skip_serializing_if = "Option::is_none")]
pub annotation_id: Option<ChangeAnnotationIdentifier>,
}
```
Note the `kind` property is not present. However, when serializing, it will
always be present with a value of `"delete"`. Deserializing only succeeds when
the field is present and has a `"delete"` value.
### Stronger typing
This library's `Request` and `Notification` traits define not only the
associated `Params` and `Result` objects, but also their message directions
(i.e. `ClientToServer`, `ServerToClient`, `Both`). It also defines the method
string as an `enum`, rather than a `&'static str`.
Additionally, the library defines a `RequestWithPartialResults` trait for
requests which support streaming partial results. The trait exposes a
`PartialResult` type for this use case.
## Notable changes from `lsp-types`
[`lsp-types` controversially
switched to `fluent-uri`](https://github.com/gluon-lang/lsp-types/issues/284)
for better spec correctness, at the cost of decreased interoperability and
poorer DX. Previously, the `url` crate was used, which is significantly more
battle-tested, but isn't spec-compliant in all cases.
This library deliberately puts the URI encoding decision in the hands of the
downstream consumer. Developers migrating from the original `lsp-types` crate
will notice that the URI type is just a newtype wrapper over a `String`,
allowing them to internally represent URIs however they wish.
This crate also uses feature flags to allow downstream consumers to tell it to
prefer a specific URI representation. The `url` feature instructs the library to
deserialize URIs as `url::Url` (matching `lsp-types <=0.95.0`), and the
`fluent-uri` feature instructs it to deserialize them as `fluent_uri::Uri`. Note
that the two features are mutually exclusive, and by default just the `String`
wrapper will be used.
## Stability
Changes that are backwards compatible from a protocol perspective (e.g.
`foo: integer | string` -> `foo: integer | string | boolean`) are still breaking
from the point of view of this library. In the above example, an extra `enum`
variant would be added to the `Foo` type, which is a breaking change. Thus the
library will be perpetually kept at [zerover](https://0ver.org/) versioning,
like the original `lsp-types` crate.