Skip to content
Merged
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
1 change: 1 addition & 0 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<PackageReference Update="FluentAssertions" Version="5.10.3" />
<PackageReference Update="NSubstitute" Version="4.2.2" />
<PackageReference Update="Serilog.Extensions.Logging" Version="2.0.2" />
<PackageReference Update="Serilog.Sinks.Observable" Version="2.0.2" />
<PackageReference Update="Serilog.Sinks.XUnit" Version="2.0.4" />
<PackageReference Update="XunitXml.TestLogger" Version="2.1.26" />
<PackageReference Update="coverlet.collector" Version="1.3.0" />
Expand Down
8 changes: 4 additions & 4 deletions src/Client/LanguageClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,6 @@ await LanguageProtocolEventingHelper.Run(

_connection.Open();
var serverParams = await SendRequest(ClientSettings, token).ConfigureAwait(false);
_receiver.Initialized();

ServerSettings = serverParams;

Expand All @@ -279,11 +278,15 @@ await LanguageProtocolEventingHelper.Run(
token
).ConfigureAwait(false);

_receiver.Initialized();
// post init

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

// TODO: pull supported fields and add any static registrations to the registration manager
this.SendLanguageProtocolInitialized(new InitializedParams());

await LanguageProtocolEventingHelper.Run(
_startedDelegates,
(handler, ct) => handler(this, ct),
Expand All @@ -294,9 +297,6 @@ await LanguageProtocolEventingHelper.Run(
).ConfigureAwait(false);

_instanceHasStarted.Started = true;

// TODO: pull supported fields and add any static registrations to the registration manager
this.SendLanguageProtocolInitialized(new InitializedParams());
}

private void RegisterCapabilities(ClientCapabilities capabilities)
Expand Down
1 change: 1 addition & 0 deletions src/Client/LanguageClientServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ internal static IContainer AddLanguageClientInternals(this IContainer container,
nonPublicServiceTypes: true,
ifAlreadyRegistered: IfAlreadyRegistered.Keep
);
container.RegisterMany<LspClientOutputFilter>(Reuse.Singleton, nonPublicServiceTypes: true);
if (!EqualityComparer<OnUnhandledExceptionHandler?>.Default.Equals(options.OnUnhandledException, default))
{
container.RegisterInstance(options.OnUnhandledException);
Expand Down
33 changes: 33 additions & 0 deletions src/Client/LspClientOutputFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.JsonRpc;
using OmniSharp.Extensions.JsonRpc.Client;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;

namespace OmniSharp.Extensions.LanguageServer.Client
{
class LspClientOutputFilter : IOutputFilter
{
private readonly ILogger<LspClientOutputFilter> _logger;

public LspClientOutputFilter(ILogger<LspClientOutputFilter> logger)
{
_logger = logger;
}

public bool ShouldOutput(object value)
{
var result = value switch {
OutgoingResponse => true,
OutgoingRequest { Params: InitializeParams } => true,
OutgoingNotification { Params: InitializedParams } => true,
_ => false
};
if (!result)
{
_logger.LogTrace("Tried to send request or notification before initialization was completed and will be sent later {@Request}", value);
}

return result;
}
}
}
58 changes: 27 additions & 31 deletions src/Client/LspClientReceiver.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using OmniSharp.Extensions.JsonRpc;
using OmniSharp.Extensions.JsonRpc.Client;
using OmniSharp.Extensions.JsonRpc.Server;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Shared;
using OmniSharp.Extensions.LanguageServer.Protocol.Window;

namespace OmniSharp.Extensions.LanguageServer.Client
{
public class LspClientReceiver : Receiver, ILspClientReceiver
{
private readonly ILspHandlerTypeDescriptorProvider _handlerTypeDescriptorProvider;
private bool _initialized;
private readonly ILogger<LspClientReceiver> _logger;

public LspClientReceiver(ILspHandlerTypeDescriptorProvider handlerTypeDescriptorProvider)
public LspClientReceiver(ILogger<LspClientReceiver> logger)
{
_handlerTypeDescriptorProvider = handlerTypeDescriptorProvider;
_logger = logger;
}

public override (IEnumerable<Renor> results, bool hasResponse) GetRequests(JToken container)
Expand All @@ -29,36 +28,33 @@ public override (IEnumerable<Renor> results, bool hasResponse) GetRequests(JToke
var (results, hasResponse) = base.GetRequests(container);
foreach (var item in results)
{
if (item.IsRequest && _handlerTypeDescriptorProvider.IsMethodName(item.Request!.Method, typeof(IShowMessageRequestHandler)))
switch (item)
{
newResults.Add(item);
}
else if (item.IsResponse)
{
newResults.Add(item);
}
else if (item.IsNotification &&
_handlerTypeDescriptorProvider.IsMethodName(
item.Notification!.Method,
typeof(IShowMessageHandler),
typeof(ILogMessageHandler),
typeof(ITelemetryEventHandler)
)
)
{
newResults.Add(item);
case { IsResponse: true }:
case { IsRequest: true, Request: { Method: WindowNames.ShowMessageRequest } }:
case { IsNotification: true, Notification: { Method: WindowNames.ShowMessage } }:
case { IsNotification: true, Notification: { Method: WindowNames.LogMessage } }:
case { IsNotification: true, Notification: { Method: WindowNames.TelemetryEvent } }:
case { IsNotification: true, Notification: { Method: WindowNames.WorkDoneProgressCancel } }:
case { IsNotification: true, Notification: { Method: WindowNames.WorkDoneProgressCreate } }:
newResults.Add(item);
break;
case { IsRequest: true, Request: { } }:
_logger.LogWarning("Unexpected request {Method} {@Request}", item.Request.Method, item.Request);
break;
case { IsNotification: true, Notification: { } }:
_logger.LogWarning("Unexpected notification {Method} {@Request}", item.Notification.Method, item.Notification);
break;
case { IsError: true, Error: { } }:
_logger.LogWarning("Unexpected error {Method} {@Request}", item.Error.Method, item.Error);
break;
default:
_logger.LogError("Unexpected Renor {@Renor}", item);
break;
}
}

return ( newResults, hasResponse );
}

public void Initialized() => _initialized = true;

public override bool ShouldFilterOutput(object value)
{
if (_initialized) return true;
return value is OutgoingResponse || value is OutgoingRequest v && v.Params is InitializeParams;
}
}
}
3 changes: 2 additions & 1 deletion src/Dap.Client/DebugAdapterClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@ await DebugAdapterEventingHelper.Run(
var serverParams = await this.RequestDebugAdapterInitialize(ClientSettings, token).ConfigureAwait(false);

ServerSettings = serverParams;
_receiver.Initialized();

await DebugAdapterEventingHelper.Run(
_initializedDelegates,
Expand All @@ -147,6 +146,8 @@ await DebugAdapterEventingHelper.Run(
token
).ConfigureAwait(false);

_receiver.Initialized();

await _initializedComplete.ToTask(token);

await DebugAdapterEventingHelper.Run(
Expand Down
31 changes: 31 additions & 0 deletions src/Dap.Protocol/DapOutputFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.DebugAdapter.Protocol.Events;
using OmniSharp.Extensions.DebugAdapter.Protocol.Requests;
using OmniSharp.Extensions.JsonRpc;
using OmniSharp.Extensions.JsonRpc.Client;

namespace OmniSharp.Extensions.DebugAdapter.Protocol
{
class DapOutputFilter : IOutputFilter
{
private readonly ILogger<DapOutputFilter> _logger;
public DapOutputFilter(ILogger<DapOutputFilter> logger)
{
_logger = logger;
}

public bool ShouldOutput(object value)
{
var result = value is OutgoingResponse ||
value is OutgoingNotification { Params: InitializedEvent } ||
value is OutgoingRequest { Params: InitializeRequestArguments };

if (!result)
{
_logger.LogWarning("Tried to send request or notification before initialization was completed and will be sent later {@Request}", value);
}

return result;
}
}
}
12 changes: 3 additions & 9 deletions src/Dap.Protocol/DapReceiver.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using OmniSharp.Extensions.DebugAdapter.Protocol.Events;
using OmniSharp.Extensions.DebugAdapter.Protocol.Requests;
Expand All @@ -11,7 +12,7 @@

namespace OmniSharp.Extensions.DebugAdapter.Protocol
{
public class DapReceiver : IReceiver
public class DapReceiver : IReceiver, IOutputFilter
{
private bool _initialized;

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

public void Initialized() => _initialized = true;

public bool ShouldFilterOutput(object value)
{
if (_initialized) return true;
return value is OutgoingResponse ||
value is OutgoingNotification n && n.Params is InitializedEvent ||
value is OutgoingRequest r && r.Params is InitializeRequestArguments;
}
public bool ShouldOutput(object value) => _initialized;
}
}
4 changes: 2 additions & 2 deletions src/Dap.Server/DebugAdapterServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,6 @@ await DebugAdapterEventingHelper.Run(
cancellationToken
).ConfigureAwait(false);

_receiver.Initialized();

var response = new InitializeResponse {
AdditionalModuleColumns = _capabilities.AdditionalModuleColumns,
ExceptionBreakpointFilters = _capabilities.ExceptionBreakpointFilters,
Expand Down Expand Up @@ -236,6 +234,8 @@ await DebugAdapterEventingHelper.Run(
cancellationToken
).ConfigureAwait(false);

_receiver.Initialized();

_initializeComplete.OnNext(response);
_initializeComplete.OnCompleted();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ internal static IContainer AddDebugAdapterProtocolInternals<T>(this IContainer c
container.RegisterInstance(options.RequestProcessIdentifier);
container.RegisterMany<DebugAdapterSettingsBag>(nonPublicServiceTypes: true, reuse: Reuse.Singleton);
container.RegisterMany<DapReceiver>(nonPublicServiceTypes: true, reuse: Reuse.Singleton);
container.RegisterMany<DapOutputFilter>(nonPublicServiceTypes: true, reuse: Reuse.Singleton);
container.RegisterMany<DebugAdapterRequestRouter>(Reuse.Singleton);
container.RegisterMany<DebugAdapterHandlerCollection>(nonPublicServiceTypes: true, reuse: Reuse.Singleton);
container.RegisterInitializer<DebugAdapterHandlerCollection>(
Expand Down
12 changes: 12 additions & 0 deletions src/JsonRpc/IOutputFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace OmniSharp.Extensions.JsonRpc
{
/// <summary>
/// Used to determine if a given message is allowed to be sent before the receiver has been initialized.
/// </summary>
public interface IOutputFilter
{
bool ShouldOutput(object value);
}

class AlwaysOutputFilter : IOutputFilter { public bool ShouldOutput(object value) => true; }
}
2 changes: 1 addition & 1 deletion src/JsonRpc/IReceiver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ public interface IReceiver
{
(IEnumerable<Renor> results, bool hasResponse) GetRequests(JToken container);
bool IsValid(JToken container);
bool ShouldFilterOutput(object value);
void Initialized();
}
}
3 changes: 2 additions & 1 deletion src/JsonRpc/JsonRpcServerOptions.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using OmniSharp.Extensions.JsonRpc.Serialization;

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

public JsonRpcServerOptions WithReceiver(IReceiver receiver)
{
Expand Down
18 changes: 11 additions & 7 deletions src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ internal static IContainer AddJsonRpcServerCore<T>(this IContainer container, Js
}

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

if (options.Receiver == null)
{
throw new ArgumentException("Receiver is missing!", nameof(options));
}

if (options.RequestProcessIdentifier == null)
{
throw new ArgumentException("RequestProcessIdentifier is missing!", nameof(options));
Expand All @@ -149,7 +144,16 @@ internal static IContainer AddJsonRpcServerInternals(this IContainer container,
container.RegisterInstanceMany(new HandlerTypeDescriptorProvider(options.Assemblies), nonPublicServiceTypes: true);

container.RegisterInstance(options.Serializer);
container.RegisterInstance(options.Receiver);
if (options.Receiver == null)
{
container.Register<IReceiver, Receiver>(Reuse.Singleton);
}
else
{
container.RegisterInstance(options.Receiver);
}
container.RegisterMany<AlwaysOutputFilter>(Reuse.Singleton, nonPublicServiceTypes: true);

container.RegisterInstance(options.RequestProcessIdentifier);
container.RegisterInstance(options.OnUnhandledException ?? ( e => { } ));

Expand Down
Loading