Skip to content

Commit d24a9c2

Browse files
Feature/hosting (#278)
* Initial work on splitting out service creation from jsonrpcserver * updates for the clients * Added test to support service collection usage * Added resolver and tests around it * Updated shared handler collection for lsp * Fixed all LSP tests * fixed lsp/rpc handler selection * Updated and fixed remaining unit tests and unit test issues * Ensure mediatr is using correct scope * Added workspace folder tests * Make it so that identified handlers are not required
1 parent 58011ad commit d24a9c2

File tree

256 files changed

+5272
-2231
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

256 files changed

+5272
-2231
lines changed

Directory.Build.targets

+1
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,6 @@
4848
<PackageReference Update="CodeGeneration.Roslyn.Tool" Version="0.7.63" PrivateAssets="all" />
4949
<PackageReference Update="Microsoft.CodeAnalysis.CSharp" Version="3.4.0" />
5050
<PackageReference Update="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.4.0" />
51+
<PackageReference Update="DryIoc.Internal" Version="4.3.0" />
5152
</ItemGroup>
5253
</Project>

LSP.sln

+15
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Generation.Tests", "test\Ge
7474
EndProject
7575
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dap.Shared", "src\Dap.Shared\Dap.Shared.csproj", "{010D4BE7-6A92-4A04-B4EB-745FA3130DF2}"
7676
EndProject
77+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestingUtils", "test\TestingUtils\TestingUtils.csproj", "{58E83291-1ED9-4921-A12F-F2450AB17F47}"
78+
EndProject
7779
Global
7880
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7981
Debug|Any CPU = Debug|Any CPU
@@ -330,6 +332,18 @@ Global
330332
{010D4BE7-6A92-4A04-B4EB-745FA3130DF2}.Release|x64.Build.0 = Release|Any CPU
331333
{010D4BE7-6A92-4A04-B4EB-745FA3130DF2}.Release|x86.ActiveCfg = Release|Any CPU
332334
{010D4BE7-6A92-4A04-B4EB-745FA3130DF2}.Release|x86.Build.0 = Release|Any CPU
335+
{58E83291-1ED9-4921-A12F-F2450AB17F47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
336+
{58E83291-1ED9-4921-A12F-F2450AB17F47}.Debug|Any CPU.Build.0 = Debug|Any CPU
337+
{58E83291-1ED9-4921-A12F-F2450AB17F47}.Debug|x64.ActiveCfg = Debug|Any CPU
338+
{58E83291-1ED9-4921-A12F-F2450AB17F47}.Debug|x64.Build.0 = Debug|Any CPU
339+
{58E83291-1ED9-4921-A12F-F2450AB17F47}.Debug|x86.ActiveCfg = Debug|Any CPU
340+
{58E83291-1ED9-4921-A12F-F2450AB17F47}.Debug|x86.Build.0 = Debug|Any CPU
341+
{58E83291-1ED9-4921-A12F-F2450AB17F47}.Release|Any CPU.ActiveCfg = Release|Any CPU
342+
{58E83291-1ED9-4921-A12F-F2450AB17F47}.Release|Any CPU.Build.0 = Release|Any CPU
343+
{58E83291-1ED9-4921-A12F-F2450AB17F47}.Release|x64.ActiveCfg = Release|Any CPU
344+
{58E83291-1ED9-4921-A12F-F2450AB17F47}.Release|x64.Build.0 = Release|Any CPU
345+
{58E83291-1ED9-4921-A12F-F2450AB17F47}.Release|x86.ActiveCfg = Release|Any CPU
346+
{58E83291-1ED9-4921-A12F-F2450AB17F47}.Release|x86.Build.0 = Release|Any CPU
333347
EndGlobalSection
334348
GlobalSection(SolutionProperties) = preSolution
335349
HideSolutionNode = FALSE
@@ -356,6 +370,7 @@ Global
356370
{DE259174-73DC-4532-B641-AD218971EE29} = {D764E024-3D3F-4112-B932-2DB722A1BACC}
357371
{671FFF78-BDD2-4389-B29C-BFD183DA9120} = {2F323ED5-EBF8-45E1-B9D3-C014561B3DDA}
358372
{010D4BE7-6A92-4A04-B4EB-745FA3130DF2} = {D764E024-3D3F-4112-B932-2DB722A1BACC}
373+
{58E83291-1ED9-4921-A12F-F2450AB17F47} = {2F323ED5-EBF8-45E1-B9D3-C014561B3DDA}
359374
EndGlobalSection
360375
GlobalSection(ExtensibilityGlobals) = postSolution
361376
SolutionGuid = {D38DD0EC-D095-4BCD-B8AF-2D788AF3B9AE}

src/Client/ILspClientReceiver.cs

-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,5 @@ namespace OmniSharp.Extensions.LanguageServer.Client
55
public interface ILspClientReceiver : IReceiver
66
{
77
void Initialized();
8-
bool ShouldFilterOutput(object value);
98
}
109
}

src/Client/LangaugeClientRegistry.cs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using OmniSharp.Extensions.JsonRpc;
3+
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
4+
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
5+
using OmniSharp.Extensions.LanguageServer.Shared;
6+
7+
namespace OmniSharp.Extensions.LanguageServer.Client
8+
{
9+
class LangaugeClientRegistry : InterimLanguageProtocolRegistry<ILanguageClientRegistry>, ILanguageClientRegistry
10+
{
11+
public LangaugeClientRegistry(IServiceProvider serviceProvider, CompositeHandlersManager handlersManager, TextDocumentIdentifiers textDocumentIdentifiers) : base(
12+
serviceProvider, handlersManager, textDocumentIdentifiers)
13+
{
14+
}
15+
}
16+
}

src/Client/LanguageClient.cs

+142-153
Large diffs are not rendered by default.

src/Client/LanguageClientOptions.cs

+9-13
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ public class LanguageClientOptions : LanguageProtocolRpcOptionsBase<LanguageClie
3636
public bool WorkspaceFolders { get; set; } = true;
3737
public bool DynamicRegistration { get; set; } = true;
3838
public bool ProgressTokens { get; set; } = true;
39-
internal List<WorkspaceFolder> Folders { get; set; } = new List<WorkspaceFolder>();
4039

4140
public string RootPath
4241
{
@@ -48,28 +47,25 @@ public string RootPath
4847

4948
public object InitializationOptions { get; set; }
5049

51-
public ISerializer Serializer { get; set; } = new Protocol.Serialization.Serializer(ClientVersion.Lsp3);
5250
public ILspClientReceiver Receiver { get; set; } = new LspClientReceiver();
53-
internal List<ICapability> SupportedCapabilities { get; set; } = new List<ICapability>();
5451

55-
internal readonly List<OnClientStartedDelegate> StartedDelegates = new List<OnClientStartedDelegate>();
56-
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler(string method, IJsonRpcHandler handler, JsonRpcHandlerOptions options) => this.AddHandler(method, handler, options);
52+
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler(string method, IJsonRpcHandler handler, JsonRpcHandlerOptions options) => AddHandler(method, handler, options);
5753

58-
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler(string method, Func<IServiceProvider, IJsonRpcHandler> handlerFunc, JsonRpcHandlerOptions options) => this.AddHandler(method, handlerFunc, options);
54+
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler(string method, JsonRpcHandlerFactory handlerFunc, JsonRpcHandlerOptions options) => AddHandler(method, handlerFunc, options);
5955

60-
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandlers(params IJsonRpcHandler[] handlers) => this.AddHandlers(handlers);
56+
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandlers(params IJsonRpcHandler[] handlers) => AddHandlers(handlers);
6157

62-
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler<THandler>(Func<IServiceProvider, THandler> handlerFunc, JsonRpcHandlerOptions options) => this.AddHandler(handlerFunc, options);
58+
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler(JsonRpcHandlerFactory handlerFunc, JsonRpcHandlerOptions options) => AddHandler(handlerFunc, options);
6359

64-
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler<THandler>(THandler handler, JsonRpcHandlerOptions options) => this.AddHandler(handler, options);
60+
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler(IJsonRpcHandler handler, JsonRpcHandlerOptions options) => AddHandler(handler, options);
6561

66-
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler<TTHandler>(JsonRpcHandlerOptions options) => this.AddHandler<TTHandler>(options);
62+
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler<TTHandler>(JsonRpcHandlerOptions options) => AddHandler<TTHandler>(options);
6763

68-
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler<TTHandler>(string method, JsonRpcHandlerOptions options) => this.AddHandler<TTHandler>(method, options);
64+
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler<TTHandler>(string method, JsonRpcHandlerOptions options) => AddHandler<TTHandler>(method, options);
6965

70-
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler(Type type, JsonRpcHandlerOptions options) => this.AddHandler(type, options);
66+
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler(Type type, JsonRpcHandlerOptions options) => AddHandler(type, options);
7167

72-
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler(string method, Type type, JsonRpcHandlerOptions options) => this.AddHandler(method, type, options);
68+
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.AddHandler(string method, Type type, JsonRpcHandlerOptions options) => AddHandler(method, type, options);
7369

7470
ILanguageClientRegistry IJsonRpcHandlerRegistry<ILanguageClientRegistry>.OnJsonRequest(string method, Func<JToken, Task<JToken>> handler, JsonRpcHandlerOptions options) => OnJsonRequest(method, handler, options);
7571

src/Client/LanguageClientOptionsExtensions.cs

+21-11
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using OmniSharp.Extensions.JsonRpc;
1010
using OmniSharp.Extensions.JsonRpc.Server;
1111
using OmniSharp.Extensions.LanguageServer.Protocol;
12+
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
1213
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
1314
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
1415
using OmniSharp.Extensions.LanguageServer.Protocol.Shared;
@@ -31,13 +32,6 @@ public static LanguageClientOptions WithReceiver(this LanguageClientOptions opti
3132
return options;
3233
}
3334

34-
public static LanguageClientOptions WithServices(this LanguageClientOptions options,
35-
Action<IServiceCollection> servicesAction)
36-
{
37-
servicesAction(options.Services);
38-
return options;
39-
}
40-
4135
public static LanguageClientOptions WithClientInfo(this LanguageClientOptions options, ClientInfo clientInfo)
4236
{
4337
options.ClientInfo = clientInfo;
@@ -56,6 +50,18 @@ public static LanguageClientOptions WithRootPath(this LanguageClientOptions opti
5650
return options;
5751
}
5852

53+
public static LanguageClientOptions WithWorkspaceFolder(this LanguageClientOptions options, WorkspaceFolder workspaceFolder)
54+
{
55+
options.Services.AddSingleton(workspaceFolder);
56+
return options;
57+
}
58+
59+
public static LanguageClientOptions WithWorkspaceFolder(this LanguageClientOptions options, DocumentUri documentUri, string name)
60+
{
61+
options.Services.AddSingleton(new WorkspaceFolder() { Name = name, Uri = documentUri});
62+
return options;
63+
}
64+
5965
public static LanguageClientOptions WithTrace(this LanguageClientOptions options, InitializeTrace trace)
6066
{
6167
options.Trace = trace;
@@ -69,9 +75,13 @@ public static LanguageClientOptions WithInitializationOptions(this LanguageClien
6975
return options;
7076
}
7177

72-
public static LanguageClientOptions WithCapability(this LanguageClientOptions options, params ICapability[] capabilities)
78+
public static LanguageClientOptions WithCapability(this LanguageClientOptions options, ICapability capability, params ICapability[] capabilities)
7379
{
74-
options.SupportedCapabilities.AddRange(capabilities);
80+
options.Services.AddSingleton(capability);
81+
foreach (var item in capabilities)
82+
{
83+
options.Services.AddSingleton(item);
84+
}
7585
return options;
7686
}
7787

@@ -82,9 +92,9 @@ public static LanguageClientOptions WithClientCapabilities(this LanguageClientOp
8292
}
8393

8494
public static LanguageClientOptions OnStarted(this LanguageClientOptions options,
85-
OnClientStartedDelegate @delegate)
95+
OnLanguageClientStartedDelegate @delegate)
8696
{
87-
options.StartedDelegates.Add(@delegate);
97+
options.Services.AddSingleton(@delegate);
8898
return options;
8999
}
90100

src/Client/RegistrationManager.cs renamed to src/Client/LanguageClientRegistrationManager.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@
1919

2020
namespace OmniSharp.Extensions.LanguageServer.Client
2121
{
22-
class RegistrationManager : IRegisterCapabilityHandler, IUnregisterCapabilityHandler, IRegistrationManager, IDisposable
22+
class LanguageClientRegistrationManager : IRegisterCapabilityHandler, IUnregisterCapabilityHandler, IRegistrationManager, IDisposable
2323
{
2424
private readonly ISerializer _serializer;
25-
private readonly ILogger<RegistrationManager> _logger;
25+
private readonly ILogger<LanguageClientRegistrationManager> _logger;
2626
private readonly ConcurrentDictionary<string, Registration> _registrations;
2727
private readonly ReplaySubject<IEnumerable<Registration>> _registrationSubject;
2828

29-
public RegistrationManager(ISerializer serializer, ILogger<RegistrationManager> logger)
29+
public LanguageClientRegistrationManager(ISerializer serializer, ILogger<LanguageClientRegistrationManager> logger)
3030
{
3131
_serializer = serializer;
3232
_logger = logger;

src/Client/LanguageClientResolver.cs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Collections.Concurrent;
3+
using DryIoc;
4+
using Microsoft.Extensions.Options;
5+
6+
namespace OmniSharp.Extensions.LanguageServer.Client
7+
{
8+
public class LanguageClientResolver : IDisposable
9+
{
10+
private readonly IOptionsMonitor<LanguageClientOptions> _monitor;
11+
private readonly IServiceProvider _outerServiceProvider;
12+
private readonly ConcurrentDictionary<string, LanguageClient> _clients = new ConcurrentDictionary<string, LanguageClient>();
13+
14+
public LanguageClientResolver(IOptionsMonitor<LanguageClientOptions> monitor, IServiceProvider outerServiceProvider)
15+
{
16+
_monitor = monitor;
17+
_outerServiceProvider = outerServiceProvider;
18+
}
19+
20+
public LanguageClient Get(string name)
21+
{
22+
if (_clients.TryGetValue(name, out var client)) return client;
23+
24+
var options = name == Options.DefaultName ? _monitor.CurrentValue : _monitor.Get(name);
25+
26+
var container = LanguageClient.CreateContainer(options, _outerServiceProvider);
27+
client = container.Resolve<LanguageClient>();
28+
_clients.TryAdd(name, client);
29+
30+
return client;
31+
}
32+
33+
public void Dispose()
34+
{
35+
foreach (var item in _clients.Values) item.Dispose();
36+
}
37+
}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using System;
2+
using System.Linq;
3+
using System.Reflection;
4+
using DryIoc;
5+
using Microsoft.Extensions.Configuration;
6+
using Microsoft.Extensions.DependencyInjection;
7+
using Microsoft.Extensions.DependencyInjection.Extensions;
8+
using Microsoft.Extensions.Options;
9+
using OmniSharp.Extensions.JsonRpc;
10+
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
11+
using OmniSharp.Extensions.LanguageServer.Protocol.Client.WorkDone;
12+
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
13+
using OmniSharp.Extensions.LanguageServer.Shared;
14+
15+
namespace OmniSharp.Extensions.LanguageServer.Client
16+
{
17+
public static class LanguageClientServiceCollectionExtensions
18+
{
19+
internal static IContainer AddLanguageClientInternals(this IContainer container, LanguageClientOptions options, IServiceProvider outerServiceProvider)
20+
{
21+
if (options.Receiver == null)
22+
{
23+
throw new ArgumentException("Receiver is missing!", nameof(options));
24+
}
25+
26+
container = container.AddLanguageProtocolInternals(options);
27+
28+
container.RegisterInstance(options.ClientCapabilities);
29+
container.RegisterInstanceMany(options.Receiver);
30+
if (options.OnUnhandledException != null)
31+
{
32+
container.RegisterInstance(options.OnUnhandledException);
33+
}
34+
else
35+
{
36+
container.RegisterDelegate(_ => new OnUnhandledExceptionHandler(e => _.GetRequiredService<LanguageClient>().Shutdown()), reuse: Reuse.Singleton);
37+
}
38+
39+
container.RegisterMany<TextDocumentLanguageClient>(serviceTypeCondition: type => type.Name.Contains(nameof(TextDocumentLanguageClient)), reuse: Reuse.Singleton);
40+
container.RegisterMany<ClientLanguageClient>(serviceTypeCondition: type => type.Name.Contains(nameof(ClientLanguageClient)), reuse: Reuse.Singleton);
41+
container.RegisterMany<GeneralLanguageClient>(serviceTypeCondition: type => type.Name.Contains(nameof(GeneralLanguageClient)), reuse: Reuse.Singleton);
42+
container.RegisterMany<WindowLanguageClient>(serviceTypeCondition: type => type.Name.Contains(nameof(WindowLanguageClient)), reuse: Reuse.Singleton);
43+
container.RegisterMany<WorkspaceLanguageClient>(serviceTypeCondition: type => type.Name.Contains(nameof(WorkspaceLanguageClient)), reuse: Reuse.Singleton);
44+
container.RegisterInstance<IOptionsFactory<LanguageClientOptions>>(new ValueOptionsFactory<LanguageClientOptions>(options));
45+
46+
container.RegisterMany<LanguageClient>(serviceTypeCondition: type => type == typeof(ILanguageClient) || type == typeof(LanguageClient), reuse: Reuse.Singleton);
47+
48+
container.RegisterInstance(options.ClientInfo ?? new ClientInfo() {
49+
Name = Assembly.GetEntryAssembly()?.GetName().Name,
50+
Version = Assembly.GetEntryAssembly()?.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
51+
?.InformationalVersion ??
52+
Assembly.GetEntryAssembly()?.GetCustomAttribute<AssemblyVersionAttribute>()?.Version
53+
});
54+
55+
var providedConfiguration = options.Services.FirstOrDefault(z => z.ServiceType == typeof(IConfiguration) && z.ImplementationInstance is IConfiguration);
56+
container.RegisterDelegate<IConfiguration>(_ => {
57+
var builder = new ConfigurationBuilder();
58+
var outerConfiguration = outerServiceProvider?.GetService<IConfiguration>();
59+
if (outerConfiguration != null)
60+
{
61+
builder.AddConfiguration(outerConfiguration, false);
62+
}
63+
64+
if (providedConfiguration != null)
65+
{
66+
builder.AddConfiguration(providedConfiguration.ImplementationInstance as IConfiguration);
67+
}
68+
69+
//var didChangeConfigurationProvider = _.GetRequiredService<DidChangeConfigurationProvider>();
70+
return builder
71+
//.AddConfiguration(didChangeConfigurationProvider)
72+
.Build();
73+
},
74+
reuse: Reuse.Singleton);
75+
76+
container.RegisterMany<LanguageClientWorkDoneManager>(reuse: Reuse.Singleton);
77+
container.RegisterMany<LanguageClientWorkspaceFoldersManager>(serviceTypeCondition: type => options.WorkspaceFolders || type != typeof(IJsonRpcHandler), reuse: Reuse.Singleton);
78+
container.RegisterMany<LanguageClientRegistrationManager>(serviceTypeCondition: type => options.DynamicRegistration || type != typeof(IJsonRpcHandler), reuse: Reuse.Singleton);
79+
80+
return container;
81+
}
82+
83+
public static IServiceCollection AddLanguageClient(this IServiceCollection services, Action<LanguageClientOptions> configureOptions = null)
84+
{
85+
return AddLanguageClient(services, Options.DefaultName, configureOptions);
86+
}
87+
88+
public static IServiceCollection AddLanguageClient(this IServiceCollection services, string name, Action<LanguageClientOptions> configureOptions = null)
89+
{
90+
// If we get called multiple times we're going to remove the default server
91+
// and force consumers to use the resolver.
92+
if (services.Any(d => d.ServiceType == typeof(LanguageClient) || d.ServiceType == typeof(ILanguageClient)))
93+
{
94+
services.RemoveAll<LanguageClient>();
95+
services.RemoveAll<ILanguageClient>();
96+
services.AddSingleton<ILanguageClient>(_ =>
97+
throw new NotSupportedException("LanguageClient has been registered multiple times, you must use LanguageClient instead"));
98+
services.AddSingleton<LanguageClient>(_ =>
99+
throw new NotSupportedException("LanguageClient has been registered multiple times, you must use LanguageClient instead"));
100+
}
101+
102+
services
103+
.AddOptions()
104+
.AddLogging();
105+
services.TryAddSingleton<LanguageClientResolver>();
106+
services.TryAddSingleton(_ => _.GetRequiredService<LanguageClientResolver>().Get(name));
107+
services.TryAddSingleton<ILanguageClient>(_ => _.GetRequiredService<LanguageClientResolver>().Get(name));
108+
109+
if (configureOptions != null)
110+
{
111+
services.Configure(name, configureOptions);
112+
}
113+
114+
return services;
115+
}
116+
}
117+
}

0 commit comments

Comments
 (0)