From 22298434f10e203bc1adb8eebfdfbfbff05db977 Mon Sep 17 00:00:00 2001
From: Michael Davis <mcarsondavis@gmail.com>
Date: Tue, 10 Jan 2023 03:31:41 -0600
Subject: [PATCH] jsonrpc: Skip serializing params if params are None (#5471)

The JSONRPC spec says:

> If present, parameters for the rpc call MUST be provided as a
> Structured value

https://www.jsonrpc.org/specification#parameter_structures

(Where a "Structured value" is elsewhere defined as either a map or
array.)

This change skips the serialization of the `params` field for JSONRPC
method calls and notifications if the `params` field is the `None`
variant. This fixes compatibility with LSP servers which adhere closely
to that part of the spec: `ocamllsp` in the wild.
---
 helix-lsp/src/jsonrpc.rs | 35 +++++++++++++++++++++++++++++++++--
 1 file changed, 33 insertions(+), 2 deletions(-)

diff --git a/helix-lsp/src/jsonrpc.rs b/helix-lsp/src/jsonrpc.rs
index 75ac9309..69d02707 100644
--- a/helix-lsp/src/jsonrpc.rs
+++ b/helix-lsp/src/jsonrpc.rs
@@ -170,6 +170,10 @@ impl Params {
         serde_json::from_value(value)
             .map_err(|err| Error::invalid_params(format!("Invalid params: {}.", err)))
     }
+
+    pub fn is_none(&self) -> bool {
+        self == &Params::None
+    }
 }
 
 impl From<Params> for Value {
@@ -187,7 +191,7 @@ impl From<Params> for Value {
 pub struct MethodCall {
     pub jsonrpc: Option<Version>,
     pub method: String,
-    #[serde(default = "default_params")]
+    #[serde(default = "default_params", skip_serializing_if = "Params::is_none")]
     pub params: Params,
     pub id: Id,
 }
@@ -197,7 +201,7 @@ pub struct MethodCall {
 pub struct Notification {
     pub jsonrpc: Option<Version>,
     pub method: String,
-    #[serde(default = "default_params")]
+    #[serde(default = "default_params", skip_serializing_if = "Params::is_none")]
     pub params: Params,
 }
 
@@ -334,6 +338,33 @@ fn notification_serialize() {
     );
 }
 
+#[test]
+fn serialize_skip_none_params() {
+    use serde_json;
+
+    let m = MethodCall {
+        jsonrpc: Some(Version::V2),
+        method: "shutdown".to_owned(),
+        params: Params::None,
+        id: Id::Num(1),
+    };
+
+    let serialized = serde_json::to_string(&m).unwrap();
+    assert_eq!(
+        serialized,
+        r#"{"jsonrpc":"2.0","method":"shutdown","id":1}"#
+    );
+
+    let n = Notification {
+        jsonrpc: Some(Version::V2),
+        method: "exit".to_owned(),
+        params: Params::None,
+    };
+
+    let serialized = serde_json::to_string(&n).unwrap();
+    assert_eq!(serialized, r#"{"jsonrpc":"2.0","method":"exit"}"#);
+}
+
 #[test]
 fn success_output_deserialize() {
     use serde_json;