Skip to content

Commit 9df048e

Browse files
Added warning message when a message is dropped during initialization (#417)
* Added warning message when a message is dropped during initialization * updated rules for input and output * Updated to send outgoing messages once initialization has completed Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent a08ad10 commit 9df048e

29 files changed

+396
-116
lines changed

Directory.Build.targets

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
<PackageReference Update="FluentAssertions" Version="5.10.3" />
2929
<PackageReference Update="NSubstitute" Version="4.2.2" />
3030
<PackageReference Update="Serilog.Extensions.Logging" Version="2.0.2" />
31+
<PackageReference Update="Serilog.Sinks.Observable" Version="2.0.2" />
3132
<PackageReference Update="Serilog.Sinks.XUnit" Version="2.0.4" />
3233
<PackageReference Update="XunitXml.TestLogger" Version="2.1.26" />
3334
<PackageReference Update="coverlet.collector" Version="1.3.0" />

src/Client/LanguageClient.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,6 @@ await LanguageProtocolEventingHelper.Run(
266266

267267
_connection.Open();
268268
var serverParams = await SendRequest(ClientSettings, token).ConfigureAwait(false);
269-
_receiver.Initialized();
270269

271270
ServerSettings = serverParams;
272271

@@ -279,11 +278,15 @@ await LanguageProtocolEventingHelper.Run(
279278
token
280279
).ConfigureAwait(false);
281280

281+
_receiver.Initialized();
282282
// post init
283283

284284
if (_collection.ContainsHandler(typeof(IRegisterCapabilityHandler)))
285285
RegistrationManager.RegisterCapabilities(serverParams.Capabilities);
286286

287+
// TODO: pull supported fields and add any static registrations to the registration manager
288+
this.SendLanguageProtocolInitialized(new InitializedParams());
289+
287290
await LanguageProtocolEventingHelper.Run(
288291
_startedDelegates,
289292
(handler, ct) => handler(this, ct),
@@ -294,9 +297,6 @@ await LanguageProtocolEventingHelper.Run(
294297
).ConfigureAwait(false);
295298

296299
_instanceHasStarted.Started = true;
297-
298-
// TODO: pull supported fields and add any static registrations to the registration manager
299-
this.SendLanguageProtocolInitialized(new InitializedParams());
300300
}
301301

302302
private void RegisterCapabilities(ClientCapabilities capabilities)

src/Client/LanguageClientServiceCollectionExtensions.cs

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ internal static IContainer AddLanguageClientInternals(this IContainer container,
2929
nonPublicServiceTypes: true,
3030
ifAlreadyRegistered: IfAlreadyRegistered.Keep
3131
);
32+
container.RegisterMany<LspClientOutputFilter>(Reuse.Singleton, nonPublicServiceTypes: true);
3233
if (!EqualityComparer<OnUnhandledExceptionHandler?>.Default.Equals(options.OnUnhandledException, default))
3334
{
3435
container.RegisterInstance(options.OnUnhandledException);

src/Client/LspClientOutputFilter.cs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using Microsoft.Extensions.Logging;
2+
using OmniSharp.Extensions.JsonRpc;
3+
using OmniSharp.Extensions.JsonRpc.Client;
4+
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
5+
6+
namespace OmniSharp.Extensions.LanguageServer.Client
7+
{
8+
class LspClientOutputFilter : IOutputFilter
9+
{
10+
private readonly ILogger<LspClientOutputFilter> _logger;
11+
12+
public LspClientOutputFilter(ILogger<LspClientOutputFilter> logger)
13+
{
14+
_logger = logger;
15+
}
16+
17+
public bool ShouldOutput(object value)
18+
{
19+
var result = value switch {
20+
OutgoingResponse => true,
21+
OutgoingRequest { Params: InitializeParams } => true,
22+
OutgoingNotification { Params: InitializedParams } => true,
23+
_ => false
24+
};
25+
if (!result)
26+
{
27+
_logger.LogTrace("Tried to send request or notification before initialization was completed and will be sent later {@Request}", value);
28+
}
29+
30+
return result;
31+
}
32+
}
33+
}

src/Client/LspClientReceiver.cs

+27-31
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
using System.Collections.Generic;
2+
using Microsoft.Extensions.Logging;
23
using Newtonsoft.Json.Linq;
34
using OmniSharp.Extensions.JsonRpc;
4-
using OmniSharp.Extensions.JsonRpc.Client;
55
using OmniSharp.Extensions.JsonRpc.Server;
6-
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
6+
using OmniSharp.Extensions.LanguageServer.Protocol;
77
using OmniSharp.Extensions.LanguageServer.Protocol.Shared;
88
using OmniSharp.Extensions.LanguageServer.Protocol.Window;
99

1010
namespace OmniSharp.Extensions.LanguageServer.Client
1111
{
1212
public class LspClientReceiver : Receiver, ILspClientReceiver
1313
{
14-
private readonly ILspHandlerTypeDescriptorProvider _handlerTypeDescriptorProvider;
15-
private bool _initialized;
14+
private readonly ILogger<LspClientReceiver> _logger;
1615

17-
public LspClientReceiver(ILspHandlerTypeDescriptorProvider handlerTypeDescriptorProvider)
16+
public LspClientReceiver(ILogger<LspClientReceiver> logger)
1817
{
19-
_handlerTypeDescriptorProvider = handlerTypeDescriptorProvider;
18+
_logger = logger;
2019
}
2120

2221
public override (IEnumerable<Renor> results, bool hasResponse) GetRequests(JToken container)
@@ -29,36 +28,33 @@ public override (IEnumerable<Renor> results, bool hasResponse) GetRequests(JToke
2928
var (results, hasResponse) = base.GetRequests(container);
3029
foreach (var item in results)
3130
{
32-
if (item.IsRequest && _handlerTypeDescriptorProvider.IsMethodName(item.Request!.Method, typeof(IShowMessageRequestHandler)))
31+
switch (item)
3332
{
34-
newResults.Add(item);
35-
}
36-
else if (item.IsResponse)
37-
{
38-
newResults.Add(item);
39-
}
40-
else if (item.IsNotification &&
41-
_handlerTypeDescriptorProvider.IsMethodName(
42-
item.Notification!.Method,
43-
typeof(IShowMessageHandler),
44-
typeof(ILogMessageHandler),
45-
typeof(ITelemetryEventHandler)
46-
)
47-
)
48-
{
49-
newResults.Add(item);
33+
case { IsResponse: true }:
34+
case { IsRequest: true, Request: { Method: WindowNames.ShowMessageRequest } }:
35+
case { IsNotification: true, Notification: { Method: WindowNames.ShowMessage } }:
36+
case { IsNotification: true, Notification: { Method: WindowNames.LogMessage } }:
37+
case { IsNotification: true, Notification: { Method: WindowNames.TelemetryEvent } }:
38+
case { IsNotification: true, Notification: { Method: WindowNames.WorkDoneProgressCancel } }:
39+
case { IsNotification: true, Notification: { Method: WindowNames.WorkDoneProgressCreate } }:
40+
newResults.Add(item);
41+
break;
42+
case { IsRequest: true, Request: { } }:
43+
_logger.LogWarning("Unexpected request {Method} {@Request}", item.Request.Method, item.Request);
44+
break;
45+
case { IsNotification: true, Notification: { } }:
46+
_logger.LogWarning("Unexpected notification {Method} {@Request}", item.Notification.Method, item.Notification);
47+
break;
48+
case { IsError: true, Error: { } }:
49+
_logger.LogWarning("Unexpected error {Method} {@Request}", item.Error.Method, item.Error);
50+
break;
51+
default:
52+
_logger.LogError("Unexpected Renor {@Renor}", item);
53+
break;
5054
}
5155
}
5256

5357
return ( newResults, hasResponse );
5458
}
55-
56-
public void Initialized() => _initialized = true;
57-
58-
public override bool ShouldFilterOutput(object value)
59-
{
60-
if (_initialized) return true;
61-
return value is OutgoingResponse || value is OutgoingRequest v && v.Params is InitializeParams;
62-
}
6359
}
6460
}

src/Dap.Client/DebugAdapterClient.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ await DebugAdapterEventingHelper.Run(
136136
var serverParams = await this.RequestDebugAdapterInitialize(ClientSettings, token).ConfigureAwait(false);
137137

138138
ServerSettings = serverParams;
139-
_receiver.Initialized();
140139

141140
await DebugAdapterEventingHelper.Run(
142141
_initializedDelegates,
@@ -147,6 +146,8 @@ await DebugAdapterEventingHelper.Run(
147146
token
148147
).ConfigureAwait(false);
149148

149+
_receiver.Initialized();
150+
150151
await _initializedComplete.ToTask(token);
151152

152153
await DebugAdapterEventingHelper.Run(

src/Dap.Protocol/DapOutputFilter.cs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Microsoft.Extensions.Logging;
2+
using OmniSharp.Extensions.DebugAdapter.Protocol.Events;
3+
using OmniSharp.Extensions.DebugAdapter.Protocol.Requests;
4+
using OmniSharp.Extensions.JsonRpc;
5+
using OmniSharp.Extensions.JsonRpc.Client;
6+
7+
namespace OmniSharp.Extensions.DebugAdapter.Protocol
8+
{
9+
class DapOutputFilter : IOutputFilter
10+
{
11+
private readonly ILogger<DapOutputFilter> _logger;
12+
public DapOutputFilter(ILogger<DapOutputFilter> logger)
13+
{
14+
_logger = logger;
15+
}
16+
17+
public bool ShouldOutput(object value)
18+
{
19+
var result = value is OutgoingResponse ||
20+
value is OutgoingNotification { Params: InitializedEvent } ||
21+
value is OutgoingRequest { Params: InitializeRequestArguments };
22+
23+
if (!result)
24+
{
25+
_logger.LogWarning("Tried to send request or notification before initialization was completed and will be sent later {@Request}", value);
26+
}
27+
28+
return result;
29+
}
30+
}
31+
}

src/Dap.Protocol/DapReceiver.cs

+3-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using Microsoft.Extensions.Logging;
45
using Newtonsoft.Json.Linq;
56
using OmniSharp.Extensions.DebugAdapter.Protocol.Events;
67
using OmniSharp.Extensions.DebugAdapter.Protocol.Requests;
@@ -11,7 +12,7 @@
1112

1213
namespace OmniSharp.Extensions.DebugAdapter.Protocol
1314
{
14-
public class DapReceiver : IReceiver
15+
public class DapReceiver : IReceiver, IOutputFilter
1516
{
1617
private bool _initialized;
1718

@@ -134,13 +135,6 @@ protected virtual IEnumerable<Renor> GetRenor(JToken @object)
134135
}
135136

136137
public void Initialized() => _initialized = true;
137-
138-
public bool ShouldFilterOutput(object value)
139-
{
140-
if (_initialized) return true;
141-
return value is OutgoingResponse ||
142-
value is OutgoingNotification n && n.Params is InitializedEvent ||
143-
value is OutgoingRequest r && r.Params is InitializeRequestArguments;
144-
}
138+
public bool ShouldOutput(object value) => _initialized;
145139
}
146140
}

src/Dap.Server/DebugAdapterServer.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,6 @@ await DebugAdapterEventingHelper.Run(
184184
cancellationToken
185185
).ConfigureAwait(false);
186186

187-
_receiver.Initialized();
188-
189187
var response = new InitializeResponse {
190188
AdditionalModuleColumns = _capabilities.AdditionalModuleColumns,
191189
ExceptionBreakpointFilters = _capabilities.ExceptionBreakpointFilters,
@@ -236,6 +234,8 @@ await DebugAdapterEventingHelper.Run(
236234
cancellationToken
237235
).ConfigureAwait(false);
238236

237+
_receiver.Initialized();
238+
239239
_initializeComplete.OnNext(response);
240240
_initializeComplete.OnCompleted();
241241

src/Dap.Shared/DebugAdapterProtocolServiceCollectionExtensions.cs

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ internal static IContainer AddDebugAdapterProtocolInternals<T>(this IContainer c
2121
container.RegisterInstance(options.RequestProcessIdentifier);
2222
container.RegisterMany<DebugAdapterSettingsBag>(nonPublicServiceTypes: true, reuse: Reuse.Singleton);
2323
container.RegisterMany<DapReceiver>(nonPublicServiceTypes: true, reuse: Reuse.Singleton);
24+
container.RegisterMany<DapOutputFilter>(nonPublicServiceTypes: true, reuse: Reuse.Singleton);
2425
container.RegisterMany<DebugAdapterRequestRouter>(Reuse.Singleton);
2526
container.RegisterMany<DebugAdapterHandlerCollection>(nonPublicServiceTypes: true, reuse: Reuse.Singleton);
2627
container.RegisterInitializer<DebugAdapterHandlerCollection>(

src/JsonRpc/IOutputFilter.cs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace OmniSharp.Extensions.JsonRpc
2+
{
3+
/// <summary>
4+
/// Used to determine if a given message is allowed to be sent before the receiver has been initialized.
5+
/// </summary>
6+
public interface IOutputFilter
7+
{
8+
bool ShouldOutput(object value);
9+
}
10+
11+
class AlwaysOutputFilter : IOutputFilter { public bool ShouldOutput(object value) => true; }
12+
}

src/JsonRpc/IReceiver.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ public interface IReceiver
88
{
99
(IEnumerable<Renor> results, bool hasResponse) GetRequests(JToken container);
1010
bool IsValid(JToken container);
11-
bool ShouldFilterOutput(object value);
11+
void Initialized();
1212
}
1313
}

src/JsonRpc/JsonRpcServerOptions.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Diagnostics.CodeAnalysis;
12
using OmniSharp.Extensions.JsonRpc.Serialization;
23

34
namespace OmniSharp.Extensions.JsonRpc
@@ -9,7 +10,7 @@ public JsonRpcServerOptions()
910
RequestProcessIdentifier = new ParallelRequestProcessIdentifier();
1011
}
1112
public ISerializer Serializer { get; set; } = new JsonRpcSerializer();
12-
public IReceiver Receiver { get; set; } = new Receiver();
13+
[DisallowNull] public IReceiver? Receiver { get; set; } = null!;
1314

1415
public JsonRpcServerOptions WithReceiver(IReceiver receiver)
1516
{

src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs

+11-7
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ internal static IContainer AddJsonRpcServerCore<T>(this IContainer container, Js
3838
}
3939

4040
container.RegisterMany<OutputHandler>(
41-
serviceTypeCondition: type => type.IsInterface,
41+
nonPublicServiceTypes: true,
4242
made: Parameters.Of
4343
.Type<PipeWriter>(serviceKey: nameof(options.Output)),
4444
reuse: Reuse.Singleton
@@ -135,11 +135,6 @@ internal static IContainer AddJsonRpcServerInternals(this IContainer container,
135135
throw new ArgumentException("Serializer is missing!", nameof(options));
136136
}
137137

138-
if (options.Receiver == null)
139-
{
140-
throw new ArgumentException("Receiver is missing!", nameof(options));
141-
}
142-
143138
if (options.RequestProcessIdentifier == null)
144139
{
145140
throw new ArgumentException("RequestProcessIdentifier is missing!", nameof(options));
@@ -149,7 +144,16 @@ internal static IContainer AddJsonRpcServerInternals(this IContainer container,
149144
container.RegisterInstanceMany(new HandlerTypeDescriptorProvider(options.Assemblies), nonPublicServiceTypes: true);
150145

151146
container.RegisterInstance(options.Serializer);
152-
container.RegisterInstance(options.Receiver);
147+
if (options.Receiver == null)
148+
{
149+
container.Register<IReceiver, Receiver>(Reuse.Singleton);
150+
}
151+
else
152+
{
153+
container.RegisterInstance(options.Receiver);
154+
}
155+
container.RegisterMany<AlwaysOutputFilter>(Reuse.Singleton, nonPublicServiceTypes: true);
156+
153157
container.RegisterInstance(options.RequestProcessIdentifier);
154158
container.RegisterInstance(options.OnUnhandledException ?? ( e => { } ));
155159

0 commit comments

Comments
 (0)