Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/Client.fs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type LspClient() =

/// The telemetry notification is sent from the server to the client to ask the client to log
/// a telemetry event.
abstract member TelemetryEvent: Newtonsoft.Json.Linq.JToken -> Async<unit>
abstract member TelemetryEvent: LSPAny -> Async<unit>

default __.TelemetryEvent(_) = ignoreNotification

Expand Down Expand Up @@ -81,7 +81,7 @@ type LspClient() =
/// The request can fetch n configuration settings in one roundtrip. The order of the returned configuration
/// settings correspond to the order of the passed ConfigurationItems (e.g. the first item in the response
/// is the result for the first configuration item in the params).
abstract member WorkspaceConfiguration: ConfigurationParams -> AsyncLspResult<Newtonsoft.Json.Linq.JToken[]>
abstract member WorkspaceConfiguration: ConfigurationParams -> AsyncLspResult<LSPAny[]>

default __.WorkspaceConfiguration(_) = notImplemented

Expand Down Expand Up @@ -172,7 +172,7 @@ type LspClient() =
member this.WindowShowMessageRequest(p: ShowMessageRequestParams) = this.WindowShowMessageRequest(p)
member this.WindowLogMessage(p: LogMessageParams) = this.WindowLogMessage(p)
member this.WindowShowDocument(p: ShowDocumentParams) = this.WindowShowDocument(p)
member this.TelemetryEvent(p: Newtonsoft.Json.Linq.JToken) = this.TelemetryEvent(p)
member this.TelemetryEvent(p: LSPAny) = this.TelemetryEvent(p)
member this.ClientRegisterCapability(p: RegistrationParams) = this.ClientRegisterCapability(p)
member this.ClientUnregisterCapability(p: UnregistrationParams) = this.ClientUnregisterCapability(p)
member this.WorkspaceWorkspaceFolders() = this.WorkspaceWorkspaceFolders()
Expand Down
1,147 changes: 584 additions & 563 deletions src/ClientServer.cg.fs

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions src/Ionide.LanguageServerProtocol.fsproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<ChangelogFile>$(MSBuildThisFileDirectory)../CHANGELOG.md</ChangelogFile>
<Description>Library for implementing Language Server Protocol in F#.</Description>
Expand Down Expand Up @@ -36,8 +36,7 @@
<PackageReference Include="DotNet.ReproducibleBuilds" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="Ionide.KeepAChangelog.Tasks" Version="0.1.8" PrivateAssets="All" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<!-- Explicitly pinning our FSharp.Core to 6.0.0 so that consumers can use _any_ 6.x version. -->
<PackageReference Update="FSharp.Core" Version="6.0.0" />

<PackageReference Include="StreamJsonRpc" Version="2.16.36" />
</ItemGroup>

Expand Down
96 changes: 79 additions & 17 deletions src/JsonRpc.fs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
module Ionide.LanguageServerProtocol.JsonRpc

open Newtonsoft.Json
open Newtonsoft.Json.Linq
open System.Text.Json
open System.Text.Json.Serialization

type MessageTypeTest = {
[<JsonProperty("jsonrpc")>]
[<JsonPropertyName("jsonrpc")>]
Version: string
Id: int option
Method: string option
Expand All @@ -25,28 +25,28 @@ let getMessageType messageTest =
| _ -> MessageType.Error

type Request = {
[<JsonProperty("jsonrpc")>]
[<JsonPropertyName("jsonrpc")>]
Version: string
Id: int
Method: string
Params: JToken option
Params: JsonElement option
} with

static member Create(id: int, method': string, rpcParams: JToken option) = {
static member Create(id: int, method': string, rpcParams: JsonElement option) = {
Version = "2.0"
Id = id
Method = method'
Params = rpcParams
}

type Notification = {
[<JsonProperty("jsonrpc")>]
[<JsonPropertyName("jsonrpc")>]
Version: string
Method: string
Params: JToken option
Params: JsonElement option
} with

static member Create(method': string, rpcParams: JToken option) = {
static member Create(method': string, rpcParams: JsonElement option) = {
Version = "2.0"
Method = method'
Params = rpcParams
Expand All @@ -72,7 +72,7 @@ open Ionide.LanguageServerProtocol.Types
type Error = {
Code: int
Message: string
Data: JToken option
Data: JsonElement option
} with

static member Create(code: int, message: string) = { Code = code; Message = message; Data = None }
Expand Down Expand Up @@ -102,18 +102,14 @@ type Error = {
Error.Create(int LSPErrorCodes.RequestCancelled, message)

type Response = {
[<JsonProperty("jsonrpc")>]
[<JsonPropertyName("jsonrpc")>]
Version: string
Id: int option
Error: Error option
[<JsonProperty(NullValueHandling = NullValueHandling.Include)>]
Result: JToken option
Result: JsonElement option
} with

/// Json.NET conditional property serialization, controlled by naming convention
member x.ShouldSerializeResult() = x.Error.IsNone

static member Success(id: int, result: JToken option) = {
static member Success(id: int, result: JsonElement option) = {
Version = "2.0"
Id = Some id
Result = result
Expand All @@ -122,6 +118,71 @@ type Response = {

static member Failure(id: int, error: Error) = { Version = "2.0"; Id = Some id; Result = None; Error = Some error }

/// Custom STJ converter for Response:
/// - Always writes "result" (as null) when Error is None
/// - Omits "result" when Error is Some
type ResponseConverter() =
inherit JsonConverter<Response>()

override _.Read(reader, _t, opts) =
// Parse manually to avoid infinite recursion
let mutable version = ""
let mutable id: int option = None
let mutable error: Error option = None
let mutable result: JsonElement option = None

if reader.TokenType = JsonTokenType.StartObject then
while reader.Read()
&& reader.TokenType
<> JsonTokenType.EndObject do
if reader.TokenType = JsonTokenType.PropertyName then
let propName = reader.GetString()

reader.Read()
|> ignore

match propName with
| "jsonrpc" -> version <- reader.GetString()
| "id" when
reader.TokenType
<> JsonTokenType.Null
->
id <- Some(reader.GetInt32())
| "error" when
reader.TokenType
<> JsonTokenType.Null
->
error <- Some(JsonSerializer.Deserialize<Error>(&reader, opts))
| "result" when
reader.TokenType
<> JsonTokenType.Null
->
result <- Some(JsonSerializer.Deserialize<JsonElement>(&reader, opts))
| _ -> reader.Skip()

{ Version = version; Id = id; Error = error; Result = result }

override _.Write(writer, value, opts) =
writer.WriteStartObject()
writer.WriteString("jsonrpc", value.Version)

match value.Id with
| Some id -> writer.WriteNumber("id", id)
| None -> ()

match value.Error with
| Some err ->
writer.WritePropertyName("error")
JsonSerializer.Serialize(writer, err, opts)
| None ->
writer.WritePropertyName("result")

match value.Result with
| Some je -> je.WriteTo(writer)
| None -> writer.WriteNullValue()

writer.WriteEndObject()


/// Result type composed of a success value or an error of type JsonRpc.Error
type LspResult<'t> = Result<'t, Error>
Expand Down Expand Up @@ -180,6 +241,7 @@ module Requests =

rpcException.ErrorData <-
error.Data
|> Option.map (fun je -> je :> obj)
|> Option.defaultValue null

raise rpcException
Expand Down
Loading
Loading