Skip to content

Commit 5ae6c9a

Browse files
ensure that internal classes can be registered via Microsoft DI (#324)
* ensure that internal classes can be registered via Microsoft DI * Added facade for client and server * Fixed semantic delta params * Updated test
1 parent 82d78ec commit 5ae6c9a

16 files changed

+160
-25
lines changed

sample/SampleServer/SemanticTokensHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public override async Task<SemanticTokens> Handle(
4646
return result;
4747
}
4848

49-
public override async Task<SemanticTokensFullOrDelta> Handle(
49+
public override async Task<SemanticTokensFullOrDelta?> Handle(
5050
SemanticTokensDeltaParams request,
5151
CancellationToken cancellationToken
5252
)

src/Client/LanguageClientServiceCollectionExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ internal static IContainer AddLanguageClientInternals(this IContainer container,
4040
container.RegisterMany<GeneralLanguageClient>(serviceTypeCondition: type => type.Name.Contains(nameof(GeneralLanguageClient)), reuse: Reuse.Singleton);
4141
container.RegisterMany<WindowLanguageClient>(serviceTypeCondition: type => type.Name.Contains(nameof(WindowLanguageClient)), reuse: Reuse.Singleton);
4242
container.RegisterMany<WorkspaceLanguageClient>(serviceTypeCondition: type => type.Name.Contains(nameof(WorkspaceLanguageClient)), reuse: Reuse.Singleton);
43+
container.RegisterMany<DefaultLanguageClientFacade>(serviceTypeCondition: type => type.Name.Contains("LanguageClientFacade"), reuse: Reuse.Singleton);
4344
container.RegisterInstance<IOptionsFactory<LanguageClientOptions>>(new ValueOptionsFactory<LanguageClientOptions>(options));
4445

4546
container.RegisterMany<LanguageClient>(serviceTypeCondition: type => type == typeof(ILanguageClient) || type == typeof(LanguageClient), reuse: Reuse.Singleton);

src/JsonRpc/DryIoc/DryIocAdapter.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Dynamic;
34
using DryIoc;
45
using Microsoft.Extensions.DependencyInjection;
56
using OmniSharp.Extensions.JsonRpc;
@@ -150,7 +151,9 @@ public static void RegisterDescriptor(this IContainer container, ServiceDescript
150151
container.RegisterMany(
151152
new [] {descriptor.ImplementationType},
152153
reuse: reuse,
153-
serviceTypeCondition: type => type == descriptor.ImplementationType || type == descriptor.ServiceType || typeof(IEventingHandler).IsAssignableFrom(type) || typeof(IJsonRpcHandler).IsAssignableFrom(type));
154+
serviceTypeCondition: type => type == descriptor.ImplementationType || type == descriptor.ServiceType || typeof(IEventingHandler).IsAssignableFrom(type) || typeof(IJsonRpcHandler).IsAssignableFrom(type),
155+
nonPublicServiceTypes: true
156+
);
154157
}
155158
else if (descriptor.ImplementationFactory != null)
156159
{
@@ -160,15 +163,19 @@ public static void RegisterDescriptor(this IContainer container, ServiceDescript
160163

161164
container.RegisterDelegate(true, descriptor.ServiceType,
162165
descriptor.ImplementationFactory,
163-
reuse);
166+
reuse
167+
);
164168
}
165169
else
166170
{
167171
// ensure eventing handlers are pulled in automagically.
168172
if (!(descriptor.ImplementationInstance is IEnumerable<object>) && (descriptor.ImplementationInstance is IEventingHandler || descriptor.ImplementationInstance is IJsonRpcHandler))
169173
{
170174

171-
container.RegisterInstanceMany(descriptor.ImplementationInstance);
175+
container.RegisterInstanceMany(
176+
descriptor.ImplementationInstance,
177+
nonPublicServiceTypes: true
178+
);
172179
}
173180
else
174181
{

src/JsonRpc/JsonRpcServer.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77

88
namespace OmniSharp.Extensions.JsonRpc
99
{
10-
public class JsonRpcServer : JsonRpcServerBase, IJsonRpcServer
10+
public class JsonRpcServer : JsonRpcServerBase, IJsonRpcServer, IServiceProvider
1111
{
1212
private readonly Connection _connection;
13+
private readonly IServiceProvider _serviceProvider;
1314
private readonly CompositeDisposable _disposable;
1415

1516
internal static IContainer CreateContainer(JsonRpcServerOptions options, IServiceProvider outerServiceProvider) =>
@@ -61,6 +62,7 @@ IServiceProvider serviceProvider
6162
) : base(handlerCollection, responseRouter)
6263
{
6364
_connection = connection;
65+
_serviceProvider = serviceProvider;
6466
_disposable = options.Value.CompositeDisposable;
6567
_disposable.Add(_connection);
6668
if (serviceProvider is IDisposable disposable)
@@ -83,5 +85,7 @@ public IDisposable Register(Action<IJsonRpcServerRegistry> registryAction)
8385
registryAction(new JsonRpcServerRegistry(manager));
8486
return manager.GetDisposable();
8587
}
88+
89+
object IServiceProvider.GetService(Type serviceType) => _serviceProvider.GetService(serviceType);
8690
}
8791
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using OmniSharp.Extensions.JsonRpc;
3+
using OmniSharp.Extensions.LanguageServer.Protocol.Progress;
4+
5+
namespace OmniSharp.Extensions.LanguageServer.Protocol.Client
6+
{
7+
internal class DefaultLanguageClientFacade : LanguageProtocolProxy, ILanguageClientFacade
8+
{
9+
public DefaultLanguageClientFacade(
10+
IResponseRouter requestRouter, IServiceProvider serviceProvider, IProgressManager progressManager,
11+
ILanguageProtocolSettings languageProtocolSettings, ITextDocumentLanguageClient textDocument, IClientLanguageClient client, IGeneralLanguageClient general, IWindowLanguageClient window, IWorkspaceLanguageClient workspace
12+
) : base(requestRouter, serviceProvider, progressManager, languageProtocolSettings)
13+
{
14+
TextDocument = textDocument;
15+
Client = client;
16+
General = general;
17+
Window = window;
18+
Workspace = workspace;
19+
}
20+
21+
public ITextDocumentLanguageClient TextDocument { get; }
22+
public IClientLanguageClient Client { get; }
23+
public IGeneralLanguageClient General { get; }
24+
public IWindowLanguageClient Window { get; }
25+
public IWorkspaceLanguageClient Workspace { get; }
26+
}
27+
}

src/Protocol/Client/ILanguageClient.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,12 @@
77

88
namespace OmniSharp.Extensions.LanguageServer.Protocol.Client
99
{
10-
public interface ILanguageClient : ILanguageClientProxy, IJsonRpcHandlerInstance<ILanguageClientRegistry>, IDisposable
10+
public interface ILanguageClient : ILanguageClientFacade, IJsonRpcHandlerInstance<ILanguageClientRegistry>, IDisposable
1111
{
12-
ITextDocumentLanguageClient TextDocument { get; }
13-
IClientLanguageClient Client { get; }
14-
IGeneralLanguageClient General { get; }
15-
IWindowLanguageClient Window { get; }
16-
IWorkspaceLanguageClient Workspace { get; }
1712
IServiceProvider Services { get; }
1813
IClientWorkDoneManager WorkDoneManager { get; }
1914
IRegistrationManager RegistrationManager { get; }
2015
ILanguageClientWorkspaceFoldersManager WorkspaceFoldersManager { get; }
21-
InitializeParams ClientSettings { get; }
22-
InitializeResult ServerSettings { get; }
2316
Task Initialize(CancellationToken token);
2417
Task Shutdown();
2518
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace OmniSharp.Extensions.LanguageServer.Protocol.Client
2+
{
3+
public interface ILanguageClientFacade : ILanguageClientProxy
4+
{
5+
ITextDocumentLanguageClient TextDocument { get; }
6+
IClientLanguageClient Client { get; }
7+
IGeneralLanguageClient General { get; }
8+
IWindowLanguageClient Window { get; }
9+
IWorkspaceLanguageClient Workspace { get; }
10+
}
11+
}

src/Protocol/Document/Proposals/ISemanticTokensDeltaHandler.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public interface ISemanticTokensHandler : IJsonRpcRequestHandler<SemanticTokensP
2323
[Parallel]
2424
[Method(TextDocumentNames.SemanticTokensFullDelta, Direction.ClientToServer)]
2525
public interface ISemanticTokensDeltaHandler :
26-
IJsonRpcRequestHandler<SemanticTokensDeltaParams, SemanticTokensFullOrDelta>,
26+
IJsonRpcRequestHandler<SemanticTokensDeltaParams, SemanticTokensFullOrDelta?>,
2727
IRegistration<SemanticTokensRegistrationOptions>, ICapability<SemanticTokensCapability>, IDoesNotParticipateInRegistration
2828
{
2929
}
@@ -54,7 +54,7 @@ public virtual async Task<SemanticTokens> Handle(SemanticTokensParams request, C
5454
return builder.Commit().GetSemanticTokens();
5555
}
5656

57-
public virtual async Task<SemanticTokensFullOrDelta> Handle(SemanticTokensDeltaParams request, CancellationToken cancellationToken)
57+
public virtual async Task<SemanticTokensFullOrDelta?> Handle(SemanticTokensDeltaParams request, CancellationToken cancellationToken)
5858
{
5959
var document = await GetSemanticTokensDocument(request, cancellationToken);
6060
var builder = document.Edit(request);
@@ -206,7 +206,7 @@ public static IRequestProgressObservable<SemanticTokensPartialResult, SemanticTo
206206
}, cancellationToken
207207
);
208208

209-
public static IRequestProgressObservable<SemanticTokensFullOrDeltaPartialResult, SemanticTokensFullOrDelta> RequestSemanticTokensDelta(
209+
public static IRequestProgressObservable<SemanticTokensFullOrDeltaPartialResult, SemanticTokensFullOrDelta?> RequestSemanticTokensDelta(
210210
this ITextDocumentLanguageClient mediator, SemanticTokensDeltaParams @params, CancellationToken cancellationToken = default
211211
) =>
212212
mediator.ProgressManager.MonitorUntil(
@@ -216,7 +216,7 @@ public static IRequestProgressObservable<SemanticTokensFullOrDeltaPartialResult,
216216
return new SemanticTokensFullOrDelta(
217217
new SemanticTokensDelta {
218218
Edits = partial.Delta.Edits,
219-
ResultId = result.Delta?.ResultId ?? result.Full?.ResultId
219+
ResultId = result?.Delta?.ResultId ?? result?.Full?.ResultId
220220
}
221221
);
222222
}
@@ -226,7 +226,7 @@ public static IRequestProgressObservable<SemanticTokensFullOrDeltaPartialResult,
226226
return new SemanticTokensFullOrDelta(
227227
new SemanticTokens {
228228
Data = partial.Full.Data,
229-
ResultId = result.Full?.ResultId ?? result.Delta?.ResultId
229+
ResultId = result?.Full?.ResultId ?? result?.Delta?.ResultId
230230
}
231231
);
232232
}

src/Protocol/Models/Proposals/SemanticTokensDeltaParams.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Models.Proposals
99
[Obsolete(Constants.Proposal)]
1010
[Method(TextDocumentNames.SemanticTokensFullDelta, Direction.ClientToServer)]
1111
public class SemanticTokensDeltaParams : IWorkDoneProgressParams, ITextDocumentIdentifierParams,
12-
IPartialItemRequest<SemanticTokensFullOrDelta, SemanticTokensFullOrDeltaPartialResult>
12+
IPartialItemRequest<SemanticTokensFullOrDelta?, SemanticTokensFullOrDeltaPartialResult>
1313
{
1414
/// <summary>
1515
/// The text document.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using OmniSharp.Extensions.JsonRpc;
3+
using OmniSharp.Extensions.LanguageServer.Protocol.Progress;
4+
5+
namespace OmniSharp.Extensions.LanguageServer.Protocol.Server
6+
{
7+
internal class DefaultLanguageServerFacade : LanguageProtocolProxy, ILanguageServerFacade
8+
{
9+
public DefaultLanguageServerFacade(
10+
IResponseRouter requestRouter, IServiceProvider serviceProvider, IProgressManager progressManager,
11+
ILanguageProtocolSettings languageProtocolSettings, ITextDocumentLanguageServer textDocument, IClientLanguageServer client, IGeneralLanguageServer general, IWindowLanguageServer window, IWorkspaceLanguageServer workspace
12+
) : base(requestRouter, serviceProvider, progressManager, languageProtocolSettings)
13+
{
14+
TextDocument = textDocument;
15+
Client = client;
16+
General = general;
17+
Window = window;
18+
Workspace = workspace;
19+
}
20+
21+
public ITextDocumentLanguageServer TextDocument { get; }
22+
public IClientLanguageServer Client { get; }
23+
public IGeneralLanguageServer General { get; }
24+
public IWindowLanguageServer Window { get; }
25+
public IWorkspaceLanguageServer Workspace { get; }
26+
}
27+
}

src/Protocol/Server/ILanguageServer.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,8 @@
77

88
namespace OmniSharp.Extensions.LanguageServer.Protocol.Server
99
{
10-
public interface ILanguageServer : ILanguageServerProxy, IJsonRpcHandlerInstance<ILanguageServerRegistry>, IDisposable
10+
public interface ILanguageServer : ILanguageServerFacade, IJsonRpcHandlerInstance<ILanguageServerRegistry>, IDisposable
1111
{
12-
ITextDocumentLanguageServer TextDocument { get; }
13-
IClientLanguageServer Client { get; }
14-
IGeneralLanguageServer General { get; }
15-
IWindowLanguageServer Window { get; }
16-
IWorkspaceLanguageServer Workspace { get; }
1712
IServiceProvider Services { get; }
1813
IServerWorkDoneManager WorkDoneManager { get; }
1914
ILanguageServerConfiguration Configuration { get; }
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace OmniSharp.Extensions.LanguageServer.Protocol.Server
2+
{
3+
public interface ILanguageServerFacade : ILanguageServerProxy
4+
{
5+
ITextDocumentLanguageServer TextDocument { get; }
6+
IClientLanguageServer Client { get; }
7+
IGeneralLanguageServer General { get; }
8+
IWindowLanguageServer Window { get; }
9+
IWorkspaceLanguageServer Workspace { get; }
10+
}
11+
}

src/Server/LanguageServerServiceCollectionExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ internal static IContainer AddLanguageServerInternals(this IContainer container,
4343
container.RegisterMany<GeneralLanguageServer>(serviceTypeCondition: type => type.Name.Contains(nameof(GeneralLanguageServer)), reuse: Reuse.Singleton);
4444
container.RegisterMany<WindowLanguageServer>(serviceTypeCondition: type => type.Name.Contains(nameof(WindowLanguageServer)), reuse: Reuse.Singleton);
4545
container.RegisterMany<WorkspaceLanguageServer>(serviceTypeCondition: type => type.Name.Contains(nameof(WorkspaceLanguageServer)), reuse: Reuse.Singleton);
46+
container.RegisterMany<DefaultLanguageServerFacade>(serviceTypeCondition: type => type.Name.Contains("LanguageServerFacade"), reuse: Reuse.Singleton);
4647
container.RegisterInstance<IOptionsFactory<LanguageServerOptions>>(new ValueOptionsFactory<LanguageServerOptions>(options));
4748

4849
container.RegisterMany<LanguageServer>(serviceTypeCondition: type => type == typeof(ILanguageServer) || type == typeof(LanguageServer), reuse: Reuse.Singleton);

test/JsonRpc.Tests/ServiceCollectionSupportTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Threading.Tasks;
55
using FluentAssertions;
66
using MediatR;
7+
using Microsoft.CodeAnalysis.CSharp.Syntax;
78
using Microsoft.Extensions.DependencyInjection;
89
using Microsoft.Extensions.Logging;
910
using NSubstitute;
@@ -255,6 +256,24 @@ public void Should_Throw_When_Multiple_Servers_Are_Added_And_Attempt_To_Resolve_
255256
a.Should().Throw<NotSupportedException>();
256257
}
257258

259+
[Fact]
260+
public async Task Should_Register_Private_Classes_With_Dry_Ioc()
261+
{
262+
var server = await JsonRpcServer.From(
263+
options => {
264+
265+
var pipe = new Pipe();
266+
options
267+
.WithInput(pipe.Reader)
268+
.WithOutput(pipe.Writer);
269+
270+
options.Services.AddSingleton<TestClass, InternalTestClass>();
271+
}
272+
);
273+
server.GetService<InternalTestClass>().Should().NotBeNull();
274+
server.GetService<TestClass>().Should().NotBeNull().And.BeOfType<InternalTestClass>();
275+
}
276+
258277
[Method("outside")]
259278
private class Request : IRequest<Response>
260279
{
@@ -297,5 +316,14 @@ private class OutsideService
297316

298317
public string Value { get; }
299318
}
319+
320+
internal class TestClass
321+
{
322+
323+
}
324+
325+
internal class InternalTestClass : TestClass
326+
{
327+
}
300328
}
301329
}

test/Lsp.Tests/Integration/DynamicRegistrationTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ public async Task Should_Unregister_Dynamically_While_Server_Is_Running()
118118
await WaitForRegistrationUpdate();
119119
disposable.Dispose();
120120
await WaitForRegistrationUpdate();
121+
await Task.Delay(1000);
121122
}
122123

123124
Client.RegistrationManager.CurrentRegistrations.Should().NotContain(

test/Lsp.Tests/Integration/InitializationTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
using System.Collections.Generic;
2+
using System.Threading;
23
using System.Threading.Tasks;
34
using FluentAssertions;
5+
using Microsoft.Extensions.DependencyInjection;
46
using NSubstitute;
57
using OmniSharp.Extensions.JsonRpc.Testing;
68
using OmniSharp.Extensions.LanguageProtocol.Testing;
79
using OmniSharp.Extensions.LanguageServer.Client;
10+
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
11+
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
12+
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
13+
using OmniSharp.Extensions.LanguageServer.Protocol.Server;
814
using OmniSharp.Extensions.LanguageServer.Protocol.Window;
915
using OmniSharp.Extensions.LanguageServer.Server;
1016
using Xunit;
@@ -27,6 +33,28 @@ public async Task Logs_should_be_allowed_during_startup()
2733
_logs.Should().ContainInOrder("OnInitialize", "OnInitialized");
2834
}
2935

36+
[Fact]
37+
public async Task Facades_should_be_resolvable()
38+
{
39+
var (client, server) = await Initialize(ConfigureClient, ConfigureServer);
40+
41+
client.Services.GetService<ILanguageClientFacade>().Should().NotBeNull();
42+
server.Services.GetService<ILanguageServerFacade>().Should().NotBeNull();
43+
// This ensures that the facade made it.
44+
var response = await client.RequestCodeLens(new CodeLensParams(), CancellationToken);
45+
}
46+
47+
class CodeLensHandlerA : CodeLensHandler
48+
{
49+
public CodeLensHandlerA(ILanguageServerFacade languageServerFacade) : base(new CodeLensRegistrationOptions())
50+
{
51+
languageServerFacade.Should().NotBeNull();
52+
}
53+
54+
public override Task<CodeLensContainer> Handle(CodeLensParams request, CancellationToken cancellationToken) => Task.FromResult(new CodeLensContainer());
55+
56+
public override Task<CodeLens> Handle(CodeLens request, CancellationToken cancellationToken) => Task.FromResult(request);
57+
}
3058
private readonly List<string> _logs = new List<string>();
3159

3260
private void ConfigureClient(LanguageClientOptions options) =>
@@ -40,6 +68,7 @@ private void ConfigureServer(LanguageServerOptions options)
4068
return Task.CompletedTask;
4169
}
4270
);
71+
options.AddHandler<CodeLensHandlerA>();
4372
options.OnInitialized(
4473
(server, request, response, token) => {
4574
server.Window.LogInfo("OnInitialized");

0 commit comments

Comments
 (0)