From d66e816c5c979b7ce71c3ca8116fd7db74298831 Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Mon, 12 Oct 2020 20:33:42 -0400 Subject: [PATCH 01/11] Fixed a bug in configuration (configuration values were case-sensitive). Added ability to disable default handlers for language client / language server. Added unit tests demonstrating Configuration.Binder, and Options usages --- Directory.Build.targets | 5 +- src/Client/LanguageClientOptions.cs | 6 +- src/Client/LanguageClientOptionsExtensions.cs | 12 ++ ...nguageClientServiceCollectionExtensions.cs | 22 ++- ...pterProtocolServiceCollectionExtensions.cs | 2 +- .../JsonRpcHandlerCollectionExtensions.cs | 4 +- ...sonRpcServerServiceCollectionExtensions.cs | 2 +- .../ConfigureByConfigurationPathExtension.cs | 176 ++++++++++++++++++ src/Protocol/Protocol.csproj | 2 + .../DidChangeConfigurationProvider.cs | 2 +- src/Server/LanguageServerOptions.cs | 6 +- src/Server/LanguageServerOptionsExtensions.cs | 12 ++ ...nguageServerServiceCollectionExtensions.cs | 24 ++- ...uageProtocolServiceCollectionExtensions.cs | 4 +- .../Integration/DisableDefaultsTests.cs | 76 ++++++++ .../LanguageServerConfigurationTests.cs | 91 ++++++++- test/Lsp.Tests/Lsp.Tests.csproj | 9 + 17 files changed, 442 insertions(+), 13 deletions(-) create mode 100644 src/Protocol/ConfigureByConfigurationPathExtension.cs create mode 100644 test/Lsp.Tests/Integration/DisableDefaultsTests.cs diff --git a/Directory.Build.targets b/Directory.Build.targets index 4575754d0..a120fa374 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -17,6 +17,9 @@ + + + @@ -45,4 +48,4 @@ - \ No newline at end of file + diff --git a/src/Client/LanguageClientOptions.cs b/src/Client/LanguageClientOptions.cs index ba77406c8..2911b4777 100644 --- a/src/Client/LanguageClientOptions.cs +++ b/src/Client/LanguageClientOptions.cs @@ -18,6 +18,7 @@ public LanguageClientOptions() { WithAssemblies(typeof(LanguageClientOptions).Assembly, typeof(LspRequestRouter).Assembly); } + public ClientCapabilities ClientCapabilities { get; set; } = new ClientCapabilities { Experimental = new Dictionary(), Window = new WindowClientCapabilities(), @@ -40,6 +41,8 @@ public string RootPath public InitializeTrace Trace { get; set; } public object? InitializationOptions { get; set; } + public bool DefaultRegistrationManager { get; set; } = true; + public bool DefaultWorkspaceFolderManager { get; set; } = true; ILanguageClientRegistry IJsonRpcHandlerRegistry.AddHandler(string method, IJsonRpcHandler handler, JsonRpcHandlerOptions? options) => AddHandler(method, handler, options); @@ -52,7 +55,8 @@ ILanguageClientRegistry IJsonRpcHandlerRegistry.AddHand ILanguageClientRegistry IJsonRpcHandlerRegistry.AddHandler(JsonRpcHandlerFactory handlerFunc, JsonRpcHandlerOptions? options) => AddHandler(handlerFunc, options); - ILanguageClientRegistry IJsonRpcHandlerRegistry.AddHandler(IJsonRpcHandler handler, JsonRpcHandlerOptions? options) => AddHandler(handler, options); + ILanguageClientRegistry IJsonRpcHandlerRegistry.AddHandler(IJsonRpcHandler handler, JsonRpcHandlerOptions? options) => + AddHandler(handler, options); ILanguageClientRegistry IJsonRpcHandlerRegistry.AddHandler(JsonRpcHandlerOptions? options) => AddHandler(options); diff --git a/src/Client/LanguageClientOptionsExtensions.cs b/src/Client/LanguageClientOptionsExtensions.cs index 80d7e53bf..75159bec0 100644 --- a/src/Client/LanguageClientOptionsExtensions.cs +++ b/src/Client/LanguageClientOptionsExtensions.cs @@ -108,6 +108,18 @@ public static LanguageClientOptions OnStarted(this LanguageClientOptions options return options; } + public static LanguageClientOptions UseDefaultRegistrationManager(this LanguageClientOptions options, bool value = true) + { + options.DefaultRegistrationManager = value; + return options; + } + + public static LanguageClientOptions UseDefaultWorkspaceFolderManager(this LanguageClientOptions options, bool value = true) + { + options.DefaultWorkspaceFolderManager = value; + return options; + } + public static LanguageClientOptions ConfigureLogging( this LanguageClientOptions options, Action builderAction diff --git a/src/Client/LanguageClientServiceCollectionExtensions.cs b/src/Client/LanguageClientServiceCollectionExtensions.cs index 7fec5fc1d..21385e053 100644 --- a/src/Client/LanguageClientServiceCollectionExtensions.cs +++ b/src/Client/LanguageClientServiceCollectionExtensions.cs @@ -20,7 +20,27 @@ public static class LanguageClientServiceCollectionExtensions { internal static IContainer AddLanguageClientInternals(this IContainer container, LanguageClientOptions options, IServiceProvider? outerServiceProvider) { - container = container.AddLanguageProtocolInternals(options); + bool Filter(JsonRpcHandlerDescription description) + { + // Disable just the handlers, so that anything that uses the existing classes doesn't break + // They are exposed as properties currently replacing them makes it harder + // TODO: Add a decorator that logs warnings when a disabled interface is used. + { + if (!options.DefaultRegistrationManager && + description is JsonRpcHandlerInstanceDescription instance && + instance.HandlerInstance is LanguageClientRegistrationManager) return false; + } + + { + if (!options.DefaultWorkspaceFolderManager && + description is JsonRpcHandlerInstanceDescription instance && + instance.HandlerInstance is LanguageClientWorkspaceFoldersManager) return false; + } + + return true; + } + + container = container.AddLanguageProtocolInternals(options, Filter); container.RegisterInstance(options.ClientCapabilities); container.RegisterMany( diff --git a/src/Dap.Shared/DebugAdapterProtocolServiceCollectionExtensions.cs b/src/Dap.Shared/DebugAdapterProtocolServiceCollectionExtensions.cs index fb7bc4136..797c7d314 100644 --- a/src/Dap.Shared/DebugAdapterProtocolServiceCollectionExtensions.cs +++ b/src/Dap.Shared/DebugAdapterProtocolServiceCollectionExtensions.cs @@ -26,7 +26,7 @@ internal static IContainer AddDebugAdapterProtocolInternals(this IContainer c container.RegisterInitializer( (manager, context) => { var descriptions = context.Resolve(); - descriptions.Populate(context, manager); + descriptions.Populate(context, manager, _ => true); } ); container.RegisterMany(Reuse.Singleton); diff --git a/src/JsonRpc/JsonRpcHandlerCollectionExtensions.cs b/src/JsonRpc/JsonRpcHandlerCollectionExtensions.cs index 65695712e..e89c95290 100644 --- a/src/JsonRpc/JsonRpcHandlerCollectionExtensions.cs +++ b/src/JsonRpc/JsonRpcHandlerCollectionExtensions.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using DryIoc; @@ -5,11 +6,12 @@ namespace OmniSharp.Extensions.JsonRpc { internal static class JsonRpcHandlerCollectionExtensions { - public static void Populate(this IJsonRpcHandlerCollection collection, IResolverContext resolverContext, IHandlersManager handlersManager) + public static void Populate(this IJsonRpcHandlerCollection collection, IResolverContext resolverContext, IHandlersManager handlersManager, Func filter) { var links = new List(); foreach (var item in collection) { + if (!filter(item)) continue; switch (item) { case JsonRpcHandlerFactoryDescription factory when !(factory.Method is null): diff --git a/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs b/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs index 234193771..5ba0123b9 100644 --- a/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs +++ b/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs @@ -132,7 +132,7 @@ internal static IContainer AddJsonRpcServerInternals(this IContainer container, container.RegisterInitializer( (manager, context) => { var descriptions = context.Resolve(); - descriptions.Populate(context, manager); + descriptions.Populate(context, manager, _ => true); } ); diff --git a/src/Protocol/ConfigureByConfigurationPathExtension.cs b/src/Protocol/ConfigureByConfigurationPathExtension.cs new file mode 100644 index 000000000..fef7723ec --- /dev/null +++ b/src/Protocol/ConfigureByConfigurationPathExtension.cs @@ -0,0 +1,176 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration.Binder; +using Microsoft.Extensions.Primitives; + +// ReSharper disable once CheckNamespace +namespace Microsoft.Extensions.Configuration +{ + public static class ConfigureByConfigurationPathExtension + { + /// + /// Registers a injected configuration service which TOptions will bind against. + /// + /// The type of options being configured. + /// The to add the services to. + /// The so that additional calls can be chained. + public static IServiceCollection Configure(this IServiceCollection services) + where TOptions : class + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + return Configure(services, null); + } + + /// + /// Registers a injected configuration service which TOptions will bind against. + /// + /// The type of options being configured. + /// The to add the services to. + /// The name of the options instance. + /// The so that additional calls can be chained. + public static IServiceCollection Configure(this IServiceCollection services, string? sectionName) + where TOptions : class + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + services.AddOptions(); + services.AddSingleton>( + _ => new ConfigurationChangeTokenSource( + Options.Options.DefaultName, + sectionName == null ? _.GetRequiredService() : _.GetRequiredService().GetSection(sectionName) + ) + ); + return services.AddSingleton>( + _ => new NamedConfigureFromConfigurationOptions( + Options.Options.DefaultName, + sectionName == null ? _.GetRequiredService() : _.GetRequiredService().GetSection(sectionName) + ) + ); + } + +#if false + /// + /// Registers a injected configuration service which TOptions will bind against. + /// + /// The type of options being configured. + /// The to add the services to. + /// Used to configure the . + /// The so that additional calls can be chained. + public static IServiceCollection Configure(this IServiceCollection services, Action configureBinder) + where TOptions : class + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + return Configure(services, Options.Options.DefaultName, configureBinder); + } + + /// + /// Registers a injected configuration service which TOptions will bind against. + /// + /// The type of options being configured. + /// The to add the services to. + /// The name of the options instance. + /// Used to configure the . + /// The so that additional calls can be chained. + public static IServiceCollection Configure(this IServiceCollection services, string sectionName, Action configureBinder) + where TOptions : class + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + services.AddOptions(); + services.AddSingleton>(_ => new ConfigurationChangeTokenSource(Options.Options.DefaultName, _.GetRequiredService().GetSection(sectionName))); + return services.AddSingleton>(_ => new NamedConfigureFromConfigurationOptions(Options.Options.DefaultName, _.GetRequiredService().GetSection(sectionName), configureBinder)); + } + + /// + /// Registers a injected configuration service which TOptions will bind against. + /// + /// The type of options being configured. + /// The to configure. + /// The so that additional calls can be chained. + public static OptionsBuilder Configure(this OptionsBuilder builder) + where TOptions : class + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + return Configure(builder, Options.Options.DefaultName); + } + + /// + /// Registers a injected configuration service which TOptions will bind against. + /// + /// The type of options being configured. + /// The to configure. + /// The name of the options instance. + /// The so that additional calls can be chained. + public static OptionsBuilder Configure(this OptionsBuilder builder, string sectionName) + where TOptions : class + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + Configure(builder.Services, name); + return builder; + } + + /// + /// Registers a injected configuration service which TOptions will bind against. + /// + /// The type of options being configured. + /// The to configure. + /// Used to configure the . + /// The so that additional calls can be chained. + public static OptionsBuilder Configure(this OptionsBuilder builder, Action configureBinder) + where TOptions : class + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + return Configure(builder, Options.Options.DefaultName, configureBinder); + } + + /// + /// Registers a injected configuration service which TOptions will bind against. + /// + /// The type of options being configured. + /// The to configure. + /// The name of the options instance. + /// Used to configure the . + /// The so that additional calls can be chained. + public static OptionsBuilder Configure(this OptionsBuilder builder, string sectionName, Action configureBinder) + where TOptions : class + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + + Configure(builder.Services, name, configureBinder); + return builder; + } +#endif + } +} diff --git a/src/Protocol/Protocol.csproj b/src/Protocol/Protocol.csproj index 00c78fc5e..4ea430d99 100644 --- a/src/Protocol/Protocol.csproj +++ b/src/Protocol/Protocol.csproj @@ -9,6 +9,8 @@ + + diff --git a/src/Server/Configuration/DidChangeConfigurationProvider.cs b/src/Server/Configuration/DidChangeConfigurationProvider.cs index aeedb2dfd..6688971a8 100644 --- a/src/Server/Configuration/DidChangeConfigurationProvider.cs +++ b/src/Server/Configuration/DidChangeConfigurationProvider.cs @@ -96,7 +96,7 @@ public Task Handle(DidChangeConfigurationParams request, CancellationToken return Concat( Create( observer => { - var newData = new Dictionary(); + var newData = new Dictionary(StringComparer.OrdinalIgnoreCase); // configuration is case-insensitive return GetConfigurationFromClient(_configurationItems) .Select( x => { diff --git a/src/Server/LanguageServerOptions.cs b/src/Server/LanguageServerOptions.cs index 16710bf8e..8fac9a7e8 100644 --- a/src/Server/LanguageServerOptions.cs +++ b/src/Server/LanguageServerOptions.cs @@ -16,7 +16,10 @@ public LanguageServerOptions() { WithAssemblies(typeof(LanguageServerOptions).Assembly, typeof(LspRequestRouter).Assembly); } + public ServerInfo? ServerInfo { get; set; } + public bool DefaultServerConfiguration { get; set; } = true; + public bool DefaultWorkspaceFolderManager { get; set; } = true; ILanguageServerRegistry IJsonRpcHandlerRegistry.AddHandler(string method, IJsonRpcHandler handler, JsonRpcHandlerOptions? options) => AddHandler(method, handler, options); @@ -29,7 +32,8 @@ ILanguageServerRegistry IJsonRpcHandlerRegistry.AddHand ILanguageServerRegistry IJsonRpcHandlerRegistry.AddHandler(JsonRpcHandlerFactory handlerFunc, JsonRpcHandlerOptions? options) => AddHandler(handlerFunc, options); - ILanguageServerRegistry IJsonRpcHandlerRegistry.AddHandler(IJsonRpcHandler handler, JsonRpcHandlerOptions? options) => AddHandler(handler, options); + ILanguageServerRegistry IJsonRpcHandlerRegistry.AddHandler(IJsonRpcHandler handler, JsonRpcHandlerOptions? options) => + AddHandler(handler, options); ILanguageServerRegistry IJsonRpcHandlerRegistry.AddHandler(JsonRpcHandlerOptions? options) => AddHandler(options); diff --git a/src/Server/LanguageServerOptionsExtensions.cs b/src/Server/LanguageServerOptionsExtensions.cs index 0b7a18d4e..93de43734 100644 --- a/src/Server/LanguageServerOptionsExtensions.cs +++ b/src/Server/LanguageServerOptionsExtensions.cs @@ -38,6 +38,18 @@ public static LanguageServerOptions WithServerInfo(this LanguageServerOptions op return options; } + public static LanguageServerOptions UseDefaultServerConfiguration(this LanguageServerOptions options, bool value = true) + { + options.DefaultServerConfiguration = value; + return options; + } + + public static LanguageServerOptions UseDefaultWorkspaceFolderManager(this LanguageServerOptions options, bool value = true) + { + options.DefaultWorkspaceFolderManager = value; + return options; + } + public static LanguageServerOptions OnInitialize(this LanguageServerOptions options, OnLanguageServerInitializeDelegate @delegate) { options.Services.AddSingleton(@delegate); diff --git a/src/Server/LanguageServerServiceCollectionExtensions.cs b/src/Server/LanguageServerServiceCollectionExtensions.cs index 03061a356..ca89952ce 100644 --- a/src/Server/LanguageServerServiceCollectionExtensions.cs +++ b/src/Server/LanguageServerServiceCollectionExtensions.cs @@ -23,7 +23,27 @@ public static class LanguageServerServiceCollectionExtensions { internal static IContainer AddLanguageServerInternals(this IContainer container, LanguageServerOptions options, IServiceProvider? outerServiceProvider) { - container = container.AddLanguageProtocolInternals(options); + bool Filter(JsonRpcHandlerDescription description) + { + // Disable just the handlers, so that anything that uses the existing classes doesn't break + // They are exposed as properties currently replacing them makes it harder + // TODO: Add a decorator that logs warnings when a disabled interface is used. + { + if (!options.DefaultServerConfiguration && + description is JsonRpcHandlerInstanceDescription instance && + instance.HandlerInstance is DidChangeConfigurationProvider) return false; + } + + { + if (!options.DefaultWorkspaceFolderManager && + description is JsonRpcHandlerInstanceDescription instance && + instance.HandlerInstance is LanguageServerWorkspaceFolderManager) return false; + } + + return true; + } + + container = container.AddLanguageProtocolInternals(options, Filter); container.RegisterMany( reuse: Reuse.Singleton, nonPublicServiceTypes: true, @@ -75,7 +95,7 @@ internal static IContainer AddLanguageServerInternals(this IContainer container, container.RegisterDelegate( _ => { var builder = new ConfigurationBuilder(); - var didChangeConfigurationProvider = _.GetRequiredService(); + var didChangeConfigurationProvider = _.GetRequiredService(); var outerConfiguration = outerServiceProvider?.GetService(); if (outerConfiguration != null) { diff --git a/src/Shared/LanguageProtocolServiceCollectionExtensions.cs b/src/Shared/LanguageProtocolServiceCollectionExtensions.cs index 52ba50957..bf5fafb2f 100644 --- a/src/Shared/LanguageProtocolServiceCollectionExtensions.cs +++ b/src/Shared/LanguageProtocolServiceCollectionExtensions.cs @@ -12,7 +12,7 @@ namespace OmniSharp.Extensions.LanguageServer.Shared { internal static class LanguageProtocolServiceCollectionExtensions { - internal static IContainer AddLanguageProtocolInternals(this IContainer container, LanguageProtocolRpcOptionsBase options) where T : IJsonRpcHandlerRegistry + internal static IContainer AddLanguageProtocolInternals(this IContainer container, LanguageProtocolRpcOptionsBase options, Func filter) where T : IJsonRpcHandlerRegistry { options.RequestProcessIdentifier ??= options.SupportsContentModified ? new RequestProcessIdentifier(RequestProcessType.Parallel) @@ -40,7 +40,7 @@ internal static IContainer AddLanguageProtocolInternals(this IContainer conta container.RegisterInitializer( (manager, context) => { var descriptions = context.Resolve(); - descriptions.Populate(context, manager); + descriptions.Populate(context, manager, filter); } ); container.RegisterMany(Reuse.Singleton); diff --git a/test/Lsp.Tests/Integration/DisableDefaultsTests.cs b/test/Lsp.Tests/Integration/DisableDefaultsTests.cs new file mode 100644 index 000000000..a420c5244 --- /dev/null +++ b/test/Lsp.Tests/Integration/DisableDefaultsTests.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; +using System.Composition; +using System.Reactive.Linq; +using System.Reactive.Threading.Tasks; +using System.Threading; +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.Client; +using OmniSharp.Extensions.LanguageServer.Protocol.Client; +using OmniSharp.Extensions.LanguageServer.Protocol.Document; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Server; +using OmniSharp.Extensions.LanguageServer.Protocol.Window; +using OmniSharp.Extensions.LanguageServer.Protocol.Workspace; +using OmniSharp.Extensions.LanguageServer.Server; +using OmniSharp.Extensions.LanguageServer.Shared; +using Xunit; +using Xunit.Abstractions; + +namespace Lsp.Tests.Integration +{ + public class DisableDefaultsTests : LanguageProtocolTestBase + { + public DisableDefaultsTests(ITestOutputHelper outputHelper) : base(new JsonRpcTestOptions().ConfigureForXUnit(outputHelper)) + { + } + + [Theory] + [InlineData(true), InlineData(false)] + public async Task Should_Disable_Registration_Manager(bool enabled) + { + var (client, _) = await Initialize( + options => options.UseDefaultRegistrationManager(enabled), + options => { } + ); + + var clientManager = client.Services.GetRequiredService(); + clientManager.ContainsHandler(typeof(IRegisterCapabilityHandler)).Should().Be(enabled); + clientManager.ContainsHandler(typeof(IUnregisterCapabilityHandler)).Should().Be(enabled); + } + + [Theory] + [InlineData(true), InlineData(false)] + public async Task Should_Disable_Workspace_Folder_Manager(bool enabled) + { + var (client, server) = await Initialize( + options => options.UseDefaultWorkspaceFolderManager(enabled), + options => options.UseDefaultWorkspaceFolderManager(enabled) + ); + + var clientManager = client.Services.GetRequiredService(); + clientManager.ContainsHandler(typeof(IWorkspaceFoldersHandler)).Should().Be(enabled); + + var serverManager = server.Services.GetRequiredService(); + serverManager.ContainsHandler(typeof(IDidChangeWorkspaceFoldersHandler)).Should().Be(enabled); + } + + [Theory] + [InlineData(true), InlineData(false)] + public async Task Should_Disable_Configuration(bool enabled) + { + var (_, server) = await Initialize( + options => { }, + options => options.UseDefaultServerConfiguration(enabled) + ); + + var serverManager = server.Services.GetRequiredService(); + serverManager.ContainsHandler(typeof(IDidChangeConfigurationHandler)).Should().Be(enabled); + } + } +} diff --git a/test/Lsp.Tests/Integration/LanguageServerConfigurationTests.cs b/test/Lsp.Tests/Integration/LanguageServerConfigurationTests.cs index 1a25ae43c..3ac29cc32 100644 --- a/test/Lsp.Tests/Integration/LanguageServerConfigurationTests.cs +++ b/test/Lsp.Tests/Integration/LanguageServerConfigurationTests.cs @@ -1,8 +1,13 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using FluentAssertions; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using NSubstitute; +using NSubstitute.Extensions; using OmniSharp.Extensions.JsonRpc.Testing; using OmniSharp.Extensions.LanguageProtocol.Testing; using OmniSharp.Extensions.LanguageServer.Client; @@ -161,6 +166,90 @@ public async Task Should_Only_Update_Configuration_Items_That_Are_Defined() server.Configuration["notmysection:key"].Should().BeNull(); } + [Fact] + public async Task Should_Support_Configuration_Binding() + { + var (_, server, configuration) = await InitializeWithConfiguration(ConfigureClient, ConfigureServer); + + configuration.Update("mysection", new Dictionary { ["host"] = "localhost", ["port"] = "443" }); + configuration.Update("notmysection", new Dictionary { ["host"] = "127.0.0.1", ["port"] = "123" }); + await TestHelper.DelayUntil(() => server.Configuration.AsEnumerable().Any(z => z.Key.StartsWith("mysection")), CancellationToken); + + var data = new BinderSourceUrl(); + server.Configuration.Bind("mysection", data); + data.Host.Should().Be("localhost"); + data.Port.Should().Be(443); + + configuration.Update("mysection", new Dictionary { ["host"] = "127.0.0.1", ["port"] = "80" }); + await server.Configuration.WaitForChange(CancellationToken); + await SettleNext(); + + data = new BinderSourceUrl(); + server.Configuration.Bind("mysection", data); + data.Host.Should().Be("127.0.0.1"); + data.Port.Should().Be(80); + } + + [Fact] + public async Task Should_Support_Options() + { + var (_, server, configuration) = await InitializeWithConfiguration(ConfigureClient, options => { + ConfigureServer(options); + options.Services.Configure("mysection"); + }); + + configuration.Update("mysection", new Dictionary { ["host"] = "localhost", ["port"] = "443" }); + configuration.Update("notmysection", new Dictionary { ["host"] = "127.0.0.1", ["port"] = "123" }); + await TestHelper.DelayUntil(() => server.Configuration.AsEnumerable().Any(z => z.Key.StartsWith("mysection")), CancellationToken); + + // + var options = server.GetService>(); + options.Value.Host.Should().Be("localhost"); + options.Value.Port.Should().Be(443); + + configuration.Update("mysection", new Dictionary { ["host"] = "127.0.0.1", ["port"] = "80" }); + await server.Configuration.WaitForChange(CancellationToken); + await SettleNext(); + + options.Value.Host.Should().Be("localhost"); + options.Value.Port.Should().Be(443); + } + + [Fact] + public async Task Should_Support_Options_Monitor() + { + var (_, server, configuration) = await InitializeWithConfiguration(ConfigureClient, options => { + ConfigureServer(options); + options.Services.Configure("mysection"); + }); + + var options = server.GetService>(); + var sub = Substitute.For>(); + options.OnChange(sub); + + configuration.Update("mysection", new Dictionary { ["host"] = "localhost", ["port"] = "443" }); + configuration.Update("notmysection", new Dictionary { ["host"] = "127.0.0.1", ["port"] = "123" }); + await options.WaitForChange(CancellationToken); + + // IOptionsMonitor<> is registered as a singleton, so this will update + options.CurrentValue.Host.Should().Be("localhost"); + options.CurrentValue.Port.Should().Be(443); + sub.Received(1).Invoke(Arg.Any()); + + configuration.Update("mysection", new Dictionary { ["host"] = "127.0.0.1", ["port"] = "80" }); + await options.WaitForChange(CancellationToken); + + options.CurrentValue.Host.Should().Be("127.0.0.1"); + options.CurrentValue.Port.Should().Be(80); + sub.Received(2).Invoke(Arg.Any()); + } + + class BinderSourceUrl + { + public string Host { get; set; } + public int Port { get; set; } + } + private void ConfigureClient(LanguageClientOptions options) { } diff --git a/test/Lsp.Tests/Lsp.Tests.csproj b/test/Lsp.Tests/Lsp.Tests.csproj index ec24f0d07..a903ade0f 100644 --- a/test/Lsp.Tests/Lsp.Tests.csproj +++ b/test/Lsp.Tests/Lsp.Tests.csproj @@ -15,8 +15,17 @@ + + + + + + + MSBuild:GenerateCodeFromAttributes + + From 6dc17ea3ef08db031441dbf66ad5ddb2bee945f9 Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Wed, 14 Oct 2020 22:15:00 -0400 Subject: [PATCH 02/11] fixed an issue where the MedatR handler was being registered in the container for 'built-in' handlers, these were overriding any custom ones that were added later --- ...nguageClientServiceCollectionExtensions.cs | 6 +- .../GenerateHandlerMethodsGenerator.cs | 5 +- .../LanguageProtocolDelegatingHandlers.cs | 11 ++ .../IDidChangeConfigurationHandler.cs | 5 +- .../IDidChangeWorkspaceFoldersHandler.cs | 3 +- src/Server/DefaultLanguageServerFacade.cs | 2 +- src/Server/LanguageServer.cs | 2 +- src/Server/LanguageServerHelpers.cs | 34 +++-- ...nguageServerServiceCollectionExtensions.cs | 11 +- src/Testing/LanguageProtocolTestBase.cs | 2 +- src/Testing/LanguageServerTestBase.cs | 2 +- .../Integration/DisableDefaultsTests.cs | 144 +++++++++++++++++- 12 files changed, 203 insertions(+), 24 deletions(-) diff --git a/src/Client/LanguageClientServiceCollectionExtensions.cs b/src/Client/LanguageClientServiceCollectionExtensions.cs index 21385e053..6199c0d97 100644 --- a/src/Client/LanguageClientServiceCollectionExtensions.cs +++ b/src/Client/LanguageClientServiceCollectionExtensions.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection; using DryIoc; +using MediatR; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -18,6 +19,7 @@ namespace OmniSharp.Extensions.LanguageServer.Client { public static class LanguageClientServiceCollectionExtensions { + private static readonly Assembly MediatRAssembly = typeof(IMediator).Assembly; internal static IContainer AddLanguageClientInternals(this IContainer container, LanguageClientOptions options, IServiceProvider? outerServiceProvider) { bool Filter(JsonRpcHandlerDescription description) @@ -112,10 +114,10 @@ description is JsonRpcHandlerInstanceDescription instance && container.RegisterMany(Reuse.Singleton); container.RegisterMany( - serviceTypeCondition: type => options.WorkspaceFolders || type != typeof(IJsonRpcHandler), reuse: Reuse.Singleton + serviceTypeCondition: type => type.Assembly != MediatRAssembly && (options.WorkspaceFolders || type != typeof(IJsonRpcHandler)), reuse: Reuse.Singleton ); container.RegisterMany( - serviceTypeCondition: type => options.DynamicRegistration || type != typeof(IJsonRpcHandler), reuse: Reuse.Singleton + serviceTypeCondition: type => type.Assembly != MediatRAssembly && (options.DynamicRegistration || type != typeof(IJsonRpcHandler)), reuse: Reuse.Singleton ); return container; diff --git a/src/JsonRpc.Generators/GenerateHandlerMethodsGenerator.cs b/src/JsonRpc.Generators/GenerateHandlerMethodsGenerator.cs index b5075b059..a207514ae 100644 --- a/src/JsonRpc.Generators/GenerateHandlerMethodsGenerator.cs +++ b/src/JsonRpc.Generators/GenerateHandlerMethodsGenerator.cs @@ -171,8 +171,9 @@ MemberDeclarationSyntax MakeAction(TypeSyntax syntax) yield return MakeAction(CreateAsyncAction(true, requestType)); if (capability != null) { - yield return MakeAction(CreateAction(requestType, capability)); - yield return MakeAction(CreateAsyncAction(requestType, capability)); + method = method.WithExpressionBody( + GetNotificationCapabilityHandlerExpression(GetMethodName(handlerInterface), requestType, capability) + ); yield return MakeAction(CreateAction(requestType, capability)); yield return MakeAction(CreateAsyncAction(requestType, capability)); } diff --git a/src/Protocol/LanguageProtocolDelegatingHandlers.cs b/src/Protocol/LanguageProtocolDelegatingHandlers.cs index 2136ca6ee..eba50a456 100644 --- a/src/Protocol/LanguageProtocolDelegatingHandlers.cs +++ b/src/Protocol/LanguageProtocolDelegatingHandlers.cs @@ -1088,6 +1088,17 @@ public NotificationCapability(Func handler) : { } + public NotificationCapability(Action handler) : + this( + Guid.Empty, (request, capability, ct) => { + handler(request, capability, ct); + return Task.CompletedTask; + } + ) + { + } + + public NotificationCapability(Func handler) : this(Guid.Empty, handler) { diff --git a/src/Protocol/Workspace/IDidChangeConfigurationHandler.cs b/src/Protocol/Workspace/IDidChangeConfigurationHandler.cs index 7f80e3fa9..789d699e8 100644 --- a/src/Protocol/Workspace/IDidChangeConfigurationHandler.cs +++ b/src/Protocol/Workspace/IDidChangeConfigurationHandler.cs @@ -14,15 +14,16 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Workspace [GenerateHandlerMethods] [GenerateRequestMethods(typeof(IWorkspaceLanguageClient), typeof(ILanguageClient))] public interface IDidChangeConfigurationHandler : IJsonRpcNotificationHandler, - IRegistration, ICapability + IRegistration, // TODO: Remove this in the future + ICapability { } public abstract class DidChangeConfigurationHandler : IDidChangeConfigurationHandler { - public object GetRegistrationOptions() => new object(); public abstract Task Handle(DidChangeConfigurationParams request, CancellationToken cancellationToken); public virtual void SetCapability(DidChangeConfigurationCapability capability) => Capability = capability; protected DidChangeConfigurationCapability Capability { get; private set; } = null!; + public object GetRegistrationOptions() => new object(); } } diff --git a/src/Protocol/Workspace/IDidChangeWorkspaceFoldersHandler.cs b/src/Protocol/Workspace/IDidChangeWorkspaceFoldersHandler.cs index f8ae20910..60b2d7812 100644 --- a/src/Protocol/Workspace/IDidChangeWorkspaceFoldersHandler.cs +++ b/src/Protocol/Workspace/IDidChangeWorkspaceFoldersHandler.cs @@ -12,7 +12,8 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Workspace [Method(WorkspaceNames.DidChangeWorkspaceFolders, Direction.ClientToServer)] [GenerateHandlerMethods] [GenerateRequestMethods(typeof(IWorkspaceLanguageClient), typeof(ILanguageClient))] - public interface IDidChangeWorkspaceFoldersHandler : IJsonRpcNotificationHandler, IRegistration + public interface IDidChangeWorkspaceFoldersHandler : IJsonRpcNotificationHandler, + IRegistration // TODO: Remove this in the future { } diff --git a/src/Server/DefaultLanguageServerFacade.cs b/src/Server/DefaultLanguageServerFacade.cs index e2ba1ad5d..3331596f3 100644 --- a/src/Server/DefaultLanguageServerFacade.cs +++ b/src/Server/DefaultLanguageServerFacade.cs @@ -71,7 +71,7 @@ public IDisposable Register(Action registryAction) var result = manager.GetDisposable(); if (_instancesHasStarted.Started) { - LanguageServerHelpers.InitHandlers(ResolverContext.Resolve(), result); + LanguageServerHelpers.InitHandlers(ResolverContext.Resolve(), result, _supportedCapabilities.Value); } return LanguageServerHelpers.RegisterHandlers(_hasStarted, Client, _workDoneManager.Value, _supportedCapabilities.Value, result); diff --git a/src/Server/LanguageServer.cs b/src/Server/LanguageServer.cs index e7cee98db..5e95f44df 100644 --- a/src/Server/LanguageServer.cs +++ b/src/Server/LanguageServer.cs @@ -500,7 +500,7 @@ public IDisposable Register(Action registryAction) var result = manager.GetDisposable(); if (_instanceHasStarted.Started) { - LanguageServerHelpers.InitHandlers(this, result); + LanguageServerHelpers.InitHandlers(this, result, _supportedCapabilities); } return LanguageServerHelpers.RegisterHandlers(_initializeComplete.Select(z => System.Reactive.Unit.Default), Client, WorkDoneManager, _supportedCapabilities, result); diff --git a/src/Server/LanguageServerHelpers.cs b/src/Server/LanguageServerHelpers.cs index 5c480c096..662cb5889 100644 --- a/src/Server/LanguageServerHelpers.cs +++ b/src/Server/LanguageServerHelpers.cs @@ -5,6 +5,7 @@ using System.Reactive.Disposables; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; +using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Protocol; using OmniSharp.Extensions.LanguageServer.Protocol.Client; using OmniSharp.Extensions.LanguageServer.Protocol.Models; @@ -18,29 +19,42 @@ namespace OmniSharp.Extensions.LanguageServer.Server static class LanguageServerHelpers { static IEnumerable GetUniqueHandlers(CompositeDisposable disposable) + { + return GetUniqueHandlers(disposable).OfType(); + } + + static IEnumerable GetUniqueHandlers(CompositeDisposable disposable) + { + return GetAllDescriptors(disposable) + .Concat(disposable.OfType().SelectMany(GetAllDescriptors)) + .Concat(disposable.OfType().SelectMany(GetAllDescriptors)) + .Select(z => z.Handler) + .Distinct(); + } + + static IEnumerable GetAllDescriptors(CompositeDisposable disposable) { return disposable.OfType() - .Select(z => z.Handler) - .OfType() - .Concat(disposable.OfType().SelectMany(GetUniqueHandlers)) - .Concat(disposable.OfType().SelectMany(GetLspHandlers)) + .Concat(disposable.OfType().SelectMany(GetAllDescriptors)) + .Concat(disposable.OfType().SelectMany(GetAllDescriptors)) .Distinct(); } - static IEnumerable GetLspHandlers(LspHandlerDescriptorDisposable disposable) + static IEnumerable GetAllDescriptors(LspHandlerDescriptorDisposable disposable) { - return disposable.Descriptors - .Select(z => z.Handler) - .OfType() - .Distinct(); + return disposable.Descriptors; } - internal static void InitHandlers(ILanguageServer client, CompositeDisposable result) + internal static void InitHandlers(ILanguageServer client, CompositeDisposable result, ISupportedCapabilities supportedCapabilities ) { Observable.Concat( GetUniqueHandlers(result) .Select(handler => Observable.FromAsync(ct => handler.OnInitialize(client, client.ClientSettings, ct))) .Merge(), + GetAllDescriptors(result) + .Select(item => LspHandlerDescriptorHelpers.InitializeHandler(item, supportedCapabilities, item.Handler)) + .ToObservable() + .Select(z => Unit.Default), GetUniqueHandlers(result) .Select(handler => Observable.FromAsync(ct => handler.OnInitialized(client, client.ClientSettings, client.ServerSettings, ct))) .Merge(), diff --git a/src/Server/LanguageServerServiceCollectionExtensions.cs b/src/Server/LanguageServerServiceCollectionExtensions.cs index ca89952ce..5d745073b 100644 --- a/src/Server/LanguageServerServiceCollectionExtensions.cs +++ b/src/Server/LanguageServerServiceCollectionExtensions.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Reflection; using DryIoc; +using MediatR; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -15,12 +16,14 @@ using OmniSharp.Extensions.LanguageServer.Server.Matchers; using OmniSharp.Extensions.LanguageServer.Server.Pipelines; using OmniSharp.Extensions.LanguageServer.Shared; + #pragma warning disable CS0618 namespace OmniSharp.Extensions.LanguageServer.Server { public static class LanguageServerServiceCollectionExtensions { + private static readonly Assembly MediatRAssembly = typeof(IMediator).Assembly; internal static IContainer AddLanguageServerInternals(this IContainer container, LanguageServerOptions options, IServiceProvider? outerServiceProvider) { bool Filter(JsonRpcHandlerDescription description) @@ -78,6 +81,7 @@ description is JsonRpcHandlerInstanceDescription instance && ); container.RegisterMany( + serviceTypeCondition: type => type.Assembly != MediatRAssembly, made: Parameters.Of .Type>(defaultValue: options.ConfigurationBuilderAction), reuse: Reuse.Singleton @@ -104,7 +108,7 @@ description is JsonRpcHandlerInstanceDescription instance && if (providedConfiguration != null) { - builder.CustomAddConfiguration((providedConfiguration.ImplementationInstance as IConfiguration)!); + builder.CustomAddConfiguration(( providedConfiguration.ImplementationInstance as IConfiguration )!); } return builder.CustomAddConfiguration(didChangeConfigurationProvider).Build(); @@ -128,7 +132,10 @@ description is JsonRpcHandlerInstanceDescription instance && container.RegisterMany(new[] { typeof(ResolveCommandPipeline<,>) }); container.RegisterMany(new[] { typeof(SemanticTokensDeltaPipeline<,>) }); container.RegisterMany(Reuse.Singleton); - container.RegisterMany(Reuse.Singleton); + container.RegisterMany( + serviceTypeCondition: type => type.Assembly != MediatRAssembly, + reuse: Reuse.Singleton + ); return container; } diff --git a/src/Testing/LanguageProtocolTestBase.cs b/src/Testing/LanguageProtocolTestBase.cs index 77f5ae0a2..0368692cd 100644 --- a/src/Testing/LanguageProtocolTestBase.cs +++ b/src/Testing/LanguageProtocolTestBase.cs @@ -98,7 +98,7 @@ Action serverOptionsAction var (client, server) = Create( options => { clientOptionsAction.Invoke(options); - options.WithCapability(new DidChangeConfigurationCapability()); + options.WithCapability(new DidChangeConfigurationCapability() { DynamicRegistration = true}); options.Services.AddSingleton(); }, serverOptionsAction ); diff --git a/src/Testing/LanguageServerTestBase.cs b/src/Testing/LanguageServerTestBase.cs index 61868007c..ebe8af95a 100644 --- a/src/Testing/LanguageServerTestBase.cs +++ b/src/Testing/LanguageServerTestBase.cs @@ -63,7 +63,7 @@ protected virtual async Task InitializeClient(Action { clientOptionsAction?.Invoke(options); - options.WithCapability(new DidChangeConfigurationCapability()); + options.WithCapability(new DidChangeConfigurationCapability() { DynamicRegistration = true}); options.Services.AddSingleton(); } ); diff --git a/test/Lsp.Tests/Integration/DisableDefaultsTests.cs b/test/Lsp.Tests/Integration/DisableDefaultsTests.cs index a420c5244..6d731384b 100644 --- a/test/Lsp.Tests/Integration/DisableDefaultsTests.cs +++ b/test/Lsp.Tests/Integration/DisableDefaultsTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Composition; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; @@ -6,12 +7,16 @@ using System.Threading.Tasks; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json.Linq; using NSubstitute; +using NSubstitute.Exceptions; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.JsonRpc.Testing; using OmniSharp.Extensions.LanguageProtocol.Testing; using OmniSharp.Extensions.LanguageServer.Client; +using OmniSharp.Extensions.LanguageServer.Protocol; using OmniSharp.Extensions.LanguageServer.Protocol.Client; +using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; using OmniSharp.Extensions.LanguageServer.Protocol.Document; using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Server; @@ -19,6 +24,7 @@ using OmniSharp.Extensions.LanguageServer.Protocol.Workspace; using OmniSharp.Extensions.LanguageServer.Server; using OmniSharp.Extensions.LanguageServer.Shared; +using TestingUtils; using Xunit; using Xunit.Abstractions; @@ -60,6 +66,67 @@ public async Task Should_Disable_Workspace_Folder_Manager(bool enabled) serverManager.ContainsHandler(typeof(IDidChangeWorkspaceFoldersHandler)).Should().Be(enabled); } + [Fact] + public async Task Should_Allow_Custom_Workspace_Folder_Manager_Delegate() + { + var action = Substitute.For>(); + var (client, server) = await Initialize( + options => {}, + options => options + .UseDefaultWorkspaceFolderManager(false) + .OnDidChangeWorkspaceFolders(action, new object()) + ); + + var config = client.Services.GetRequiredService(); + config.Update("mysection", new Dictionary() { ["data"] = "value" }); + + client.WorkspaceFoldersManager.Add(new WorkspaceFolder() { Name = "foldera", Uri = "/some/path" }); + + await TestHelper.DelayUntil( + () => { + try + { + action.Received(1).Invoke(Arg.Any()); + return true; + } + catch (ReceivedCallsException e) + { + return false; + } + }, CancellationToken + ); + } + + [Fact] + public async Task Should_Allow_Custom_Workspace_Folder_Manager_Delegate_Without_Disabling_Default_Handlers() + { + var action = Substitute.For>(); + var (client, server) = await Initialize( + options => {}, + options => options + .OnDidChangeWorkspaceFolders(action, new object()) + ); + + var config = client.Services.GetRequiredService(); + config.Update("mysection", new Dictionary() { ["data"] = "value" }); + + client.WorkspaceFoldersManager.Add(new WorkspaceFolder() { Name = "foldera", Uri = "/some/path" }); + + await TestHelper.DelayUntil( + () => { + try + { + action.Received(1).Invoke(Arg.Any()); + return true; + } + catch (ReceivedCallsException e) + { + return false; + } + }, CancellationToken + ); + } + [Theory] [InlineData(true), InlineData(false)] public async Task Should_Disable_Configuration(bool enabled) @@ -72,5 +139,80 @@ public async Task Should_Disable_Configuration(bool enabled) var serverManager = server.Services.GetRequiredService(); serverManager.ContainsHandler(typeof(IDidChangeConfigurationHandler)).Should().Be(enabled); } + + [Fact] + public async Task Should_Allow_Custom_Configuration_Delegate() + { + var action = Substitute.For>(); + var (client, server) = await Initialize( + options => options + .WithCapability(new DidChangeConfigurationCapability() { DynamicRegistration = true }) + .WithServices(z => z.AddSingleton()), + options => options + .UseDefaultServerConfiguration(false) + .WithConfigurationSection("mysection") + .OnDidChangeConfiguration(action, new object()) + ); + + var clientManager = client.Services.GetRequiredService(); + clientManager.ContainsHandler(typeof(IConfigurationHandler)).Should().BeTrue(); + + var serverManager = server.Services.GetRequiredService(); + serverManager.ContainsHandler(typeof(IDidChangeConfigurationHandler)).Should().BeTrue(); + + var config = client.Services.GetRequiredService(); + config.Update("mysection", new Dictionary() { ["data"] = "value" }); + + await TestHelper.DelayUntil( + () => { + try + { + action.Received(1).Invoke(Arg.Is(z => Equals(z.Settings, JValue.CreateNull()))); + return true; + } + catch (ReceivedCallsException e) + { + return false; + } + }, CancellationToken + ); + } + + [Fact] + public async Task Should_Allow_Custom_Configuration_Without_Disabling_Default_Handlers() + { + var action = Substitute.For>(); + var (client, server) = await Initialize( + options => options + .WithCapability( + new DidChangeConfigurationCapability() { + DynamicRegistration = true + } + ) + .WithServices(z => z.AddSingleton()), + options => options + .WithConfigurationSection("mysection") + .OnDidChangeConfiguration(action, new object()) + ); + + var clientManager = client.Services.GetRequiredService(); + clientManager.ContainsHandler(typeof(IConfigurationHandler)).Should().BeTrue(); + var config = client.Services.GetRequiredService(); + config.Update("mysection", new Dictionary() { ["data"] = "value" }); + + await TestHelper.DelayUntil( + () => { + try + { + action.Received(1).Invoke(Arg.Is(z => Equals(z.Settings, JValue.CreateNull()))); + return true; + } + catch (ReceivedCallsException e) + { + return false; + } + }, CancellationToken + ); + } } } From 5bc36be3b42e928af406751e6e0330da8947897e Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Wed, 14 Oct 2020 22:40:53 -0400 Subject: [PATCH 03/11] added default request handler, and decorator to ensure that if a IRequestContext is given, then that handler is used. --- ...nguageClientServiceCollectionExtensions.cs | 5 +- ...sonRpcServerServiceCollectionExtensions.cs | 60 ++++++++++++++----- ...nguageServerServiceCollectionExtensions.cs | 7 +-- 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/src/Client/LanguageClientServiceCollectionExtensions.cs b/src/Client/LanguageClientServiceCollectionExtensions.cs index 6199c0d97..8d2a9db2b 100644 --- a/src/Client/LanguageClientServiceCollectionExtensions.cs +++ b/src/Client/LanguageClientServiceCollectionExtensions.cs @@ -19,7 +19,6 @@ namespace OmniSharp.Extensions.LanguageServer.Client { public static class LanguageClientServiceCollectionExtensions { - private static readonly Assembly MediatRAssembly = typeof(IMediator).Assembly; internal static IContainer AddLanguageClientInternals(this IContainer container, LanguageClientOptions options, IServiceProvider? outerServiceProvider) { bool Filter(JsonRpcHandlerDescription description) @@ -114,10 +113,10 @@ description is JsonRpcHandlerInstanceDescription instance && container.RegisterMany(Reuse.Singleton); container.RegisterMany( - serviceTypeCondition: type => type.Assembly != MediatRAssembly && (options.WorkspaceFolders || type != typeof(IJsonRpcHandler)), reuse: Reuse.Singleton + serviceTypeCondition: type => options.WorkspaceFolders || type != typeof(IJsonRpcHandler), reuse: Reuse.Singleton ); container.RegisterMany( - serviceTypeCondition: type => type.Assembly != MediatRAssembly && (options.DynamicRegistration || type != typeof(IJsonRpcHandler)), reuse: Reuse.Singleton + serviceTypeCondition: type => options.DynamicRegistration || type != typeof(IJsonRpcHandler), reuse: Reuse.Singleton ); return container; diff --git a/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs b/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs index 5ba0123b9..d384ef1ca 100644 --- a/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs +++ b/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs @@ -1,6 +1,8 @@ using System; using System.IO.Pipelines; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using DryIoc; using MediatR; using Microsoft.Extensions.DependencyInjection; @@ -79,23 +81,51 @@ internal static IContainer AddJsonRpcMediatR(this IContainer container) container.RegisterMany(new[] { typeof(IMediator).GetAssembly() }, Registrator.Interfaces, Reuse.ScopedOrSingleton); container.RegisterMany(Reuse.Scoped); container.RegisterDelegate(context => context.Resolve, Reuse.ScopedOrSingleton); + container.Register(typeof(IRequestHandler<,>), typeof(RequestHandler<,>)); + container.Register(typeof(IRequestHandler<,>), typeof(RequestHandlerDecorator<,>), setup: Setup.Decorator); + + return container; + } + + class RequestHandler : IRequestHandler where T : IRequest + { + private readonly IRequestContext _requestContext; + + public RequestHandler(IRequestContext requestContext) + { + _requestContext = requestContext; + } + public Task Handle(T request, CancellationToken cancellationToken) + { + return ((IRequestHandler) _requestContext.Descriptor.Handler).Handle(request, cancellationToken); + } + } + + class RequestHandlerDecorator : IRequestHandler where T : IRequest + { + private readonly IRequestHandler? _handler; + private readonly IRequestContext? _requestContext; + + public RequestHandlerDecorator(IRequestHandler? handler = null, IRequestContext? requestContext = null) + { + _handler = handler; + _requestContext = requestContext; + } + public Task Handle(T request, CancellationToken cancellationToken) + { + if (_requestContext == null) + { + if (_handler == null) + { + throw new NotImplementedException($"No request handler was registered for type {typeof(IRequestHandler).FullName}"); - return container.With( - rules => rules.WithUnknownServiceResolvers( - request => { - if (request.ServiceType.IsGenericType && typeof(IRequestHandler<,>).IsAssignableFrom(request.ServiceType.GetGenericTypeDefinition())) - { - var context = request.Container.Resolve(); - if (context != null) - { - return new RegisteredInstanceFactory(context.Descriptor.Handler); - } - } - - return null; } - ) - ); + + return _handler.Handle(request, cancellationToken); + } + + return ((IRequestHandler) _requestContext.Descriptor.Handler).Handle(request, cancellationToken); + } } internal static IContainer AddJsonRpcServerInternals(this IContainer container, JsonRpcServerOptions options) diff --git a/src/Server/LanguageServerServiceCollectionExtensions.cs b/src/Server/LanguageServerServiceCollectionExtensions.cs index 5d745073b..2e10a4774 100644 --- a/src/Server/LanguageServerServiceCollectionExtensions.cs +++ b/src/Server/LanguageServerServiceCollectionExtensions.cs @@ -23,7 +23,6 @@ namespace OmniSharp.Extensions.LanguageServer.Server { public static class LanguageServerServiceCollectionExtensions { - private static readonly Assembly MediatRAssembly = typeof(IMediator).Assembly; internal static IContainer AddLanguageServerInternals(this IContainer container, LanguageServerOptions options, IServiceProvider? outerServiceProvider) { bool Filter(JsonRpcHandlerDescription description) @@ -81,7 +80,6 @@ description is JsonRpcHandlerInstanceDescription instance && ); container.RegisterMany( - serviceTypeCondition: type => type.Assembly != MediatRAssembly, made: Parameters.Of .Type>(defaultValue: options.ConfigurationBuilderAction), reuse: Reuse.Singleton @@ -132,10 +130,7 @@ description is JsonRpcHandlerInstanceDescription instance && container.RegisterMany(new[] { typeof(ResolveCommandPipeline<,>) }); container.RegisterMany(new[] { typeof(SemanticTokensDeltaPipeline<,>) }); container.RegisterMany(Reuse.Singleton); - container.RegisterMany( - serviceTypeCondition: type => type.Assembly != MediatRAssembly, - reuse: Reuse.Singleton - ); + container.RegisterMany(reuse: Reuse.Singleton); return container; } From 135154207b3596fcf3aac063a72609d90c18b040 Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Wed, 14 Oct 2020 23:40:28 -0400 Subject: [PATCH 04/11] removed some changes that are no longer needed --- src/Client/LanguageClientOptions.cs | 2 - src/Client/LanguageClientOptionsExtensions.cs | 12 -- ...nguageClientServiceCollectionExtensions.cs | 22 +--- ...pterProtocolServiceCollectionExtensions.cs | 2 +- .../JsonRpcHandlerCollectionExtensions.cs | 3 +- ...sonRpcServerServiceCollectionExtensions.cs | 2 +- src/Server/LanguageServerOptions.cs | 2 - src/Server/LanguageServerOptionsExtensions.cs | 12 -- ...nguageServerServiceCollectionExtensions.cs | 22 +--- ...uageProtocolServiceCollectionExtensions.cs | 4 +- .../Integration/DisableDefaultsTests.cs | 116 ++++-------------- 11 files changed, 33 insertions(+), 166 deletions(-) diff --git a/src/Client/LanguageClientOptions.cs b/src/Client/LanguageClientOptions.cs index 2911b4777..69483f8f2 100644 --- a/src/Client/LanguageClientOptions.cs +++ b/src/Client/LanguageClientOptions.cs @@ -41,8 +41,6 @@ public string RootPath public InitializeTrace Trace { get; set; } public object? InitializationOptions { get; set; } - public bool DefaultRegistrationManager { get; set; } = true; - public bool DefaultWorkspaceFolderManager { get; set; } = true; ILanguageClientRegistry IJsonRpcHandlerRegistry.AddHandler(string method, IJsonRpcHandler handler, JsonRpcHandlerOptions? options) => AddHandler(method, handler, options); diff --git a/src/Client/LanguageClientOptionsExtensions.cs b/src/Client/LanguageClientOptionsExtensions.cs index 75159bec0..80d7e53bf 100644 --- a/src/Client/LanguageClientOptionsExtensions.cs +++ b/src/Client/LanguageClientOptionsExtensions.cs @@ -108,18 +108,6 @@ public static LanguageClientOptions OnStarted(this LanguageClientOptions options return options; } - public static LanguageClientOptions UseDefaultRegistrationManager(this LanguageClientOptions options, bool value = true) - { - options.DefaultRegistrationManager = value; - return options; - } - - public static LanguageClientOptions UseDefaultWorkspaceFolderManager(this LanguageClientOptions options, bool value = true) - { - options.DefaultWorkspaceFolderManager = value; - return options; - } - public static LanguageClientOptions ConfigureLogging( this LanguageClientOptions options, Action builderAction diff --git a/src/Client/LanguageClientServiceCollectionExtensions.cs b/src/Client/LanguageClientServiceCollectionExtensions.cs index 8d2a9db2b..dcd571da7 100644 --- a/src/Client/LanguageClientServiceCollectionExtensions.cs +++ b/src/Client/LanguageClientServiceCollectionExtensions.cs @@ -21,27 +21,7 @@ public static class LanguageClientServiceCollectionExtensions { internal static IContainer AddLanguageClientInternals(this IContainer container, LanguageClientOptions options, IServiceProvider? outerServiceProvider) { - bool Filter(JsonRpcHandlerDescription description) - { - // Disable just the handlers, so that anything that uses the existing classes doesn't break - // They are exposed as properties currently replacing them makes it harder - // TODO: Add a decorator that logs warnings when a disabled interface is used. - { - if (!options.DefaultRegistrationManager && - description is JsonRpcHandlerInstanceDescription instance && - instance.HandlerInstance is LanguageClientRegistrationManager) return false; - } - - { - if (!options.DefaultWorkspaceFolderManager && - description is JsonRpcHandlerInstanceDescription instance && - instance.HandlerInstance is LanguageClientWorkspaceFoldersManager) return false; - } - - return true; - } - - container = container.AddLanguageProtocolInternals(options, Filter); + container = container.AddLanguageProtocolInternals(options); container.RegisterInstance(options.ClientCapabilities); container.RegisterMany( diff --git a/src/Dap.Shared/DebugAdapterProtocolServiceCollectionExtensions.cs b/src/Dap.Shared/DebugAdapterProtocolServiceCollectionExtensions.cs index 797c7d314..fb7bc4136 100644 --- a/src/Dap.Shared/DebugAdapterProtocolServiceCollectionExtensions.cs +++ b/src/Dap.Shared/DebugAdapterProtocolServiceCollectionExtensions.cs @@ -26,7 +26,7 @@ internal static IContainer AddDebugAdapterProtocolInternals(this IContainer c container.RegisterInitializer( (manager, context) => { var descriptions = context.Resolve(); - descriptions.Populate(context, manager, _ => true); + descriptions.Populate(context, manager); } ); container.RegisterMany(Reuse.Singleton); diff --git a/src/JsonRpc/JsonRpcHandlerCollectionExtensions.cs b/src/JsonRpc/JsonRpcHandlerCollectionExtensions.cs index e89c95290..ed10d3c1f 100644 --- a/src/JsonRpc/JsonRpcHandlerCollectionExtensions.cs +++ b/src/JsonRpc/JsonRpcHandlerCollectionExtensions.cs @@ -6,12 +6,11 @@ namespace OmniSharp.Extensions.JsonRpc { internal static class JsonRpcHandlerCollectionExtensions { - public static void Populate(this IJsonRpcHandlerCollection collection, IResolverContext resolverContext, IHandlersManager handlersManager, Func filter) + public static void Populate(this IJsonRpcHandlerCollection collection, IResolverContext resolverContext, IHandlersManager handlersManager) { var links = new List(); foreach (var item in collection) { - if (!filter(item)) continue; switch (item) { case JsonRpcHandlerFactoryDescription factory when !(factory.Method is null): diff --git a/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs b/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs index d384ef1ca..b27a6e475 100644 --- a/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs +++ b/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs @@ -162,7 +162,7 @@ internal static IContainer AddJsonRpcServerInternals(this IContainer container, container.RegisterInitializer( (manager, context) => { var descriptions = context.Resolve(); - descriptions.Populate(context, manager, _ => true); + descriptions.Populate(context, manager); } ); diff --git a/src/Server/LanguageServerOptions.cs b/src/Server/LanguageServerOptions.cs index 8fac9a7e8..4f332b634 100644 --- a/src/Server/LanguageServerOptions.cs +++ b/src/Server/LanguageServerOptions.cs @@ -18,8 +18,6 @@ public LanguageServerOptions() } public ServerInfo? ServerInfo { get; set; } - public bool DefaultServerConfiguration { get; set; } = true; - public bool DefaultWorkspaceFolderManager { get; set; } = true; ILanguageServerRegistry IJsonRpcHandlerRegistry.AddHandler(string method, IJsonRpcHandler handler, JsonRpcHandlerOptions? options) => AddHandler(method, handler, options); diff --git a/src/Server/LanguageServerOptionsExtensions.cs b/src/Server/LanguageServerOptionsExtensions.cs index 93de43734..0b7a18d4e 100644 --- a/src/Server/LanguageServerOptionsExtensions.cs +++ b/src/Server/LanguageServerOptionsExtensions.cs @@ -38,18 +38,6 @@ public static LanguageServerOptions WithServerInfo(this LanguageServerOptions op return options; } - public static LanguageServerOptions UseDefaultServerConfiguration(this LanguageServerOptions options, bool value = true) - { - options.DefaultServerConfiguration = value; - return options; - } - - public static LanguageServerOptions UseDefaultWorkspaceFolderManager(this LanguageServerOptions options, bool value = true) - { - options.DefaultWorkspaceFolderManager = value; - return options; - } - public static LanguageServerOptions OnInitialize(this LanguageServerOptions options, OnLanguageServerInitializeDelegate @delegate) { options.Services.AddSingleton(@delegate); diff --git a/src/Server/LanguageServerServiceCollectionExtensions.cs b/src/Server/LanguageServerServiceCollectionExtensions.cs index 2e10a4774..464f1b89c 100644 --- a/src/Server/LanguageServerServiceCollectionExtensions.cs +++ b/src/Server/LanguageServerServiceCollectionExtensions.cs @@ -25,27 +25,7 @@ public static class LanguageServerServiceCollectionExtensions { internal static IContainer AddLanguageServerInternals(this IContainer container, LanguageServerOptions options, IServiceProvider? outerServiceProvider) { - bool Filter(JsonRpcHandlerDescription description) - { - // Disable just the handlers, so that anything that uses the existing classes doesn't break - // They are exposed as properties currently replacing them makes it harder - // TODO: Add a decorator that logs warnings when a disabled interface is used. - { - if (!options.DefaultServerConfiguration && - description is JsonRpcHandlerInstanceDescription instance && - instance.HandlerInstance is DidChangeConfigurationProvider) return false; - } - - { - if (!options.DefaultWorkspaceFolderManager && - description is JsonRpcHandlerInstanceDescription instance && - instance.HandlerInstance is LanguageServerWorkspaceFolderManager) return false; - } - - return true; - } - - container = container.AddLanguageProtocolInternals(options, Filter); + container = container.AddLanguageProtocolInternals(options); container.RegisterMany( reuse: Reuse.Singleton, nonPublicServiceTypes: true, diff --git a/src/Shared/LanguageProtocolServiceCollectionExtensions.cs b/src/Shared/LanguageProtocolServiceCollectionExtensions.cs index bf5fafb2f..52ba50957 100644 --- a/src/Shared/LanguageProtocolServiceCollectionExtensions.cs +++ b/src/Shared/LanguageProtocolServiceCollectionExtensions.cs @@ -12,7 +12,7 @@ namespace OmniSharp.Extensions.LanguageServer.Shared { internal static class LanguageProtocolServiceCollectionExtensions { - internal static IContainer AddLanguageProtocolInternals(this IContainer container, LanguageProtocolRpcOptionsBase options, Func filter) where T : IJsonRpcHandlerRegistry + internal static IContainer AddLanguageProtocolInternals(this IContainer container, LanguageProtocolRpcOptionsBase options) where T : IJsonRpcHandlerRegistry { options.RequestProcessIdentifier ??= options.SupportsContentModified ? new RequestProcessIdentifier(RequestProcessType.Parallel) @@ -40,7 +40,7 @@ internal static IContainer AddLanguageProtocolInternals(this IContainer conta container.RegisterInitializer( (manager, context) => { var descriptions = context.Resolve(); - descriptions.Populate(context, manager, filter); + descriptions.Populate(context, manager); } ); container.RegisterMany(Reuse.Singleton); diff --git a/test/Lsp.Tests/Integration/DisableDefaultsTests.cs b/test/Lsp.Tests/Integration/DisableDefaultsTests.cs index 6d731384b..5266c7f13 100644 --- a/test/Lsp.Tests/Integration/DisableDefaultsTests.cs +++ b/test/Lsp.Tests/Integration/DisableDefaultsTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Composition; +using System.Linq; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; using System.Threading; @@ -36,34 +37,38 @@ public DisableDefaultsTests(ITestOutputHelper outputHelper) : base(new JsonRpcTe { } - [Theory] - [InlineData(true), InlineData(false)] - public async Task Should_Disable_Registration_Manager(bool enabled) + [Fact] + public async Task Should_Disable_Registration_Manager() { + var registrationAction = Substitute.For>(); + var unregistrationAction = Substitute.For>(); var (client, _) = await Initialize( - options => options.UseDefaultRegistrationManager(enabled), + options => options + .OnRegisterCapability(registrationAction) + .OnUnregisterCapability(unregistrationAction), options => { } ); - var clientManager = client.Services.GetRequiredService(); - clientManager.ContainsHandler(typeof(IRegisterCapabilityHandler)).Should().Be(enabled); - clientManager.ContainsHandler(typeof(IUnregisterCapabilityHandler)).Should().Be(enabled); + var clientManager = client.Services.GetRequiredService(); + clientManager.Descriptors.Should().Contain(f => f.Handler is DelegatingHandlers.Request); + clientManager.Descriptors.Should().Contain(f => f.Handler is DelegatingHandlers.Request); } - [Theory] - [InlineData(true), InlineData(false)] - public async Task Should_Disable_Workspace_Folder_Manager(bool enabled) + [Fact] + public async Task Should_Disable_Workspace_Folder_Manager() { + var clientAction = Substitute.For?>>>(); + var serverAction = Substitute.For>(); var (client, server) = await Initialize( - options => options.UseDefaultWorkspaceFolderManager(enabled), - options => options.UseDefaultWorkspaceFolderManager(enabled) + options => options.OnWorkspaceFolders(clientAction), + options => options.OnDidChangeWorkspaceFolders(serverAction, new object()) ); - var clientManager = client.Services.GetRequiredService(); - clientManager.ContainsHandler(typeof(IWorkspaceFoldersHandler)).Should().Be(enabled); + var clientManager = client.Services.GetRequiredService(); + clientManager.Descriptors.Should().Contain(f => f.Handler is DelegatingHandlers.Request?>); - var serverManager = server.Services.GetRequiredService(); - serverManager.ContainsHandler(typeof(IDidChangeWorkspaceFoldersHandler)).Should().Be(enabled); + var serverManager = server.Services.GetRequiredService(); + serverManager.Descriptors.Should().Contain(f => f.Handler is LanguageProtocolDelegatingHandlers.Notification); } [Fact] @@ -73,7 +78,6 @@ public async Task Should_Allow_Custom_Workspace_Folder_Manager_Delegate() var (client, server) = await Initialize( options => {}, options => options - .UseDefaultWorkspaceFolderManager(false) .OnDidChangeWorkspaceFolders(action, new object()) ); @@ -98,46 +102,16 @@ await TestHelper.DelayUntil( } [Fact] - public async Task Should_Allow_Custom_Workspace_Folder_Manager_Delegate_Without_Disabling_Default_Handlers() - { - var action = Substitute.For>(); - var (client, server) = await Initialize( - options => {}, - options => options - .OnDidChangeWorkspaceFolders(action, new object()) - ); - - var config = client.Services.GetRequiredService(); - config.Update("mysection", new Dictionary() { ["data"] = "value" }); - - client.WorkspaceFoldersManager.Add(new WorkspaceFolder() { Name = "foldera", Uri = "/some/path" }); - - await TestHelper.DelayUntil( - () => { - try - { - action.Received(1).Invoke(Arg.Any()); - return true; - } - catch (ReceivedCallsException e) - { - return false; - } - }, CancellationToken - ); - } - - [Theory] - [InlineData(true), InlineData(false)] - public async Task Should_Disable_Configuration(bool enabled) + public async Task Should_Disable_Configuration() { + var action = Substitute.For>(); var (_, server) = await Initialize( options => { }, - options => options.UseDefaultServerConfiguration(enabled) + options => options.OnDidChangeConfiguration(action, new object()) ); - var serverManager = server.Services.GetRequiredService(); - serverManager.ContainsHandler(typeof(IDidChangeConfigurationHandler)).Should().Be(enabled); + var serverManager = server.Services.GetRequiredService(); + serverManager.Descriptors.Should().Contain(f => f.Handler is LanguageProtocolDelegatingHandlers.Notification); } [Fact] @@ -149,7 +123,6 @@ public async Task Should_Allow_Custom_Configuration_Delegate() .WithCapability(new DidChangeConfigurationCapability() { DynamicRegistration = true }) .WithServices(z => z.AddSingleton()), options => options - .UseDefaultServerConfiguration(false) .WithConfigurationSection("mysection") .OnDidChangeConfiguration(action, new object()) ); @@ -177,42 +150,5 @@ await TestHelper.DelayUntil( }, CancellationToken ); } - - [Fact] - public async Task Should_Allow_Custom_Configuration_Without_Disabling_Default_Handlers() - { - var action = Substitute.For>(); - var (client, server) = await Initialize( - options => options - .WithCapability( - new DidChangeConfigurationCapability() { - DynamicRegistration = true - } - ) - .WithServices(z => z.AddSingleton()), - options => options - .WithConfigurationSection("mysection") - .OnDidChangeConfiguration(action, new object()) - ); - - var clientManager = client.Services.GetRequiredService(); - clientManager.ContainsHandler(typeof(IConfigurationHandler)).Should().BeTrue(); - var config = client.Services.GetRequiredService(); - config.Update("mysection", new Dictionary() { ["data"] = "value" }); - - await TestHelper.DelayUntil( - () => { - try - { - action.Received(1).Invoke(Arg.Is(z => Equals(z.Settings, JValue.CreateNull()))); - return true; - } - catch (ReceivedCallsException e) - { - return false; - } - }, CancellationToken - ); - } } } From 22adbc9b6275ec94534de090b2d7997a4475a8cd Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Wed, 14 Oct 2020 23:57:22 -0400 Subject: [PATCH 05/11] Removed extra dependencies, we can add these at another time --- .../ConfigureByConfigurationPathExtension.cs | 4 +- src/Protocol/Protocol.csproj | 3 ++ test/Lsp.Tests/FluentAssertionsExtensions.cs | 52 +++++++++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/Protocol/ConfigureByConfigurationPathExtension.cs b/src/Protocol/ConfigureByConfigurationPathExtension.cs index fef7723ec..336c9e6dd 100644 --- a/src/Protocol/ConfigureByConfigurationPathExtension.cs +++ b/src/Protocol/ConfigureByConfigurationPathExtension.cs @@ -1,3 +1,4 @@ +#if false using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -57,7 +58,6 @@ public static IServiceCollection Configure(this IServiceCollection ser ); } -#if false /// /// Registers a injected configuration service which TOptions will bind against. /// @@ -171,6 +171,6 @@ public static OptionsBuilder Configure(this OptionsBuilder(builder.Services, name, configureBinder); return builder; } -#endif } } +#endif diff --git a/src/Protocol/Protocol.csproj b/src/Protocol/Protocol.csproj index 4ea430d99..0114e9d23 100644 --- a/src/Protocol/Protocol.csproj +++ b/src/Protocol/Protocol.csproj @@ -9,8 +9,11 @@ + diff --git a/test/Lsp.Tests/FluentAssertionsExtensions.cs b/test/Lsp.Tests/FluentAssertionsExtensions.cs index befb2cd27..4c7e47f0d 100644 --- a/test/Lsp.Tests/FluentAssertionsExtensions.cs +++ b/test/Lsp.Tests/FluentAssertionsExtensions.cs @@ -1,6 +1,10 @@ +using System; using FluentAssertions.Equivalency; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; using NSubstitute; using OmniSharp.Extensions.LanguageServer.Protocol; using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; @@ -9,6 +13,54 @@ namespace Lsp.Tests { public static class FluentAssertionsExtensions { + + /// + /// Registers a injected configuration service which TOptions will bind against. + /// + /// The type of options being configured. + /// The to add the services to. + /// The so that additional calls can be chained. + public static IServiceCollection Configure(this IServiceCollection services) + where TOptions : class + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + return Configure(services, null); + } + + /// + /// Registers a injected configuration service which TOptions will bind against. + /// + /// The type of options being configured. + /// The to add the services to. + /// The name of the options instance. + /// The so that additional calls can be chained. + public static IServiceCollection Configure(this IServiceCollection services, string? sectionName) + where TOptions : class + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + services.AddOptions(); + services.AddSingleton>( + _ => new ConfigurationChangeTokenSource( + Options.DefaultName, + sectionName == null ? _.GetRequiredService() : _.GetRequiredService().GetSection(sectionName) + ) + ); + return services.AddSingleton>( + _ => new NamedConfigureFromConfigurationOptions( + Options.DefaultName, + sectionName == null ? _.GetRequiredService() : _.GetRequiredService().GetSection(sectionName) + ) + ); + } + public static EquivalencyAssertionOptions ConfigureForSupports(this EquivalencyAssertionOptions options, ILogger? logger = null) => options .WithTracing(new TraceWriter(logger ?? NullLogger.Instance)) From 9191acb1df75ebdb0a921e1caca375a084f83bde Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Wed, 14 Oct 2020 23:59:18 -0400 Subject: [PATCH 06/11] cleanup --- src/Client/LanguageClientServiceCollectionExtensions.cs | 6 +++--- src/JsonRpc/JsonRpcHandlerCollectionExtensions.cs | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Client/LanguageClientServiceCollectionExtensions.cs b/src/Client/LanguageClientServiceCollectionExtensions.cs index dcd571da7..3f4564135 100644 --- a/src/Client/LanguageClientServiceCollectionExtensions.cs +++ b/src/Client/LanguageClientServiceCollectionExtensions.cs @@ -29,7 +29,7 @@ internal static IContainer AddLanguageClientInternals(this IContainer container, nonPublicServiceTypes: true, ifAlreadyRegistered: IfAlreadyRegistered.Keep ); - if (!EqualityComparer.Default.Equals( options.OnUnhandledException, default)) + if (!EqualityComparer.Default.Equals(options.OnUnhandledException, default)) { container.RegisterInstance(options.OnUnhandledException); } @@ -80,7 +80,7 @@ internal static IContainer AddLanguageClientInternals(this IContainer container, if (providedConfiguration != null) { - builder.CustomAddConfiguration((providedConfiguration.ImplementationInstance as IConfiguration)!); + builder.CustomAddConfiguration(( providedConfiguration.ImplementationInstance as IConfiguration )!); } //var didChangeConfigurationProvider = _.GetRequiredService(); @@ -93,7 +93,7 @@ internal static IContainer AddLanguageClientInternals(this IContainer container, container.RegisterMany(Reuse.Singleton); container.RegisterMany( - serviceTypeCondition: type => options.WorkspaceFolders || type != typeof(IJsonRpcHandler), reuse: Reuse.Singleton + serviceTypeCondition: type => options.WorkspaceFolders || type != typeof(IJsonRpcHandler), reuse: Reuse.Singleton ); container.RegisterMany( serviceTypeCondition: type => options.DynamicRegistration || type != typeof(IJsonRpcHandler), reuse: Reuse.Singleton diff --git a/src/JsonRpc/JsonRpcHandlerCollectionExtensions.cs b/src/JsonRpc/JsonRpcHandlerCollectionExtensions.cs index ed10d3c1f..65695712e 100644 --- a/src/JsonRpc/JsonRpcHandlerCollectionExtensions.cs +++ b/src/JsonRpc/JsonRpcHandlerCollectionExtensions.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using DryIoc; From 14aa2751ee596bee5be35ae822a945f85ec8d773 Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Thu, 15 Oct 2020 08:11:13 -0400 Subject: [PATCH 07/11] removed unneeded code --- src/Testing/LanguageProtocolTestBase.cs | 2 +- src/Testing/LanguageServerTestBase.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Testing/LanguageProtocolTestBase.cs b/src/Testing/LanguageProtocolTestBase.cs index 0368692cd..77f5ae0a2 100644 --- a/src/Testing/LanguageProtocolTestBase.cs +++ b/src/Testing/LanguageProtocolTestBase.cs @@ -98,7 +98,7 @@ Action serverOptionsAction var (client, server) = Create( options => { clientOptionsAction.Invoke(options); - options.WithCapability(new DidChangeConfigurationCapability() { DynamicRegistration = true}); + options.WithCapability(new DidChangeConfigurationCapability()); options.Services.AddSingleton(); }, serverOptionsAction ); diff --git a/src/Testing/LanguageServerTestBase.cs b/src/Testing/LanguageServerTestBase.cs index ebe8af95a..61868007c 100644 --- a/src/Testing/LanguageServerTestBase.cs +++ b/src/Testing/LanguageServerTestBase.cs @@ -63,7 +63,7 @@ protected virtual async Task InitializeClient(Action { clientOptionsAction?.Invoke(options); - options.WithCapability(new DidChangeConfigurationCapability() { DynamicRegistration = true}); + options.WithCapability(new DidChangeConfigurationCapability()); options.Services.AddSingleton(); } ); From ca564c0185c796e886fd31d745a2595b35ba143a Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Thu, 15 Oct 2020 08:24:51 -0400 Subject: [PATCH 08/11] Added additional assertions --- test/Lsp.Tests/Integration/DisableDefaultsTests.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/Lsp.Tests/Integration/DisableDefaultsTests.cs b/test/Lsp.Tests/Integration/DisableDefaultsTests.cs index 5266c7f13..d40fdef46 100644 --- a/test/Lsp.Tests/Integration/DisableDefaultsTests.cs +++ b/test/Lsp.Tests/Integration/DisableDefaultsTests.cs @@ -44,14 +44,16 @@ public async Task Should_Disable_Registration_Manager() var unregistrationAction = Substitute.For>(); var (client, _) = await Initialize( options => options - .OnRegisterCapability(registrationAction) + .OnRegisterCapability(registrationAction) .OnUnregisterCapability(unregistrationAction), options => { } ); var clientManager = client.Services.GetRequiredService(); clientManager.Descriptors.Should().Contain(f => f.Handler is DelegatingHandlers.Request); + clientManager.Descriptors.Should().ContainSingle(f => f.Method == ClientNames.RegisterCapability); clientManager.Descriptors.Should().Contain(f => f.Handler is DelegatingHandlers.Request); + clientManager.Descriptors.Should().ContainSingle(f => f.Method == ClientNames.UnregisterCapability); } [Fact] @@ -66,9 +68,11 @@ public async Task Should_Disable_Workspace_Folder_Manager() var clientManager = client.Services.GetRequiredService(); clientManager.Descriptors.Should().Contain(f => f.Handler is DelegatingHandlers.Request?>); + clientManager.Descriptors.Should().ContainSingle(f => f.Method == WorkspaceNames.WorkspaceFolders); var serverManager = server.Services.GetRequiredService(); serverManager.Descriptors.Should().Contain(f => f.Handler is LanguageProtocolDelegatingHandlers.Notification); + serverManager.Descriptors.Should().ContainSingle(f => f.Method == WorkspaceNames.DidChangeWorkspaceFolders); } [Fact] @@ -76,9 +80,9 @@ public async Task Should_Allow_Custom_Workspace_Folder_Manager_Delegate() { var action = Substitute.For>(); var (client, server) = await Initialize( - options => {}, + options => { }, options => options - .OnDidChangeWorkspaceFolders(action, new object()) + .OnDidChangeWorkspaceFolders(action, new object()) ); var config = client.Services.GetRequiredService(); @@ -112,6 +116,7 @@ public async Task Should_Disable_Configuration() var serverManager = server.Services.GetRequiredService(); serverManager.Descriptors.Should().Contain(f => f.Handler is LanguageProtocolDelegatingHandlers.Notification); + serverManager.Descriptors.Should().ContainSingle(f => f.Method == WorkspaceNames.DidChangeConfiguration); } [Fact] From 751692233afe7ac57ce3af4bfaf0f8c047de589c Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Tue, 27 Oct 2020 18:43:52 -0400 Subject: [PATCH 09/11] the default Debug Adapter Protocol serializer was not set for camelcase --- src/Dap.Protocol/DapSerializer.cs | 13 +++++------- .../DebugAdapterRpcOptionsBase.cs | 4 +++- ...ractResolver.cs => DapContractResolver.cs} | 4 ++-- src/Dap.Protocol/Serialization/Serializer.cs | 15 ++++++++++---- ...ractResolver.cs => LspContractResolver.cs} | 4 ++-- src/Protocol/Serialization/Serializer.cs | 8 ++++---- test/Dap.Tests/DapOutputHandlerTests.cs | 3 ++- .../DebugAdapterSpecifictionRecieverTests.cs | 20 +++++++++++++++++-- 8 files changed, 47 insertions(+), 24 deletions(-) rename src/Dap.Protocol/Serialization/{ContractResolver.cs => DapContractResolver.cs} (80%) rename src/Protocol/Serialization/{ContractResolver.cs => LspContractResolver.cs} (98%) diff --git a/src/Dap.Protocol/DapSerializer.cs b/src/Dap.Protocol/DapSerializer.cs index 6619812b0..9e6430618 100644 --- a/src/Dap.Protocol/DapSerializer.cs +++ b/src/Dap.Protocol/DapSerializer.cs @@ -1,18 +1,15 @@ +using System; using System.Collections.Generic; using Newtonsoft.Json; using OmniSharp.Extensions.DebugAdapter.Protocol.DebugAdapterConverters; +using OmniSharp.Extensions.DebugAdapter.Protocol.Serialization; using OmniSharp.Extensions.JsonRpc.Serialization; namespace OmniSharp.Extensions.DebugAdapter.Protocol { - public class DapSerializer : SerializerBase +#pragma warning disable 618 + public class DapSerializer : DapProtocolSerializer +#pragma warning restore 618 { - protected override void AddOrReplaceConverters(ICollection converters) - { - ReplaceConverter(converters, new DapClientNotificationConverter(this)); - ReplaceConverter(converters, new DapClientResponseConverter(this)); - ReplaceConverter(converters, new DapClientRequestConverter()); - ReplaceConverter(converters, new DapRpcErrorConverter(this)); - } } } diff --git a/src/Dap.Protocol/DebugAdapterRpcOptionsBase.cs b/src/Dap.Protocol/DebugAdapterRpcOptionsBase.cs index b73837ba1..93a5b5d97 100644 --- a/src/Dap.Protocol/DebugAdapterRpcOptionsBase.cs +++ b/src/Dap.Protocol/DebugAdapterRpcOptionsBase.cs @@ -2,7 +2,9 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using OmniSharp.Extensions.DebugAdapter.Protocol.Serialization; using OmniSharp.Extensions.JsonRpc; +using ISerializer = OmniSharp.Extensions.JsonRpc.ISerializer; namespace OmniSharp.Extensions.DebugAdapter.Protocol { @@ -15,7 +17,7 @@ protected DebugAdapterRpcOptionsBase() RequestProcessIdentifier = new ParallelRequestProcessIdentifier(); } - public ISerializer Serializer { get; set; } = new DapSerializer(); + public ISerializer Serializer { get; set; } = new DapProtocolSerializer(); internal bool AddDefaultLoggingProvider { get; set; } internal Action LoggingBuilderAction { get; set; } = _ => { }; internal Action ConfigurationBuilderAction { get; set; } = _ => { }; diff --git a/src/Dap.Protocol/Serialization/ContractResolver.cs b/src/Dap.Protocol/Serialization/DapContractResolver.cs similarity index 80% rename from src/Dap.Protocol/Serialization/ContractResolver.cs rename to src/Dap.Protocol/Serialization/DapContractResolver.cs index edf3fb36a..116f07d56 100644 --- a/src/Dap.Protocol/Serialization/ContractResolver.cs +++ b/src/Dap.Protocol/Serialization/DapContractResolver.cs @@ -5,9 +5,9 @@ namespace OmniSharp.Extensions.DebugAdapter.Protocol.Serialization { - internal class ContractResolver : DefaultContractResolver + internal class DapContractResolver : DefaultContractResolver { - public ContractResolver() => NamingStrategy = new CamelCaseNamingStrategy(true, false, true); + public DapContractResolver() => NamingStrategy = new CamelCaseNamingStrategy(true, false, true); protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { diff --git a/src/Dap.Protocol/Serialization/Serializer.cs b/src/Dap.Protocol/Serialization/Serializer.cs index 8bed33a7c..3fba6c996 100644 --- a/src/Dap.Protocol/Serialization/Serializer.cs +++ b/src/Dap.Protocol/Serialization/Serializer.cs @@ -1,27 +1,34 @@ +using System; using System.Collections.Generic; using Newtonsoft.Json; +using OmniSharp.Extensions.DebugAdapter.Protocol.DebugAdapterConverters; +using OmniSharp.Extensions.JsonRpc.Serialization; namespace OmniSharp.Extensions.DebugAdapter.Protocol.Serialization { - public class DapProtocolSerializer : DapSerializer, ISerializer + [Obsolete("DapProtocolSerializer will be removed in a future version, DapSerializer will be a drop in replacement")] + public class DapProtocolSerializer : SerializerBase, ISerializer { protected override void AddOrReplaceConverters(ICollection converters) { ReplaceConverter(converters, new NumberStringConverter()); - base.AddOrReplaceConverters(converters); + ReplaceConverter(converters, new DapClientNotificationConverter(this)); + ReplaceConverter(converters, new DapClientResponseConverter(this)); + ReplaceConverter(converters, new DapClientRequestConverter()); + ReplaceConverter(converters, new DapRpcErrorConverter(this)); } protected override JsonSerializer CreateSerializer() { var serializer = base.CreateSerializer(); - serializer.ContractResolver = new ContractResolver(); + serializer.ContractResolver = new DapContractResolver(); return serializer; } protected override JsonSerializerSettings CreateSerializerSettings() { var settings = base.CreateSerializerSettings(); - settings.ContractResolver = new ContractResolver(); + settings.ContractResolver = new DapContractResolver(); return settings; } } diff --git a/src/Protocol/Serialization/ContractResolver.cs b/src/Protocol/Serialization/LspContractResolver.cs similarity index 98% rename from src/Protocol/Serialization/ContractResolver.cs rename to src/Protocol/Serialization/LspContractResolver.cs index f049ec7d2..8c8a9b19a 100644 --- a/src/Protocol/Serialization/ContractResolver.cs +++ b/src/Protocol/Serialization/LspContractResolver.cs @@ -11,7 +11,7 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Serialization { - internal class ContractResolver : DefaultContractResolver + internal class LspContractResolver : DefaultContractResolver { private readonly CompletionItemKind[] _completionItemKinds; private readonly CompletionItemTag[] _completionItemTags; @@ -22,7 +22,7 @@ internal class ContractResolver : DefaultContractResolver private readonly DiagnosticTag[] _diagnosticTags; private readonly CodeActionKind[] _codeActionKinds; - public ContractResolver( + public LspContractResolver( CompletionItemKind[] completionItemKinds, CompletionItemTag[] completionItemTags, SymbolKind[] documentSymbolKinds, diff --git a/src/Protocol/Serialization/Serializer.cs b/src/Protocol/Serialization/Serializer.cs index aed2e556c..a6d7b5b6f 100644 --- a/src/Protocol/Serialization/Serializer.cs +++ b/src/Protocol/Serialization/Serializer.cs @@ -54,7 +54,7 @@ public Serializer() : this(ClientVersion.Lsp3) protected override JsonSerializer CreateSerializer() { var serializer = base.CreateSerializer(); - serializer.ContractResolver = new ContractResolver( + serializer.ContractResolver = new LspContractResolver( DefaultCompletionItemKinds, DefaultCompletionItemTags, DefaultSymbolKinds, @@ -70,7 +70,7 @@ protected override JsonSerializer CreateSerializer() protected override JsonSerializerSettings CreateSerializerSettings() { var settings = base.CreateSerializerSettings(); - settings.ContractResolver = new ContractResolver( + settings.ContractResolver = new LspContractResolver( DefaultCompletionItemKinds, DefaultCompletionItemTags, DefaultSymbolKinds, @@ -200,7 +200,7 @@ public void SetClientCapabilities(ClientVersion clientVersion, ClientCapabilitie AddOrReplaceConverters(Settings.Converters); - Settings.ContractResolver = new ContractResolver( + Settings.ContractResolver = new LspContractResolver( completionItemKinds, completionItemTags, documentSymbolKinds, @@ -212,7 +212,7 @@ public void SetClientCapabilities(ClientVersion clientVersion, ClientCapabilitie ); AddOrReplaceConverters(JsonSerializer.Converters); - JsonSerializer.ContractResolver = new ContractResolver( + JsonSerializer.ContractResolver = new LspContractResolver( completionItemKinds, completionItemTags, documentSymbolKinds, diff --git a/test/Dap.Tests/DapOutputHandlerTests.cs b/test/Dap.Tests/DapOutputHandlerTests.cs index 65338dc62..01e6ba391 100644 --- a/test/Dap.Tests/DapOutputHandlerTests.cs +++ b/test/Dap.Tests/DapOutputHandlerTests.cs @@ -7,6 +7,7 @@ using Newtonsoft.Json.Linq; using NSubstitute; using OmniSharp.Extensions.DebugAdapter.Protocol; +using OmniSharp.Extensions.DebugAdapter.Protocol.Serialization; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.JsonRpc.Client; using OmniSharp.Extensions.JsonRpc.Server; @@ -21,7 +22,7 @@ private static OutputHandler NewHandler(PipeWriter writer) { var rec = Substitute.For(); rec.ShouldFilterOutput(Arg.Any()).Returns(true); - return new OutputHandler(writer, new DapSerializer(), rec, Scheduler.Immediate, NullLogger.Instance); + return new OutputHandler(writer, new DapProtocolSerializer(), rec, Scheduler.Immediate, NullLogger.Instance); } [Fact] diff --git a/test/Dap.Tests/DebugAdapterSpecifictionRecieverTests.cs b/test/Dap.Tests/DebugAdapterSpecifictionRecieverTests.cs index 0e23d4af2..a8dd0fedc 100644 --- a/test/Dap.Tests/DebugAdapterSpecifictionRecieverTests.cs +++ b/test/Dap.Tests/DebugAdapterSpecifictionRecieverTests.cs @@ -2,6 +2,9 @@ using FluentAssertions; using Newtonsoft.Json.Linq; using OmniSharp.Extensions.DebugAdapter.Protocol; +using OmniSharp.Extensions.DebugAdapter.Protocol.Requests; +using OmniSharp.Extensions.DebugAdapter.Protocol.Serialization; +using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.JsonRpc.Server; using OmniSharp.Extensions.JsonRpc.Server.Messages; using Xunit; @@ -15,8 +18,8 @@ public class DebugAdapterSpecificationReceiverTests public void ShouldRespond_AsExpected(string json, Renor[] request) { var receiver = new DapReceiver(); - var inSerializer = new DapSerializer(); - var outSerializer = new DapSerializer(); + var inSerializer = new DapProtocolSerializer(); + var outSerializer = new DapProtocolSerializer(); var (requests, _) = receiver.GetRequests(JToken.Parse(json)); var result = requests.ToArray(); request.Length.Should().Be(result.Length); @@ -31,6 +34,19 @@ public void ShouldRespond_AsExpected(string json, Renor[] request) } } + [Fact] + public void Should_Camel_Case_As_Expected() + { + var serializer = new DapProtocolSerializer(); + var response = serializer.SerializeObject( + new InitializeResponse() { + SupportsCancelRequest = true + } + ); + + response.Should().Be("{\"supportsConfigurationDoneRequest\":false,\"supportsFunctionBreakpoints\":false,\"supportsConditionalBreakpoints\":false,\"supportsHitConditionalBreakpoints\":false,\"supportsEvaluateForHovers\":false,\"supportsStepBack\":false,\"supportsSetVariable\":false,\"supportsRestartFrame\":false,\"supportsGotoTargetsRequest\":false,\"supportsStepInTargetsRequest\":false,\"supportsCompletionsRequest\":false,\"supportsModulesRequest\":false,\"supportsRestartRequest\":false,\"supportsExceptionOptions\":false,\"supportsValueFormattingOptions\":false,\"supportsExceptionInfoRequest\":false,\"supportTerminateDebuggee\":false,\"supportsDelayedStackTraceLoading\":false,\"supportsLoadedSourcesRequest\":false,\"supportsLogPoints\":false,\"supportsTerminateThreadsRequest\":false,\"supportsSetExpression\":false,\"supportsTerminateRequest\":false,\"supportsDataBreakpoints\":false,\"supportsReadMemoryRequest\":false,\"supportsDisassembleRequest\":false,\"supportsCancelRequest\":true,\"supportsBreakpointLocationsRequest\":false,\"supportsClipboardContext\":false,\"supportsSteppingGranularity\":false,\"supportsInstructionBreakpoints\":false}"); + } + private class SpecificationMessages : TheoryData { public SpecificationMessages() From 410678765aa6a23bf9f48297bdaf1c8ab851355a Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Tue, 27 Oct 2020 19:38:53 -0400 Subject: [PATCH 10/11] fixed failing tests --- .../Dap.Tests/Integration/CustomRequestsTests.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/Dap.Tests/Integration/CustomRequestsTests.cs b/test/Dap.Tests/Integration/CustomRequestsTests.cs index dac194d35..ad8770534 100644 --- a/test/Dap.Tests/Integration/CustomRequestsTests.cs +++ b/test/Dap.Tests/Integration/CustomRequestsTests.cs @@ -58,9 +58,9 @@ await client.RequestAttach( var call = fake.ReceivedCalls().Single(); var args = call.GetArguments(); var request = args[0].Should().BeOfType().Which; - request.ExtensionData.Should().ContainKey("ComputerName").And.Subject["ComputerName"].Should().Be("computer"); - request.ExtensionData.Should().ContainKey("RunspaceId").And.Subject["RunspaceId"].Should().Be("1234"); - request.ExtensionData.Should().ContainKey("ProcessId").And.Subject["ProcessId"].Should().Be("4321"); + request.ExtensionData.Should().ContainKey("computerName").And.Subject["computerName"].Should().Be("computer"); + request.ExtensionData.Should().ContainKey("runspaceId").And.Subject["runspaceId"].Should().Be("1234"); + request.ExtensionData.Should().ContainKey("processId").And.Subject["processId"].Should().Be("4321"); } [Fact] @@ -120,7 +120,7 @@ await client.RequestLaunch( var call = fake.ReceivedCalls().Single(); var args = call.GetArguments(); var request = args[0].Should().BeOfType().Which; - request.ExtensionData.Should().ContainKey("Script").And.Subject["Script"].Should().Be("build.ps1"); + request.ExtensionData.Should().ContainKey("script").And.Subject["script"].Should().Be("build.ps1"); } [Fact] @@ -182,9 +182,9 @@ await client.RequestAttach( var call = fake.ReceivedCalls().Single(); var args = call.GetArguments(); var request = args[0].Should().BeOfType().Which; - request.ExtensionData.Should().ContainKey("ComputerName").And.Subject["ComputerName"].Should().Be("computer"); - request.ExtensionData.Should().ContainKey("RunspaceId").And.Subject["RunspaceId"].Should().Be("1234"); - request.ExtensionData.Should().ContainKey("ProcessId").And.Subject["ProcessId"].Should().Be("4321"); + request.ExtensionData.Should().ContainKey("computerName").And.Subject["computerName"].Should().Be("computer"); + request.ExtensionData.Should().ContainKey("runspaceId").And.Subject["runspaceId"].Should().Be("1234"); + request.ExtensionData.Should().ContainKey("processId").And.Subject["processId"].Should().Be("4321"); } [Fact] @@ -244,7 +244,7 @@ await client.RequestLaunch( var call = fake.ReceivedCalls().Single(); var args = call.GetArguments(); var request = args[0].Should().BeOfType().Which; - request.ExtensionData.Should().ContainKey("Script").And.Subject["Script"].Should().Be("build.ps1"); + request.ExtensionData.Should().ContainKey("script").And.Subject["script"].Should().Be("build.ps1"); } [Fact] From b983447e82f32b17ca08e321516e5c5a6fbacd75 Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Tue, 27 Oct 2020 20:09:12 -0400 Subject: [PATCH 11/11] intermittent test --- test/Lsp.Tests/Integration/TypedCodeActionTests.cs | 2 +- test/Lsp.Tests/Integration/TypedCodeLensTests.cs | 2 +- test/Lsp.Tests/Integration/TypedCompletionTests.cs | 2 +- test/Lsp.Tests/Integration/TypedDocumentLinkTests.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/Lsp.Tests/Integration/TypedCodeActionTests.cs b/test/Lsp.Tests/Integration/TypedCodeActionTests.cs index 2ccb077c9..ce657ac18 100644 --- a/test/Lsp.Tests/Integration/TypedCodeActionTests.cs +++ b/test/Lsp.Tests/Integration/TypedCodeActionTests.cs @@ -297,7 +297,7 @@ public async Task Should_Resolve_With_Data_CancellationToken() item.CodeAction!.Command!.Name.Should().Be("resolved"); } - [Fact] + [FactWithSkipOn(SkipOnPlatform.Mac)] public async Task Should_Resolve_With_Partial_Data_CancellationToken() { var (client, _) = await Initialize( diff --git a/test/Lsp.Tests/Integration/TypedCodeLensTests.cs b/test/Lsp.Tests/Integration/TypedCodeLensTests.cs index 8f52e5a2a..06a4b20ea 100644 --- a/test/Lsp.Tests/Integration/TypedCodeLensTests.cs +++ b/test/Lsp.Tests/Integration/TypedCodeLensTests.cs @@ -284,7 +284,7 @@ public async Task Should_Resolve_With_Data_CancellationToken() item.Command!.Name.Should().Be("resolved"); } - [Fact] + [FactWithSkipOn(SkipOnPlatform.Mac)] public async Task Should_Resolve_With_Partial_Data_CancellationToken() { var (client, _) = await Initialize( diff --git a/test/Lsp.Tests/Integration/TypedCompletionTests.cs b/test/Lsp.Tests/Integration/TypedCompletionTests.cs index df40d3ed6..aa2de3946 100644 --- a/test/Lsp.Tests/Integration/TypedCompletionTests.cs +++ b/test/Lsp.Tests/Integration/TypedCompletionTests.cs @@ -284,7 +284,7 @@ public async Task Should_Resolve_With_Data_CancellationToken() item.Detail.Should().Be("resolved"); } - [Fact] + [FactWithSkipOn(SkipOnPlatform.Mac)] public async Task Should_Resolve_With_Partial_Data_CancellationToken() { var (client, _) = await Initialize( diff --git a/test/Lsp.Tests/Integration/TypedDocumentLinkTests.cs b/test/Lsp.Tests/Integration/TypedDocumentLinkTests.cs index ef8c00b88..ad6b5b36b 100644 --- a/test/Lsp.Tests/Integration/TypedDocumentLinkTests.cs +++ b/test/Lsp.Tests/Integration/TypedDocumentLinkTests.cs @@ -261,7 +261,7 @@ public async Task Should_Resolve_With_Data_CancellationToken() item.Tooltip.Should().Be("resolved"); } - [Fact] + [FactWithSkipOn(SkipOnPlatform.Mac)] public async Task Should_Resolve_With_Partial_Data_CancellationToken() { var (client, _) = await Initialize(