diff --git a/Directory.Build.targets b/Directory.Build.targets index 465ac93aa..294d6a132 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -24,7 +24,7 @@ - + @@ -37,14 +37,15 @@ - + + - + diff --git a/src/JsonRpc/AggregateResponse.cs b/src/JsonRpc/AggregateResponse.cs index a96c5af74..62a628920 100644 --- a/src/JsonRpc/AggregateResponse.cs +++ b/src/JsonRpc/AggregateResponse.cs @@ -8,6 +8,8 @@ public class AggregateResponse: IEnumerable where T : IEnumerable { public IEnumerable Items { get; } + public AggregateResponse(IEnumerable items) => Items = items.OfType().ToArray(); + public AggregateResponse(IEnumerable items) => Items = items.ToArray(); public AggregateResponse(IEnumerable items) => Items = items.OfType().ToArray(); diff --git a/src/JsonRpc/DefaultJsonRpcServerFacade.cs b/src/JsonRpc/DefaultJsonRpcServerFacade.cs index e9ef8a4d9..d2b695be5 100644 --- a/src/JsonRpc/DefaultJsonRpcServerFacade.cs +++ b/src/JsonRpc/DefaultJsonRpcServerFacade.cs @@ -20,19 +20,17 @@ public DefaultJsonRpcServerFacade(IResponseRouter requestRouter, IServiceProvide _handlersManager = handlersManager; } - public void SendNotification(string method) => _responseRouter.SendNotification(method); + public Task SendNotification(string method) => _responseRouter.SendNotification(method); - public void SendNotification(string method, T @params) => _responseRouter.SendNotification(method, @params); + public Task SendNotification(string method, T @params) => _responseRouter.SendNotification(method, @params); - public void SendNotification(IRequest request) => _responseRouter.SendNotification(request); + public Task SendNotification(IRequest request) => _responseRouter.SendNotification(request); public IResponseRouterReturns SendRequest(string method, T @params) => _responseRouter.SendRequest(method, @params); public IResponseRouterReturns SendRequest(string method) => _responseRouter.SendRequest(method); public Task SendRequest(IRequest request, CancellationToken cancellationToken) => _responseRouter.SendRequest(request, cancellationToken); - - bool IResponseRouter.TryGetRequest(long id, [NotNullWhen(true)] out string method, [NotNullWhen(true)] out TaskCompletionSource pendingTask) => _responseRouter.TryGetRequest(id, out method, out pendingTask); object IServiceProvider.GetService(Type serviceType) => _serviceProvider.GetService(serviceType); public IDisposable Register(Action registryAction) diff --git a/src/JsonRpc/HandlerCollection.cs b/src/JsonRpc/HandlerCollection.cs index e8f1d5ac6..ee6fc7b88 100644 --- a/src/JsonRpc/HandlerCollection.cs +++ b/src/JsonRpc/HandlerCollection.cs @@ -5,21 +5,39 @@ using System.Linq; using System.Reactive.Disposables; using System.Reflection; +using System.Threading; +using System.Threading.Tasks; using DryIoc; +using FastExpressionCompiler.LightExpression; using MediatR; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using OmniSharp.Extensions.JsonRpc.Client; +using StreamJsonRpc; namespace OmniSharp.Extensions.JsonRpc { internal class HandlerCollection : IHandlersManager, IEnumerable { + private readonly StreamJsonRpc.JsonRpc _rpc; private readonly IResolverContext _resolverContext; private readonly IHandlerTypeDescriptorProvider _handlerTypeDescriptorProvider; private ImmutableArray _descriptors = ImmutableArray.Empty; + private ILookup? _lookup; + private ImmutableHashSet _rpcMethods = ImmutableHashSet.Empty; + + internal ILookup GetLookup() => _lookup ??= _descriptors.ToLookup(z => z.Method); public IEnumerable Descriptors => _descriptors; - public HandlerCollection(IResolverContext resolverContext, IHandlerTypeDescriptorProvider handlerTypeDescriptorProvider) + public HandlerCollection( + StreamJsonRpc.JsonRpc rpc, + IResolverContext resolverContext, + IHandlerTypeDescriptorProvider handlerTypeDescriptorProvider + ) { + _rpc = rpc; _resolverContext = resolverContext; _handlerTypeDescriptorProvider = handlerTypeDescriptorProvider; } @@ -33,6 +51,7 @@ private void Remove(IJsonRpcHandler handler) } ImmutableInterlocked.InterlockedExchange(ref _descriptors, descriptors.ToImmutableArray()); + Interlocked.Exchange(ref _lookup, null); } public IDisposable Add(params IJsonRpcHandler[] handlers) @@ -77,16 +96,179 @@ public IDisposable Add(string method, IJsonRpcHandler handler, JsonRpcHandlerOpt var descriptor = new HandlerInstance(method, handler, @interface, @params!, response!, requestProcessType, () => Remove(handler)); ImmutableInterlocked.InterlockedExchange(ref _descriptors, _descriptors.Add(descriptor)); + Interlocked.Exchange(ref _lookup, null); + + if (!_rpcMethods.Contains(method)) + { + AddRpcMethod(method, descriptor); + } + return descriptor; } + private void AddRpcMethod(string method, IHandlerDescriptor descriptor) + { + Type rpcMethodType = null; + if (typeof(IEnumerable).IsAssignableFrom(descriptor.Response) + && typeof(string) != descriptor.Response + && !typeof(JToken).IsAssignableFrom(descriptor.Response)) + { + rpcMethodType = typeof(RpcRequestAggregateMethod<,>).MakeGenericType(descriptor.Params, descriptor.Response); + } + else if (descriptor.HasReturnType) + { + rpcMethodType = typeof(RpcRequestMethod<,>).MakeGenericType(descriptor.Params, descriptor.Response); + } + else + { + rpcMethodType = typeof(RpcNotificationMethod<>).MakeGenericType(descriptor.Params); + } + + var instance = (IRpcMethod) ActivatorUtilities.CreateInstance( + _resolverContext, + rpcMethodType, + new CustomDescriptorContainer(method, this) + ); + if (instance == null) return; + _rpc.AddLocalRpcMethod(method, instance.Method, instance); + } + + class CustomDescriptorContainer : IRequestDescriptor + { + private readonly string _method; + private readonly HandlerCollection _collection; + + public CustomDescriptorContainer(string method, HandlerCollection collection) + { + _method = method; + _collection = collection; + } + + public IEnumerator GetEnumerator() => _collection.GetLookup()[_method].GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public IHandlerDescriptor Default => _collection.GetLookup()[_method].FirstOrDefault(); + } + + interface IRpcMethod + { + MethodInfo Method { get; } + } + + class RpcNotificationMethod : IRpcMethod + where TRequest : IRequest + { + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly IRequestDescriptor _descriptors; + + public RpcNotificationMethod( + IServiceScopeFactory serviceScopeFactory, + IRequestDescriptor descriptors + ) + { + _serviceScopeFactory = serviceScopeFactory; + _descriptors = descriptors; + } + + public async Task Request(TRequest request, CancellationToken cancellationToken) + { + using var scope = _serviceScopeFactory.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + context.Descriptor = _descriptors.Default; + var mediator = scope.ServiceProvider.GetRequiredService(); + + cancellationToken.ThrowIfCancellationRequested(); + + await mediator.Send(request, cancellationToken).ConfigureAwait(false); + } + + public MethodInfo Method { get; } = typeof(RpcNotificationMethod).GetMethod(nameof(Request), BindingFlags.Instance | BindingFlags.Public); + } + + class RpcRequestMethod : IRpcMethod + where TRequest : IRequest + { + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly IRequestDescriptor _descriptors; + + public RpcRequestMethod( + IServiceScopeFactory serviceScopeFactory, + IRequestDescriptor descriptors + ) + { + _serviceScopeFactory = serviceScopeFactory; + _descriptors = descriptors; + } + + public async Task Request(TRequest request, CancellationToken cancellationToken) + { + using var scope = _serviceScopeFactory.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + context.Descriptor = _descriptors.Default; + var mediator = scope.ServiceProvider.GetRequiredService(); + + cancellationToken.ThrowIfCancellationRequested(); + + return await mediator.Send(request, cancellationToken).ConfigureAwait(false); + } + + public MethodInfo Method { get; } = typeof(RpcRequestMethod).GetMethod(nameof(Request), BindingFlags.Instance | BindingFlags.Public); + } + + class RpcRequestAggregateMethod : IRpcMethod + where TRequest : IRequest + where TResponse : IEnumerable + { + private readonly IServiceScopeFactory _serviceScopeFactory; + private readonly IRequestDescriptor _descriptors; + + public RpcRequestAggregateMethod( + IServiceScopeFactory serviceScopeFactory, + IRequestDescriptor descriptors + ) + { + _serviceScopeFactory = serviceScopeFactory; + _descriptors = descriptors; + } + + public async Task> Request(TRequest request, CancellationToken cancellationToken) + { + // TODO: Do we want to support more handlers as "aggregate"? +// typeof(IEnumerable).IsAssignableFrom(_descriptors.Default!.Response) +// && typeof(string) != _descriptors.Default!.Response +// && !typeof(JToken).IsAssignableFrom(_descriptors.Default!.Response) + var responses = await Task.WhenAll(_descriptors.Select(descriptor => InnerRoute(_serviceScopeFactory, descriptor, request, cancellationToken))) + .ConfigureAwait(false); + + return new AggregateResponse(responses); + } + + static async Task InnerRoute( + IServiceScopeFactory serviceScopeFactory, IHandlerDescriptor descriptor, TRequest @params, CancellationToken token + ) + { + using var scope = serviceScopeFactory.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + context.Descriptor = descriptor; + var mediator = scope.ServiceProvider.GetRequiredService(); + + token.ThrowIfCancellationRequested(); + + return await mediator.Send(@params, token).ConfigureAwait(false); + } + + public MethodInfo Method { get; } = typeof(RpcRequestMethod).GetMethod(nameof(Request), BindingFlags.Instance | BindingFlags.Public); + } + public IDisposable Add(JsonRpcHandlerFactory factory, JsonRpcHandlerOptions? options) => Add(factory(_resolverContext), options); public IDisposable Add(string method, JsonRpcHandlerFactory factory, JsonRpcHandlerOptions? options) => Add(method, factory(_resolverContext), options); - public IDisposable Add(Type handlerType, JsonRpcHandlerOptions? options) => Add((_resolverContext.Resolve(handlerType) as IJsonRpcHandler)!, options); + public IDisposable Add(Type handlerType, JsonRpcHandlerOptions? options) => Add(( _resolverContext.Resolve(handlerType) as IJsonRpcHandler )!, options); - public IDisposable Add(string method, Type handlerType, JsonRpcHandlerOptions? options) => Add(method, (_resolverContext.Resolve(handlerType) as IJsonRpcHandler)!, options); + public IDisposable Add(string method, Type handlerType, JsonRpcHandlerOptions? options) => + Add(method, ( _resolverContext.Resolve(handlerType) as IJsonRpcHandler )!, options); public IDisposable AddLink(string fromMethod, string toMethod) { @@ -96,7 +278,8 @@ public IDisposable AddLink(string fromMethod, string toMethod) if (_descriptors.Any(z => z.Method == toMethod)) { throw new ArgumentException( - $"Could not find descriptor for '{fromMethod}', but I did find one for '{toMethod}'. Did you mean to link '{toMethod}' to '{fromMethod}' instead?", fromMethod + $"Could not find descriptor for '{fromMethod}', but I did find one for '{toMethod}'. Did you mean to link '{toMethod}' to '{fromMethod}' instead?", + fromMethod ); } @@ -108,6 +291,7 @@ public IDisposable AddLink(string fromMethod, string toMethod) // ReSharper disable once ReturnValueOfPureMethodIsNotUsed var descriptor = new LinkedHandler(toMethod, source, () => _descriptors.RemoveAll(z => z.Method == toMethod)); ImmutableInterlocked.InterlockedExchange(ref _descriptors, _descriptors.Add(descriptor)); + Interlocked.Exchange(ref _lookup, null); return descriptor; } diff --git a/src/JsonRpc/IResponseRouter.cs b/src/JsonRpc/IResponseRouter.cs index 6d3337ee2..544026614 100644 --- a/src/JsonRpc/IResponseRouter.cs +++ b/src/JsonRpc/IResponseRouter.cs @@ -8,12 +8,11 @@ namespace OmniSharp.Extensions.JsonRpc { public interface IResponseRouter { - void SendNotification(string method); - void SendNotification(string method, T @params); - void SendNotification(IRequest request); + Task SendNotification(string method); + Task SendNotification(string method, T @params); + Task SendNotification(IRequest request); IResponseRouterReturns SendRequest(string method, T @params); IResponseRouterReturns SendRequest(string method); Task SendRequest(IRequest request, CancellationToken cancellationToken); - bool TryGetRequest(long id, [NotNullWhen(true)] out string method, [NotNullWhen(true)] out TaskCompletionSource pendingTask); } } diff --git a/src/JsonRpc/InputHandler.cs b/src/JsonRpc/InputHandler.cs index 6d267c101..258b9fb3c 100644 --- a/src/JsonRpc/InputHandler.cs +++ b/src/JsonRpc/InputHandler.cs @@ -377,31 +377,55 @@ private void HandleRequest(in ReadOnlySequence request) continue; } - if (!_responseRouter.TryGetRequest(id, out var method, out var tcs)) - { - // _logger.LogDebug("Request {ResponseId} was not found in the response router, unable to complete", response.Id); - continue; - } - - _inputQueue.OnNext( - Observable.Create( - observer => { - if (response is ServerResponse serverResponse) - { - // _logger.LogDebug("Setting successful Response for {ResponseId}", response.Id); - tcs.TrySetResult(serverResponse.Result); - } - else if (response is ServerError serverError) - { - // _logger.LogDebug("Setting error for {ResponseId}", response.Id); - tcs.TrySetException(DefaultErrorParser(method, serverError, _getException)); - } - - observer.OnCompleted(); - return Disposable.Empty; - } - ) - ); +// if (!_responseRouter.TryGetRequest(id, out var method, out var tcs)) +// { +// // _logger.LogDebug("Request {ResponseId} was not found in the response router, unable to complete", response.Id); +// continue; +// } +// +// _inputQueue.OnNext( +// Observable.Create( +// observer => { +// if (response is ServerResponse serverResponse) +// { +// // _logger.LogDebug("Setting suRouter.TryGetRequest(id, out var method, out var tcs)) + // { + // // _logger.LogDebug("Request {ResponseId} was not found in the response router, unable to complete", response.Id); + // continue; + // } + // + // _inputQueue.OnNext( + // Observable.Create( + // observer => { + // if (response is ServerResponse serverResponse) + // { + // // _logger.LogDebug("Setting successful Response for {ResponseId}", response.Id); + // tcs.TrySetResult(serverResponse.Result); + // } + // else if (response is ServerError serverError) + // { + // // _logger.LogDebug("Setting error for {ResponseId}", response.Id); + // tcs.TrySetException(DefaultErrorParser(method, serverError, _getException)); + // } + // + // observer.OnCompleted(); + // return Disposable.Empty; + // } + // ) + // );ccessful Response for {ResponseId}", response.Id); +// tcs.TrySetResult(serverResponse.Result); +// } +// else if (response is ServerError serverError) +// { +// // _logger.LogDebug("Setting error for {ResponseId}", response.Id); +// tcs.TrySetException(DefaultErrorParser(method, serverError, _getException)); +// } +// +// observer.OnCompleted(); +// return Disposable.Empty; +// } +// ) +// ); } return; diff --git a/src/JsonRpc/JsonRpc.csproj b/src/JsonRpc/JsonRpc.csproj index f62bc92b6..da7f76f3b 100644 --- a/src/JsonRpc/JsonRpc.csproj +++ b/src/JsonRpc/JsonRpc.csproj @@ -17,6 +17,7 @@ + diff --git a/src/JsonRpc/JsonRpcCommonMethodsBase.cs b/src/JsonRpc/JsonRpcCommonMethodsBase.cs index 81898519e..f6050f362 100644 --- a/src/JsonRpc/JsonRpcCommonMethodsBase.cs +++ b/src/JsonRpc/JsonRpcCommonMethodsBase.cs @@ -12,7 +12,7 @@ public abstract class JsonRpcCommonMethodsBase : IJsonRpcHandlerRegistry w #region OnRequest / OnNotification public T OnJsonRequest(string method, Func> handler, JsonRpcHandlerOptions? options = null) => - AddHandler(method, _ => new DelegatingJsonRequestHandler(handler), options); + AddHandler(method, handler, options); public T OnJsonRequest(string method, Func> handler, JsonRpcHandlerOptions? options = null) => OnJsonRequest(method, HandlerAdapter.Adapt(handler), options); @@ -23,9 +23,8 @@ public T OnRequest(string method, Func(string method, Func> handler, JsonRpcHandlerOptions? options = null) => OnRequest(method, (_, _) => handler(), options); - public T OnRequest(string method, Func> handler, JsonRpcHandlerOptions? options = null) => AddHandler( - method, _ => new DelegatingRequestHandler(_.GetRequiredService(), handler), options - ); + public T OnRequest(string method, Func> handler, JsonRpcHandlerOptions? options = null) => + AddHandler(method, handler, options); public T OnRequest(string method, Func> handler, JsonRpcHandlerOptions? options = null) => OnRequest(method, (_, cancellationToken) => handler(cancellationToken), options); @@ -33,9 +32,8 @@ public T OnRequest(string method, Func(string method, Func handler, JsonRpcHandlerOptions? options = null) => OnRequest(method, HandlerAdapter.Adapt(handler), options); - public T OnRequest(string method, Func handler, JsonRpcHandlerOptions? options = null) => AddHandler( - method, _ => new DelegatingRequestHandler(_.GetRequiredService(), handler), options - ); + public T OnRequest(string method, Func handler, JsonRpcHandlerOptions? options = null) => + AddHandler(method, handler, options); public T OnRequest(string method, Func handler, JsonRpcHandlerOptions? options = null) => OnRequest(method, (_, cancellationToken) => handler(cancellationToken), options); @@ -47,7 +45,7 @@ public T OnJsonNotification(string method, Action handler, JsonRpcHandle OnJsonNotification(method, HandlerAdapter.Adapt(handler), options); public T OnJsonNotification(string method, Func handler, JsonRpcHandlerOptions? options = null) => - AddHandler(method, _ => new DelegatingJsonNotificationHandler(handler), options); + AddHandler(method, handler, options); public T OnJsonNotification(string method, Func handler, JsonRpcHandlerOptions? options = null) => OnJsonNotification(method, HandlerAdapter.Adapt(handler), options); @@ -58,9 +56,8 @@ public T OnNotification(string method, Action(string method, Action handler, JsonRpcHandlerOptions? options = null) => OnNotification(method, HandlerAdapter.Adapt(handler), options); - public T OnNotification(string method, Func handler, JsonRpcHandlerOptions? options = null) => AddHandler( - method, _ => new DelegatingNotificationHandler(_.GetRequiredService(), handler), options - ); + public T OnNotification(string method, Func handler, JsonRpcHandlerOptions? options = null) => + AddHandler(method, handler, options); public T OnNotification(string method, Func handler, JsonRpcHandlerOptions? options = null) => OnNotification(method, HandlerAdapter.Adapt(handler), options); @@ -73,9 +70,8 @@ public T OnNotification(string method, Action handler, JsonRpcHandlerOptions? op }, options ); - public T OnNotification(string method, Func handler, JsonRpcHandlerOptions? options = null) => AddHandler( - method, _ => new DelegatingNotificationHandler(_.GetRequiredService(), (_, token) => handler(token)), options - ); + public T OnNotification(string method, Func handler, JsonRpcHandlerOptions? options = null) => + AddHandler(method, handler, options); public T OnNotification(string method, Func handler, JsonRpcHandlerOptions? options = null) => OnNotification(method, _ => handler(), options); @@ -83,6 +79,11 @@ public T OnNotification(string method, Func handler, Js #region AddHandler + public T AddHandler(string method, Func> handler, JsonRpcHandlerOptions? options = null)=> + Services + public T AddHandler(string method, Func handler, JsonRpcHandlerOptions? options = null); + public T AddHandler(string method, Func handler, JsonRpcHandlerOptions? options = null); + public T AddHandler(string method, Func> handler, JsonRpcHandlerOptions? options = null); public abstract T AddHandler(string method, IJsonRpcHandler handler, JsonRpcHandlerOptions? options = null); public abstract T AddHandler(string method, JsonRpcHandlerFactory handlerFunc, JsonRpcHandlerOptions? options = null); public abstract T AddHandler(IJsonRpcHandler handler, JsonRpcHandlerOptions? options = null); diff --git a/src/JsonRpc/JsonRpcServer.cs b/src/JsonRpc/JsonRpcServer.cs index e6b749620..ed02e8a6d 100644 --- a/src/JsonRpc/JsonRpcServer.cs +++ b/src/JsonRpc/JsonRpcServer.cs @@ -9,7 +9,7 @@ namespace OmniSharp.Extensions.JsonRpc { public class JsonRpcServer : JsonRpcServerBase, IJsonRpcServer { - private readonly Connection _connection; + private readonly StreamJsonRpc.JsonRpc _rpc; private readonly IServiceProvider _serviceProvider; private readonly InstanceHasStarted _instanceHasStarted; private readonly CompositeDisposable _disposable; @@ -56,18 +56,17 @@ public static async Task From(JsonRpcServerOptions options, IServ internal JsonRpcServer( IOptions options, - Connection connection, + StreamJsonRpc.JsonRpc rpc, IHandlersManager handlerCollection, IResponseRouter responseRouter, IServiceProvider serviceProvider, InstanceHasStarted instanceHasStarted ) : base(handlerCollection, responseRouter) { - _connection = connection; + _rpc = rpc; _serviceProvider = serviceProvider; _instanceHasStarted = instanceHasStarted; _disposable = options.Value.CompositeDisposable; - _disposable.Add(_connection); if (serviceProvider is IDisposable disposable) { _disposable.Add(disposable); @@ -77,7 +76,7 @@ InstanceHasStarted instanceHasStarted public virtual async Task Initialize(CancellationToken cancellationToken) { await Task.Yield(); - _connection.Open(); + _rpc.StartListening(); _instanceHasStarted.Started = true; } diff --git a/src/JsonRpc/JsonRpcServerBase.cs b/src/JsonRpc/JsonRpcServerBase.cs index 123627289..1f9380c96 100644 --- a/src/JsonRpc/JsonRpcServerBase.cs +++ b/src/JsonRpc/JsonRpcServerBase.cs @@ -17,19 +17,16 @@ protected JsonRpcServerBase(IHandlersManager handlersManager, IResponseRouter re public IResponseRouter ResponseRouter { get; } public IHandlersManager HandlersManager { get; } - public void SendNotification(string method) => ResponseRouter.SendNotification(method); + public Task SendNotification(string method) => ResponseRouter.SendNotification(method); - public void SendNotification(string method, T @params) => ResponseRouter.SendNotification(method, @params); + public Task SendNotification(string method, T @params) => ResponseRouter.SendNotification(method, @params); - public void SendNotification(IRequest @params) => ResponseRouter.SendNotification(@params); + public Task SendNotification(IRequest @params) => ResponseRouter.SendNotification(@params); public Task SendRequest(IRequest @params, CancellationToken cancellationToken) => ResponseRouter.SendRequest(@params, cancellationToken); public IResponseRouterReturns SendRequest(string method, T @params) => ResponseRouter.SendRequest(method, @params); public IResponseRouterReturns SendRequest(string method) => ResponseRouter.SendRequest(method); - - bool IResponseRouter.TryGetRequest(long id, [NotNullWhen(true)] out string method, [NotNullWhen(true)] out TaskCompletionSource pendingTask) => - ResponseRouter.TryGetRequest(id, out method, out pendingTask); } } diff --git a/src/JsonRpc/JsonRpcServerOptionsBase.cs b/src/JsonRpc/JsonRpcServerOptionsBase.cs index 22847aec2..0b262c187 100644 --- a/src/JsonRpc/JsonRpcServerOptionsBase.cs +++ b/src/JsonRpc/JsonRpcServerOptionsBase.cs @@ -11,6 +11,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Nerdbank.Streams; +using Newtonsoft.Json; namespace OmniSharp.Extensions.JsonRpc { @@ -30,14 +31,14 @@ public ILoggerFactory LoggerFactory } public IEnumerable Assemblies { get; set; } = Enumerable.Empty(); + public IEnumerable JsonConverters { get; set; }= Enumerable.Empty(); + public IEnumerable> JsonRpcConfiguration { get; set; }= Enumerable.Empty>(); /// /// Experimental support for using assembly attributes /// public bool UseAssemblyAttributeScanning { get; set; } = false; public IRequestProcessIdentifier? RequestProcessIdentifier { get; set; } public int? Concurrency { get; set; } - public IScheduler InputScheduler { get; set; } = TaskPoolScheduler.Default; - public IScheduler OutputScheduler { get; set; } = Scheduler.Immediate; public IScheduler DefaultScheduler { get; set; } = TaskPoolScheduler.Default; public CreateResponseExceptionHandler? CreateResponseException { get; set; } public OnUnhandledExceptionHandler? OnUnhandledException { get; set; } @@ -60,6 +61,24 @@ public T WithAssemblies(params Assembly[] assemblies) return (T) (object) this; } + public T WithJsonConverter(IEnumerable? jsonConverters) + { + JsonConverters = JsonConverters.Union(jsonConverters ?? Enumerable.Empty()).ToArray(); + return (T) (object) this; + } + + public T WithJsonConverter(params JsonConverter[] jsonConverters) + { + JsonConverters = JsonConverters.Union(jsonConverters).ToArray(); + return (T) (object) this; + } + + public T WithJsonRpc(Action action) + { + JsonRpcConfiguration = JsonRpcConfiguration.Append(action).ToArray(); + return (T) (object) this; + } + public T WithInput(Stream input) { Input = input.UsePipeReader(); diff --git a/src/JsonRpc/JsonRpcServerOptionsExtensions.cs b/src/JsonRpc/JsonRpcServerOptionsExtensions.cs index 6c341cc7a..93908e0da 100644 --- a/src/JsonRpc/JsonRpcServerOptionsExtensions.cs +++ b/src/JsonRpc/JsonRpcServerOptionsExtensions.cs @@ -24,43 +24,7 @@ public static JsonRpcServerOptions WithAssemblyAttributeScanning(this JsonRpcSer /// public static JsonRpcServerOptions WithScheduler(this JsonRpcServerOptions options, IScheduler inputScheduler) { - options.InputScheduler = options.OutputScheduler = options.DefaultScheduler = inputScheduler; - return options; - } - - /// - /// Sets the scheduler used during reading input - /// - /// - /// - /// - public static JsonRpcServerOptions WithInputScheduler(this JsonRpcServerOptions options, IScheduler inputScheduler) - { - options.InputScheduler = inputScheduler; - return options; - } - - /// - /// Sets the default scheduler to be used when scheduling other tasks - /// - /// - /// - /// - public static JsonRpcServerOptions WithDefaultScheduler(this JsonRpcServerOptions options, IScheduler defaultScheduler) - { - options.DefaultScheduler = defaultScheduler; - return options; - } - - /// - /// Sets the scheduler use during writing output - /// - /// - /// - /// - public static JsonRpcServerOptions WithOutputScheduler(this JsonRpcServerOptions options, IScheduler outputScheduler) - { - options.OutputScheduler = outputScheduler; + options.DefaultScheduler = inputScheduler; return options; } } diff --git a/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs b/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs index 1859fd078..4e079dbaa 100644 --- a/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs +++ b/src/JsonRpc/JsonRpcServerServiceCollectionExtensions.cs @@ -9,6 +9,9 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; +using Nerdbank.Streams; +using Newtonsoft.Json; +using StreamJsonRpc; namespace OmniSharp.Extensions.JsonRpc { @@ -33,32 +36,38 @@ internal static IContainer AddJsonRpcServerCore(this IContainer container, Js container.RegisterInstance(options.MaximumRequestTimeout, serviceKey: nameof(options.MaximumRequestTimeout)); container.RegisterInstance(options.SupportsContentModified, serviceKey: nameof(options.SupportsContentModified)); container.RegisterInstance(options.Concurrency ?? -1, serviceKey: nameof(options.Concurrency)); - container.RegisterInstance(options.InputScheduler, serviceKey: nameof(options.InputScheduler)); - container.RegisterInstance(options.OutputScheduler, serviceKey: nameof(options.OutputScheduler)); if (options.CreateResponseException != null) { container.RegisterInstance(options.CreateResponseException); } - container.RegisterMany( - nonPublicServiceTypes: true, - made: Parameters.Of - .Type(serviceKey: nameof(options.Output)) - .Type(serviceKey: nameof(options.OutputScheduler)), + container.RegisterInstance(options.DefaultScheduler); + + container.Register( + made: Made.Of(() => new StreamJsonRpc.JsonRpc(Arg.Of())), reuse: Reuse.Singleton ); - container.Register( - made: new Made.TypedMade().Parameters - .Type(serviceKey: nameof(options.Input)) - .Type(serviceKey: nameof(options.MaximumRequestTimeout)) - .Type(serviceKey: nameof(options.SupportsContentModified)) - .Name("concurrency", serviceKey: nameof(options.Concurrency)) - .Type(serviceKey: nameof(options.InputScheduler)) - , + container.RegisterInitializer( + (rpc, context) => { + rpc.AllowModificationWhileListening = true; + rpc.CancelLocallyInvokedMethodsWhenConnectionIsClosed = true; + } + ); + + container.RegisterMany( + made: Parameters.Of + .Type(serviceKey: nameof(options.Input)) + .Type(serviceKey: nameof(options.Output)), reuse: Reuse.Singleton ); - container.RegisterInstance(options.DefaultScheduler); + container.RegisterMany(reuse: Reuse.Singleton); + container.RegisterInitializer( + (formatter, context) => { + foreach (var converter in context.ResolveMany().Union(options.JsonConverters)) + formatter.JsonSerializer.Converters.Add(converter); + } + ); container.RegisterMany( serviceTypeCondition: type => type.IsInterface, @@ -103,9 +112,10 @@ public RequestHandler(IRequestContext requestContext) { _requestContext = requestContext; } + public Task Handle(T request, CancellationToken cancellationToken) { - return ((IRequestHandler) _requestContext.Descriptor.Handler).Handle(request, cancellationToken); + return ( (IRequestHandler) _requestContext.Descriptor.Handler ).Handle(request, cancellationToken); } } @@ -119,6 +129,7 @@ public RequestHandlerDecorator(IRequestHandler? handler = null, IRequestC _handler = handler; _requestContext = requestContext; } + public Task Handle(T request, CancellationToken cancellationToken) { if (_requestContext == null) @@ -126,13 +137,12 @@ public Task Handle(T request, CancellationToken cancellationToken) if (_handler == null) { throw new NotImplementedException($"No request handler was registered for type {typeof(IRequestHandler).FullName}"); - } return _handler.Handle(request, cancellationToken); } - return ((IRequestHandler) _requestContext.Descriptor.Handler).Handle(request, cancellationToken); + return ( (IRequestHandler) _requestContext.Descriptor.Handler ).Handle(request, cancellationToken); } } @@ -169,6 +179,7 @@ internal static IContainer AddJsonRpcServerInternals(this IContainer container, { container.RegisterInstance(options.Receiver); } + container.RegisterMany(Reuse.Singleton, nonPublicServiceTypes: true); container.RegisterInstance(options.RequestProcessIdentifier); diff --git a/src/JsonRpc/NoopResponseRouter.cs b/src/JsonRpc/NoopResponseRouter.cs deleted file mode 100644 index 52a78648f..000000000 --- a/src/JsonRpc/NoopResponseRouter.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Threading; -using System.Threading.Tasks; -using MediatR; -using Newtonsoft.Json.Linq; - -namespace OmniSharp.Extensions.JsonRpc -{ - /// - /// Used top ensure noop behaviors don't throw if they consume a router - /// - internal class NoopResponseRouter : IResponseRouter - { - private NoopResponseRouter() - { - } - - public static NoopResponseRouter Instance { get; } = new NoopResponseRouter(); - - public void SendNotification(string method) - { - } - - public void SendNotification(string method, T @params) - { - } - - public void SendNotification(IRequest request) - { - } - - public IResponseRouterReturns SendRequest(string method, T @params) => new Impl(); - - public IResponseRouterReturns SendRequest(string method) => new Impl(); - - public Task SendRequest(IRequest request, CancellationToken cancellationToken) => Task.FromResult(default!); - - bool IResponseRouter.TryGetRequest(long id, [NotNullWhen(true)] out string method, [NotNullWhen(true)] out TaskCompletionSource pendingTask) - { - method = default!; - pendingTask = default!; - return false; - } - - private class Impl : IResponseRouterReturns - { - public Task Returning(CancellationToken cancellationToken) => Task.FromResult(default!); - - public Task ReturningVoid(CancellationToken cancellationToken) => Task.CompletedTask; - } - } -} diff --git a/src/JsonRpc/RequestRouterBase.cs b/src/JsonRpc/RequestRouterBase.cs index dcfafde60..71de0e74b 100644 --- a/src/JsonRpc/RequestRouterBase.cs +++ b/src/JsonRpc/RequestRouterBase.cs @@ -122,7 +122,6 @@ public virtual async Task RouteRequest(IRequestDescriptor OutputHandler; - internal readonly ISerializer Serializer; private readonly IHandlerTypeDescriptorProvider _handlerTypeDescriptorProvider; + private readonly StreamJsonRpc.JsonRpc _rpc; - internal readonly ConcurrentDictionary pendingTask)> Requests = - new ConcurrentDictionary pendingTask)>(); - - public ResponseRouter(Lazy outputHandler, ISerializer serializer, IHandlerTypeDescriptorProvider handlerTypeDescriptorProvider) + public ResponseRouter(IHandlerTypeDescriptorProvider handlerTypeDescriptorProvider, StreamJsonRpc.JsonRpc rpc) { - OutputHandler = outputHandler; - Serializer = serializer; _handlerTypeDescriptorProvider = handlerTypeDescriptorProvider; + _rpc = rpc; } - public void SendNotification(string method) => - OutputHandler.Value.Send( - new OutgoingNotification { - Method = method - } - ); + public Task SendNotification(string method) => _rpc.NotifyAsync(method); - public void SendNotification(string method, T @params) => - OutputHandler.Value.Send( - new OutgoingNotification { - Method = method, - Params = @params - } - ); + public Task SendNotification(string method, T @params) => _rpc.NotifyWithParameterObjectAsync(method, @params); - public void SendNotification(IRequest @params) => SendNotification(GetMethodName(@params.GetType()), @params); + public Task SendNotification(IRequest @params) => SendNotification(GetMethodName(@params.GetType()), @params); public Task SendRequest(IRequest @params, CancellationToken cancellationToken) => SendRequest(GetMethodName(@params.GetType()), @params).Returning(cancellationToken); - public IResponseRouterReturns SendRequest(string method) => new ResponseRouterReturnsImpl(this, method, new object()); + public IResponseRouterReturns SendRequest(string method) => new ResponseRouterReturnsImpl(_rpc, method, null); - public IResponseRouterReturns SendRequest(string method, T @params) => new ResponseRouterReturnsImpl(this, method, @params); - - public bool TryGetRequest(long id, [NotNullWhen(true)] out string method, [NotNullWhen(true)] out TaskCompletionSource pendingTask) - { - var result = Requests.TryGetValue(id, out var source); - method = source.method; - pendingTask = source.pendingTask; - return result; - } + public IResponseRouterReturns SendRequest(string method, T @params) => new ResponseRouterReturnsImpl(_rpc, method, @params); private string GetMethodName(Type type) => _handlerTypeDescriptorProvider.GetMethodName(type) ?? throw new NotSupportedException($"Unable to infer method name for type {type.FullName}"); private class ResponseRouterReturnsImpl : IResponseRouterReturns { - private readonly ResponseRouter _router; + private readonly StreamJsonRpc.JsonRpc _rpc; private readonly string _method; private readonly object? _params; - public ResponseRouterReturnsImpl(ResponseRouter router, string method, object? @params) + public ResponseRouterReturnsImpl(StreamJsonRpc.JsonRpc rpc, string method, object? @params) { - _router = router; + _rpc = rpc; _method = method; _params = @params; } - public async Task Returning(CancellationToken cancellationToken) - { - var nextId = _router.Serializer.GetNextId(); - var tcs = new TaskCompletionSource(); - _router.Requests.TryAdd(nextId, ( _method, tcs )); - - cancellationToken.ThrowIfCancellationRequested(); - - try - { - _router.OutputHandler.Value.Send( - new OutgoingRequest { - Method = _method, - Params = _params, - Id = nextId - } - ); - cancellationToken.Register( - () => { - if (tcs.Task.IsCompleted) return; - _router.CancelRequest(new CancelParams { Id = nextId }); - } - ); - - var result = await tcs.Task.ConfigureAwait(false); - if (typeof(TResponse) == typeof(Unit)) - { - return (TResponse) (object) Unit.Value; - } - - return result.ToObject(_router.Serializer.JsonSerializer); - } - finally - { - _router.Requests.TryRemove(nextId, out _); - } - } + public Task Returning(CancellationToken cancellationToken) => + // TODO: Track request for content modified cancellation? + _rpc.InvokeWithParameterObjectAsync(_method, _params, cancellationToken); - public async Task ReturningVoid(CancellationToken cancellationToken) => await Returning(cancellationToken).ConfigureAwait(false); + public async Task ReturningVoid(CancellationToken cancellationToken) => + // TODO: Track request for content modified cancellation? + _rpc.InvokeWithParameterObjectAsync(_method, _params, cancellationToken); } } } diff --git a/test/JsonRpc.Tests/HandlerResolverTests.cs b/test/JsonRpc.Tests/HandlerResolverTests.cs index fcdf543f9..ebe5b1735 100644 --- a/test/JsonRpc.Tests/HandlerResolverTests.cs +++ b/test/JsonRpc.Tests/HandlerResolverTests.cs @@ -6,6 +6,7 @@ using MediatR; using NSubstitute; using OmniSharp.Extensions.JsonRpc; +using StreamJsonRpc; using Xunit; namespace JsonRpc.Tests @@ -45,7 +46,7 @@ public interface IJsonRpcNotificationDataHandler : IJsonRpcNotificationHandler(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })) { + var handler = new HandlerCollection(new StreamJsonRpc.JsonRpc(Substitute.For()), Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })) { (IJsonRpcHandler) Substitute.For(new[] { requestHandler }, new object[0]) }; handler.Should().Contain(x => x.Method == key); @@ -57,7 +58,7 @@ public void Should_Contain_AllDefinedMethods(Type requestHandler, string key) [InlineData(typeof(IJsonRpcNotificationDataHandler), "notificationdata", null)] public void Should_Have_CorrectParams(Type requestHandler, string key, Type expected) { - var handler = new HandlerCollection(Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })) { + var handler = new HandlerCollection(new StreamJsonRpc.JsonRpc(Substitute.For()),Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })) { (IJsonRpcHandler) Substitute.For(new[] { requestHandler }, new object[0]) }; handler.First(x => x.Method == key).Params.Should().IsSameOrEqualTo(expected); diff --git a/test/JsonRpc.Tests/MediatorTestsNotificationHandler.cs b/test/JsonRpc.Tests/MediatorTestsNotificationHandler.cs index f492e853f..21476674d 100644 --- a/test/JsonRpc.Tests/MediatorTestsNotificationHandler.cs +++ b/test/JsonRpc.Tests/MediatorTestsNotificationHandler.cs @@ -5,6 +5,7 @@ using NSubstitute; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.JsonRpc.Server; +using StreamJsonRpc; using Xunit; using Xunit.Abstractions; using Arg = NSubstitute.Arg; @@ -30,7 +31,7 @@ public async Task ExecutesHandler() { var exitHandler = Substitute.For(); - var collection = new HandlerCollection(Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })) { exitHandler }; + var collection = new HandlerCollection(new StreamJsonRpc.JsonRpc(Substitute.For()),Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })) { exitHandler }; AutoSubstitute.Provide(collection); var router = AutoSubstitute.Resolve(); diff --git a/test/JsonRpc.Tests/MediatorTestsNotificationHandlerOfT.cs b/test/JsonRpc.Tests/MediatorTestsNotificationHandlerOfT.cs index bffd51124..e97a3ac45 100644 --- a/test/JsonRpc.Tests/MediatorTestsNotificationHandlerOfT.cs +++ b/test/JsonRpc.Tests/MediatorTestsNotificationHandlerOfT.cs @@ -8,6 +8,7 @@ using NSubstitute; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.JsonRpc.Server; +using StreamJsonRpc; using Xunit; using Xunit.Abstractions; using Arg = NSubstitute.Arg; @@ -33,7 +34,7 @@ public async Task ExecutesHandler() { var cancelRequestHandler = Substitute.For(); - var collection = new HandlerCollection(Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })) { cancelRequestHandler }; + var collection = new HandlerCollection(new StreamJsonRpc.JsonRpc(Substitute.For()),Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })) { cancelRequestHandler }; AutoSubstitute.Provide(collection); var router = AutoSubstitute.Resolve(); diff --git a/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequest.cs b/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequest.cs index 00ec86b50..a6fd27774 100644 --- a/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequest.cs +++ b/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequest.cs @@ -7,6 +7,7 @@ using Newtonsoft.Json.Linq; using NSubstitute; using OmniSharp.Extensions.JsonRpc; +using StreamJsonRpc; using Xunit; using Xunit.Abstractions; using Arg = NSubstitute.Arg; @@ -33,7 +34,7 @@ public async Task ExecutesHandler() { var executeCommandHandler = Substitute.For(); - var collection = new HandlerCollection(Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })) { executeCommandHandler }; + var collection = new HandlerCollection(new StreamJsonRpc.JsonRpc(Substitute.For()),Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })) { executeCommandHandler }; AutoSubstitute.Provide(collection); var router = AutoSubstitute.Resolve(); diff --git a/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequestTResponse.cs b/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequestTResponse.cs index 1e4988362..3caab5747 100644 --- a/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequestTResponse.cs +++ b/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequestTResponse.cs @@ -8,6 +8,7 @@ using Newtonsoft.Json.Linq; using NSubstitute; using OmniSharp.Extensions.JsonRpc; +using StreamJsonRpc; using Xunit; using Xunit.Abstractions; using Arg = NSubstitute.Arg; @@ -43,7 +44,7 @@ public async Task ExecutesHandler() { var codeActionHandler = Substitute.For(); - var collection = new HandlerCollection(Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })) { codeActionHandler }; + var collection = new HandlerCollection(new StreamJsonRpc.JsonRpc(Substitute.For()),Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })) { codeActionHandler }; AutoSubstitute.Provide(collection); var router = AutoSubstitute.Resolve(); diff --git a/test/JsonRpc.Tests/RequestRouterTests.cs b/test/JsonRpc.Tests/RequestRouterTests.cs index 8f429802b..4c68c337b 100644 --- a/test/JsonRpc.Tests/RequestRouterTests.cs +++ b/test/JsonRpc.Tests/RequestRouterTests.cs @@ -6,6 +6,7 @@ using NSubstitute; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.JsonRpc.Server; +using StreamJsonRpc; using Xunit; using Xunit.Abstractions; using Arg = NSubstitute.Arg; @@ -20,7 +21,7 @@ public class RequestRouterTests : AutoTestBase [Fact] public async Task ShouldRoute_CustomRequestResponse() { - var collection = new HandlerCollection(Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })); + var collection = new HandlerCollection(new StreamJsonRpc.JsonRpc(Substitute.For()),Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })); var registry = new TestLanguageServerRegistry(); AutoSubstitute.Provide(collection); AutoSubstitute.Provide>(collection); @@ -40,7 +41,7 @@ public async Task ShouldRoute_CustomRequestResponse() [Fact] public async Task ShouldRoute_CustomRequest() { - var collection = new HandlerCollection(Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })); + var collection = new HandlerCollection(new StreamJsonRpc.JsonRpc(Substitute.For()),Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })); var registry = new TestLanguageServerRegistry(); AutoSubstitute.Provide(collection); AutoSubstitute.Provide>(collection); @@ -60,7 +61,7 @@ public async Task ShouldRoute_CustomRequest() [Fact] public async Task ShouldRoute_CustomNotification() { - var collection = new HandlerCollection(Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })); + var collection = new HandlerCollection(new StreamJsonRpc.JsonRpc(Substitute.For()),Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })); var registry = new TestLanguageServerRegistry(); AutoSubstitute.Provide(collection); AutoSubstitute.Provide>(collection); @@ -79,7 +80,7 @@ public async Task ShouldRoute_CustomNotification() [Fact] public async Task ShouldRoute_CustomEmptyNotification() { - var collection = new HandlerCollection(Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })); + var collection = new HandlerCollection(new StreamJsonRpc.JsonRpc(Substitute.For()),Substitute.For(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })); var registry = new TestLanguageServerRegistry(); AutoSubstitute.Provide(collection); AutoSubstitute.Provide>(collection); diff --git a/test/JsonRpc.Tests/ResponseRouterTests.cs b/test/JsonRpc.Tests/ResponseRouterTests.cs deleted file mode 100644 index d2ec2b1a2..000000000 --- a/test/JsonRpc.Tests/ResponseRouterTests.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using FluentAssertions; -using MediatR; -using Newtonsoft.Json.Linq; -using NSubstitute; -using OmniSharp.Extensions.JsonRpc; -using OmniSharp.Extensions.JsonRpc.Client; -using OmniSharp.Extensions.JsonRpc.Serialization; -using Xunit; - -namespace JsonRpc.Tests -{ - public class ResponseRouterTests - { - [Fact] - public async Task WorksWithResultType() - { - var outputHandler = Substitute.For(); - var router = new ResponseRouter(new Lazy(() => outputHandler), new JsonRpcSerializer(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })); - - outputHandler - .When(x => x.Send(Arg.Is(z => z.GetType() == typeof(OutgoingRequest)))) - .Do( - call => { - router.TryGetRequest((long) call.Arg().Id!, out _, out var tcs); - tcs.TrySetResult(new JObject()); - } - ); - - var response = await router.SendRequest(new ItemParams(), CancellationToken.None); - - var request = outputHandler.ReceivedCalls().Single().GetArguments()[0] as OutgoingRequest; - request!.Method.Should().Be("abcd"); - - response.Should().NotBeNull(); - response.Should().BeOfType(); - } - - [Fact] - public async Task WorksWithUnitType() - { - var outputHandler = Substitute.For(); - var router = new ResponseRouter(new Lazy(() => outputHandler), new JsonRpcSerializer(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })); - - outputHandler - .When(x => x.Send(Arg.Is(z => z.GetType() == typeof(OutgoingRequest)))) - .Do( - call => { - router.TryGetRequest((long) call.Arg().Id!, out _, out var tcs); - tcs.SetResult(new JObject()); - } - ); - - await router.SendRequest(new UnitParams(), CancellationToken.None); - - var request = outputHandler.ReceivedCalls().Single().GetArguments()[0] as OutgoingRequest; - request!.Method.Should().Be("unit"); - } - - [Fact] - public async Task WorksWithNotification() - { - var outputHandler = Substitute.For(); - var router = new ResponseRouter(new Lazy(() => outputHandler), new JsonRpcSerializer(), new AssemblyScanningHandlerTypeDescriptorProvider(new [] { typeof(AssemblyScanningHandlerTypeDescriptorProvider).Assembly, typeof(HandlerResolverTests).Assembly })); - - router.SendNotification(new NotificationParams()); - - var request = outputHandler.ReceivedCalls().Single().GetArguments()[0] as OutgoingNotification; - request!.Method.Should().Be("notification"); - } - - [Method("abcd")] - public class ItemParams : IRequest - { - } - - public class ItemResult - { - } - - [Method("unit")] - public class UnitParams : IRequest - { - } - - [Method("notification")] - public class NotificationParams : IRequest - { - } - } -} diff --git a/test/Lsp.Integration.Tests/Lsp.Integration.Tests.csproj b/test/Lsp.Integration.Tests/Lsp.Integration.Tests.csproj index 46970d637..cd7a9c437 100644 --- a/test/Lsp.Integration.Tests/Lsp.Integration.Tests.csproj +++ b/test/Lsp.Integration.Tests/Lsp.Integration.Tests.csproj @@ -5,20 +5,22 @@ AnyCPU - - + + - - - - - - + + + + + + - - - - + + + + + +