Skip to content

IHandlersManager and Handler linking improvments #334

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 26, 2020
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
4 changes: 2 additions & 2 deletions src/Client/LanguageClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHand
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler(string method, Type type, JsonRpcHandlerOptions options) =>
AddHandler(method, type, options);

ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandlerLink(string sourceMethod, string destinationMethod) =>
AddHandlerLink(sourceMethod, destinationMethod);
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandlerLink(string fromMethod, string toMethod) =>
AddHandlerLink(fromMethod, toMethod);

ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.OnJsonRequest(string method, Func<JToken, Task<JToken>> handler, JsonRpcHandlerOptions options) =>
OnJsonRequest(method, handler, options);
Expand Down
4 changes: 2 additions & 2 deletions src/Dap.Client/DebugAdapterClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ IDebugAdapterClientRegistry IJsonRpcHandlerRegistry<IDebugAdapterClientRegistry>
IDebugAdapterClientRegistry IJsonRpcHandlerRegistry<IDebugAdapterClientRegistry>.AddHandler(string method, Type type, JsonRpcHandlerOptions options) =>
AddHandler(method, type, options);

IDebugAdapterClientRegistry IJsonRpcHandlerRegistry<IDebugAdapterClientRegistry>.AddHandlerLink(string sourceMethod, string destinationMethod) =>
AddHandlerLink(sourceMethod, destinationMethod);
IDebugAdapterClientRegistry IJsonRpcHandlerRegistry<IDebugAdapterClientRegistry>.AddHandlerLink(string fromMethod, string toMethod) =>
AddHandlerLink(fromMethod, toMethod);

IDebugAdapterClientRegistry IJsonRpcHandlerRegistry<IDebugAdapterClientRegistry>.OnJsonRequest(
string method, Func<JToken, Task<JToken>> handler, JsonRpcHandlerOptions options
Expand Down
4 changes: 2 additions & 2 deletions src/Dap.Server/DebugAdapterServerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ IDebugAdapterServerRegistry IJsonRpcHandlerRegistry<IDebugAdapterServerRegistry>
IDebugAdapterServerRegistry IJsonRpcHandlerRegistry<IDebugAdapterServerRegistry>.AddHandler(string method, Type type, JsonRpcHandlerOptions options) =>
AddHandler(method, type, options);

IDebugAdapterServerRegistry IJsonRpcHandlerRegistry<IDebugAdapterServerRegistry>.AddHandlerLink(string sourceMethod, string destinationMethod) =>
AddHandlerLink(sourceMethod, destinationMethod);
IDebugAdapterServerRegistry IJsonRpcHandlerRegistry<IDebugAdapterServerRegistry>.AddHandlerLink(string fromMethod, string toMethod) =>
AddHandlerLink(fromMethod, toMethod);

IDebugAdapterServerRegistry IJsonRpcHandlerRegistry<IDebugAdapterServerRegistry>.OnJsonRequest(
string method, Func<JToken, Task<JToken>> handler, JsonRpcHandlerOptions options
Expand Down
20 changes: 17 additions & 3 deletions src/Dap.Shared/DebugAdapterHandlerCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,26 @@ public IDisposable Add(string method, Type handlerType, JsonRpcHandlerOptions op
method, ActivatorUtilities.CreateInstance(_serviceProvider, handlerType) as IJsonRpcHandler, options
);

IDisposable IHandlersManager.AddLink(string sourceMethod, string destinationMethod)
IDisposable IHandlersManager.AddLink(string fromMethod, string toMethod)
{
var source = _descriptors.First(z => z.Method == sourceMethod);
var source = _descriptors.FirstOrDefault(z => z.Method == fromMethod);
if (source == null)
{
if (_descriptors.Any(z => z.Method == toMethod))
{
throw new ArgumentException(
$"Could not find descriptor for '{fromMethod}', but I did find one for '{toMethod}'. Did you mean to link '{toMethod}' to '{fromMethod}' instead?", fromMethod
);
}

throw new ArgumentException(
$"Could not find descriptor for '{fromMethod}', has it been registered yet? Descriptors must be registered before links can be created!", nameof(fromMethod)
);
}

HandlerDescriptor descriptor = null;
descriptor = GetDescriptor(
destinationMethod,
toMethod,
source.HandlerType,
source.Handler,
source.RequestProcessType.HasValue ? new JsonRpcHandlerOptions { RequestProcessType = source.RequestProcessType.Value } : null,
Expand Down
4 changes: 2 additions & 2 deletions src/JsonRpc/CompositeHandlersManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ public IDisposable Add(string method, Type handlerType, JsonRpcHandlerOptions op
return result;
}

public IDisposable AddLink(string sourceMethod, string destinationMethod)
public IDisposable AddLink(string fromMethod, string toMethod)
{
var result = _parent.AddLink(sourceMethod,destinationMethod);
var result = _parent.AddLink(fromMethod,toMethod);
_compositeDisposable.Add(result);
return result;
}
Expand Down
20 changes: 17 additions & 3 deletions src/JsonRpc/HandlerCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,24 @@ public IDisposable Add(string method, Type handlerType, JsonRpcHandlerOptions op
method, ActivatorUtilities.CreateInstance(_serviceProvider, handlerType) as IJsonRpcHandler, options
);

public IDisposable AddLink(string sourceMethod, string destinationMethod)
public IDisposable AddLink(string fromMethod, string toMethod)
{
var source = _descriptors.FirstOrDefault(z => z.Method == sourceMethod);
var descriptor = new LinkedHandler(destinationMethod, source, () => _descriptors.RemoveAll(z => z.Method == destinationMethod));
var source = _descriptors.FirstOrDefault(z => z.Method == fromMethod);
if (source == null)
{
if (_descriptors.Any(z => z.Method == toMethod))
{
throw new ArgumentException(
$"Could not find descriptor for '{fromMethod}', but I did find one for '{toMethod}'. Did you mean to link '{toMethod}' to '{fromMethod}' instead?", fromMethod
);
}

throw new ArgumentException(
$"Could not find descriptor for '{fromMethod}', has it been registered yet? Descriptors must be registered before links can be created!", nameof(fromMethod)
);
}

var descriptor = new LinkedHandler(toMethod, source, () => _descriptors.RemoveAll(z => z.Method == toMethod));
ImmutableInterlocked.InterlockedExchange(ref _descriptors, _descriptors.Add(descriptor));
return descriptor;
}
Expand Down
18 changes: 18 additions & 0 deletions src/JsonRpc/HandlersManagerExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Linq;

namespace OmniSharp.Extensions.JsonRpc
{
public static class HandlersManagerExtensions
{
/// <summary>
/// Gets all the unique handlers currently registered with the manager
/// </summary>
/// <param name="handlersManager"></param>
/// <returns></returns>
public static IEnumerable<IJsonRpcHandler> GetHandlers(this IHandlersManager handlersManager)
{
return handlersManager.Descriptors.Select(z => z.Handler).Distinct();
}
}
}
2 changes: 1 addition & 1 deletion src/JsonRpc/IHandlersManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public interface IHandlersManager
IDisposable Add(string method, JsonRpcHandlerFactory factory, JsonRpcHandlerOptions options);
IDisposable Add(Type handlerType, JsonRpcHandlerOptions options);
IDisposable Add(string method, Type handlerType, JsonRpcHandlerOptions options);
IDisposable AddLink(string sourceMethod, string destinationMethod);
IDisposable AddLink(string fromMethod, string toMethod);
IEnumerable<IHandlerDescriptor> Descriptors { get; }
}
}
2 changes: 1 addition & 1 deletion src/JsonRpc/IJsonRpcHandlerRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public interface IJsonRpcHandlerRegistry<out T> : IJsonRpcHandlerRegistry where
T AddHandler<TTHandler>(string method, JsonRpcHandlerOptions options = null) where TTHandler : IJsonRpcHandler;
T AddHandler(Type type, JsonRpcHandlerOptions options = null);
T AddHandler(string method, Type type, JsonRpcHandlerOptions options = null);
T AddHandlerLink(string sourceMethod, string destinationMethod);
T AddHandlerLink(string fromMethod, string toMethod);

T OnJsonRequest(string method, Func<JToken, Task<JToken>> handler, JsonRpcHandlerOptions options = null);
T OnJsonRequest(string method, Func<JToken, CancellationToken, Task<JToken>> handler, JsonRpcHandlerOptions options = null);
Expand Down
4 changes: 2 additions & 2 deletions src/JsonRpc/InterimJsonRpcServerRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ public sealed override T AddHandler(string method, Type type, JsonRpcHandlerOpti
return (T) (object) this;
}

public sealed override T AddHandlerLink(string sourceMethod, string destinationMethod)
public sealed override T AddHandlerLink(string fromMethod, string toMethod)
{
_handlersManager.AddLink(sourceMethod, destinationMethod);
_handlersManager.AddLink(fromMethod, toMethod);
return (T) (object) this;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/JsonRpc/JsonRpcCommonMethodsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public T OnNotification(string method, Func<CancellationToken, Task> handler, Js
public abstract T AddHandler<THandler>(string method, JsonRpcHandlerOptions options = null) where THandler : IJsonRpcHandler;
public abstract T AddHandler(Type type, JsonRpcHandlerOptions options = null);
public abstract T AddHandler(string method, Type type, JsonRpcHandlerOptions options = null);
public abstract T AddHandlerLink(string sourceMethod, string destinationMethod);
public abstract T AddHandlerLink(string fromMethod, string toMethod);

#endregion
}
Expand Down
4 changes: 2 additions & 2 deletions src/JsonRpc/JsonRpcOptionsRegistryBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ public sealed override T AddHandler(string method, Type type, JsonRpcHandlerOpti
return (T) (object) this;
}

public sealed override T AddHandlerLink(string sourceMethod, string destinationMethod)
public sealed override T AddHandlerLink(string fromMethod, string toMethod)
{
Handlers.Add(JsonRpcHandlerDescription.Link(sourceMethod, destinationMethod));
Handlers.Add(JsonRpcHandlerDescription.Link(fromMethod, toMethod));
return (T) (object) this;
}

Expand Down
4 changes: 2 additions & 2 deletions src/JsonRpc/JsonRpcServerOptionsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ public T WithMaximumRequestTimeout(TimeSpan maximumRequestTimeout)
return (T) (object) this;
}

public T WithLink(string source, string destination)
public T WithLink(string fromMethod, string toMethod)
{
Handlers.Add(JsonRpcHandlerDescription.Link(source, destination));
Handlers.Add(JsonRpcHandlerDescription.Link(fromMethod, toMethod));
return (T) (object) this;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/Server/LanguageServerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ ILanguageServerRegistry IJsonRpcHandlerRegistry<ILanguageServerRegistry>.AddHand
ILanguageServerRegistry IJsonRpcHandlerRegistry<ILanguageServerRegistry>.AddHandler(string method, Type type, JsonRpcHandlerOptions options) =>
AddHandler(method, type, options);

ILanguageServerRegistry IJsonRpcHandlerRegistry<ILanguageServerRegistry>.AddHandlerLink(string sourceMethod, string destinationMethod) =>
AddHandlerLink(sourceMethod, destinationMethod);
ILanguageServerRegistry IJsonRpcHandlerRegistry<ILanguageServerRegistry>.AddHandlerLink(string fromMethod, string toMethod) =>
AddHandlerLink(fromMethod, toMethod);

ILanguageServerRegistry IJsonRpcHandlerRegistry<ILanguageServerRegistry>.OnJsonRequest(string method, Func<JToken, Task<JToken>> handler, JsonRpcHandlerOptions options) =>
OnJsonRequest(method, handler, options);
Expand Down
20 changes: 17 additions & 3 deletions src/Shared/SharedHandlerCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,26 @@ IDisposable IHandlersManager.Add(Type handlerType, JsonRpcHandlerOptions options
IDisposable IHandlersManager.Add(string method, Type handlerType, JsonRpcHandlerOptions options) =>
Add(method, ActivatorUtilities.CreateInstance(_serviceProvider, handlerType) as IJsonRpcHandler, options);

IDisposable IHandlersManager.AddLink(string sourceMethod, string destinationMethod)
IDisposable IHandlersManager.AddLink(string fromMethod, string toMethod)
{
var source = _descriptors.First(z => z.Method == sourceMethod);
var source = _descriptors.FirstOrDefault(z => z.Method == fromMethod);
if (source == null)
{
if (_descriptors.Any(z => z.Method == toMethod))
{
throw new ArgumentException(
$"Could not find descriptor for '{fromMethod}', but I did find one for '{toMethod}'. Did you mean to link '{toMethod}' to '{fromMethod}' instead?", fromMethod
);
}

throw new ArgumentException(
$"Could not find descriptor for '{fromMethod}', has it been registered yet? Descriptors must be registered before links can be created!", nameof(fromMethod)
);
}

LspHandlerDescriptor descriptor = null;
descriptor = GetDescriptor(
destinationMethod,
toMethod,
source.HandlerType,
source.Handler,
source.RequestProcessType.HasValue ? new JsonRpcHandlerOptions { RequestProcessType = source.RequestProcessType.Value } : null,
Expand Down
54 changes: 54 additions & 0 deletions test/Dap.Tests/Integration/HandlersManagerIntegrationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using OmniSharp.Extensions.DebugAdapter.Protocol.Requests;
using OmniSharp.Extensions.DebugAdapter.Testing;
using OmniSharp.Extensions.JsonRpc;
using OmniSharp.Extensions.JsonRpc.Testing;
using Xunit;
using Xunit.Abstractions;

namespace Dap.Tests.Integration
{
public class HandlersManagerIntegrationTests : DebugAdapterProtocolTestBase
{
public HandlersManagerIntegrationTests(ITestOutputHelper testOutputHelper) : base(new JsonRpcTestOptions().ConfigureForXUnit(testOutputHelper))
{
}

[Fact]
public async Task Should_Return_Default_Handlers()
{
var (client, server) = await Initialize(options => {}, options => {});

var handlersManager = server.GetRequiredService<IHandlersManager>();
handlersManager.Descriptors.Should().HaveCount(2);
handlersManager.GetHandlers().Should().HaveCount(2);
}

[Fact]
public async Task Link_Should_Fail_If_No_Handler_Is_Defined()
{
var (client, server) = await Initialize(options => {}, options => {});

var handlersManager = server.GetRequiredService<IHandlersManager>();

Action a = () => handlersManager.AddLink(RequestNames.Completions, "my/completions");
a.Should().Throw<ArgumentException>().Which.Message.Should().Contain("Descriptors must be registered before links can be created");
}

[Fact]
public async Task Link_Should_Fail_If_Link_Is_On_The_Wrong_Side()
{
var (client, server) = await Initialize(options => {}, options => {});

var handlersManager = server.GetRequiredService<IHandlersManager>();
handlersManager.Add(Substitute.For(new Type[] { typeof(ICompletionsHandler) }, Array.Empty<object>()) as IJsonRpcHandler, new JsonRpcHandlerOptions());

Action a = () => handlersManager.AddLink("my/completions", RequestNames.Completions);
a.Should().Throw<ArgumentException>().Which.Message.Should().Contain($"Did you mean to link '{RequestNames.Completions}' to 'my/completions' instead");
}
}
}
2 changes: 1 addition & 1 deletion test/JsonRpc.Tests/TestLanguageServerRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public override IJsonRpcServerRegistry AddHandler(string method, IJsonRpcHandler
public override IJsonRpcServerRegistry AddHandler(Type type, JsonRpcHandlerOptions options = null) => throw new NotImplementedException();

public override IJsonRpcServerRegistry AddHandler(string method, Type type, JsonRpcHandlerOptions options = null) => throw new NotImplementedException();
public override IJsonRpcServerRegistry AddHandlerLink(string sourceMethod, string destinationMethod) => throw new NotImplementedException();
public override IJsonRpcServerRegistry AddHandlerLink(string fromMethod, string toMethod) => throw new NotImplementedException();

public override IJsonRpcServerRegistry AddHandler(string method, JsonRpcHandlerFactory handlerFunc, JsonRpcHandlerOptions options = null)
{
Expand Down
66 changes: 66 additions & 0 deletions test/Lsp.Tests/Integration/HandlersManagerIntegrationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using OmniSharp.Extensions.JsonRpc;
using OmniSharp.Extensions.JsonRpc.Testing;
using OmniSharp.Extensions.LanguageProtocol.Testing;
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
using Xunit;
using Xunit.Abstractions;

namespace Lsp.Tests.Integration
{
public class HandlersManagerIntegrationTests : LanguageProtocolTestBase
{
public HandlersManagerIntegrationTests(ITestOutputHelper testOutputHelper) : base(new JsonRpcTestOptions().ConfigureForXUnit(testOutputHelper))
{
}

[Fact]
public async Task Should_Return_Default_Handlers()
{
var (client, server) = await Initialize(options => {}, options => {});

var handlersManager = server.GetRequiredService<IHandlersManager>();
handlersManager.Descriptors.Should().HaveCount(8);
handlersManager.GetHandlers().Should().HaveCount(5);
}

[Fact]
public async Task Should_Return_Additional_Handlers()
{
var (client, server) = await Initialize(options => {}, options => {});

server.Register(o => o.AddHandler(Substitute.For(new Type[] { typeof (ICompletionHandler), typeof(ICompletionResolveHandler) }, Array.Empty<object>()) as IJsonRpcHandler));
var handlersManager = server.GetRequiredService<IHandlersManager>();
handlersManager.Descriptors.Should().HaveCount(10);
handlersManager.GetHandlers().Should().HaveCount(6);
}

[Fact]
public async Task Link_Should_Fail_If_No_Handler_Is_Defined()
{
var (client, server) = await Initialize(options => {}, options => {});

var handlersManager = server.GetRequiredService<IHandlersManager>();

Action a = () => handlersManager.AddLink(TextDocumentNames.Completion, "my/completion");
a.Should().Throw<ArgumentException>().Which.Message.Should().Contain("Descriptors must be registered before links can be created");
}

[Fact]
public async Task Link_Should_Fail_If_Link_Is_On_The_Wrong_Side()
{
var (client, server) = await Initialize(options => {}, options => {});

server.Register(o => o.AddHandler(Substitute.For(new Type[] { typeof (ICompletionHandler), typeof(ICompletionResolveHandler) }, Array.Empty<object>()) as IJsonRpcHandler));
var handlersManager = server.GetRequiredService<IHandlersManager>();

Action a = () => handlersManager.AddLink("my/completion", TextDocumentNames.Completion);
a.Should().Throw<ArgumentException>().Which.Message.Should().Contain($"Did you mean to link '{TextDocumentNames.Completion}' to 'my/completion' instead");
}
}
}