Skip to content

Commit 7f16f6a

Browse files
TrevorThoelebaronfel
authored andcommitted
Allow multiple instances of the server on a single process.
1 parent 1fce71b commit 7f16f6a

File tree

4 files changed

+93
-13
lines changed

4 files changed

+93
-13
lines changed

src/LanguageServerProtocol.fs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,21 @@ module Server =
1616

1717
let logger = LogProvider.getLoggerByName "LSP Server"
1818

19-
let jsonRpcFormatter = new JsonMessageFormatter()
20-
jsonRpcFormatter.JsonSerializer.NullValueHandling <- NullValueHandling.Ignore
21-
jsonRpcFormatter.JsonSerializer.ConstructorHandling <- ConstructorHandling.AllowNonPublicDefaultConstructor
22-
jsonRpcFormatter.JsonSerializer.MissingMemberHandling <- MissingMemberHandling.Ignore
23-
jsonRpcFormatter.JsonSerializer.Converters.Add(StrictNumberConverter())
24-
jsonRpcFormatter.JsonSerializer.Converters.Add(StrictStringConverter())
25-
jsonRpcFormatter.JsonSerializer.Converters.Add(StrictBoolConverter())
26-
jsonRpcFormatter.JsonSerializer.Converters.Add(SingleCaseUnionConverter())
27-
jsonRpcFormatter.JsonSerializer.Converters.Add(OptionConverter())
28-
jsonRpcFormatter.JsonSerializer.Converters.Add(ErasedUnionConverter())
29-
jsonRpcFormatter.JsonSerializer.ContractResolver <- OptionAndCamelCasePropertyNamesContractResolver()
19+
let defaultJsonRpcFormatter() =
20+
let jsonRpcFormatter = new JsonMessageFormatter()
21+
jsonRpcFormatter.JsonSerializer.NullValueHandling <- NullValueHandling.Ignore
22+
jsonRpcFormatter.JsonSerializer.ConstructorHandling <- ConstructorHandling.AllowNonPublicDefaultConstructor
23+
jsonRpcFormatter.JsonSerializer.MissingMemberHandling <- MissingMemberHandling.Ignore
24+
jsonRpcFormatter.JsonSerializer.Converters.Add(StrictNumberConverter())
25+
jsonRpcFormatter.JsonSerializer.Converters.Add(StrictStringConverter())
26+
jsonRpcFormatter.JsonSerializer.Converters.Add(StrictBoolConverter())
27+
jsonRpcFormatter.JsonSerializer.Converters.Add(SingleCaseUnionConverter())
28+
jsonRpcFormatter.JsonSerializer.Converters.Add(OptionConverter())
29+
jsonRpcFormatter.JsonSerializer.Converters.Add(ErasedUnionConverter())
30+
jsonRpcFormatter.JsonSerializer.ContractResolver <- OptionAndCamelCasePropertyNamesContractResolver()
31+
jsonRpcFormatter
32+
33+
let jsonRpcFormatter = defaultJsonRpcFormatter()
3034

3135
let deserialize<'t> (token: JToken) = token.ToObject<'t>(jsonRpcFormatter.JsonSerializer)
3236
let serialize<'t> (o: 't) = JToken.FromObject(o, jsonRpcFormatter.JsonSerializer)
@@ -95,7 +99,7 @@ module Server =
9599
(customizeRpc: IJsonRpcMessageHandler -> JsonRpc)
96100
=
97101

98-
use jsonRpcHandler = new HeaderDelimitedMessageHandler(output, input, jsonRpcFormatter)
102+
use jsonRpcHandler = new HeaderDelimitedMessageHandler(output, input, defaultJsonRpcFormatter())
99103
// Without overriding isFatalException, JsonRpc serializes exceptions and sends them to the client.
100104
// This is particularly bad for notifications such as textDocument/didChange which don't require a response,
101105
// and thus any exception that happens during e.g. text sync gets swallowed.

tests/Ionide.LanguageServerProtocol.Tests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<Compile Include="Utils.fs" />
1111
<Compile Include="Benchmarks.fs" />
1212
<Compile Include="Shotgun.fs" />
13+
<Compile Include="StartWithSetup.fs" />
1314
<Compile Include="Tests.fs" />
1415
<Compile Include="Program.fs" />
1516
</ItemGroup>

tests/StartWithSetup.fs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
module Ionide.LanguageServerProtocol.Tests.StartWithSetup
2+
3+
open Expecto
4+
open System.IO.Pipes
5+
open System.IO
6+
open Ionide.LanguageServerProtocol
7+
open Ionide.LanguageServerProtocol.Server
8+
9+
type TestLspClient(sendServerNotification: ClientNotificationSender, sendServerRequest: ClientRequestSender) =
10+
inherit LspClient ()
11+
12+
let setupEndpoints(_: LspClient): Map<string, System.Delegate> =
13+
[] |> Map.ofList
14+
15+
let requestWithContentLength(request: string) =
16+
@$"Content-Length: {request.Length}
17+
18+
{request}"
19+
20+
let shutdownRequest = @"{""jsonrpc"":""2.0"",""method"":""shutdown"",""id"":1}"
21+
22+
let exitRequest = @"{""jsonrpc"":""2.0"",""method"":""exit"",""id"":1}"
23+
24+
let tests =
25+
testList
26+
"startWithSetup"
27+
[
28+
testAsync "can start up multiple times in same process" {
29+
use inputServerPipe1 = new AnonymousPipeServerStream()
30+
use inputClientPipe1 = new AnonymousPipeClientStream(inputServerPipe1.GetClientHandleAsString())
31+
use outputServerPipe1 = new AnonymousPipeServerStream()
32+
33+
use inputWriter1 = new StreamWriter(inputServerPipe1)
34+
inputWriter1.AutoFlush <- true
35+
let server1 = async {
36+
let result = (startWithSetup
37+
setupEndpoints
38+
inputClientPipe1
39+
outputServerPipe1
40+
TestLspClient
41+
defaultRpc)
42+
Expect.equal (int result) 0 "server startup failed"
43+
}
44+
45+
let! server1Async = Async.StartChild(server1)
46+
47+
use inputServerPipe2 = new AnonymousPipeServerStream()
48+
use inputClientPipe2 = new AnonymousPipeClientStream(inputServerPipe2.GetClientHandleAsString())
49+
use outputServerPipe2 = new AnonymousPipeServerStream()
50+
51+
use inputWriter2 = new StreamWriter(inputServerPipe2)
52+
inputWriter2.AutoFlush <- true
53+
let server2 = async {
54+
let result = (startWithSetup
55+
setupEndpoints
56+
inputClientPipe2
57+
outputServerPipe2
58+
TestLspClient
59+
defaultRpc)
60+
Expect.equal (int result) 0 "server startup failed"
61+
}
62+
63+
let! server2Async = Async.StartChild(server2)
64+
65+
inputWriter1.Write(requestWithContentLength(shutdownRequest))
66+
inputWriter1.Write(requestWithContentLength(exitRequest))
67+
68+
inputWriter2.Write(requestWithContentLength(shutdownRequest))
69+
inputWriter2.Write(requestWithContentLength(exitRequest))
70+
71+
do! server1Async
72+
do! server2Async
73+
}
74+
]

tests/Tests.fs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,8 @@ let private serializationTests =
874874
Data = None }
875875
testThereAndBackAgain item
876876
]
877-
Shotgun.tests ]
877+
Shotgun.tests
878+
StartWithSetup.tests ]
878879

879880
[<Tests>]
880881
let tests = testList "LSP" [ serializationTests; Utils.tests ]

0 commit comments

Comments
 (0)