Skip to content

Commit

Permalink
LSP Client: Accept floats with trailing zeros as valid JSONRPC IDs (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelselleck authored Dec 31, 2024
1 parent 2b4a77b commit 4a59f68
Showing 1 changed file with 44 additions and 1 deletion.
45 changes: 44 additions & 1 deletion helix-lsp/src/jsonrpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,37 @@ impl std::error::Error for Error {}
#[serde(untagged)]
pub enum Id {
Null,
Num(u64),
Num(#[serde(deserialize_with = "deserialize_jsonrpc_id_num")] u64),
Str(String),
}

fn deserialize_jsonrpc_id_num<'de, D>(deserializer: D) -> Result<u64, D::Error>
where
D: serde::Deserializer<'de>,
{
let num = serde_json::Number::deserialize(deserializer)?;

if let Some(val) = num.as_u64() {
return Ok(val);
};

// Accept floats as long as they represent positive whole numbers.
// The JSONRPC spec says "Numbers SHOULD NOT contain fractional parts" so we should try to
// accept them if possible. The JavaScript type system lumps integers and floats together so
// some languages may serialize integer IDs as floats with a zeroed fractional part.
// See <https://github.com/helix-editor/helix/issues/12367>.
if let Some(val) = num
.as_f64()
.filter(|f| f.is_sign_positive() && f.fract() == 0.0)
{
return Ok(val as u64);
}

Err(de::Error::custom(
"number must be integer or float representing a whole number in valid u64 range",
))
}

impl std::fmt::Display for Id {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expand Down Expand Up @@ -375,6 +402,22 @@ fn serialize_skip_none_params() {
assert_eq!(serialized, r#"{"jsonrpc":"2.0","method":"exit"}"#);
}

#[test]
fn id_deserialize() {
use serde_json;

let id = r#"8"#;
let deserialized: Id = serde_json::from_str(id).unwrap();
assert_eq!(deserialized, Id::Num(8));

let id = r#"4.0"#;
let deserialized: Id = serde_json::from_str(id).unwrap();
assert_eq!(deserialized, Id::Num(4));

let id = r#"0.01"#;
assert!(serde_json::from_str::<Id>(id).is_err());
}

#[test]
fn success_output_deserialize() {
use serde_json;
Expand Down

0 comments on commit 4a59f68

Please sign in to comment.