embed jsonrpc types from jsonrpc-core crate (#2801)
We should not depend on jsonrpc-core anymore: * The project just announced it's no longer actively maintained[^1], preferring their new implementation in `jsonrpsee`. * The types are too strict: we would benefit from removing some `#[serde(deny_unknown_fields)]` annotations to allow language servers that disrespect the spec[^2]. * We don't use much of the project. Just the types out of core. These are easy to embed directly into the `helix-lsp` crate. [^1]: https://github.com/paritytech/jsonrpc/pull/674 [^2]: https://github.com/helix-editor/helix/issues/2786
This commit is contained in:
parent
b13e534b92
commit
45ce1ebdb6
6 changed files with 373 additions and 19 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
@ -421,7 +421,6 @@ dependencies = [
|
|||
"futures-executor",
|
||||
"futures-util",
|
||||
"helix-core",
|
||||
"jsonrpc-core",
|
||||
"log",
|
||||
"lsp-types",
|
||||
"serde",
|
||||
|
@ -551,19 +550,6 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc-core"
|
||||
version = "18.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
|
|
|
@ -17,7 +17,6 @@ helix-core = { version = "0.6", path = "../helix-core" }
|
|||
anyhow = "1.0"
|
||||
futures-executor = "0.3"
|
||||
futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
|
||||
jsonrpc-core = { version = "18.0", default-features = false } # don't pull in all of futures
|
||||
log = "0.4"
|
||||
lsp-types = { version = "0.93", features = ["proposed"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::{
|
||||
jsonrpc,
|
||||
transport::{Payload, Transport},
|
||||
Call, Error, OffsetEncoding, Result,
|
||||
};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use helix_core::{find_root, ChangeSet, Rope};
|
||||
use jsonrpc_core as jsonrpc;
|
||||
use lsp_types as lsp;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
|
|
370
helix-lsp/src/jsonrpc.rs
Normal file
370
helix-lsp/src/jsonrpc.rs
Normal file
|
@ -0,0 +1,370 @@
|
|||
//! An implementation of the JSONRPC 2.0 spec types
|
||||
|
||||
// Upstream implementation: https://github.com/paritytech/jsonrpc/tree/38af3c9439aa75481805edf6c05c6622a5ab1e70/core/src/types
|
||||
// Changes from upstream:
|
||||
// * unused functions (almost all non-trait-implementation functions) have been removed
|
||||
// * `#[serde(deny_unknown_fields)]` annotations have been removed on response types
|
||||
// for compatibility with non-strict language server implementations like Ruby Sorbet
|
||||
// (see https://github.com/helix-editor/helix/issues/2786)
|
||||
// * some variable names have been lengthened for readability
|
||||
|
||||
use serde::de::{self, DeserializeOwned, Visitor};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
// https://www.jsonrpc.org/specification#error_object
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum ErrorCode {
|
||||
ParseError,
|
||||
InvalidRequest,
|
||||
MethodNotFound,
|
||||
InvalidParams,
|
||||
InternalError,
|
||||
ServerError(i64),
|
||||
}
|
||||
|
||||
impl ErrorCode {
|
||||
pub fn code(&self) -> i64 {
|
||||
match *self {
|
||||
ErrorCode::ParseError => -32700,
|
||||
ErrorCode::InvalidRequest => -32600,
|
||||
ErrorCode::MethodNotFound => -32601,
|
||||
ErrorCode::InvalidParams => -32602,
|
||||
ErrorCode::InternalError => -32603,
|
||||
ErrorCode::ServerError(code) => code,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for ErrorCode {
|
||||
fn from(code: i64) -> Self {
|
||||
match code {
|
||||
-32700 => ErrorCode::ParseError,
|
||||
-32600 => ErrorCode::InvalidRequest,
|
||||
-32601 => ErrorCode::MethodNotFound,
|
||||
-32602 => ErrorCode::InvalidParams,
|
||||
-32603 => ErrorCode::InternalError,
|
||||
code => ErrorCode::ServerError(code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ErrorCode {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let code: i64 = Deserialize::deserialize(deserializer)?;
|
||||
Ok(ErrorCode::from(code))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ErrorCode {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_i64(self.code())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct Error {
|
||||
pub code: ErrorCode,
|
||||
pub message: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub data: Option<Value>,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn invalid_params<M>(message: M) -> Self
|
||||
where
|
||||
M: Into<String>,
|
||||
{
|
||||
Error {
|
||||
code: ErrorCode::InvalidParams,
|
||||
message: message.into(),
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}: {}", self.code, self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
// https://www.jsonrpc.org/specification#request_object
|
||||
|
||||
/// Request ID
|
||||
#[derive(Debug, PartialEq, Clone, Hash, Eq, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Id {
|
||||
Null,
|
||||
Num(u64),
|
||||
Str(String),
|
||||
}
|
||||
|
||||
/// Protocol Version
|
||||
#[derive(Debug, PartialEq, Clone, Copy, Hash, Eq)]
|
||||
pub enum Version {
|
||||
V2,
|
||||
}
|
||||
|
||||
impl Serialize for Version {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
match *self {
|
||||
Version::V2 => serializer.serialize_str("2.0"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct VersionVisitor;
|
||||
|
||||
impl<'v> Visitor<'v> for VersionVisitor {
|
||||
type Value = Version;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a string")
|
||||
}
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
match value {
|
||||
"2.0" => Ok(Version::V2),
|
||||
_ => Err(de::Error::custom("invalid version")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Version {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_identifier(VersionVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Params {
|
||||
None,
|
||||
Array(Vec<Value>),
|
||||
Map(serde_json::Map<String, Value>),
|
||||
}
|
||||
|
||||
impl Params {
|
||||
pub fn parse<D>(self) -> Result<D, Error>
|
||||
where
|
||||
D: DeserializeOwned,
|
||||
{
|
||||
let value: Value = self.into();
|
||||
serde_json::from_value(value)
|
||||
.map_err(|err| Error::invalid_params(format!("Invalid params: {}.", err)))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Params> for Value {
|
||||
fn from(params: Params) -> Value {
|
||||
match params {
|
||||
Params::Array(vec) => Value::Array(vec),
|
||||
Params::Map(map) => Value::Object(map),
|
||||
Params::None => Value::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct MethodCall {
|
||||
pub jsonrpc: Option<Version>,
|
||||
pub method: String,
|
||||
#[serde(default = "default_params")]
|
||||
pub params: Params,
|
||||
pub id: Id,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Notification {
|
||||
pub jsonrpc: Option<Version>,
|
||||
pub method: String,
|
||||
#[serde(default = "default_params")]
|
||||
pub params: Params,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(untagged)]
|
||||
pub enum Call {
|
||||
MethodCall(MethodCall),
|
||||
Notification(Notification),
|
||||
Invalid {
|
||||
// We can attempt to salvage the id out of the invalid request
|
||||
// for better debugging
|
||||
#[serde(default = "default_id")]
|
||||
id: Id,
|
||||
},
|
||||
}
|
||||
|
||||
fn default_params() -> Params {
|
||||
Params::None
|
||||
}
|
||||
|
||||
fn default_id() -> Id {
|
||||
Id::Null
|
||||
}
|
||||
|
||||
impl From<MethodCall> for Call {
|
||||
fn from(method_call: MethodCall) -> Self {
|
||||
Call::MethodCall(method_call)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Notification> for Call {
|
||||
fn from(notification: Notification) -> Self {
|
||||
Call::Notification(notification)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[serde(untagged)]
|
||||
pub enum Request {
|
||||
Single(Call),
|
||||
Batch(Vec<Call>),
|
||||
}
|
||||
|
||||
// https://www.jsonrpc.org/specification#response_object
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct Success {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub jsonrpc: Option<Version>,
|
||||
pub result: Value,
|
||||
pub id: Id,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||
pub struct Failure {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub jsonrpc: Option<Version>,
|
||||
pub error: Error,
|
||||
pub id: Id,
|
||||
}
|
||||
|
||||
// Note that failure comes first because we're not using
|
||||
// #[serde(deny_unknown_field)]: we want a request that contains
|
||||
// both `result` and `error` to be a `Failure`.
|
||||
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Output {
|
||||
Failure(Failure),
|
||||
Success(Success),
|
||||
}
|
||||
|
||||
impl From<Output> for Result<Value, Error> {
|
||||
fn from(output: Output) -> Self {
|
||||
match output {
|
||||
Output::Success(success) => Ok(success.result),
|
||||
Output::Failure(failure) => Err(failure.error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Response {
|
||||
Single(Output),
|
||||
Batch(Vec<Output>),
|
||||
}
|
||||
|
||||
impl From<Failure> for Response {
|
||||
fn from(failure: Failure) -> Self {
|
||||
Response::Single(Output::Failure(failure))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Success> for Response {
|
||||
fn from(success: Success) -> Self {
|
||||
Response::Single(Output::Success(success))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method_call_serialize() {
|
||||
use serde_json;
|
||||
|
||||
let m = MethodCall {
|
||||
jsonrpc: Some(Version::V2),
|
||||
method: "update".to_owned(),
|
||||
params: Params::Array(vec![Value::from(1), Value::from(2)]),
|
||||
id: Id::Num(1),
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&m).unwrap();
|
||||
assert_eq!(
|
||||
serialized,
|
||||
r#"{"jsonrpc":"2.0","method":"update","params":[1,2],"id":1}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notification_serialize() {
|
||||
use serde_json;
|
||||
|
||||
let n = Notification {
|
||||
jsonrpc: Some(Version::V2),
|
||||
method: "update".to_owned(),
|
||||
params: Params::Array(vec![Value::from(1), Value::from(2)]),
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&n).unwrap();
|
||||
assert_eq!(
|
||||
serialized,
|
||||
r#"{"jsonrpc":"2.0","method":"update","params":[1,2]}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn success_output_deserialize() {
|
||||
use serde_json;
|
||||
|
||||
let dso = r#"{"jsonrpc":"2.0","result":1,"id":1}"#;
|
||||
|
||||
let deserialized: Output = serde_json::from_str(dso).unwrap();
|
||||
assert_eq!(
|
||||
deserialized,
|
||||
Output::Success(Success {
|
||||
jsonrpc: Some(Version::V2),
|
||||
result: Value::from(1),
|
||||
id: Id::Num(1)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn success_output_deserialize_with_extra_fields() {
|
||||
use serde_json;
|
||||
|
||||
// https://github.com/helix-editor/helix/issues/2786
|
||||
let dso = r#"{"jsonrpc":"2.0","result":1,"id":1,"requestMethod":"initialize"}"#;
|
||||
|
||||
let deserialized: Output = serde_json::from_str(dso).unwrap();
|
||||
assert_eq!(
|
||||
deserialized,
|
||||
Output::Success(Success {
|
||||
jsonrpc: Some(Version::V2),
|
||||
result: Value::from(1),
|
||||
id: Id::Num(1)
|
||||
})
|
||||
);
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
mod client;
|
||||
pub mod jsonrpc;
|
||||
mod transport;
|
||||
|
||||
pub use client::Client;
|
||||
pub use futures_executor::block_on;
|
||||
pub use jsonrpc::Call;
|
||||
pub use jsonrpc_core as jsonrpc;
|
||||
pub use lsp::{Position, Url};
|
||||
pub use lsp_types as lsp;
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::{Error, Result};
|
||||
use crate::{jsonrpc, Error, Result};
|
||||
use anyhow::Context;
|
||||
use jsonrpc_core as jsonrpc;
|
||||
use log::{error, info};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
|
Loading…
Add table
Reference in a new issue