diff --git a/src/JsonRpc/AggregateResponse.cs b/src/JsonRpc/AggregateResponse.cs new file mode 100644 index 000000000..fd6941810 --- /dev/null +++ b/src/JsonRpc/AggregateResponse.cs @@ -0,0 +1,21 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace OmniSharp.Extensions.JsonRpc +{ + public class AggregateResponse where T : IEnumerable + { + public IEnumerable Items { get; } + + public AggregateResponse(IEnumerable items) + { + Items = items.ToArray(); + } + + public AggregateResponse(IEnumerable items) + { + Items = items.OfType().ToArray(); + } + } +} diff --git a/src/JsonRpc/Client/OutgoingResponse.cs b/src/JsonRpc/Client/OutgoingResponse.cs index 230039856..15acb95a4 100644 --- a/src/JsonRpc/Client/OutgoingResponse.cs +++ b/src/JsonRpc/Client/OutgoingResponse.cs @@ -18,7 +18,6 @@ public OutgoingResponse(object id, object result, ServerRequest request) } public object Id { get; set; } - public object Result { get; set; } public ServerRequest Request { get; } } diff --git a/src/JsonRpc/IRequestDescriptor.cs b/src/JsonRpc/IRequestDescriptor.cs new file mode 100644 index 000000000..e906e1a54 --- /dev/null +++ b/src/JsonRpc/IRequestDescriptor.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace OmniSharp.Extensions.JsonRpc +{ + public interface IRequestDescriptor : IEnumerable + { + TDescriptor Default { get; } + } +} diff --git a/src/JsonRpc/IRequestRouter.cs b/src/JsonRpc/IRequestRouter.cs index 58f3fedf9..fcf0b48e2 100644 --- a/src/JsonRpc/IRequestRouter.cs +++ b/src/JsonRpc/IRequestRouter.cs @@ -12,9 +12,9 @@ public interface IRequestRouter public interface IRequestRouter : IRequestRouter { - TDescriptor GetDescriptor(Notification notification); - TDescriptor GetDescriptor(Request request); - Task RouteNotification(TDescriptor descriptor, Notification notification, CancellationToken token); - Task RouteRequest(TDescriptor descriptor, Request request, CancellationToken token); + IRequestDescriptor GetDescriptors(Notification notification); + IRequestDescriptor GetDescriptors(Request request); + Task RouteNotification(IRequestDescriptor descriptors, Notification notification, CancellationToken token); + Task RouteRequest(IRequestDescriptor descriptors, Request request, CancellationToken token); } } diff --git a/src/JsonRpc/InputHandler.cs b/src/JsonRpc/InputHandler.cs index a511e605d..f4f4077ef 100644 --- a/src/JsonRpc/InputHandler.cs +++ b/src/JsonRpc/InputHandler.cs @@ -54,8 +54,8 @@ public class InputHandler : IInputHandler, IDisposable private readonly CompositeDisposable _disposable; private readonly AsyncSubject _inputActive; - private readonly ConcurrentDictionary _requests = - new ConcurrentDictionary(); + private readonly ConcurrentDictionary descriptor)> _requests = + new ConcurrentDictionary descriptor)>(); private readonly Subject> _inputQueue; @@ -410,15 +410,15 @@ private void HandleRequest(in ReadOnlySequence request) if (item.IsRequest) { // _logger.LogDebug("Handling Request {Method} {ResponseId}", item.Request.Method, item.Request.Id); - var descriptor = _requestRouter.GetDescriptor(item.Request); - if (descriptor is null) + var descriptor = _requestRouter.GetDescriptors(item.Request); + if (descriptor.Default is null) { _logger.LogDebug("Request handler was not found (or not setup) {Method} {ResponseId}", item.Request.Method, item.Request.Id); _outputHandler.Send(new MethodNotFound(item.Request.Id, item.Request.Method)); return; } - var type = _requestProcessIdentifier.Identify(descriptor); + var type = _requestProcessIdentifier.Identify(descriptor.Default); _scheduler.Add(type, $"{item.Request.Method}:{item.Request.Id}", RouteRequest(descriptor, item.Request)); } @@ -445,8 +445,8 @@ private void HandleRequest(in ReadOnlySequence request) } // _logger.LogDebug("Handling Request {Method}", item.Notification.Method); - var descriptor = _requestRouter.GetDescriptor(item.Notification); - if (descriptor is null) + var descriptor = _requestRouter.GetDescriptors(item.Notification); + if (descriptor.Default is null) { _logger.LogDebug("Notification handler was not found (or not setup) {Method}", item.Notification.Method); // TODO: Figure out a good way to send this feedback back. @@ -454,7 +454,7 @@ private void HandleRequest(in ReadOnlySequence request) return; } - var type = _requestProcessIdentifier.Identify(descriptor); + var type = _requestProcessIdentifier.Identify(descriptor.Default); _scheduler.Add(type, item.Notification.Method, RouteNotification(descriptor, item.Notification)); } @@ -465,11 +465,11 @@ private void HandleRequest(in ReadOnlySequence request) } } - private SchedulerDelegate RouteRequest(IHandlerDescriptor descriptor, Request request) + private SchedulerDelegate RouteRequest(IRequestDescriptor descriptors, Request request) { // start request, create cts, etc var cts = new CancellationTokenSource(); - _requests.TryAdd(request.Id, (cts, descriptor)); + _requests.TryAdd(request.Id, (cts, descriptors)); return (contentModifiedToken, scheduler) => Observable.Create(observer => { // ITS A RACE! @@ -485,7 +485,7 @@ private SchedulerDelegate RouteRequest(IHandlerDescriptor descriptor, Request re // ObservableToToken(contentModifiedToken).Register(cts.Cancel); try { - return await _requestRouter.RouteRequest(descriptor, request, cts.Token); + return await _requestRouter.RouteRequest(descriptors, request, cts.Token); } catch (OperationCanceledException) { @@ -521,7 +521,7 @@ private SchedulerDelegate RouteRequest(IHandlerDescriptor descriptor, Request re }); } - private SchedulerDelegate RouteNotification(IHandlerDescriptor descriptor, Notification notification) + private SchedulerDelegate RouteNotification(IRequestDescriptor descriptors, Notification notification) { return (contentModifiedToken, scheduler) => // ITS A RACE! @@ -536,7 +536,7 @@ private SchedulerDelegate RouteNotification(IHandlerDescriptor descriptor, Notif using var timer = _logger.TimeDebug("Processing notification {Method}", notification.Method); try { - await _requestRouter.RouteNotification(descriptor, notification, ct); + await _requestRouter.RouteNotification(descriptors, notification, ct); } catch (OperationCanceledException) { diff --git a/src/JsonRpc/RequestDescriptor.cs b/src/JsonRpc/RequestDescriptor.cs new file mode 100644 index 000000000..b0273441b --- /dev/null +++ b/src/JsonRpc/RequestDescriptor.cs @@ -0,0 +1,30 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace OmniSharp.Extensions.JsonRpc +{ + class RequestDescriptor : IRequestDescriptor + { + private IEnumerable _descriptors; + + public RequestDescriptor(IEnumerable descriptors) + { + var enumerable = descriptors as TDescriptor[] ?? descriptors.ToArray(); + _descriptors = enumerable; + Default = enumerable.FirstOrDefault(); + } + + public RequestDescriptor(params TDescriptor[] descriptors) + { + var enumerable = descriptors.ToArray(); + _descriptors = enumerable; + Default = enumerable.FirstOrDefault(); + } + + public IEnumerator GetEnumerator() => _descriptors.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable) _descriptors).GetEnumerator(); + + public TDescriptor Default { get; } + } +} diff --git a/src/JsonRpc/RequestRouter.cs b/src/JsonRpc/RequestRouter.cs index 456fa7491..51b05d00d 100644 --- a/src/JsonRpc/RequestRouter.cs +++ b/src/JsonRpc/RequestRouter.cs @@ -27,14 +27,14 @@ private IHandlerDescriptor FindDescriptor(IMethodWithParams instance) return _collection.FirstOrDefault(x => x.Method == instance.Method); } - public override IHandlerDescriptor GetDescriptor(Notification notification) + public override IRequestDescriptor GetDescriptors(Notification notification) { - return FindDescriptor(notification); + return new RequestDescriptor(FindDescriptor(notification)); } - public override IHandlerDescriptor GetDescriptor(Request request) + public override IRequestDescriptor GetDescriptors(Request request) { - return FindDescriptor(request); + return new RequestDescriptor(FindDescriptor(request)); } } } diff --git a/src/JsonRpc/RequestRouterBase.cs b/src/JsonRpc/RequestRouterBase.cs index db0d6ca75..af560abed 100644 --- a/src/JsonRpc/RequestRouterBase.cs +++ b/src/JsonRpc/RequestRouterBase.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging; using System.Collections.Generic; using System.Linq; +using OmniSharp.Extensions.JsonRpc.Client; namespace OmniSharp.Extensions.JsonRpc { @@ -30,42 +31,51 @@ public RequestRouterBase(ISerializer serializer, IServiceProvider serviceProvide public IServiceProvider ServiceProvider { get; } - public async Task RouteNotification(TDescriptor descriptor, Notification notification, CancellationToken token) + public async Task RouteNotification(IRequestDescriptor descriptors, Notification notification, CancellationToken token) { using var debug = _logger.TimeDebug("Routing Notification {Method}", notification.Method); using var _ = _logger.BeginScope(new[] { new KeyValuePair("Method", notification.Method), new KeyValuePair("Params", notification.Params?.ToString()) }); - using var scope = _serviceScopeFactory.CreateScope(); - var context = scope.ServiceProvider.GetRequiredService(); - context.Descriptor = descriptor; - var mediator = scope.ServiceProvider.GetRequiredService(); - if (descriptor.Params is null) + object @params = null; + if (!(descriptors.Default.Params is null)) { - await HandleNotification(mediator, descriptor, EmptyRequest.Instance, token); + if (descriptors.Default.IsDelegatingHandler) + { + _logger.LogTrace("Converting params for Notification {Method} to {Type}", notification.Method, descriptors.Default.Params.GetGenericArguments()[0].FullName); + var o = notification.Params?.ToObject(descriptors.Default.Params.GetGenericArguments()[0], _serializer.JsonSerializer); + @params = Activator.CreateInstance(descriptors.Default.Params, new object[] {o}); + } + else + { + _logger.LogTrace("Converting params for Notification {Method} to {Type}", notification.Method, descriptors.Default.Params.FullName); + @params = notification.Params?.ToObject(descriptors.Default.Params, _serializer.JsonSerializer); + } } - else + + await Task.WhenAll(descriptors.Select(descriptor => InnerRoute(_serviceScopeFactory, descriptor, @params, token))); + + static async Task InnerRoute(IServiceScopeFactory serviceScopeFactory, TDescriptor descriptor, object @params, CancellationToken token) { - _logger.LogDebug("Converting params for Notification {Method} to {Type}", notification.Method, descriptor.Params.FullName); - object @params; - if (descriptor.IsDelegatingHandler) + using var scope = serviceScopeFactory.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + context.Descriptor = descriptor; + var mediator = scope.ServiceProvider.GetRequiredService(); + + if (descriptor.Params is null) { - // new DelegatingRequest(); - var o = notification.Params?.ToObject(descriptor.Params.GetGenericArguments()[0], _serializer.JsonSerializer); - @params = Activator.CreateInstance(descriptor.Params, new object[] {o}); + await HandleNotification(mediator, descriptor, EmptyRequest.Instance, token); } else { - @params = notification.Params?.ToObject(descriptor.Params, _serializer.JsonSerializer); + await HandleNotification(mediator, descriptor, @params ?? Activator.CreateInstance(descriptor.Params), token); } - - await HandleNotification(mediator, descriptor, @params ?? Activator.CreateInstance(descriptor.Params), token); } } - public virtual async Task RouteRequest(TDescriptor descriptor, Request request, CancellationToken token) + public virtual async Task RouteRequest(IRequestDescriptor descriptors, Request request, CancellationToken token) { using var debug = _logger.TimeDebug("Routing Request ({Id}) {Method}", request.Id, request.Method); using var _ = _logger.BeginScope(new[] { @@ -73,35 +83,22 @@ public virtual async Task RouteRequest(TDescriptor descriptor, Re new KeyValuePair("Method", request.Method), new KeyValuePair("Params", request.Params?.ToString()) }); - using var scope = _serviceScopeFactory.CreateScope(); - var context = scope.ServiceProvider.GetRequiredService(); - context.Descriptor = descriptor; - var mediator = scope.ServiceProvider.GetRequiredService(); - - token.ThrowIfCancellationRequested(); - // To avoid boxing, the best way to compare generics for equality is with EqualityComparer.Default. - // This respects IEquatable (without boxing) as well as object.Equals, and handles all the Nullable "lifted" nuances. - // https://stackoverflow.com/a/864860 - if (EqualityComparer.Default.Equals(descriptor, default)) - { - _logger.LogTrace("descriptor not found for Request ({Id}) {Method}", request.Id, request.Method); - return new MethodNotFound(request.Id, request.Method); - } - - object @params; + object @params = null; try { - _logger.LogTrace("Converting params for Request ({Id}) {Method} to {Type}", request.Id, request.Method, descriptor.Params.FullName); - if (descriptor.IsDelegatingHandler) + if (descriptors.Default.IsDelegatingHandler) { - // new DelegatingRequest(); - var o = request.Params?.ToObject(descriptor.Params.GetGenericArguments()[0], _serializer.JsonSerializer); - @params = Activator.CreateInstance(descriptor.Params, new object[] {o}); + _logger.LogTrace("Converting params for Request ({Id}) {Method} to {Type}", request.Id, request.Method, + descriptors.Default.Params.GetGenericArguments()[0].FullName); + var o = request.Params?.ToObject(descriptors.Default.Params.GetGenericArguments()[0], _serializer.JsonSerializer); + @params = Activator.CreateInstance(descriptors.Default.Params, new object[] {o}); } else { - @params = request.Params?.ToObject(descriptor.Params, _serializer.JsonSerializer); + _logger.LogTrace("Converting params for Request ({Id}) {Method} to {Type}", request.Id, request.Method, descriptors.Default.Params.FullName); + _logger.LogTrace("Converting params for Notification {Method} to {Type}", request.Method, descriptors.Default.Params.FullName); + @params = request.Params?.ToObject(descriptors.Default.Params, _serializer.JsonSerializer); } } catch (Exception cannotDeserializeRequestParams) @@ -110,34 +107,65 @@ public virtual async Task RouteRequest(TDescriptor descriptor, Re return new InvalidParams(request.Id); } - token.ThrowIfCancellationRequested(); + using var scope = _serviceScopeFactory.CreateScope(); + // TODO: Do we want to support more handlers as "aggregate"? + if (typeof(IEnumerable).IsAssignableFrom(descriptors.Default.Response)) + { + var responses = await Task.WhenAll(descriptors.Select(descriptor => InnerRoute(_serviceScopeFactory, request, descriptor, @params, token, _logger))); + var errorResponse = responses.FirstOrDefault(x => x.IsError); + if (errorResponse.IsError) return errorResponse; + if (responses.Length == 1) + { + return responses[0]; + } - var result = HandleRequest(mediator, descriptor, @params ?? Activator.CreateInstance(descriptor.Params), token); - await result; + var response = Activator.CreateInstance( + typeof(AggregateResponse<>).MakeGenericType(descriptors.Default.Response), + new object[] {responses.Select(z => z.Response.Result)} + ); + return new OutgoingResponse(request.Id, response, request); + } - token.ThrowIfCancellationRequested(); + return await InnerRoute(_serviceScopeFactory, request, descriptors.Default, @params, token, _logger); - object responseValue = null; - if (result.GetType().GetTypeInfo().IsGenericType) + static async Task InnerRoute(IServiceScopeFactory serviceScopeFactory, Request request, TDescriptor descriptor, object @params, CancellationToken token, + ILogger logger) { - var property = typeof(Task<>) - .MakeGenericType(result.GetType().GetTypeInfo().GetGenericArguments()[0]).GetTypeInfo() - .GetProperty(nameof(Task.Result), BindingFlags.Public | BindingFlags.Instance); + using var scope = serviceScopeFactory.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + context.Descriptor = descriptor; + var mediator = scope.ServiceProvider.GetRequiredService(); - responseValue = property.GetValue(result); - if (responseValue?.GetType() == typeof(Unit)) + token.ThrowIfCancellationRequested(); + + var result = HandleRequest(mediator, descriptor, @params ?? Activator.CreateInstance(descriptor.Params), token); + await result; + + token.ThrowIfCancellationRequested(); + + object responseValue = null; + if (result.GetType().GetTypeInfo().IsGenericType) { - responseValue = null; + var property = typeof(Task<>) + .MakeGenericType(result.GetType().GetTypeInfo().GetGenericArguments()[0]).GetTypeInfo() + .GetProperty(nameof(Task.Result), BindingFlags.Public | BindingFlags.Instance); + + responseValue = property.GetValue(result); + if (responseValue?.GetType() == typeof(Unit)) + { + responseValue = null; + } + + logger.LogTrace("Response value was {Type}", responseValue?.GetType().FullName); } - _logger.LogTrace("Response value was {Type}", responseValue?.GetType().FullName); + return new OutgoingResponse(request.Id, responseValue, request); + return new JsonRpc.Client.OutgoingResponse(request.Id, responseValue, request); } - - return new JsonRpc.Client.OutgoingResponse(request.Id, responseValue, request); } - public abstract TDescriptor GetDescriptor(Notification notification); - public abstract TDescriptor GetDescriptor(Request request); + public abstract IRequestDescriptor GetDescriptors(Notification notification); + public abstract IRequestDescriptor GetDescriptors(Request request); private static readonly MethodInfo SendRequestUnit = typeof(RequestRouterBase) .GetMethods(BindingFlags.NonPublic | BindingFlags.Static) diff --git a/src/JsonRpc/Serialization/Converters/AggregateConverter.cs b/src/JsonRpc/Serialization/Converters/AggregateConverter.cs new file mode 100644 index 000000000..b2c18ec48 --- /dev/null +++ b/src/JsonRpc/Serialization/Converters/AggregateConverter.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace OmniSharp.Extensions.JsonRpc.Serialization.Converters +{ + public class AggregateConverter : JsonConverter> where T : IEnumerable + { + public override void WriteJson(JsonWriter writer, AggregateResponse value, JsonSerializer serializer) + { + writer.WriteStartArray(); + foreach (var item in value.Items) + { + foreach (var v in item) + { + serializer.Serialize(writer, v); + } + } + writer.WriteEndArray(); + } + + public override bool CanRead => false; + + public override AggregateResponse ReadJson(JsonReader reader, Type objectType, AggregateResponse existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException(); + } +} diff --git a/src/Protocol/AbstractHandlers.cs b/src/Protocol/AbstractHandlers.cs index 32e34bdb3..a73864c26 100644 --- a/src/Protocol/AbstractHandlers.cs +++ b/src/Protocol/AbstractHandlers.cs @@ -81,10 +81,10 @@ protected abstract void Handle(TItem request, IObserver results, void ICapability.SetCapability(TCapability capability) => Capability = capability; } - public abstract class PartialResults : - IJsonRpcRequestHandler, + public abstract class PartialResults : + IJsonRpcRequestHandler, IRegistration, ICapability - where TItem : IPartialItemsRequest + where TParams : IPartialItemsRequest where TResponse : IEnumerable, new() where TRegistrationOptions : class, new() where TCapability : ICapability @@ -104,7 +104,7 @@ protected PartialResults( _factory = factory; } - async Task IRequestHandler.Handle(TItem request, + async Task IRequestHandler.Handle(TParams request, CancellationToken cancellationToken) { var observer = _progressManager.For(request, cancellationToken); @@ -125,7 +125,7 @@ async Task IRequestHandler.Handle(TItem request, return _factory(await task); } - protected abstract void Handle(TItem request, IObserver> results, + protected abstract void Handle(TParams request, IObserver> results, CancellationToken cancellationToken); TRegistrationOptions IRegistration.GetRegistrationOptions() => _registrationOptions; diff --git a/src/Protocol/DelegatingHandlers.cs b/src/Protocol/DelegatingHandlers.cs index 9fc124343..fd0b8d8d5 100644 --- a/src/Protocol/DelegatingHandlers.cs +++ b/src/Protocol/DelegatingHandlers.cs @@ -13,12 +13,12 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol { - public static class LanguageProtocolDelegatingHandlers { public sealed class Request : IJsonRpcRequestHandler, - IRegistration, ICapability + IRegistration, ICapability, + ICanBeIdentifiedHandler where TParams : IRequest where TRegistrationOptions : class, new() where TCapability : ICapability @@ -26,17 +26,27 @@ public sealed class Request private readonly Func> _handler; private readonly TRegistrationOptions _registrationOptions; private TCapability _capability; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; + + public Request(Func> handler, TRegistrationOptions registrationOptions) : + this(Guid.Empty, (a, c, ct) => handler(a, c), registrationOptions) + { + } + + public Request(Func> handler, TRegistrationOptions registrationOptions) : + this(Guid.Empty, handler, registrationOptions) + { + } - public Request( - Func> handler, - TRegistrationOptions registrationOptions) : this((a, c, ct) => handler(a, c), registrationOptions) + public Request(Guid id, Func> handler, TRegistrationOptions registrationOptions) : + this(id, (a, c, ct) => handler(a, c), registrationOptions) { } - public Request( - Func> handler, - TRegistrationOptions registrationOptions) + public Request(Guid id, Func> handler, TRegistrationOptions registrationOptions) { + _id = id; _handler = handler; _registrationOptions = registrationOptions; } @@ -57,38 +67,28 @@ public sealed class CanBeResolved : where TRegistrationOptions : class, new() where TCapability : ICapability { - private readonly Func _canResolve; private readonly Func> _resolveHandler; private readonly TRegistrationOptions _registrationOptions; private TCapability _capability; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; - public CanBeResolved( - Func> resolveHandler, - Func canResolve, - TRegistrationOptions registrationOptions) : this( - (a, c, ct) => resolveHandler(a, c) , - canResolve, - registrationOptions) + public CanBeResolved(Guid id, Func> resolveHandler, TRegistrationOptions registrationOptions) : + this(id, (a, c, ct) => resolveHandler(a, c), registrationOptions) { } - public CanBeResolved( - Func> resolveHandler, - Func canResolve, - TRegistrationOptions registrationOptions) + public CanBeResolved(Guid id, Func> resolveHandler, TRegistrationOptions registrationOptions) { - _canResolve = canResolve; _resolveHandler = resolveHandler; _registrationOptions = registrationOptions; + _id = id; } - Task IRequestHandler. - Handle(TItem request, CancellationToken cancellationToken) => - _resolveHandler(request, _capability, cancellationToken); + Task IRequestHandler.Handle(TItem request, CancellationToken cancellationToken) => _resolveHandler(request, _capability, cancellationToken); TRegistrationOptions IRegistration.GetRegistrationOptions() => _registrationOptions; void ICapability.SetCapability(TCapability capability) => _capability = capability; - bool ICanBeResolvedHandler.CanResolve(TItem value) => _canResolve(value); } public sealed class CanBeResolved : @@ -97,41 +97,32 @@ public sealed class CanBeResolved : where TItem : ICanBeResolved, IRequest where TRegistrationOptions : class, new() { - private readonly Func _canResolve; private readonly Func> _resolveHandler; private readonly TRegistrationOptions _registrationOptions; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; - public CanBeResolved( - Func> resolveHandler, - Func canResolve, - TRegistrationOptions registrationOptions) : this( - (a, c) => resolveHandler(a) , - canResolve, - registrationOptions) + public CanBeResolved(Guid id, Func> resolveHandler, TRegistrationOptions registrationOptions) : + this(id, (a, c) => resolveHandler(a), registrationOptions) { } - public CanBeResolved( - Func> resolveHandler, - Func canResolve, - TRegistrationOptions registrationOptions) + public CanBeResolved(Guid id, Func> resolveHandler, TRegistrationOptions registrationOptions) { - _canResolve = canResolve; + _id = id; _resolveHandler = resolveHandler; _registrationOptions = registrationOptions; } - Task IRequestHandler. - Handle(TItem request, CancellationToken cancellationToken) => - _resolveHandler(request, cancellationToken); + Task IRequestHandler.Handle(TItem request, CancellationToken cancellationToken) => _resolveHandler(request, cancellationToken); TRegistrationOptions IRegistration.GetRegistrationOptions() => _registrationOptions; - bool ICanBeResolvedHandler.CanResolve(TItem value) => _canResolve(value); } public sealed class Request : IJsonRpcRequestHandler, - IRegistration, ICapability + IRegistration, ICapability, + ICanBeIdentifiedHandler where TParams : IRequest where TRegistrationOptions : class, new() where TCapability : ICapability @@ -139,27 +130,36 @@ public sealed class Request : private readonly Func _handler; private readonly TRegistrationOptions _registrationOptions; private TCapability _capability; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; - public Request( - Func handler, - TRegistrationOptions registrationOptions) : this((a, c, ct) => handler(a, c), registrationOptions) + public Request(Func handler, TRegistrationOptions registrationOptions) : + this(Guid.Empty, (a, c, ct) => handler(a, c), registrationOptions) { } - public Request( - Func handler, - TRegistrationOptions registrationOptions) + public Request(Func handler, TRegistrationOptions registrationOptions) : + this(Guid.Empty, handler, registrationOptions) { + } + + + public Request(Guid id, Func handler, TRegistrationOptions registrationOptions) : + this(id, (a, c, ct) => handler(a, c), registrationOptions) + { + } + + public Request(Guid id, Func handler, TRegistrationOptions registrationOptions) + { + _id = id; _handler = handler; _registrationOptions = registrationOptions; } - async Task IRequestHandler. - Handle(TParams request, CancellationToken cancellationToken) + async Task IRequestHandler.Handle(TParams request, CancellationToken cancellationToken) { await _handler(request, _capability, cancellationToken); return Unit.Value; - ; } TRegistrationOptions IRegistration.GetRegistrationOptions() => _registrationOptions; @@ -168,53 +168,73 @@ async Task IRequestHandler. public sealed class RequestRegistration : IJsonRpcRequestHandler, - IRegistration + IRegistration, + ICanBeIdentifiedHandler where TParams : IRequest where TRegistrationOptions : class, new() { private readonly Func> _handler; private readonly TRegistrationOptions _registrationOptions; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; + + public RequestRegistration(Func> handler, TRegistrationOptions registrationOptions) : + this(Guid.Empty, (a, ct) => handler(a), registrationOptions) + { + } + + public RequestRegistration(Func> handler, TRegistrationOptions registrationOptions) : + this(Guid.Empty, handler, registrationOptions) + { + } - public RequestRegistration( - Func> handler, - TRegistrationOptions registrationOptions) : this((a, ct) => handler(a), registrationOptions) + public RequestRegistration(Guid id, Func> handler, TRegistrationOptions registrationOptions) : + this(id, (a, ct) => handler(a), registrationOptions) { } - public RequestRegistration( - Func> handler, - TRegistrationOptions registrationOptions) + public RequestRegistration(Guid id, Func> handler, TRegistrationOptions registrationOptions) { + _id = id; _handler = handler; _registrationOptions = registrationOptions; } - Task IRequestHandler. - Handle(TParams request, CancellationToken cancellationToken) => - _handler(request, cancellationToken); + Task IRequestHandler.Handle(TParams request, CancellationToken cancellationToken) => _handler(request, cancellationToken); TRegistrationOptions IRegistration.GetRegistrationOptions() => _registrationOptions; } public sealed class RequestRegistration : IJsonRpcRequestHandler, - IRegistration + IRegistration, + ICanBeIdentifiedHandler where TParams : IRequest where TRegistrationOptions : class, new() { private readonly Func _handler; private readonly TRegistrationOptions _registrationOptions; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; - public RequestRegistration( - Func handler, - TRegistrationOptions registrationOptions) : this((a, ct) => handler(a), registrationOptions) + public RequestRegistration(Func handler, TRegistrationOptions registrationOptions) : + this(Guid.Empty, (a, ct) => handler(a), registrationOptions) { } - public RequestRegistration( - Func handler, - TRegistrationOptions registrationOptions) + public RequestRegistration(Func handler, TRegistrationOptions registrationOptions): + this(Guid.Empty, handler, registrationOptions) { + } + + public RequestRegistration(Guid id, Func handler, TRegistrationOptions registrationOptions) : + this(id, (a, ct) => handler(a), registrationOptions) + { + } + + public RequestRegistration(Guid id, Func handler, TRegistrationOptions registrationOptions) + { + _id = id; _handler = handler; _registrationOptions = registrationOptions; } @@ -231,21 +251,34 @@ async Task IRequestHandler. public sealed class RequestCapability : IJsonRpcRequestHandler, - ICapability + ICapability, + ICanBeIdentifiedHandler where TParams : IRequest where TCapability : ICapability { private readonly Func> _handler; private TCapability _capability; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; + + public RequestCapability(Func> handler) : + this(Guid.Empty, (a, c, ct) => handler(a, c)) + { + } + + public RequestCapability(Func> handler): + this(Guid.Empty, handler) + { + } - public RequestCapability( - Func> handler) : this((a, c, ct) => handler(a, c)) + public RequestCapability(Guid id, Func> handler) : + this(id, (a, c, ct) => handler(a, c)) { } - public RequestCapability( - Func> handler) + public RequestCapability(Guid id, Func> handler) { + _id = id; _handler = handler; } @@ -258,21 +291,34 @@ Task IRequestHandler. public sealed class RequestCapability : IJsonRpcRequestHandler, - ICapability + ICapability, + ICanBeIdentifiedHandler where TParams : IRequest where TCapability : ICapability { private readonly Func _handler; private TCapability _capability; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; + + public RequestCapability(Func handler) : + this(Guid.Empty, handler) + { + } - public RequestCapability( - Func handler) : this((a, c, ct) => handler(a, c)) + public RequestCapability(Func handler): + this(Guid.Empty, handler) { } - public RequestCapability( - Func handler) + public RequestCapability(Guid id, Func handler) : + this(id, (a, c, ct) => handler(a, c)) { + } + + public RequestCapability(Guid id, Func handler) + { + _id = id; _handler = handler; } @@ -288,7 +334,8 @@ async Task IRequestHandler. public sealed class PartialResult : IJsonRpcRequestHandler, - IRegistration, ICapability + IRegistration, ICapability, + ICanBeIdentifiedHandler where TItem : IPartialItemRequest where TResponse : class, new() where TRegistrationOptions : class, new() @@ -299,22 +346,27 @@ public sealed class PartialResult _id; - public PartialResult( - Action, TCapability> handler, - TRegistrationOptions registrationOptions, - IProgressManager progressManager, - Func factory) : this((p, o, c, ct) => handler(p, o, c), registrationOptions, - progressManager, factory) + public PartialResult(Action, TCapability> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func factory) : + this(Guid.Empty, (p, o, c, ct) => handler(p, o, c), registrationOptions, progressManager, factory) { } - public PartialResult( - Action, TCapability, CancellationToken> handler, - TRegistrationOptions registrationOptions, - IProgressManager progressManager, - Func factory) + public PartialResult(Action, TCapability, CancellationToken> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func factory): + this(Guid.Empty, handler, registrationOptions, progressManager, factory) { + } + + public PartialResult(Guid id, Action, TCapability> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func factory) : + this(id, (p, o, c, ct) => handler(p, o, c), registrationOptions, progressManager, factory) + { + } + + public PartialResult(Guid id, Action, TCapability, CancellationToken> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func factory) + { + _id = id; _handler = handler; _registrationOptions = registrationOptions; _progressManager = progressManager; @@ -343,7 +395,8 @@ async Task IRequestHandler.Handle(TItem request, public sealed class PartialResult : IJsonRpcRequestHandler, - IRegistration + IRegistration, + ICanBeIdentifiedHandler where TItem : IPartialItemRequest where TResponse : class, new() where TRegistrationOptions : class, new() @@ -352,30 +405,34 @@ public sealed class PartialResult : private readonly TRegistrationOptions _registrationOptions; private readonly IProgressManager _progressManager; private readonly Func _factory; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; + + public PartialResult(Action> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func factory) : + this(Guid.Empty, (p, o, ct) => handler(p, o), registrationOptions, progressManager, factory) + { + } - public PartialResult( - Action> handler, - TRegistrationOptions registrationOptions, - IProgressManager progressManager, - Func factory) : this((p, o, ct) => handler(p, o), registrationOptions, - progressManager, factory) + public PartialResult(Action, CancellationToken> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func factory): + this(Guid.Empty, handler, registrationOptions, progressManager, factory) { } - public PartialResult( - Action, CancellationToken> handler, - TRegistrationOptions registrationOptions, - IProgressManager progressManager, - Func factory) + public PartialResult(Guid id, Action> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func factory) : + this(id, (p, o, ct) => handler(p, o), registrationOptions, progressManager, factory) { + } + + public PartialResult(Guid id, Action, CancellationToken> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func factory) + { + _id = id; _handler = handler; _registrationOptions = registrationOptions; _progressManager = progressManager; _factory = factory; } - async Task IRequestHandler.Handle(TItem request, - CancellationToken cancellationToken) + async Task IRequestHandler.Handle(TItem request, CancellationToken cancellationToken) { var observer = _progressManager.For(request, cancellationToken); if (observer != null) @@ -395,7 +452,8 @@ async Task IRequestHandler.Handle(TItem request, public sealed class PartialResultCapability : IJsonRpcRequestHandler, - ICapability + ICapability, + ICanBeIdentifiedHandler where TItem : IPartialItemRequest where TResponse : class, new() where TCapability : ICapability @@ -404,27 +462,33 @@ public sealed class PartialResultCapability : private readonly IProgressManager _progressManager; private readonly Func _factory; private TCapability _capability; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; - public PartialResultCapability( - Action> handler, - IProgressManager progressManager, - Func factory) : this((p, c, o, ct) => handler(p, c, o), - progressManager, factory) + public PartialResultCapability(Action> handler, IProgressManager progressManager, Func factory) : + this((p, c, o, ct) => handler(p, c, o), progressManager, factory) { } - public PartialResultCapability( - Action, CancellationToken> handler, - IProgressManager progressManager, - Func factory) + public PartialResultCapability(Action, CancellationToken> handler, IProgressManager progressManager, Func factory): + this(Guid.Empty, handler, progressManager, factory) { + } + + public PartialResultCapability(Guid id, Action> handler, IProgressManager progressManager, Func factory) : + this(id, (p, c, o, ct) => handler(p, c, o), progressManager, factory) + { + } + + public PartialResultCapability(Guid id, Action, CancellationToken> handler, IProgressManager progressManager, Func factory) + { + _id = id; _handler = handler; _progressManager = progressManager; _factory = factory; } - async Task IRequestHandler.Handle(TItem request, - CancellationToken cancellationToken) + async Task IRequestHandler.Handle(TItem request, CancellationToken cancellationToken) { var observer = _progressManager.For(request, cancellationToken); if (observer != null) @@ -443,34 +507,44 @@ async Task IRequestHandler.Handle(TItem request, } public sealed class PartialResult : - IJsonRpcRequestHandler + IJsonRpcRequestHandler, + ICanBeIdentifiedHandler where TItem : IPartialItemRequest where TResponse : class, new() { private readonly Action, CancellationToken> _handler; private readonly IProgressManager _progressManager; private readonly Func _factory; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; - public PartialResult( - Action> handler, - IProgressManager progressManager, - Func factory) : this((p, o, ct) => handler(p, o), - progressManager, factory) + public PartialResult(Action> handler, IProgressManager progressManager, Func factory) : + this(Guid.Empty, (p, o, ct) => handler(p, o), progressManager, factory) { } - public PartialResult( - Action, CancellationToken> handler, - IProgressManager progressManager, - Func factory) + public PartialResult(Action, CancellationToken> handler, IProgressManager progressManager, Func factory): + this(Guid.Empty, handler, progressManager,factory) { _handler = handler; _progressManager = progressManager; _factory = factory; } - async Task IRequestHandler.Handle(TItem request, - CancellationToken cancellationToken) + public PartialResult(Guid id, Action> handler, IProgressManager progressManager, Func factory) : + this(id, (p, o, ct) => handler(p, o), progressManager, factory) + { + } + + public PartialResult(Guid id, Action, CancellationToken> handler, IProgressManager progressManager, Func factory) + { + _id = id; + _handler = handler; + _progressManager = progressManager; + _factory = factory; + } + + async Task IRequestHandler.Handle(TItem request, CancellationToken cancellationToken) { var observer = _progressManager.For(request, cancellationToken); if (observer != null) @@ -488,7 +562,8 @@ async Task IRequestHandler.Handle(TItem request, public sealed class PartialResults : IJsonRpcRequestHandler, - IRegistration, ICapability + IRegistration, ICapability, + ICanBeIdentifiedHandler where TParams : IPartialItemsRequest where TResponse : IEnumerable, new() where TRegistrationOptions : class, new() @@ -499,30 +574,34 @@ public sealed class PartialResults, TResponse> _factory; private TCapability _capability; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; + + public PartialResults(Action>, TCapability> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func, TResponse> factory) : + this(Guid.Empty, (p, o, c, ct) => handler(p, o, c), registrationOptions, progressManager, factory) + { + } - public PartialResults( - Action>, TCapability> handler, - TRegistrationOptions registrationOptions, - IProgressManager progressManager, - Func, TResponse> factory) : this((p, o, c, ct) => handler(p, o, c), registrationOptions, - progressManager, factory) + public PartialResults(Action>, TCapability, CancellationToken> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func, TResponse> factory): + this(Guid.Empty, handler, registrationOptions, progressManager, factory) { } - public PartialResults( - Action>, TCapability, CancellationToken> handler, - TRegistrationOptions registrationOptions, - IProgressManager progressManager, - Func, TResponse> factory) + public PartialResults(Guid id, Action>, TCapability> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func, TResponse> factory) : + this(id, (p, o, c, ct) => handler(p, o, c), registrationOptions, progressManager, factory) { + } + + public PartialResults(Guid id, Action>, TCapability, CancellationToken> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func, TResponse> factory) + { + _id = id; _handler = handler; _registrationOptions = registrationOptions; _progressManager = progressManager; _factory = factory; } - async Task IRequestHandler.Handle(TParams request, - CancellationToken cancellationToken) + async Task IRequestHandler.Handle(TParams request, CancellationToken cancellationToken) { var observer = _progressManager.For(request, cancellationToken); if (observer != null) @@ -549,7 +628,8 @@ async Task IRequestHandler.Handle(TParams request public sealed class PartialResults : IJsonRpcRequestHandler, - IRegistration + IRegistration, + ICanBeIdentifiedHandler where TParams : IPartialItemsRequest where TResponse : IEnumerable, new() where TRegistrationOptions : class, new() @@ -558,30 +638,34 @@ public sealed class PartialResults, TResponse> _factory; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; + + public PartialResults(Action>> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func, TResponse> factory) : + this(Guid.Empty, (p, o, ct) => handler(p, o), registrationOptions, progressManager, factory) + { + } + + public PartialResults(Action>, CancellationToken> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func, TResponse> factory): + this(Guid.Empty, handler, registrationOptions, progressManager, factory) + { + } - public PartialResults( - Action>> handler, - TRegistrationOptions registrationOptions, - IProgressManager progressManager, - Func, TResponse> factory) : this((p, o, ct) => handler(p, o), registrationOptions, - progressManager, factory) + public PartialResults(Guid id, Action>> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func, TResponse> factory) : + this(id, (p, o, ct) => handler(p, o), registrationOptions, progressManager, factory) { } - public PartialResults( - Action>, CancellationToken> handler, - TRegistrationOptions registrationOptions, - IProgressManager progressManager, - Func, TResponse> factory) + public PartialResults(Guid id, Action>, CancellationToken> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func, TResponse> factory) { + _id = id; _handler = handler; _registrationOptions = registrationOptions; _progressManager = progressManager; _factory = factory; } - async Task IRequestHandler.Handle(TParams request, - CancellationToken cancellationToken) + async Task IRequestHandler.Handle(TParams request, CancellationToken cancellationToken) { var observer = _progressManager.For(request, cancellationToken); if (observer != null) @@ -606,7 +690,8 @@ async Task IRequestHandler.Handle(TParams request } public sealed class PartialResultsCapability : - IJsonRpcRequestHandler, ICapability + IJsonRpcRequestHandler, ICapability, + ICanBeIdentifiedHandler where TParams : IPartialItemsRequest where TResponse : IEnumerable, new() where TCapability : ICapability @@ -615,27 +700,33 @@ public sealed class PartialResultsCapability, TResponse> _factory; private TCapability _capability; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; + + public PartialResultsCapability(Action>> handler, IProgressManager progressManager, Func, TResponse> factory) : + this(Guid.Empty, (p, c, o, ct) => handler(p, c, o), progressManager, factory) + { + } - public PartialResultsCapability( - Action>> handler, - IProgressManager progressManager, - Func, TResponse> factory) : this((p, c, o, ct) => handler(p, c, o), - progressManager, factory) + public PartialResultsCapability(Action>, CancellationToken> handler, IProgressManager progressManager, Func, TResponse> factory): + this(Guid.Empty, handler, progressManager, factory) { } - public PartialResultsCapability( - Action>, CancellationToken> handler, - IProgressManager progressManager, - Func, TResponse> factory) + public PartialResultsCapability(Guid id, Action>> handler, IProgressManager progressManager, Func, TResponse> factory) : + this(id, (p, c, o, ct) => handler(p, c, o), progressManager, factory) { + } + + public PartialResultsCapability(Guid id, Action>, CancellationToken> handler, IProgressManager progressManager, Func, TResponse> factory) + { + _id = id; _handler = handler; _progressManager = progressManager; _factory = factory; } - async Task IRequestHandler.Handle(TParams request, - CancellationToken cancellationToken) + async Task IRequestHandler.Handle(TParams request, CancellationToken cancellationToken) { var observer = _progressManager.For(request, cancellationToken); if (observer != null) @@ -660,34 +751,44 @@ async Task IRequestHandler.Handle(TParams request } public sealed class PartialResults : - IJsonRpcRequestHandler + IJsonRpcRequestHandler, + ICanBeIdentifiedHandler where TParams : IPartialItemsRequest where TResponse : IEnumerable, new() { private readonly Action>, CancellationToken> _handler; private readonly IProgressManager _progressManager; private readonly Func, TResponse> _factory; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; - public PartialResults( - Action>> handler, - IProgressManager progressManager, - Func, TResponse> factory) : this((p, o, ct) => handler(p, o), - progressManager, factory) + public PartialResults(Action>> handler, IProgressManager progressManager, Func, TResponse> factory) : + this(Guid.Empty, (p, o, ct) => handler(p, o), progressManager, factory) { } - public PartialResults( - Action>, CancellationToken> handler, - IProgressManager progressManager, - Func, TResponse> factory) + public PartialResults(Action>, CancellationToken> handler, IProgressManager progressManager, Func, TResponse> factory): + this(Guid.Empty, handler, progressManager, factory) { _handler = handler; _progressManager = progressManager; _factory = factory; } - async Task IRequestHandler.Handle(TParams request, - CancellationToken cancellationToken) + public PartialResults(Guid id, Action>> handler, IProgressManager progressManager, Func, TResponse> factory) : + this(id, (p, o, ct) => handler(p, o), progressManager, factory) + { + } + + public PartialResults(Guid id, Action>, CancellationToken> handler, IProgressManager progressManager, Func, TResponse> factory) + { + _id = id; + _handler = handler; + _progressManager = progressManager; + _factory = factory; + } + + async Task IRequestHandler.Handle(TParams request, CancellationToken cancellationToken) { var observer = _progressManager.For(request, cancellationToken); if (observer != null) @@ -711,7 +812,8 @@ async Task IRequestHandler.Handle(TParams request public sealed class Notification : IJsonRpcNotificationHandler, - IRegistration, ICapability + IRegistration, ICapability, + ICanBeIdentifiedHandler where TParams : IRequest where TRegistrationOptions : class, new() where TCapability : ICapability @@ -719,42 +821,54 @@ public sealed class Notification : private readonly Func _handler; private readonly TRegistrationOptions _registrationOptions; private TCapability _capability; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; + + public Notification(Action handler, TRegistrationOptions registrationOptions) : + this(Guid.Empty, (request, capability, ct) => { handler(request, capability); return Task.CompletedTask; }, registrationOptions) + { + } - public Notification( - Action handler, - TRegistrationOptions registrationOptions) : this((request, capability, ct) => { - handler(request, capability); - return Task.CompletedTask; - }, registrationOptions) + public Notification(Action handler, TRegistrationOptions registrationOptions) : + this(Guid.Empty, (request, c, ct) => { handler(request, c, ct); return Task.CompletedTask; }, registrationOptions) { } - public Notification( - Action handler, - TRegistrationOptions registrationOptions) : this((request, c, ct) => { - handler(request, c, ct); - return Task.CompletedTask; - }, registrationOptions) + public Notification(Func handler, TRegistrationOptions registrationOptions) : + this(Guid.Empty, (request, capability, ct) => handler(request, capability), registrationOptions) { } - public Notification( - Func handler, - TRegistrationOptions registrationOptions) : this((request, capability, ct) => handler(request, capability), registrationOptions) + public Notification(Func handler, TRegistrationOptions registrationOptions): + this(Guid.Empty, handler, registrationOptions) { } - public Notification( - Func handler, - TRegistrationOptions registrationOptions) + public Notification(Guid id, Action handler, TRegistrationOptions registrationOptions) : + this(id, (request, capability, ct) => { handler(request, capability); return Task.CompletedTask; }, registrationOptions) { + } + + public Notification( Guid id, Action handler, TRegistrationOptions registrationOptions) : + this(id, (request, c, ct) => { handler(request, c, ct); return Task.CompletedTask; }, registrationOptions) + { + } + + public Notification(Guid id, Func handler, TRegistrationOptions registrationOptions) : + this(id, (request, capability, ct) => handler(request, capability), registrationOptions) + { + } + + public Notification(Guid id, Func handler, TRegistrationOptions registrationOptions) + { + _id = id; _handler = handler; _registrationOptions = registrationOptions; } async Task IRequestHandler.Handle(TParams request, CancellationToken cancellationToken) { - await _handler(request, _capability, cancellationToken); + await _handler(request, _capability, cancellationToken); return Unit.Value; } @@ -764,41 +878,54 @@ async Task IRequestHandler.Handle(TParams request, Cancella public sealed class Notification : IJsonRpcNotificationHandler, - IRegistration + IRegistration, + ICanBeIdentifiedHandler where TParams : IRequest where TRegistrationOptions : class, new() { private readonly Func _handler; private readonly TRegistrationOptions _registrationOptions; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; + + public Notification(Action handler, TRegistrationOptions registrationOptions) : + this(Guid.Empty, (request, ct) => { handler(request); return Task.CompletedTask; }, registrationOptions) + { + } - public Notification( - Action handler, - TRegistrationOptions registrationOptions) : this((request, ct) => { - handler(request); - return Task.CompletedTask; - }, registrationOptions) + public Notification(Action handler, TRegistrationOptions registrationOptions) : + this(Guid.Empty, (request, ct) => { handler(request, ct); return Task.CompletedTask; }, registrationOptions) { } - public Notification( - Action handler, - TRegistrationOptions registrationOptions) : this((request, ct) => { - handler(request, ct); - return Task.CompletedTask; - }, registrationOptions) + public Notification(Func handler, TRegistrationOptions registrationOptions) : + this(Guid.Empty, (request, ct) => handler(request), registrationOptions) { } - public Notification( - Func handler, - TRegistrationOptions registrationOptions) : this((request, ct) => handler(request), registrationOptions) + public Notification(Func handler, TRegistrationOptions registrationOptions): + this(Guid.Empty, handler, registrationOptions) { } - public Notification( - Func handler, - TRegistrationOptions registrationOptions) + public Notification(Guid id, Action handler, TRegistrationOptions registrationOptions) : + this(id,(request, ct) => { handler(request); return Task.CompletedTask; }, registrationOptions) { + } + + public Notification(Guid id, Action handler, TRegistrationOptions registrationOptions) : + this(id, (request, ct) => { handler(request, ct); return Task.CompletedTask; }, registrationOptions) + { + } + + public Notification(Guid id, Func handler, TRegistrationOptions registrationOptions) : + this(id, (request, ct) => handler(request), registrationOptions) + { + } + + public Notification(Guid id, Func handler, TRegistrationOptions registrationOptions) + { + _id = id; _handler = handler; _registrationOptions = registrationOptions; } @@ -813,29 +940,44 @@ async Task IRequestHandler.Handle(TParams request, Cancella } public sealed class NotificationCapability : - IJsonRpcNotificationHandler, ICapability + IJsonRpcNotificationHandler, ICapability, + ICanBeIdentifiedHandler where TParams : IRequest where TCapability : ICapability { private readonly Func _handler; private TCapability _capability; + private readonly Guid _id; + Guid ICanBeIdentifiedHandler.Id => _id; + + public NotificationCapability(Action handler) : + this(Guid.Empty, (request, capability, ct) => { handler(request, capability); return Task.CompletedTask; }) + { + } + + public NotificationCapability(Func handler) : + this(Guid.Empty, (request, capability, ct) => handler(request, capability)) + { + } + + public NotificationCapability(Func handler) : + this(Guid.Empty, handler) + { + } - public NotificationCapability( - Action handler) : this((request, capability, ct) => { - handler(request, capability); - return Task.CompletedTask; - }) + public NotificationCapability(Guid id, Action handler) : + this(id, (request, capability, ct) => { handler(request, capability); return Task.CompletedTask; }) { } - public NotificationCapability( - Func handler) : this((request, capability, ct) => handler(request, capability)) + public NotificationCapability(Guid id, Func handler) : + this(id, (request, capability, ct) => handler(request, capability)) { } - public NotificationCapability( - Func handler) + public NotificationCapability(Guid id, Func handler) { + _id = id; _handler = handler; } diff --git a/src/Protocol/Document/ICodeLensHandler.cs b/src/Protocol/Document/ICodeLensHandler.cs index 47cf37f28..002c1395d 100644 --- a/src/Protocol/Document/ICodeLensHandler.cs +++ b/src/Protocol/Document/ICodeLensHandler.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Reactive; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; @@ -10,11 +12,12 @@ using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Progress; using OmniSharp.Extensions.LanguageServer.Protocol.Server; +using ISerializer = OmniSharp.Extensions.LanguageServer.Protocol.Serialization.ISerializer; namespace OmniSharp.Extensions.LanguageServer.Protocol.Document { [Parallel, Method(TextDocumentNames.CodeLens, Direction.ClientToServer)] - [GenerateHandlerMethods, GenerateRequestMethods(typeof(ITextDocumentLanguageClient), typeof(ILanguageClient))] + [GenerateRequestMethods(typeof(ITextDocumentLanguageClient), typeof(ILanguageClient))] public interface ICodeLensHandler : IJsonRpcRequestHandler, IRegistration, ICapability { } @@ -32,167 +35,426 @@ public abstract class CodeLensHandler : ICodeLensHandler, ICodeLensResolveHandle public CodeLensHandler(CodeLensRegistrationOptions registrationOptions) { _options = registrationOptions; + _options.ResolveProvider = true; } public CodeLensRegistrationOptions GetRegistrationOptions() => _options; public abstract Task Handle(CodeLensParams request, CancellationToken cancellationToken); public abstract Task Handle(CodeLens request, CancellationToken cancellationToken); - public abstract bool CanResolve(CodeLens value); + public virtual Guid Id { get; } = Guid.NewGuid(); public virtual void SetCapability(CodeLensCapability capability) => Capability = capability; protected CodeLensCapability Capability { get; private set; } } + public abstract class PartialCodeLensHandlerBase : + AbstractHandlers.PartialResults, ICodeLensHandler, ICodeLensResolveHandler + { + protected PartialCodeLensHandlerBase(CodeLensRegistrationOptions registrationOptions, IProgressManager progressManager) : base(registrationOptions, progressManager, + lenses => new CodeLensContainer(lenses)) + { + } + + public abstract Task Handle(CodeLens request, CancellationToken cancellationToken); + public virtual Guid Id { get; } = Guid.NewGuid(); + } + + public abstract class CodeLensHandlerBase : CodeLensHandler where T : class + { + private readonly ISerializer _serializer; + + public CodeLensHandlerBase(CodeLensRegistrationOptions registrationOptions, ISerializer serializer) : base(registrationOptions) + { + _serializer = serializer; + } + + + public sealed override async Task Handle(CodeLensParams request, CancellationToken cancellationToken) + { + var response = await HandleParams(request, cancellationToken); + return response.Convert(_serializer); + } + + public sealed override async Task Handle(CodeLens request, CancellationToken cancellationToken) + { + var response = await HandleResolve(request.From(_serializer), cancellationToken); + return response.To(_serializer); + } + + protected abstract Task> HandleParams(CodeLensParams request, CancellationToken cancellationToken); + protected abstract Task> HandleResolve(CodeLens request, CancellationToken cancellationToken); + } + + public abstract class PartialCodeLensHandlerBase : PartialCodeLensHandlerBase where T : class + { + private readonly ISerializer _serializer; + + protected PartialCodeLensHandlerBase(CodeLensRegistrationOptions registrationOptions, IProgressManager progressManager, ISerializer serializer) : base(registrationOptions, + progressManager) + { + _serializer = serializer; + } + + protected sealed override void Handle(CodeLensParams request, IObserver> results, CancellationToken cancellationToken) => Handle( + request, + Observer.Create>>( + x => results.OnNext(x.Select(z => z.To(_serializer))), + results.OnError, + results.OnCompleted + ), cancellationToken); + + public sealed override async Task Handle(CodeLens request, CancellationToken cancellationToken) + { + var response = await HandleResolve(request.From(_serializer), cancellationToken); + return response.To(_serializer); + } + + protected abstract void Handle(CodeLensParams request, IObserver>> results, CancellationToken cancellationToken); + protected abstract Task> HandleResolve(CodeLens request, CancellationToken cancellationToken); + } + public static partial class CodeLensExtensions { public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, Func> handler, - Func canResolve, + CodeLensRegistrationOptions registrationOptions) + { + return OnCodeLens(registry, handler, null, registrationOptions); + } + + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Func> handler, Func> resolveHandler, CodeLensRegistrationOptions registrationOptions) { registrationOptions ??= new CodeLensRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, cap, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (link, cap, token) => Task.FromResult(link); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.CodeLens, new LanguageProtocolDelegatingHandlers.Request( + id, handler, registrationOptions)) .AddHandler(TextDocumentNames.CodeLensResolve, new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, resolveHandler, - canResolve, registrationOptions)) ; } + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Func>> handler, + Func, CodeLensCapability, CancellationToken, Task>> resolveHandler, + CodeLensRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new CodeLensRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (link, c, token) => Task.FromResult(link); + + return registry.AddHandler(_ => new DelegatingCodeLensHandler( + registrationOptions, + _.GetRequiredService(), + handler, + resolveHandler) + ); + } + + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Func> handler, + CodeLensRegistrationOptions registrationOptions) + { + return OnCodeLens(registry, handler, null, registrationOptions); + } + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, Func> handler, - Func canResolve, Func> resolveHandler, CodeLensRegistrationOptions registrationOptions) { registrationOptions ??= new CodeLensRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (link, token) => Task.FromResult(link); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.CodeLens, new LanguageProtocolDelegatingHandlers.RequestRegistration( + id, handler, registrationOptions)) .AddHandler(TextDocumentNames.CodeLensResolve, new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, resolveHandler, - canResolve, registrationOptions)) ; } + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Func>> handler, + Func, CancellationToken, Task>> resolveHandler, + CodeLensRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new CodeLensRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (link, token) => Task.FromResult(link); + + return registry.AddHandler(_ => new DelegatingCodeLensHandler( + registrationOptions, + _.GetRequiredService(), + (@params, capability, token) => handler(@params, token), + (lens, capability, token) => resolveHandler(lens, token)) + ); + } + + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Func> handler, + CodeLensRegistrationOptions registrationOptions) + { + return OnCodeLens(registry, handler, null, registrationOptions); + } + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, Func> handler, - Func canResolve, Func> resolveHandler, CodeLensRegistrationOptions registrationOptions) { registrationOptions ??= new CodeLensRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = true; + resolveHandler ??= Task.FromResult; + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.CodeLens, new LanguageProtocolDelegatingHandlers.RequestRegistration( + id, handler, registrationOptions)) .AddHandler(TextDocumentNames.CodeLensResolve, new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, resolveHandler, - canResolve, registrationOptions)) ; } + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Func>> handler, + Func, Task>> resolveHandler, + CodeLensRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new CodeLensRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= Task.FromResult; + + return registry.AddHandler(_ => new DelegatingCodeLensHandler( + registrationOptions, + _.GetRequiredService(), + (@params, capability, token) => handler(@params), + (lens, capability, token) => resolveHandler(lens)) + ); + } + + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Action>, CodeLensCapability, CancellationToken> handler, + CodeLensRegistrationOptions registrationOptions) + { + return OnCodeLens(registry, handler, null, registrationOptions); + } + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, Action>, CodeLensCapability, CancellationToken> handler, - Func canResolve, Func> resolveHandler, CodeLensRegistrationOptions registrationOptions) { registrationOptions ??= new CodeLensRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, cap, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (lens, capability, token) => Task.FromResult(lens); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.CodeLens, _ => new LanguageProtocolDelegatingHandlers.PartialResults( + id, handler, registrationOptions, _.GetRequiredService(), x => new CodeLensContainer(x))) .AddHandler(TextDocumentNames.CodeLensResolve, new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, resolveHandler, - canResolve, registrationOptions)) ; } + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Action>>, CodeLensCapability, CancellationToken> handler, + Func, CodeLensCapability, CancellationToken, Task>> resolveHandler, + CodeLensRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new CodeLensRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (lens, capability, token) => Task.FromResult(lens); + + return registry.AddHandler(_ => new DelegatingPartialCodeLensHandler( + registrationOptions, + _.GetRequiredService(), + _.GetRequiredService(), + handler, + resolveHandler) + ); + } + + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Action>, CancellationToken> handler, + CodeLensRegistrationOptions registrationOptions) + { + return OnCodeLens(registry, handler, null, registrationOptions); + } + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, Action>, CancellationToken> handler, - Func canResolve, Func> resolveHandler, CodeLensRegistrationOptions registrationOptions) { registrationOptions ??= new CodeLensRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (lens, token) => Task.FromResult(lens); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.CodeLens, _ => new LanguageProtocolDelegatingHandlers.PartialResults( + id, handler, registrationOptions, _.GetRequiredService(), x => new CodeLensContainer(x))) .AddHandler(TextDocumentNames.CodeLensResolve, new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, resolveHandler, - canResolve, registrationOptions)) ; } + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Action>>, CancellationToken> handler, + Func, CancellationToken, Task>> resolveHandler, + CodeLensRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new CodeLensRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (lens, token) => Task.FromResult(lens); + + return registry.AddHandler(_ => new DelegatingPartialCodeLensHandler( + registrationOptions, + _.GetRequiredService(), + _.GetRequiredService(), + (@params, observer, capability, token) => handler(@params, observer, token), + (lens, capability, token) => resolveHandler(lens, token)) + ); + } + + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Action>> handler, + CodeLensRegistrationOptions registrationOptions) + { + return OnCodeLens(registry, handler, null, registrationOptions); + } + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, Action>> handler, - Func canResolve, Func> resolveHandler, CodeLensRegistrationOptions registrationOptions) { registrationOptions ??= new CodeLensRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = true; + resolveHandler ??= Task.FromResult; + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.CodeLens, _ => new LanguageProtocolDelegatingHandlers.PartialResults( + id, handler, registrationOptions, _.GetRequiredService(), x => new CodeLensContainer(x))) .AddHandler(TextDocumentNames.CodeLensResolve, new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, resolveHandler, - canResolve, registrationOptions)) ; } + + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Action>>> handler, + Func, Task>> resolveHandler, + CodeLensRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new CodeLensRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= Task.FromResult; + + return registry.AddHandler(_ => new DelegatingPartialCodeLensHandler( + registrationOptions, + _.GetRequiredService(), + _.GetRequiredService(), + (@params, observer, capability, token) => handler(@params, observer), + (lens, capability, token) => resolveHandler(lens)) + ); + } + + class DelegatingCodeLensHandler : CodeLensHandlerBase where T : class + { + private readonly Func>> _handleParams; + private readonly Func, CodeLensCapability, CancellationToken, Task>> _handleResolve; + + public DelegatingCodeLensHandler( + CodeLensRegistrationOptions registrationOptions, + ISerializer serializer, + Func>> handleParams, + Func, CodeLensCapability, CancellationToken, Task>> handleResolve + ) : base(registrationOptions, serializer) + { + _handleParams = handleParams; + _handleResolve = handleResolve; + } + + protected override Task> HandleParams(CodeLensParams request, CancellationToken cancellationToken) => + _handleParams(request, Capability, cancellationToken); + + protected override Task> HandleResolve(CodeLens request, CancellationToken cancellationToken) => _handleResolve(request, Capability, cancellationToken); + } + + class DelegatingPartialCodeLensHandler : PartialCodeLensHandlerBase where T : class + { + private readonly Action>>, CodeLensCapability, CancellationToken> _handleParams; + private readonly Func, CodeLensCapability, CancellationToken, Task>> _handleResolve; + + public DelegatingPartialCodeLensHandler( + CodeLensRegistrationOptions registrationOptions, + IProgressManager progressManager, + ISerializer serializer, + Action>>, CodeLensCapability, CancellationToken> handleParams, + Func, CodeLensCapability, CancellationToken, Task>> handleResolve + ) : base(registrationOptions, progressManager, serializer) + { + _handleParams = handleParams; + _handleResolve = handleResolve; + } + + protected override void Handle(CodeLensParams request, IObserver>> results, CancellationToken cancellationToken) => + _handleParams(request, results, Capability, cancellationToken); + + protected override Task> HandleResolve(CodeLens request, CancellationToken cancellationToken) => _handleResolve(request, Capability, cancellationToken); + } } } diff --git a/src/Protocol/Document/ICompletionHandler.cs b/src/Protocol/Document/ICompletionHandler.cs index 8bd48aecb..acf41a00b 100644 --- a/src/Protocol/Document/ICompletionHandler.cs +++ b/src/Protocol/Document/ICompletionHandler.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Reactive; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; @@ -10,11 +12,12 @@ using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Progress; using OmniSharp.Extensions.LanguageServer.Protocol.Server; +using ISerializer = OmniSharp.Extensions.LanguageServer.Protocol.Serialization.ISerializer; namespace OmniSharp.Extensions.LanguageServer.Protocol.Document { [Parallel, Method(TextDocumentNames.Completion, Direction.ClientToServer)] - [GenerateHandlerMethods, GenerateRequestMethods(typeof(ITextDocumentLanguageClient), typeof(ILanguageClient))] + [GenerateRequestMethods(typeof(ITextDocumentLanguageClient), typeof(ILanguageClient))] public interface ICompletionHandler : IJsonRpcRequestHandler, IRegistration, ICapability { } @@ -32,167 +35,426 @@ public abstract class CompletionHandler : ICompletionHandler, ICompletionResolve public CompletionHandler(CompletionRegistrationOptions registrationOptions) { _options = registrationOptions; + _options.ResolveProvider = true; } public CompletionRegistrationOptions GetRegistrationOptions() => _options; public abstract Task Handle(CompletionParams request, CancellationToken cancellationToken); public abstract Task Handle(CompletionItem request, CancellationToken cancellationToken); - public abstract bool CanResolve(CompletionItem value); + public virtual Guid Id { get; } = Guid.NewGuid(); public virtual void SetCapability(CompletionCapability capability) => Capability = capability; protected CompletionCapability Capability { get; private set; } } + public abstract class PartialCompletionHandlerBase : + AbstractHandlers.PartialResults, ICompletionHandler, ICompletionResolveHandler + { + protected PartialCompletionHandlerBase(CompletionRegistrationOptions registrationOptions, IProgressManager progressManager) : base(registrationOptions, progressManager, + lenses => new CompletionList(lenses)) + { + } + + public abstract Task Handle(CompletionItem request, CancellationToken cancellationToken); + public virtual Guid Id { get; } = Guid.NewGuid(); + } + + public abstract class CompletionHandlerBase : CompletionHandler where T : class + { + private readonly ISerializer _serializer; + + public CompletionHandlerBase(CompletionRegistrationOptions registrationOptions, ISerializer serializer) : base(registrationOptions) + { + _serializer = serializer; + } + + + public sealed override async Task Handle(CompletionParams request, CancellationToken cancellationToken) + { + var response = await HandleParams(request, cancellationToken); + return response.Convert(_serializer); + } + + public sealed override async Task Handle(CompletionItem request, CancellationToken cancellationToken) + { + var response = await HandleResolve(request.From(_serializer), cancellationToken); + return response.To(_serializer); + } + + protected abstract Task> HandleParams(CompletionParams request, CancellationToken cancellationToken); + protected abstract Task> HandleResolve(CompletionItem request, CancellationToken cancellationToken); + } + + public abstract class PartialCompletionHandlerBase : PartialCompletionHandlerBase where T : class + { + private readonly ISerializer _serializer; + + protected PartialCompletionHandlerBase(CompletionRegistrationOptions registrationOptions, IProgressManager progressManager, ISerializer serializer) : base(registrationOptions, + progressManager) + { + _serializer = serializer; + } + + protected sealed override void Handle(CompletionParams request, IObserver> results, CancellationToken cancellationToken) => Handle( + request, + Observer.Create>>( + x => results.OnNext(x.Select(z => z.To(_serializer))), + results.OnError, + results.OnCompleted + ), cancellationToken); + + public sealed override async Task Handle(CompletionItem request, CancellationToken cancellationToken) + { + var response = await HandleResolve(request.From(_serializer), cancellationToken); + return response.To(_serializer); + } + + protected abstract void Handle(CompletionParams request, IObserver>> results, CancellationToken cancellationToken); + protected abstract Task> HandleResolve(CompletionItem request, CancellationToken cancellationToken); + } + public static partial class CompletionExtensions { - public static ILanguageServerRegistry OnCompletion( - this ILanguageServerRegistry registry, + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Func> handler, + CompletionRegistrationOptions registrationOptions) + { + return OnCompletion(registry, handler, null, registrationOptions); + } + + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, Func> handler, - Func canResolve, Func> resolveHandler, CompletionRegistrationOptions registrationOptions) { registrationOptions ??= new CompletionRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, cap, token) => Task.FromException(new NotImplementedException()); - - return registry - .AddHandler(TextDocumentNames.Completion, - new LanguageProtocolDelegatingHandlers.Request( - handler, - registrationOptions) - ) - .AddHandler(TextDocumentNames.CompletionResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( - resolveHandler, - canResolve, - registrationOptions)); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (link, cap, token) => Task.FromResult(link); + var id = Guid.NewGuid(); + + return registry.AddHandler(TextDocumentNames.Completion, + new LanguageProtocolDelegatingHandlers.Request( + id, + handler, + registrationOptions)) + .AddHandler(TextDocumentNames.CompletionResolve, + new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, + resolveHandler, + registrationOptions)) + ; + } + + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Func>> handler, + Func, CompletionCapability, CancellationToken, Task>> resolveHandler, + CompletionRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new CompletionRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (link, c, token) => Task.FromResult(link); + + return registry.AddHandler(_ => new DelegatingCompletionHandler( + registrationOptions, + _.GetRequiredService(), + handler, + resolveHandler) + ); + } + + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Func> handler, + CompletionRegistrationOptions registrationOptions) + { + return OnCompletion(registry, handler, null, registrationOptions); } public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, Func> handler, - Func canResolve, Func> resolveHandler, CompletionRegistrationOptions registrationOptions) { registrationOptions ??= new CompletionRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, token) => Task.FromException(new NotImplementedException()); - - return registry - .AddHandler(TextDocumentNames.Completion, - new LanguageProtocolDelegatingHandlers.RequestRegistration( - handler, - registrationOptions)) - .AddHandler(TextDocumentNames.CompletionResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( - resolveHandler, - canResolve, - registrationOptions)); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (link, token) => Task.FromResult(link); + var id = Guid.NewGuid(); + + return registry.AddHandler(TextDocumentNames.Completion, + new LanguageProtocolDelegatingHandlers.RequestRegistration( + id, + handler, + registrationOptions)) + .AddHandler(TextDocumentNames.CompletionResolve, + new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, + resolveHandler, + registrationOptions)) + ; + } + + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Func>> handler, + Func, CancellationToken, Task>> resolveHandler, + CompletionRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new CompletionRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (link, token) => Task.FromResult(link); + + return registry.AddHandler(_ => new DelegatingCompletionHandler( + registrationOptions, + _.GetRequiredService(), + (@params, capability, token) => handler(@params, token), + (lens, capability, token) => resolveHandler(lens, token)) + ); + } + + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Func> handler, + CompletionRegistrationOptions registrationOptions) + { + return OnCompletion(registry, handler, null, registrationOptions); } public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, Func> handler, - Func canResolve, Func> resolveHandler, CompletionRegistrationOptions registrationOptions) { registrationOptions ??= new CompletionRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link) => Task.FromException(new NotImplementedException()); - - return registry - .AddHandler(TextDocumentNames.Completion, - new LanguageProtocolDelegatingHandlers.RequestRegistration( - handler, - registrationOptions) - ) - .AddHandler(TextDocumentNames.CompletionResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( - resolveHandler, - canResolve, - registrationOptions)); + registrationOptions.ResolveProvider = true; + resolveHandler ??= Task.FromResult; + var id = Guid.NewGuid(); + + return registry.AddHandler(TextDocumentNames.Completion, + new LanguageProtocolDelegatingHandlers.RequestRegistration( + id, + handler, + registrationOptions)) + .AddHandler(TextDocumentNames.CompletionResolve, + new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, + resolveHandler, + registrationOptions)) + ; + } + + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Func>> handler, + Func, Task>> resolveHandler, + CompletionRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new CompletionRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= Task.FromResult; + + return registry.AddHandler(_ => new DelegatingCompletionHandler( + registrationOptions, + _.GetRequiredService(), + (@params, capability, token) => handler(@params), + (lens, capability, token) => resolveHandler(lens)) + ); + } + + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Action>, CompletionCapability, CancellationToken> handler, + CompletionRegistrationOptions registrationOptions) + { + return OnCompletion(registry, handler, null, registrationOptions); } public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, Action>, CompletionCapability, CancellationToken> handler, - Func canResolve, Func> resolveHandler, CompletionRegistrationOptions registrationOptions) { registrationOptions ??= new CompletionRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, cap, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (lens, capability, token) => Task.FromResult(lens); + var id = Guid.NewGuid(); - return registry.AddHandler(TextDocumentNames.Completion, - _ => new LanguageProtocolDelegatingHandlers.PartialResults( - handler, - registrationOptions, - _.GetRequiredService(), - x => new CompletionList(x)) - ) - .AddHandler(TextDocumentNames.CompletionResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( - resolveHandler, - canResolve, - registrationOptions)); + return + registry.AddHandler(TextDocumentNames.Completion, + _ => new LanguageProtocolDelegatingHandlers.PartialResults( + id, + handler, + registrationOptions, + _.GetRequiredService(), + x => new CompletionList(x))) + .AddHandler(TextDocumentNames.CompletionResolve, + new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, + resolveHandler, + registrationOptions)) + ; + } + + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Action>>, CompletionCapability, CancellationToken> handler, + Func, CompletionCapability, CancellationToken, Task>> resolveHandler, + CompletionRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new CompletionRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (lens, capability, token) => Task.FromResult(lens); + + return registry.AddHandler(_ => new DelegatingPartialCompletionHandler( + registrationOptions, + _.GetRequiredService(), + _.GetRequiredService(), + handler, + resolveHandler) + ); + } + + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Action>, CancellationToken> handler, + CompletionRegistrationOptions registrationOptions) + { + return OnCompletion(registry, handler, null, registrationOptions); } public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, Action>, CancellationToken> handler, - Func canResolve, Func> resolveHandler, CompletionRegistrationOptions registrationOptions) { registrationOptions ??= new CompletionRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (lens, token) => Task.FromResult(lens); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.Completion, - _ => new LanguageProtocolDelegatingHandlers.PartialResults( - handler, - registrationOptions, - _.GetRequiredService(), - x => new CompletionList(x))) - .AddHandler(TextDocumentNames.CompletionResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( - resolveHandler, - canResolve, - registrationOptions)); + _ => new LanguageProtocolDelegatingHandlers.PartialResults( + id, + handler, + registrationOptions, + _.GetRequiredService(), + x => new CompletionList(x))) + .AddHandler(TextDocumentNames.CompletionResolve, + new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, + resolveHandler, + registrationOptions)) + ; + } + + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Action>>, CancellationToken> handler, + Func, CancellationToken, Task>> resolveHandler, + CompletionRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new CompletionRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (lens, token) => Task.FromResult(lens); + + return registry.AddHandler(_ => new DelegatingPartialCompletionHandler( + registrationOptions, + _.GetRequiredService(), + _.GetRequiredService(), + (@params, observer, capability, token) => handler(@params, observer, token), + (lens, capability, token) => resolveHandler(lens, token)) + ); + } + + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Action>> handler, + CompletionRegistrationOptions registrationOptions) + { + return OnCompletion(registry, handler, null, registrationOptions); } public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, Action>> handler, - Func canResolve, Func> resolveHandler, CompletionRegistrationOptions registrationOptions) { registrationOptions ??= new CompletionRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = true; + resolveHandler ??= Task.FromResult; + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.Completion, - _ => new LanguageProtocolDelegatingHandlers.PartialResults( - handler, - registrationOptions, - _.GetRequiredService(), - x => new CompletionList(x))) - .AddHandler(TextDocumentNames.CompletionResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( - resolveHandler, - canResolve, - registrationOptions)); + _ => new LanguageProtocolDelegatingHandlers.PartialResults( + id, + handler, + registrationOptions, + _.GetRequiredService(), + x => new CompletionList(x))) + .AddHandler(TextDocumentNames.CompletionResolve, + new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, + resolveHandler, + registrationOptions)) + ; + } + + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Action>>> handler, + Func, Task>> resolveHandler, + CompletionRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new CompletionRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= Task.FromResult; + + return registry.AddHandler(_ => new DelegatingPartialCompletionHandler( + registrationOptions, + _.GetRequiredService(), + _.GetRequiredService(), + (@params, observer, capability, token) => handler(@params, observer), + (lens, capability, token) => resolveHandler(lens)) + ); + } + + class DelegatingCompletionHandler : CompletionHandlerBase where T : class + { + private readonly Func>> _handleParams; + private readonly Func, CompletionCapability, CancellationToken, Task>> _handleResolve; + + public DelegatingCompletionHandler( + CompletionRegistrationOptions registrationOptions, + ISerializer serializer, + Func>> handleParams, + Func, CompletionCapability, CancellationToken, Task>> handleResolve + ) : base(registrationOptions, serializer) + { + _handleParams = handleParams; + _handleResolve = handleResolve; + } + + protected override Task> HandleParams(CompletionParams request, CancellationToken cancellationToken) => + _handleParams(request, Capability, cancellationToken); + + protected override Task> HandleResolve(CompletionItem request, CancellationToken cancellationToken) => _handleResolve(request, Capability, cancellationToken); + } + + class DelegatingPartialCompletionHandler : PartialCompletionHandlerBase where T : class + { + private readonly Action>>, CompletionCapability, CancellationToken> _handleParams; + private readonly Func, CompletionCapability, CancellationToken, Task>> _handleResolve; + + public DelegatingPartialCompletionHandler( + CompletionRegistrationOptions registrationOptions, + IProgressManager progressManager, + ISerializer serializer, + Action>>, CompletionCapability, CancellationToken> handleParams, + Func, CompletionCapability, CancellationToken, Task>> handleResolve + ) : base(registrationOptions, progressManager, serializer) + { + _handleParams = handleParams; + _handleResolve = handleResolve; + } + + protected override void Handle(CompletionParams request, IObserver>> results, CancellationToken cancellationToken) => + _handleParams(request, results, Capability, cancellationToken); + + protected override Task> HandleResolve(CompletionItem request, CancellationToken cancellationToken) => _handleResolve(request, Capability, cancellationToken); } } } diff --git a/src/Protocol/Document/IDocumentLinkHandler.cs b/src/Protocol/Document/IDocumentLinkHandler.cs index dc0845e6c..be57a7026 100644 --- a/src/Protocol/Document/IDocumentLinkHandler.cs +++ b/src/Protocol/Document/IDocumentLinkHandler.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Reactive; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; @@ -10,11 +12,12 @@ using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Progress; using OmniSharp.Extensions.LanguageServer.Protocol.Server; +using ISerializer = OmniSharp.Extensions.LanguageServer.Protocol.Serialization.ISerializer; namespace OmniSharp.Extensions.LanguageServer.Protocol.Document { [Parallel, Method(TextDocumentNames.DocumentLink, Direction.ClientToServer)] - [GenerateHandlerMethods, GenerateRequestMethods(typeof(ITextDocumentLanguageClient), typeof(ILanguageClient))] + [GenerateRequestMethods(typeof(ITextDocumentLanguageClient), typeof(ILanguageClient))] public interface IDocumentLinkHandler : IJsonRpcRequestHandler, IRegistration, ICapability { @@ -33,167 +36,426 @@ public abstract class DocumentLinkHandler : IDocumentLinkHandler, IDocumentLinkR public DocumentLinkHandler(DocumentLinkRegistrationOptions registrationOptions) { _options = registrationOptions; + _options.ResolveProvider = true; } public DocumentLinkRegistrationOptions GetRegistrationOptions() => _options; - public abstract Task Handle(DocumentLinkParams request, CancellationToken cancellationToken); - public abstract Task Handle(DocumentLink request, CancellationToken cancellationToken); - public abstract bool CanResolve(DocumentLink value); + public virtual Guid Id { get; } = Guid.NewGuid(); public virtual void SetCapability(DocumentLinkCapability capability) => Capability = capability; protected DocumentLinkCapability Capability { get; private set; } } + public abstract class PartialDocumentLinkHandlerBase : + AbstractHandlers.PartialResults, IDocumentLinkHandler, IDocumentLinkResolveHandler + { + protected PartialDocumentLinkHandlerBase(DocumentLinkRegistrationOptions registrationOptions, IProgressManager progressManager) : base(registrationOptions, progressManager, + lenses => new DocumentLinkContainer(lenses)) + { + } + + public abstract Task Handle(DocumentLink request, CancellationToken cancellationToken); + public virtual Guid Id { get; } = Guid.NewGuid(); + } + + public abstract class DocumentLinkHandlerBase : DocumentLinkHandler where T : class + { + private readonly ISerializer _serializer; + + public DocumentLinkHandlerBase(DocumentLinkRegistrationOptions registrationOptions, ISerializer serializer) : base(registrationOptions) + { + _serializer = serializer; + } + + + public sealed override async Task Handle(DocumentLinkParams request, CancellationToken cancellationToken) + { + var response = await HandleParams(request, cancellationToken); + return response.Convert(_serializer); + } + + public sealed override async Task Handle(DocumentLink request, CancellationToken cancellationToken) + { + var response = await HandleResolve(request.From(_serializer), cancellationToken); + return response.To(_serializer); + } + + protected abstract Task> HandleParams(DocumentLinkParams request, CancellationToken cancellationToken); + protected abstract Task> HandleResolve(DocumentLink request, CancellationToken cancellationToken); + } + + public abstract class PartialDocumentLinkHandlerBase : PartialDocumentLinkHandlerBase where T : class + { + private readonly ISerializer _serializer; + + protected PartialDocumentLinkHandlerBase(DocumentLinkRegistrationOptions registrationOptions, IProgressManager progressManager, ISerializer serializer) : base(registrationOptions, + progressManager) + { + _serializer = serializer; + } + + protected sealed override void Handle(DocumentLinkParams request, IObserver> results, CancellationToken cancellationToken) => Handle( + request, + Observer.Create>>( + x => results.OnNext(x.Select(z => z.To(_serializer))), + results.OnError, + results.OnCompleted + ), cancellationToken); + + public sealed override async Task Handle(DocumentLink request, CancellationToken cancellationToken) + { + var response = await HandleResolve(request.From(_serializer), cancellationToken); + return response.To(_serializer); + } + + protected abstract void Handle(DocumentLinkParams request, IObserver>> results, CancellationToken cancellationToken); + protected abstract Task> HandleResolve(DocumentLink request, CancellationToken cancellationToken); + } + public static partial class DocumentLinkExtensions { public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, Func> handler, - Func canResolve, + DocumentLinkRegistrationOptions registrationOptions) + { + return OnDocumentLink(registry, handler, null, registrationOptions); + } + + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Func> handler, Func> resolveHandler, DocumentLinkRegistrationOptions registrationOptions) { registrationOptions ??= new DocumentLinkRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, cap, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (link, cap, token) => Task.FromResult(link); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.DocumentLink, new LanguageProtocolDelegatingHandlers.Request( + id, handler, registrationOptions)) .AddHandler(TextDocumentNames.DocumentLinkResolve, new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, resolveHandler, - canResolve, registrationOptions)) ; } + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Func>> handler, + Func, DocumentLinkCapability, CancellationToken, Task>> resolveHandler, + DocumentLinkRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new DocumentLinkRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (link, c, token) => Task.FromResult(link); + + return registry.AddHandler(_ => new DelegatingDocumentLinkHandler( + registrationOptions, + _.GetRequiredService(), + handler, + resolveHandler) + ); + } + + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Func> handler, + DocumentLinkRegistrationOptions registrationOptions) + { + return OnDocumentLink(registry, handler, null, registrationOptions); + } + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, Func> handler, - Func canResolve, Func> resolveHandler, DocumentLinkRegistrationOptions registrationOptions) { registrationOptions ??= new DocumentLinkRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (link, token) => Task.FromResult(link); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.DocumentLink, - new LanguageProtocolDelegatingHandlers.RequestRegistration( - handler, - registrationOptions)).AddHandler(TextDocumentNames.DocumentLinkResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( - resolveHandler, - canResolve, - registrationOptions)) + new LanguageProtocolDelegatingHandlers.RequestRegistration( + id, + handler, + registrationOptions)) + .AddHandler(TextDocumentNames.DocumentLinkResolve, + new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, + resolveHandler, + registrationOptions)) ; } + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Func>> handler, + Func, CancellationToken, Task>> resolveHandler, + DocumentLinkRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new DocumentLinkRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (link, token) => Task.FromResult(link); + + return registry.AddHandler(_ => new DelegatingDocumentLinkHandler( + registrationOptions, + _.GetRequiredService(), + (@params, capability, token) => handler(@params, token), + (lens, capability, token) => resolveHandler(lens, token)) + ); + } + + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Func> handler, + DocumentLinkRegistrationOptions registrationOptions) + { + return OnDocumentLink(registry, handler, null, registrationOptions); + } + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, Func> handler, - Func canResolve, Func> resolveHandler, DocumentLinkRegistrationOptions registrationOptions) { registrationOptions ??= new DocumentLinkRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = true; + resolveHandler ??= Task.FromResult; + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.DocumentLink, new LanguageProtocolDelegatingHandlers.RequestRegistration( + id, handler, registrationOptions)) .AddHandler(TextDocumentNames.DocumentLinkResolve, new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, resolveHandler, - canResolve, registrationOptions)) ; } + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Func>> handler, + Func, Task>> resolveHandler, + DocumentLinkRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new DocumentLinkRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= Task.FromResult; + + return registry.AddHandler(_ => new DelegatingDocumentLinkHandler( + registrationOptions, + _.GetRequiredService(), + (@params, capability, token) => handler(@params), + (lens, capability, token) => resolveHandler(lens)) + ); + } + + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Action>, DocumentLinkCapability, CancellationToken> handler, + DocumentLinkRegistrationOptions registrationOptions) + { + return OnDocumentLink(registry, handler, null, registrationOptions); + } + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, Action>, DocumentLinkCapability, CancellationToken> handler, - Func canResolve, Func> resolveHandler, DocumentLinkRegistrationOptions registrationOptions) { registrationOptions ??= new DocumentLinkRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, cap, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (lens, capability, token) => Task.FromResult(lens); + var id = Guid.NewGuid(); - return registry.AddHandler(TextDocumentNames.DocumentLink, + return + registry.AddHandler(TextDocumentNames.DocumentLink, _ => new LanguageProtocolDelegatingHandlers.PartialResults( + id, handler, registrationOptions, _.GetRequiredService(), x => new DocumentLinkContainer(x))) .AddHandler(TextDocumentNames.DocumentLinkResolve, new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, resolveHandler, - canResolve, registrationOptions)) ; } + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Action>>, DocumentLinkCapability, CancellationToken> handler, + Func, DocumentLinkCapability, CancellationToken, Task>> resolveHandler, + DocumentLinkRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new DocumentLinkRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (lens, capability, token) => Task.FromResult(lens); + + return registry.AddHandler(_ => new DelegatingPartialDocumentLinkHandler( + registrationOptions, + _.GetRequiredService(), + _.GetRequiredService(), + handler, + resolveHandler) + ); + } + + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Action>, CancellationToken> handler, + DocumentLinkRegistrationOptions registrationOptions) + { + return OnDocumentLink(registry, handler, null, registrationOptions); + } + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, Action>, CancellationToken> handler, - Func canResolve, Func> resolveHandler, DocumentLinkRegistrationOptions registrationOptions) { registrationOptions ??= new DocumentLinkRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (lens, token) => Task.FromResult(lens); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.DocumentLink, _ => new LanguageProtocolDelegatingHandlers.PartialResults( + id, handler, registrationOptions, _.GetRequiredService(), x => new DocumentLinkContainer(x))) .AddHandler(TextDocumentNames.DocumentLinkResolve, new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, resolveHandler, - canResolve, registrationOptions)) ; } + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Action>>, CancellationToken> handler, + Func, CancellationToken, Task>> resolveHandler, + DocumentLinkRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new DocumentLinkRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= (lens, token) => Task.FromResult(lens); + + return registry.AddHandler(_ => new DelegatingPartialDocumentLinkHandler( + registrationOptions, + _.GetRequiredService(), + _.GetRequiredService(), + (@params, observer, capability, token) => handler(@params, observer, token), + (lens, capability, token) => resolveHandler(lens, token)) + ); + } + + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Action>> handler, + DocumentLinkRegistrationOptions registrationOptions) + { + return OnDocumentLink(registry, handler, null, registrationOptions); + } + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, Action>> handler, - Func canResolve, Func> resolveHandler, DocumentLinkRegistrationOptions registrationOptions) { registrationOptions ??= new DocumentLinkRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = true; + resolveHandler ??= Task.FromResult; + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.DocumentLink, _ => new LanguageProtocolDelegatingHandlers.PartialResults( + id, handler, registrationOptions, _.GetRequiredService(), x => new DocumentLinkContainer(x))) .AddHandler(TextDocumentNames.DocumentLinkResolve, new LanguageProtocolDelegatingHandlers.CanBeResolved( + id, resolveHandler, - canResolve, registrationOptions)) ; } + + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Action>>> handler, + Func, Task>> resolveHandler, + DocumentLinkRegistrationOptions registrationOptions) where T : class + { + registrationOptions ??= new DocumentLinkRegistrationOptions(); + registrationOptions.ResolveProvider = true; + resolveHandler ??= Task.FromResult; + + return registry.AddHandler(_ => new DelegatingPartialDocumentLinkHandler( + registrationOptions, + _.GetRequiredService(), + _.GetRequiredService(), + (@params, observer, capability, token) => handler(@params, observer), + (lens, capability, token) => resolveHandler(lens)) + ); + } + + class DelegatingDocumentLinkHandler : DocumentLinkHandlerBase where T : class + { + private readonly Func>> _handleParams; + private readonly Func, DocumentLinkCapability, CancellationToken, Task>> _handleResolve; + + public DelegatingDocumentLinkHandler( + DocumentLinkRegistrationOptions registrationOptions, + ISerializer serializer, + Func>> handleParams, + Func, DocumentLinkCapability, CancellationToken, Task>> handleResolve + ) : base(registrationOptions, serializer) + { + _handleParams = handleParams; + _handleResolve = handleResolve; + } + + protected override Task> HandleParams(DocumentLinkParams request, CancellationToken cancellationToken) => + _handleParams(request, Capability, cancellationToken); + + protected override Task> HandleResolve(DocumentLink request, CancellationToken cancellationToken) => _handleResolve(request, Capability, cancellationToken); + } + + class DelegatingPartialDocumentLinkHandler : PartialDocumentLinkHandlerBase where T : class + { + private readonly Action>>, DocumentLinkCapability, CancellationToken> _handleParams; + private readonly Func, DocumentLinkCapability, CancellationToken, Task>> _handleResolve; + + public DelegatingPartialDocumentLinkHandler( + DocumentLinkRegistrationOptions registrationOptions, + IProgressManager progressManager, + ISerializer serializer, + Action>>, DocumentLinkCapability, CancellationToken> handleParams, + Func, DocumentLinkCapability, CancellationToken, Task>> handleResolve + ) : base(registrationOptions, progressManager, serializer) + { + _handleParams = handleParams; + _handleResolve = handleResolve; + } + + protected override void Handle(DocumentLinkParams request, IObserver>> results, CancellationToken cancellationToken) => + _handleParams(request, results, Capability, cancellationToken); + + protected override Task> HandleResolve(DocumentLink request, CancellationToken cancellationToken) => _handleResolve(request, Capability, cancellationToken); + } } } diff --git a/src/Protocol/Document/ITextDocumentSyncHandler.cs b/src/Protocol/Document/ITextDocumentSyncHandler.cs index bed599868..af026271f 100644 --- a/src/Protocol/Document/ITextDocumentSyncHandler.cs +++ b/src/Protocol/Document/ITextDocumentSyncHandler.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using MediatR; @@ -49,7 +50,7 @@ TextDocumentChangeRegistrationOptions IRegistration getTextDocumentAttributes, Func onOpenHandler, @@ -63,7 +64,7 @@ public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerReg onSaveHandler, getTextDocumentAttributes, registrationOptions, kind)); } -public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerRegistry registry, + public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerRegistry registry, TextDocumentSyncKind kind, Func getTextDocumentAttributes, Action onOpenHandler, @@ -77,7 +78,7 @@ public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerReg onSaveHandler, getTextDocumentAttributes, registrationOptions, kind)); } -public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerRegistry registry, + public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerRegistry registry, TextDocumentSyncKind kind, Func getTextDocumentAttributes, Action onOpenHandler, @@ -95,7 +96,7 @@ public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerReg getTextDocumentAttributes, registrationOptions, kind)); } -public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerRegistry registry, + public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerRegistry registry, TextDocumentSyncKind kind, Func getTextDocumentAttributes, Func onOpenHandler, @@ -113,7 +114,7 @@ public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerReg getTextDocumentAttributes, registrationOptions, kind)); } -public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerRegistry registry, + public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerRegistry registry, TextDocumentSyncKind kind, Func getTextDocumentAttributes, Action onOpenHandler, @@ -131,7 +132,7 @@ public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerReg getTextDocumentAttributes, registrationOptions, kind)); } -public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerRegistry registry, + public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerRegistry registry, TextDocumentSyncKind kind, Func getTextDocumentAttributes, Func onOpenHandler, @@ -149,7 +150,7 @@ public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerReg getTextDocumentAttributes, registrationOptions, kind)); } -public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerRegistry registry, + public static ILanguageServerRegistry OnTextDocumentSync(this ILanguageServerRegistry registry, TextDocumentSyncKind kind, Func getTextDocumentAttributes, Action onOpenHandler, diff --git a/src/Protocol/Models/CodeLens.cs b/src/Protocol/Models/CodeLens.cs index f51396934..daf22d377 100644 --- a/src/Protocol/Models/CodeLens.cs +++ b/src/Protocol/Models/CodeLens.cs @@ -1,8 +1,10 @@ +using System.ComponentModel; using System.Diagnostics; using MediatR; using Newtonsoft.Json.Linq; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; +using ISerializer = OmniSharp.Extensions.LanguageServer.Protocol.Serialization.ISerializer; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { @@ -38,5 +40,56 @@ public class CodeLens : ICanBeResolved, IRequest private string DebuggerDisplay => $"{Range}{(Command != null ? $" Command" : "")}"; /// public override string ToString() => DebuggerDisplay; + + /// + /// Convert from a + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal CodeLens From(ISerializer serializer) where T : class + { + return new CodeLens() { + Command = Command, + Data = Data?.ToObject(serializer.JsonSerializer), + Range = Range + }; + } + } + + /// + /// A code lens represents a command that should be shown along with + /// source text, like the number of references, a way to run tests, etc. + /// + /// A code lens is _unresolved_ when no command is associated to it. For performance + /// reasons the creation of a code lens and resolving should be done in two stages. + /// + /// + /// Typed code lens used for the typed handlers + /// + /// + public class CodeLens : CodeLens where T : class + { + /// + /// A data entry field that is preserved on a code lens item between + /// a code lens and a code lens resolve request. + /// + public new T Data { get; set; } + + /// + /// Convert to a + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal CodeLens To(ISerializer serializer) + { + if (Data != null) + { + base.Data = JObject.FromObject(Data, serializer.JsonSerializer); + } + + return this; + } } } diff --git a/src/Protocol/Models/CodeLensContainer.cs b/src/Protocol/Models/CodeLensContainer.cs index 8f31f89b5..c0c8d435a 100644 --- a/src/Protocol/Models/CodeLensContainer.cs +++ b/src/Protocol/Models/CodeLensContainer.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; using System.Linq; +using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { @@ -32,5 +34,60 @@ public static implicit operator CodeLensContainer(List items) { return new CodeLensContainer(items); } + + /// + /// Convert from a + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal CodeLensContainer Convert(ISerializer serializer) where T : class + { + return new CodeLensContainer(this.Select(z => z.From(serializer))); + } + } + + /// + /// Typed code lens used for the typed handlers + /// + public class CodeLensContainer : Container> where T : class + { + public CodeLensContainer() : this(Enumerable.Empty>()) + { + } + + public CodeLensContainer(IEnumerable> items) : base(items) + { + } + + public CodeLensContainer(params CodeLens[] items) : base(items) + { + } + + public static implicit operator CodeLensContainer(CodeLens[] items) + { + return new CodeLensContainer(items); + } + + public static implicit operator CodeLensContainer(Collection> items) + { + return new CodeLensContainer(items); + } + + public static implicit operator CodeLensContainer(List> items) + { + return new CodeLensContainer(items); + } + + /// + /// Convert to a + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal CodeLensContainer Convert(ISerializer serializer) + { + return new CodeLensContainer(this.Select(z => z.To(serializer))); + } } } diff --git a/src/Protocol/Models/CompletionItem.cs b/src/Protocol/Models/CompletionItem.cs index 551bb6c35..d77f71313 100644 --- a/src/Protocol/Models/CompletionItem.cs +++ b/src/Protocol/Models/CompletionItem.cs @@ -1,9 +1,11 @@ +using System.ComponentModel; using System.Diagnostics; using System.Linq; using MediatR; using Newtonsoft.Json.Linq; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; +using ISerializer = OmniSharp.Extensions.LanguageServer.Protocol.Serialization.ISerializer; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { @@ -136,5 +138,62 @@ public class CompletionItem : ICanBeResolved, IRequest private string DebuggerDisplay => $"[{Kind}] {Label}{(Tags?.Any() == true ? $" tags: {string.Join(", ", Tags.Select(z => z.ToString()))}" : "")}"; /// public override string ToString() => DebuggerDisplay; + + /// + /// Convert from a + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal CompletionItem From(ISerializer serializer) where T : class + { + return new CompletionItem() { + Command = Command, + Deprecated = Deprecated, + Detail = Detail, + Documentation = Documentation, + Kind = Kind, + Label = Label, + Preselect = Preselect, + Tags = Tags, + CommitCharacters = CommitCharacters, + FilterText = FilterText, + InsertText = InsertText, + SortText = SortText, + TextEdit = TextEdit, + AdditionalTextEdits = AdditionalTextEdits, + InsertTextFormat = InsertTextFormat, + Data = Data?.ToObject(serializer.JsonSerializer) + }; + } + } + + /// + /// Typed code lens used for the typed handlers + /// + public class CompletionItem : CompletionItem where T : class + { + /// + /// An data entry field that is preserved on a completion item between + /// a completion and a completion resolve request. + /// + [Optional] + public new T Data { get; set; } + + /// + /// Convert to a + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal CompletionItem To(ISerializer serializer) + { + if (Data != null) + { + base.Data = JObject.FromObject(Data, serializer.JsonSerializer); + } + + return this; + } } } diff --git a/src/Protocol/Models/CompletionList.cs b/src/Protocol/Models/CompletionList.cs index 29817dc51..2f4704124 100644 --- a/src/Protocol/Models/CompletionList.cs +++ b/src/Protocol/Models/CompletionList.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; using System.Linq; +using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { @@ -58,5 +60,82 @@ public static implicit operator CompletionItem[] (CompletionList list) { return list.ToArray(); } + + /// + /// Convert from a + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal CompletionList Convert(ISerializer serializer) where T : class + { + return new CompletionList(this.Select(z => z.From(serializer))); + } + } + /// + /// Represents a collection of [completion items](#CompletionItem) to be presented + /// in the editor. + /// + public class CompletionList : Container> where T : class + { + public CompletionList() : base(Enumerable.Empty>()) { } + public CompletionList(bool isIncomplete) : base(Enumerable.Empty>()) + { + IsIncomplete = isIncomplete; + } + + public CompletionList(IEnumerable> items) : base(items) { } + public CompletionList(IEnumerable> items, bool isIncomplete) : base(items) + { + IsIncomplete = isIncomplete; + } + + public CompletionList(params CompletionItem[] items) : base(items) { } + public CompletionList(bool isIncomplete, params CompletionItem[] items) : base(items) + { + IsIncomplete = isIncomplete; + } + + /// + /// This list it not complete. Further typing should result in recomputing + /// this list. + /// + public bool IsIncomplete { get; } + + /// + /// The completion items. + /// + public IEnumerable Items => this; + + public static implicit operator CompletionList(CompletionItem[] items) + { + return new CompletionList(items); + } + + public static implicit operator CompletionList(Collection> items) + { + return new CompletionList(items); + } + + public static implicit operator CompletionList(List> items) + { + return new CompletionList(items); + } + + public static implicit operator CompletionItem[] (CompletionList list) + { + return list.ToArray(); + } + + /// + /// Convert to a + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal CompletionList Convert(ISerializer serializer) + { + return new CompletionList(this.Select(z => z.To(serializer))); + } } } diff --git a/src/Protocol/Models/DocumentLink.cs b/src/Protocol/Models/DocumentLink.cs index e613a93e1..bff5805d0 100644 --- a/src/Protocol/Models/DocumentLink.cs +++ b/src/Protocol/Models/DocumentLink.cs @@ -1,8 +1,10 @@ +using System.ComponentModel; using System.Diagnostics; using MediatR; using Newtonsoft.Json.Linq; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; +using ISerializer = OmniSharp.Extensions.LanguageServer.Protocol.Serialization.ISerializer; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { @@ -25,7 +27,7 @@ public class DocumentLink : ICanBeResolved, IRequest [Optional] public DocumentUri Target { get; set; } - /// + /// /// A data entry field that is preserved on a document link between a /// DocumentLinkRequest and a DocumentLinkResolveRequest. /// @@ -47,5 +49,50 @@ public class DocumentLink : ICanBeResolved, IRequest private string DebuggerDisplay => $"{Range}{(Target != null ? $" {Target}" : "")}{(string.IsNullOrWhiteSpace(Tooltip) ? $" {Tooltip}" : "")}"; /// public override string ToString() => DebuggerDisplay; + + /// + /// Convert from a + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal DocumentLink From(ISerializer serializer) where T : class + { + return new DocumentLink() { + Range = Range, + Target = Target, + Tooltip = Tooltip, + Data = Data?.ToObject(serializer.JsonSerializer) + }; + } + } + /// + /// A document link is a range in a text document that links to an internal or external resource, like another + /// text document or a web site. + /// + public class DocumentLink : DocumentLink where T : class + { + /// + /// A data entry field that is preserved on a document link between a + /// DocumentLinkRequest and a DocumentLinkResolveRequest. + /// + [Optional] + public new T Data { get; set; } + + /// + /// Convert to a + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal DocumentLink To(ISerializer serializer) + { + if (Data != null) + { + base.Data = JObject.FromObject(Data, serializer.JsonSerializer); + } + + return this; + } } } diff --git a/src/Protocol/Models/DocumentLinkContainer.cs b/src/Protocol/Models/DocumentLinkContainer.cs index e8b2f6caf..5ecfc1516 100644 --- a/src/Protocol/Models/DocumentLinkContainer.cs +++ b/src/Protocol/Models/DocumentLinkContainer.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; using System.Linq; +using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { @@ -32,5 +34,55 @@ public static implicit operator DocumentLinkContainer(List items) { return new DocumentLinkContainer(items); } + + /// + /// Convert from a + /// + /// + /// + public DocumentLinkContainer Convert(ISerializer serializer) where T : class + { + return new DocumentLinkContainer(this.Select(z => z.From(serializer))); + } + } + public class DocumentLinkContainer : Container> where T : class + { + public DocumentLinkContainer() : this(Enumerable.Empty>()) + { + } + + public DocumentLinkContainer(IEnumerable> items) : base(items) + { + } + + public DocumentLinkContainer(params DocumentLink[] items) : base(items) + { + } + + public static implicit operator DocumentLinkContainer(DocumentLink[] items) + { + return new DocumentLinkContainer(items); + } + + public static implicit operator DocumentLinkContainer(Collection> items) + { + return new DocumentLinkContainer(items); + } + + public static implicit operator DocumentLinkContainer(List> items) + { + return new DocumentLinkContainer(items); + } + + /// + /// Convert to a + /// + /// + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal DocumentLinkContainer Convert(ISerializer serializer) + { + return new DocumentLinkContainer(this.Select(z => z.To(serializer))); + } } } diff --git a/src/Protocol/Models/ICanBeResolvedHandler.cs b/src/Protocol/Models/ICanBeResolvedHandler.cs index d4085b0ed..684f58fa6 100644 --- a/src/Protocol/Models/ICanBeResolvedHandler.cs +++ b/src/Protocol/Models/ICanBeResolvedHandler.cs @@ -1,3 +1,4 @@ +using System; using MediatR; using OmniSharp.Extensions.JsonRpc; @@ -7,14 +8,19 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Models /// Common interface for types that support resolution. /// /// - public interface ICanBeResolvedHandler : IJsonRpcRequestHandler + public interface ICanBeResolvedHandler : IJsonRpcRequestHandler, ICanBeIdentifiedHandler where T : ICanBeResolved, IRequest + { + } + + public interface ICanBeIdentifiedHandler { /// - /// Method that determines if a handler can be used to resolve this one + /// An id that that determines if a handler is unique or not for purposes of routing requests /// - /// - /// - bool CanResolve(T value); + /// + /// Some requests can "fan out" to multiple handlers to pull back data this allows them to fan out for the same document selector + /// + Guid Id { get; } } } diff --git a/src/Protocol/Serialization/Converters/AggregateCompletionListConverter.cs b/src/Protocol/Serialization/Converters/AggregateCompletionListConverter.cs new file mode 100644 index 000000000..0e6e8df80 --- /dev/null +++ b/src/Protocol/Serialization/Converters/AggregateCompletionListConverter.cs @@ -0,0 +1,50 @@ +using System; +using System.Linq; +using Newtonsoft.Json; +using OmniSharp.Extensions.JsonRpc; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; + +namespace OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters +{ + public class AggregateCompletionListConverter : JsonConverter> + { + public override void WriteJson(JsonWriter writer, AggregateResponse value, JsonSerializer serializer) + { + var values = value.Items.ToArray(); + + if (!values.Any(z => z.IsIncomplete)) + { + writer.WriteStartArray(); + foreach (var item in value.Items) + { + foreach (var v in item) + { + serializer.Serialize(writer, v); + } + } + writer.WriteEndArray(); + return; + } + + writer.WriteStartObject(); + writer.WritePropertyName("isIncomplete"); + writer.WriteValue(true); + + writer.WritePropertyName("items"); + writer.WriteStartArray(); + foreach (var item in value.Items) + { + foreach (var v in item) + { + serializer.Serialize(writer, v); + } + } + writer.WriteEndArray(); + writer.WriteEndObject(); + } + + public override bool CanRead => false; + + public override AggregateResponse ReadJson(JsonReader reader, Type objectType, AggregateResponse existingValue, bool hasExistingValue, JsonSerializer serializer) => throw new NotImplementedException(); + } +} diff --git a/src/Protocol/Serialization/Serializer.cs b/src/Protocol/Serialization/Serializer.cs index 82340d328..f474bba88 100644 --- a/src/Protocol/Serialization/Serializer.cs +++ b/src/Protocol/Serialization/Serializer.cs @@ -4,6 +4,7 @@ using System.Reflection; using Newtonsoft.Json; using OmniSharp.Extensions.JsonRpc.Serialization; +using OmniSharp.Extensions.JsonRpc.Serialization.Converters; using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; using OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters; using OmniSharp.Extensions.LanguageServer.Protocol.Models; @@ -112,6 +113,11 @@ protected override void AddOrReplaceConverters(ICollection conver ReplaceConverter(converters, new RangeOrPlaceholderRangeConverter()); ReplaceConverter(converters, new EnumLikeStringConverter()); ReplaceConverter(converters, new DocumentUriConverter()); + ReplaceConverter(converters, new AggregateConverter()); + ReplaceConverter(converters, new AggregateConverter()); + ReplaceConverter(converters, new AggregateConverter()); + ReplaceConverter(converters, new AggregateConverter()); + ReplaceConverter(converters, new AggregateCompletionListConverter()); base.AddOrReplaceConverters(converters); } diff --git a/src/Protocol/Workspace/IExecuteCommandHandler.cs b/src/Protocol/Workspace/IExecuteCommandHandler.cs index b16287dca..a99b92d14 100644 --- a/src/Protocol/Workspace/IExecuteCommandHandler.cs +++ b/src/Protocol/Workspace/IExecuteCommandHandler.cs @@ -190,128 +190,236 @@ public static Task ExecuteCommand(this IWorkspaceLanguageClient mediator, Comman public static Task ExecuteCommand(this ILanguageClient mediator, Command @params, CancellationToken cancellationToken = default) => mediator.ExecuteCommand(new ExecuteCommandParams() { Arguments = @params.Arguments, Command = @params.Name }, cancellationToken); - public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) { return registry.AddHandler(_ => new Handler(command, handler, _.GetRequiredService())); } + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + { + return registry.AddHandler(_ => new Handler( + command, + (arg1, capability, token) => handler(arg1, token), + _.GetRequiredService()) + ); + } + + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + { + return registry.AddHandler(_ => new Handler( + command, + (arg1, capability, token) => handler(arg1), + _.GetRequiredService()) + ); + } + class Handler : ExecuteCommandHandlerBase { - private readonly Func _handler; + private readonly Func _handler; - public Handler(string command, Func handler, ISerializer serializer) : base(command, serializer) + public Handler(string command, Func handler, ISerializer serializer) : base(command, serializer) { _handler = handler; } public override async Task Handle(T arg1, CancellationToken cancellationToken) { - await _handler(arg1); + await _handler(arg1, Capability, cancellationToken); return Unit.Value; } } - public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) { return registry.AddHandler(_ => new Handler(command, handler, _.GetRequiredService())); } + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + { + return registry.AddHandler(_ => new Handler( + command, + (arg1, arg2, capability, token) => handler(arg1, arg2, token), + _.GetRequiredService()) + ); + } + + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + { + return registry.AddHandler(_ => new Handler( + command, + (arg1, arg2, capability, token) => handler(arg1, arg2), + _.GetRequiredService()) + ); + } + class Handler : ExecuteCommandHandlerBase { - private readonly Func _handler; + private readonly Func _handler; - public Handler(string command, Func handler, ISerializer serializer) : base(command, serializer) + public Handler(string command, Func handler, ISerializer serializer) : base(command, serializer) { _handler = handler; } public override async Task Handle(T arg1, T2 arg2, CancellationToken cancellationToken) { - await _handler(arg1, arg2); + await _handler(arg1, arg2, Capability, cancellationToken); return Unit.Value; } } - public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) { return registry.AddHandler(_ => new Handler(command, handler, _.GetRequiredService())); } + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + { + return registry.AddHandler(_ => new Handler( + command, + (arg1, arg2, arg3, capability, token) => handler(arg1, arg2, arg3, token), + _.GetRequiredService()) + ); + } + + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + { + return registry.AddHandler(_ => new Handler( + command, + (arg1, arg2, arg3, capability, token) => handler(arg1, arg2, arg3), + _.GetRequiredService()) + ); + } + class Handler : ExecuteCommandHandlerBase { - private readonly Func _handler; + private readonly Func _handler; - public Handler(string command, Func handler, ISerializer serializer) : base(command, serializer) + public Handler(string command, Func handler, ISerializer serializer) : base(command, serializer) { _handler = handler; } public override async Task Handle(T arg1, T2 arg2, T3 arg3, CancellationToken cancellationToken) { - await _handler(arg1, arg2, arg3); + await _handler(arg1, arg2, arg3, Capability, cancellationToken); return Unit.Value; } } - public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) { return registry.AddHandler(_ => new Handler(command, handler, _.GetRequiredService())); } + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + { + return registry.AddHandler(_ => new Handler( + command, + (arg1, arg2, arg3, arg4, capability, token) => handler(arg1, arg2, arg3, arg4, token), + _.GetRequiredService()) + ); + } + + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + { + return registry.AddHandler(_ => new Handler( + command, + (arg1, arg2, arg3, arg4, capability, token) => handler(arg1, arg2, arg3, arg4), + _.GetRequiredService()) + ); + } + class Handler : ExecuteCommandHandlerBase { - private readonly Func _handler; + private readonly Func _handler; - public Handler(string command, Func handler, ISerializer serializer) : base(command, serializer) + public Handler(string command, Func handler, ISerializer serializer) : base(command, serializer) { _handler = handler; } public override async Task Handle(T arg1, T2 arg2, T3 arg3, T4 arg4, CancellationToken cancellationToken) { - await _handler(arg1, arg2, arg3, arg4); + await _handler(arg1, arg2, arg3, arg4, Capability, cancellationToken); return Unit.Value; } } - public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) { return registry.AddHandler(_ => new Handler(command, handler, _.GetRequiredService())); } + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + { + return registry.AddHandler(_ => new Handler( + command, + (arg1, arg2, arg3, arg4, arg5, capability, token) => handler(arg1, arg2, arg3, arg4, arg5, token), + _.GetRequiredService()) + ); + } + + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + { + return registry.AddHandler(_ => new Handler( + command, + (arg1, arg2, arg3, arg4, arg5, capability, token) => handler(arg1, arg2, arg3, arg4, arg5), + _.GetRequiredService()) + ); + } + class Handler : ExecuteCommandHandlerBase { - private readonly Func _handler; + private readonly Func _handler; - public Handler(string command, Func handler, ISerializer serializer) : base(command, serializer) + public Handler(string command, Func handler, ISerializer serializer) : base(command, serializer) { _handler = handler; } public override async Task Handle(T arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, CancellationToken cancellationToken) { - await _handler(arg1, arg2, arg3, arg4, arg5); + await _handler(arg1, arg2, arg3, arg4, arg5, Capability, cancellationToken); return Unit.Value; } } - public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) { return registry.AddHandler(_ => new Handler(command, handler, _.GetRequiredService())); } + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + { + return registry.AddHandler(_ => new Handler( + command, + (arg1, arg2, arg3, arg4, arg5, arg6, capability, token) => handler(arg1, arg2, arg3, arg4, arg5, arg6, token), + _.GetRequiredService()) + ); + } + + public static ILanguageServerRegistry OnExecuteCommand(this ILanguageServerRegistry registry, string command, Func handler) + { + return registry.AddHandler(_ => new Handler( + command, + (arg1, arg2, arg3, arg4, arg5, arg6, capability, token) => handler(arg1, arg2, arg3, arg4, arg5, arg6), + _.GetRequiredService()) + ); + } + class Handler : ExecuteCommandHandlerBase { - private readonly Func _handler; + private readonly Func _handler; - public Handler(string command, Func handler, ISerializer serializer) : base(command, serializer) + public Handler(string command, Func handler, ISerializer serializer) : base(command, serializer) { _handler = handler; } public override async Task Handle(T arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, CancellationToken cancellationToken) { - await _handler(arg1, arg2, arg3, arg4, arg5, arg6); + await _handler(arg1, arg2, arg3, arg4, arg5, arg6, Capability, cancellationToken); return Unit.Value; } } diff --git a/src/Server/Matchers/ResolveCommandMatcher.cs b/src/Server/Matchers/ResolveCommandMatcher.cs index 62a6121f9..b63168a7e 100644 --- a/src/Server/Matchers/ResolveCommandMatcher.cs +++ b/src/Server/Matchers/ResolveCommandMatcher.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -13,8 +14,7 @@ namespace OmniSharp.Extensions.LanguageServer.Server.Matchers public class ResolveCommandMatcher : IHandlerMatcher { private readonly ILogger _logger; - internal static string PrivateHandlerTypeName = "$$___handlerType___$$"; - internal static string PrivateHandlerKey = "$$___handlerKey___$$"; + internal static string PrivateHandlerId = "$$__handler_id__$$"; public ResolveCommandMatcher(ILogger logger) { @@ -31,64 +31,20 @@ public IEnumerable FindHandler(object parameters, IEnumer { if (parameters is ICanBeResolved canBeResolved) { - string handlerType = null; - string handlerKey = null; - if (canBeResolved.Data != null && canBeResolved.Data.Type == JTokenType.Object) + if (canBeResolved.Data != null && canBeResolved.Data is JObject jObject && jObject.TryGetValue(PrivateHandlerId, out var value)) { - handlerType = canBeResolved.Data?[PrivateHandlerTypeName]?.ToString(); - handlerKey = canBeResolved.Data?[PrivateHandlerKey]?.ToString(); - } + var id = value.Value(); - if (string.IsNullOrWhiteSpace(handlerType) && - string.IsNullOrWhiteSpace(handlerKey)) - { foreach (var descriptor in descriptors) { - if (descriptor.Params == parameters.GetType()) - // if (descriptor.CanBeResolvedHandlerType?.GetTypeInfo().IsAssignableFrom(descriptor.ImplementationType) == true) + _logger.LogTrace("Checking handler {Method}:{Handler}", descriptor.Method, descriptor.ImplementationType.FullName); + if (descriptor.Handler is ICanBeIdentifiedHandler handler && handler.Id != Guid.Empty && handler.Id == id) { - var method = CanResolveMethod - .MakeGenericMethod(descriptor.Params); - if ((bool)method.Invoke(null, new[] { descriptor.Handler, parameters })) - { - yield return descriptor; - yield break; - } + yield return descriptor; } } - - var descriptor2 = descriptors.FirstOrDefault(); - _logger.LogTrace( - "Resolve {Method} was called, but data did not have handle type defined. Using Handler {HandlerType}", - descriptor2?.Method, - descriptor2?.ImplementationType.FullName - ); - - yield return descriptor2; - yield break; - } - foreach (var descriptor in descriptors) - { - _logger.LogTrace("Checking handler {Method}:{Handler}", - descriptor.Method, - descriptor.ImplementationType.FullName); - if ((descriptor.ImplementationType.FullName == handlerType || descriptor.HandlerType.FullName == handlerType) && - ((descriptor is LspHandlerDescriptor handlerDescriptor) && handlerDescriptor.Key == handlerKey)) - { - yield return descriptor; - } } } } - - private static readonly MethodInfo CanResolveMethod = - typeof(ResolveCommandMatcher).GetTypeInfo() - .GetMethod(nameof(CanResolve), BindingFlags.NonPublic | BindingFlags.Static); - - private static bool CanResolve(ICanBeResolvedHandler handler, T value) - where T : ICanBeResolved, IRequest - { - return handler.CanResolve(value); - } } } diff --git a/src/Server/Matchers/TextDocumentMatcher.cs b/src/Server/Matchers/TextDocumentMatcher.cs index fec9ccd53..41745e805 100644 --- a/src/Server/Matchers/TextDocumentMatcher.cs +++ b/src/Server/Matchers/TextDocumentMatcher.cs @@ -62,14 +62,12 @@ private List GetTextDocumentAttributes(DocumentUri uri) return _textDocumentIdentifiers .Select(x => x.GetTextDocumentAttributes(uri)) .Where(x => x != null) - .Distinct() .ToList(); } private IEnumerable GetHandler(IEnumerable descriptors, IEnumerable attributes) { - return attributes - .SelectMany(x => GetHandler(descriptors, x)); + return attributes.SelectMany(z => GetHandler(descriptors, z)).Distinct(); } private IEnumerable GetHandler(IEnumerable descriptors, TextDocumentAttributes attributes) diff --git a/src/Server/Pipelines/ResolveCommandPipeline.cs b/src/Server/Pipelines/ResolveCommandPipeline.cs index ab1c0bc91..8b28443fc 100644 --- a/src/Server/Pipelines/ResolveCommandPipeline.cs +++ b/src/Server/Pipelines/ResolveCommandPipeline.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Reflection; using System.Threading; @@ -14,8 +15,7 @@ namespace OmniSharp.Extensions.LanguageServer.Server.Pipelines public class ResolveCommandPipeline : IPipelineBehavior { private readonly ILogger> _logger; - internal static string PrivateHandlerTypeName = "$$___handlerType___$$"; - internal static string PrivateHandlerKey = "$$___handlerKey___$$"; + internal static string PrivateHandlerId = "$$__handler_id__$$"; private readonly ILspHandlerDescriptor _descriptor; public ResolveCommandPipeline(IRequestContext context, ILogger> logger) @@ -26,18 +26,6 @@ public ResolveCommandPipeline(IRequestContext context, ILogger Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate next) { - if (request is ICanBeResolved canBeResolved) - { - string handlerType = null; - if (canBeResolved.Data != null && canBeResolved.Data.Type == JTokenType.Object) - handlerType = canBeResolved.Data?[PrivateHandlerTypeName]?.ToString(); - - if (!string.IsNullOrWhiteSpace(handlerType)) - { - canBeResolved.Data = canBeResolved.Data["data"]; - } - } - var response = await next(); cancellationToken.ThrowIfCancellationRequested(); @@ -52,14 +40,8 @@ response is IEnumerable canBeResolveds && _descriptor.ImplementationType.FullName); foreach (var item in canBeResolveds) { - // Originally we were going to change Data to be a JObject instead of JToken - // This allows us to leave data alone by simply wrapping it - // Since we're always going to intercept these items, we can control this. - var data = new JObject(); - data["data"] = item.Data; - data[PrivateHandlerTypeName] = _descriptor.ImplementationType.FullName; - data[PrivateHandlerKey] = handlerDescriptor.Key; - item.Data = data; + item.Data ??= new JObject(); + item.Data[PrivateHandlerId] = _descriptor.Handler is ICanBeIdentifiedHandler resolved ? resolved.Id : Guid.Empty; } } diff --git a/src/Shared/LspRequestRouter.cs b/src/Shared/LspRequestRouter.cs index 7b59bed39..5956399f8 100644 --- a/src/Shared/LspRequestRouter.cs +++ b/src/Shared/LspRequestRouter.cs @@ -17,7 +17,7 @@ namespace OmniSharp.Extensions.LanguageServer.Shared internal class LspRequestRouter : RequestRouterBase, IRequestRouter { private readonly IHandlerCollection _collection; - private readonly IEnumerable _handlerMatchers; + private readonly HashSet _handlerMatchers; public LspRequestRouter( IHandlerCollection collection, @@ -29,63 +29,62 @@ public LspRequestRouter( base(serializer, serviceProvider, serviceScopeFactory, loggerFactory.CreateLogger()) { _collection = collection; - _handlerMatchers = handlerMatchers; + _handlerMatchers = new HashSet(handlerMatchers); } - public override ILspHandlerDescriptor GetDescriptor(Notification notification) + public override IRequestDescriptor GetDescriptors(Notification notification) { return FindDescriptor(notification); } - public override ILspHandlerDescriptor GetDescriptor(Request request) + public override IRequestDescriptor GetDescriptors(Request request) { return FindDescriptor(request); } - private ILspHandlerDescriptor FindDescriptor(IMethodWithParams instance) + private IRequestDescriptor FindDescriptor(IMethodWithParams instance) { return FindDescriptor(instance.Method, instance.Params); } - private ILspHandlerDescriptor FindDescriptor(string method, JToken @params) + private IRequestDescriptor FindDescriptor(string method, JToken @params) { - _logger.LogDebug("Finding descriptor for {Method}", method); + _logger.LogDebug("Finding descriptors for {Method}", method); var descriptor = _collection.FirstOrDefault(x => x.Method == method); if (descriptor is null) { _logger.LogDebug("Unable to find {Method}, methods found include {Methods}", method, string.Join(", ", _collection.Select(x => x.Method + ":" + x.Handler?.GetType()?.FullName))); - return null; + return new RequestDescriptor(); } - - if (@params == null || descriptor.Params == null) return descriptor; + if (@params == null || descriptor.Params == null) return new RequestDescriptor(new [] { descriptor }); var lspHandlerDescriptors = _collection.Where(handler => handler.Method == method).ToList(); var paramsValue = @params.ToObject(descriptor.Params, _serializer.JsonSerializer); - var matchDescriptor = _handlerMatchers.SelectMany(strat => strat.FindHandler(paramsValue, lspHandlerDescriptors)).FirstOrDefault(); - if (matchDescriptor != null) return matchDescriptor; + var matchDescriptor = _handlerMatchers.SelectMany(strat => strat.FindHandler(paramsValue, lspHandlerDescriptors)).ToArray(); + if (matchDescriptor.Length > 0) return new RequestDescriptor(matchDescriptor); // execute command is a special case // if no command was found to execute this must error // this is not great coupling but other options require api changes - if (paramsValue is ExecuteCommandParams) return null; - if (lspHandlerDescriptors.Count == 1) return descriptor; - return null; + if (paramsValue is ExecuteCommandParams) return new RequestDescriptor(); + if (lspHandlerDescriptors.Count > 0) return new RequestDescriptor(lspHandlerDescriptors); + return new RequestDescriptor(); } - IHandlerDescriptor IRequestRouter.GetDescriptor(Notification notification) => GetDescriptor(notification); - IHandlerDescriptor IRequestRouter.GetDescriptor(Request request) => GetDescriptor(request); + IRequestDescriptor IRequestRouter.GetDescriptors(Notification notification) => GetDescriptors(notification); + IRequestDescriptor IRequestRouter.GetDescriptors(Request request) => GetDescriptors(request); - Task IRequestRouter.RouteNotification(IHandlerDescriptor descriptor, Notification notification, CancellationToken token) => + Task IRequestRouter.RouteNotification(IRequestDescriptor descriptors, Notification notification, CancellationToken token) => RouteNotification( - descriptor is ILspHandlerDescriptor d ? d : throw new Exception("This should really never happen, seriously, only hand this correct descriptors"), + descriptors is IRequestDescriptor d ? d : throw new Exception("This should really never happen, seriously, only hand this correct descriptors"), notification, token); - Task IRequestRouter.RouteRequest(IHandlerDescriptor descriptor, Request request, CancellationToken token) => + Task IRequestRouter.RouteRequest(IRequestDescriptor descriptors, Request request, CancellationToken token) => RouteRequest( - descriptor is ILspHandlerDescriptor d ? d : throw new Exception("This should really never happen, seriously, only hand this correct descriptors"), + descriptors is IRequestDescriptor d ? d : throw new Exception("This should really never happen, seriously, only hand this correct descriptors"), request, token); } diff --git a/src/Shared/SharedHandlerCollection.cs b/src/Shared/SharedHandlerCollection.cs index efb060ec4..02f33164b 100644 --- a/src/Shared/SharedHandlerCollection.cs +++ b/src/Shared/SharedHandlerCollection.cs @@ -269,6 +269,11 @@ private LspHandlerDescriptor GetDescriptor(string method, Type handlerType, IJso if (string.IsNullOrWhiteSpace(key)) key = "default"; + if (handler is ICanBeIdentifiedHandler identifiedHandler && identifiedHandler.Id != Guid.Empty) + { + key += ":" + identifiedHandler.Id.ToString("N"); + } + var requestProcessType = options?.RequestProcessType ?? typeDescriptor?.RequestProcessType ?? diff --git a/src/Shared/SupportedCapabilities.cs b/src/Shared/SupportedCapabilities.cs index bbd05579f..1712f4f43 100644 --- a/src/Shared/SupportedCapabilities.cs +++ b/src/Shared/SupportedCapabilities.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Reflection; using OmniSharp.Extensions.JsonRpc; @@ -42,7 +42,7 @@ public bool AllowsDynamicRegistration(Type capabilityType) public void SetCapability(ILspHandlerDescriptor descriptor, IJsonRpcHandler handler) { if (!descriptor.HasCapability) return; - if (descriptor.CapabilityType == null ||!handler.GetType().IsInstanceOfType(descriptor.CapabilityType)) return; + if (descriptor.CapabilityType == null ||!typeof(ICapability<>).MakeGenericType(descriptor.CapabilityType).IsInstanceOfType(handler)) return; if (_supports.TryGetValue(descriptor.CapabilityType, out var capability)) { diff --git a/test/JsonRpc.Tests/InputHandlerTests.cs b/test/JsonRpc.Tests/InputHandlerTests.cs index 0574fcae9..ed3ec30a2 100644 --- a/test/JsonRpc.Tests/InputHandlerTests.cs +++ b/test/JsonRpc.Tests/InputHandlerTests.cs @@ -357,7 +357,7 @@ public async Task Should_Parse_Logs(string name, Func createPipeRead { var count = group.Count(x => x == "request"); await incomingRequestRouter.Received(count).RouteRequest( - Arg.Any(), + Arg.Any>(), Arg.Is(n => group.Key == n.Method), Arg.Any() ); @@ -367,7 +367,7 @@ await incomingRequestRouter.Received(count).RouteRequest( { var count = group.Count(x => x == "notification"); await incomingRequestRouter.Received(count).RouteNotification( - Arg.Any(), + Arg.Any>(), Arg.Is(n => group.Key == n.Method), Arg.Any() ); diff --git a/test/JsonRpc.Tests/MediatorTestsNotificationHandler.cs b/test/JsonRpc.Tests/MediatorTestsNotificationHandler.cs index 31b94292f..746b049d2 100644 --- a/test/JsonRpc.Tests/MediatorTestsNotificationHandler.cs +++ b/test/JsonRpc.Tests/MediatorTestsNotificationHandler.cs @@ -34,7 +34,7 @@ public async Task ExecutesHandler() var notification = new Notification("exit", null); - await router.RouteNotification(router.GetDescriptor(notification), notification, CancellationToken.None); + await router.RouteNotification(router.GetDescriptors(notification), notification, CancellationToken.None); await exitHandler.Received(1).Handle(Arg.Any(), Arg.Any()); } diff --git a/test/JsonRpc.Tests/MediatorTestsNotificationHandlerOfT.cs b/test/JsonRpc.Tests/MediatorTestsNotificationHandlerOfT.cs index 3f76721c8..5e50d0599 100644 --- a/test/JsonRpc.Tests/MediatorTestsNotificationHandlerOfT.cs +++ b/test/JsonRpc.Tests/MediatorTestsNotificationHandlerOfT.cs @@ -45,7 +45,7 @@ public async Task ExecutesHandler() var @params = new CancelParams() { Id = Guid.NewGuid() }; var notification = new Notification("$/cancelRequest", JObject.Parse(JsonConvert.SerializeObject(@params))); - await router.RouteNotification(router.GetDescriptor(notification), notification, CancellationToken.None); + await router.RouteNotification(router.GetDescriptors(notification), notification, CancellationToken.None); await cancelRequestHandler.Received(1).Handle(Arg.Any(), Arg.Any()); } diff --git a/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequest.cs b/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequest.cs index c89326bd1..974cfd327 100644 --- a/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequest.cs +++ b/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequest.cs @@ -46,7 +46,7 @@ public async Task ExecutesHandler() var @params = new ExecuteCommandParams() { Command = "123" }; var request = new Request(id, "workspace/executeCommand", JObject.Parse(JsonConvert.SerializeObject(@params))); - var response = await router.RouteRequest(router.GetDescriptor(request), request, CancellationToken.None); + var response = await router.RouteRequest(router.GetDescriptors(request), request, CancellationToken.None); await executeCommandHandler.Received(1).Handle(Arg.Any(), Arg.Any()); diff --git a/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequestTResponse.cs b/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequestTResponse.cs index e134f55b7..1215912ac 100644 --- a/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequestTResponse.cs +++ b/test/JsonRpc.Tests/MediatorTestsRequestHandlerOfTRequestTResponse.cs @@ -56,7 +56,7 @@ public async Task ExecutesHandler() var @params = new CodeActionParams() { TextDocument = "TextDocument", Range = "Range", Context = "Context" }; var request = new Request(id, "textDocument/codeAction", JObject.Parse(JsonConvert.SerializeObject(@params))); - var response = await router.RouteRequest(router.GetDescriptor(request), request, CancellationToken.None); + var response = await router.RouteRequest(router.GetDescriptors(request), request, CancellationToken.None); await codeActionHandler.Received(1).Handle(Arg.Any(), Arg.Any()); } diff --git a/test/JsonRpc.Tests/RequestRouterTests.cs b/test/JsonRpc.Tests/RequestRouterTests.cs index 77c602816..15d931654 100644 --- a/test/JsonRpc.Tests/RequestRouterTests.cs +++ b/test/JsonRpc.Tests/RequestRouterTests.cs @@ -98,7 +98,7 @@ public async Task ShouldRoute_CustomRequestResponse() registry.Populate(collection, ServiceProvider); var request = new Request(Guid.NewGuid().ToString(), "$/my/something/awesome", "123123123123"); - await mediator.RouteRequest(mediator.GetDescriptor(request), request, CancellationToken.None); + await mediator.RouteRequest(mediator.GetDescriptors(request), request, CancellationToken.None); await method.Received(1).Invoke(Arg.Any()); } @@ -118,7 +118,7 @@ public async Task ShouldRoute_CustomRequest() registry.Populate(collection, ServiceProvider); var request = new Request(Guid.NewGuid().ToString(), "$/my/something/awesome", "123123123123"); - await mediator.RouteRequest(mediator.GetDescriptor(request), request, CancellationToken.None); + await mediator.RouteRequest(mediator.GetDescriptors(request), request, CancellationToken.None); await method.Received(1).Invoke(Arg.Any()); } @@ -137,7 +137,7 @@ public async Task ShouldRoute_CustomNotification() registry.Populate(collection, ServiceProvider); var notification = new Notification("$/my/something/awesome", "123123123123"); - await mediator.RouteNotification(mediator.GetDescriptor(notification), notification, CancellationToken.None); + await mediator.RouteNotification(mediator.GetDescriptors(notification), notification, CancellationToken.None); method.Received(1).Invoke(Arg.Any()); } @@ -156,7 +156,7 @@ public async Task ShouldRoute_CustomEmptyNotification() registry.Populate(collection, ServiceProvider); var notification = new Notification("$/my/something/awesome", null); - await mediator.RouteNotification(mediator.GetDescriptor(notification), notification, CancellationToken.None); + await mediator.RouteNotification(mediator.GetDescriptors(notification), notification, CancellationToken.None); method.Received(1).Invoke(); } diff --git a/test/Lsp.Tests/FoundationTests.cs b/test/Lsp.Tests/FoundationTests.cs index 162e6ff21..2bb4f6517 100644 --- a/test/Lsp.Tests/FoundationTests.cs +++ b/test/Lsp.Tests/FoundationTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -52,6 +52,7 @@ class DebuggerDisplayTypes : TheoryData public DebuggerDisplayTypes() { foreach (var item in typeof(DocumentSymbol).Assembly.ExportedTypes + .Where(z => !z.IsGenericTypeDefinition) .Where(z => z.GetCustomAttributes().Any(z => z.Value.StartsWith("{DebuggerDisplay"))) .Where(z => z.GetConstructors().Any(z => z.GetParameters().Length == 0)) ) diff --git a/test/Lsp.Tests/Integration/DynamicRegistrationTests.cs b/test/Lsp.Tests/Integration/DynamicRegistrationTests.cs index a85bc7e1d..1d478c3a1 100644 --- a/test/Lsp.Tests/Integration/DynamicRegistrationTests.cs +++ b/test/Lsp.Tests/Integration/DynamicRegistrationTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Reactive.Linq; using System.Reactive.Threading.Tasks; @@ -170,12 +170,7 @@ public async Task Should_Register_Static_When_Dynamic_Is_Disabled() }, ConfigureServer); client.ServerSettings.Capabilities.CompletionProvider.Should().BeEquivalentTo(new CompletionOptions() { - ResolveProvider = false, - TriggerCharacters = new Container("a", "b"), - AllCommitCharacters = new Container("1", "2"), - }, x => x.Excluding(z => z.WorkDoneProgress)); - server.ServerSettings.Capabilities.CompletionProvider.Should().BeEquivalentTo(new CompletionOptions() { - ResolveProvider = false, + ResolveProvider = true, TriggerCharacters = new Container("a", "b"), AllCommitCharacters = new Container("1", "2"), }, x => x.Excluding(z => z.WorkDoneProgress)); diff --git a/test/Lsp.Tests/Integration/ExecuteCommandTests.cs b/test/Lsp.Tests/Integration/ExecuteCommandTests.cs index c34db9c91..ec82648dd 100644 --- a/test/Lsp.Tests/Integration/ExecuteCommandTests.cs +++ b/test/Lsp.Tests/Integration/ExecuteCommandTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using FluentAssertions; using Newtonsoft.Json.Linq; @@ -8,6 +9,8 @@ using OmniSharp.Extensions.JsonRpc.Server; using OmniSharp.Extensions.JsonRpc.Testing; using OmniSharp.Extensions.LanguageProtocol.Testing; +using OmniSharp.Extensions.LanguageServer.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.Workspace; @@ -60,9 +63,13 @@ public async Task Should_Execute_A_Command() public async Task Should_Execute_The_Correct_Command() { var commanda = Substitute.For>(); - var commandb = Substitute.For>(); + var commandb = Substitute.For>(); var (client, server) = await Initialize( - options => { }, options => { + options => { + options.WithCapability(new ExecuteCommandCapability() { + DynamicRegistration = false + }); + }, options => { options.OnCompletion(x => { return Task.FromResult(new CompletionList(new CompletionItem() { Command = new Command() { @@ -91,7 +98,9 @@ public async Task Should_Execute_The_Correct_Command() await client.ExecuteCommand(item.Command); await commanda.Received(0).Invoke(Arg.Any()); - await commandb.Received(1).Invoke(Arg.Any()); + await commandb.Received(1).Invoke(Arg.Any(), Arg.Any(), Arg.Any()); + var arg = commandb.ReceivedCalls().Single().GetArguments()[1]; + arg.Should().BeOfType(); } [Fact] diff --git a/test/Lsp.Tests/Integration/PartialItemTests.cs b/test/Lsp.Tests/Integration/PartialItemTests.cs index cb90cf8f8..c785561dc 100644 --- a/test/Lsp.Tests/Integration/PartialItemTests.cs +++ b/test/Lsp.Tests/Integration/PartialItemTests.cs @@ -201,7 +201,7 @@ public override async Task Handle(CodeLensParams request, Can public override Task Handle(CodeLens request, CancellationToken cancellationToken) => Task.FromResult(request); - public override bool CanResolve(CodeLens value) => true; + public override Guid Id { get; } = Guid.NewGuid(); } } diff --git a/test/Lsp.Tests/Integration/TypedCodeLensTests.cs b/test/Lsp.Tests/Integration/TypedCodeLensTests.cs new file mode 100644 index 000000000..9bddff301 --- /dev/null +++ b/test/Lsp.Tests/Integration/TypedCodeLensTests.cs @@ -0,0 +1,582 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Reactive.Threading.Tasks; +using System.Threading.Tasks; +using FluentAssertions; +using Newtonsoft.Json.Linq; +using NSubstitute; +using OmniSharp.Extensions.JsonRpc.Testing; +using OmniSharp.Extensions.LanguageProtocol.Testing; +using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Document; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Protocol.Workspace; +using Serilog.Events; +using Xunit; +using Xunit.Abstractions; + +namespace Lsp.Tests.Integration +{ + public class TypedCodeLensTests : LanguageProtocolTestBase + { + + public TypedCodeLensTests(ITestOutputHelper outputHelper) : base(new JsonRpcTestOptions().ConfigureForXUnit(outputHelper, LogEventLevel.Verbose)) + { + } + + [Fact] + public async Task Should_Aggregate_With_All_Related_Handlers() + { + + var (client, server) = await Initialize( + options => { }, options => { + var identifier = Substitute.For(); + identifier.GetTextDocumentAttributes(Arg.Any()).Returns( + call => new TextDocumentAttributes(call.ArgAt(0), "file", "csharp")); + options.AddTextDocumentIdentifier(identifier); + + options.OnCodeLens((codeLensParams) => { + return Task.FromResult(new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "data-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + })); + }, + (codeLens) => { + codeLens.Command.Name = "resolved-a"; + return Task.FromResult(codeLens); + }, + + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + }); + + options.OnCodeLens((codeLensParams) => { + return Task.FromResult(new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "nested-b", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Nested() { + Date = DateTimeOffset.Now + } + })); + }, + (codeLens) => { + codeLens.Command.Name = "resolved-b"; + return Task.FromResult(codeLens); + }, + + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + }); + + options.OnCodeLens((codeLensParams) => { + return Task.FromResult(new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "no-data-c", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + } + })); + }, + (codeLens) => { + codeLens.Command.Name = "resolved-c"; + return Task.FromResult(codeLens); + }, + + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + }); + + options.OnCodeLens((codeLensParams) => { + return Task.FromResult(new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "not-included", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + } + })); + }, + (codeLens) => { + codeLens.Command.Name = "resolved-d"; + return Task.FromResult(codeLens); + }, + + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForLanguage("vb") + }); + }); + + var codeLens = await client.RequestCodeLens(new CodeLensParams() { + TextDocument = new TextDocumentIdentifier("/some/path/file.cs"), + }); + + var lens = codeLens.ToArray(); + + var responses = await Task.WhenAll(lens.Select(z => client.ResolveCodeLens(z))); + responses.Select(z => z.Command.Name).Should().Contain("resolved-a", "resolved-b", "resolved-c"); + responses.Select(z => z.Command.Name).Should().NotContain("resolved-d"); + lens.Length.Should().Be(3); + } + + [Fact] + public async Task Should_Resolve_With_Data_Capability() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCodeLens((codeLensParams, capability, token) => { + return Task.FromResult(new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + })); + }, + (codeLens, capability, token) => { + codeLens.Data.Id.Should().NotBeEmpty(); + codeLens.Data.Child.Should().NotBeNull(); + codeLens.Data.Name.Should().Be("name"); + codeLens.Command.Name = "resolved"; + return Task.FromResult(codeLens); + }, + + new CodeLensRegistrationOptions() { + }); + }); + + var items = await client.RequestCodeLens(new CodeLensParams()); + + var item = items.Single(); + + item = await client.ResolveCodeLens(item); + item.Command.Name.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_With_Partial_Data_Capability() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCodeLens((codeLensParams, observer, capability, token) => { + var a = new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + (codeLens, capability, token) => { + codeLens.Data.Id.Should().NotBeEmpty(); + codeLens.Data.Child.Should().NotBeNull(); + codeLens.Data.Name.Should().Be("name"); + codeLens.Command.Name = "resolved"; + return Task.FromResult(codeLens); + }, + + new CodeLensRegistrationOptions() { + }); + }); + + var item = await client.RequestCodeLens(new CodeLensParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveCodeLens(item); + item.Command.Name.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_With_Data_CancellationToken() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCodeLens((codeLensParams, token) => { + return Task.FromResult(new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + })); + }, + (codeLens, token) => { + codeLens.Data.Id.Should().NotBeEmpty(); + codeLens.Data.Child.Should().NotBeNull(); + codeLens.Data.Name.Should().Be("name"); + codeLens.Command.Name = "resolved"; + return Task.FromResult(codeLens); + }, + + new CodeLensRegistrationOptions() { + }); + }); + + var items = await client.RequestCodeLens(new CodeLensParams()); + + var item = items.Single(); + + item = await client.ResolveCodeLens(item); + item.Command.Name.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_With_Partial_Data_CancellationToken() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCodeLens((codeLensParams, observer, token) => { + var a = new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + (codeLens, token) => { + codeLens.Data.Id.Should().NotBeEmpty(); + codeLens.Data.Child.Should().NotBeNull(); + codeLens.Data.Name.Should().Be("name"); + codeLens.Command.Name = "resolved"; + return Task.FromResult(codeLens); + }, + + new CodeLensRegistrationOptions() { + }); + }); + + var item = await client.RequestCodeLens(new CodeLensParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveCodeLens(item); + item.Command.Name.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_With_Data() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCodeLens((codeLensParams) => { + return Task.FromResult(new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + })); + }, + (codeLens) => { + codeLens.Data.Id.Should().NotBeEmpty(); + codeLens.Data.Child.Should().NotBeNull(); + codeLens.Data.Name.Should().Be("name"); + codeLens.Command.Name = "resolved"; + return Task.FromResult(codeLens); + }, + + new CodeLensRegistrationOptions() { + }); + }); + + var items = await client.RequestCodeLens(new CodeLensParams()); + + var item = items.Single(); + + item = await client.ResolveCodeLens(item); + item.Command.Name.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_With_Partial_Data() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCodeLens((codeLensParams, observer) => { + var a = new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + (codeLens) => { + codeLens.Data.Id.Should().NotBeEmpty(); + codeLens.Data.Child.Should().NotBeNull(); + codeLens.Data.Name.Should().Be("name"); + codeLens.Command.Name = "resolved"; + return Task.FromResult(codeLens); + }, + + new CodeLensRegistrationOptions() { + }); + }); + + var item = await client.RequestCodeLens(new CodeLensParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveCodeLens(item); + item.Command.Name.Should().Be("resolved"); + } + + + [Fact] + public async Task Should_Resolve_Capability() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCodeLens((codeLensParams, capability, token) => { + return Task.FromResult(new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + } + })); + }, + (codeLens, capability, token) => { + codeLens.Command.Name = "resolved"; + return Task.FromResult(codeLens); + }, + new CodeLensRegistrationOptions() { + }); + }); + + var items = await client.RequestCodeLens(new CodeLensParams()); + + var item = items.Single(); + + item = await client.ResolveCodeLens(item); + item.Command.Name.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_Partial_Capability() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCodeLens((codeLensParams, observer, capability, token) => { + var a = new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + + (codeLens, capability, token) => { + codeLens.Command.Name = "resolved"; + return Task.FromResult(codeLens); + }, + new CodeLensRegistrationOptions() { + }); + }); + + var item = await client.RequestCodeLens(new CodeLensParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveCodeLens(item); + item.Command.Name.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_CancellationToken() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCodeLens((codeLensParams, token) => { + return Task.FromResult(new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + })); + }, + (codeLens, token) => { + codeLens.Command.Name = "resolved"; + return Task.FromResult(codeLens); + }, + + new CodeLensRegistrationOptions() { + }); + }); + + var items = await client.RequestCodeLens(new CodeLensParams()); + + var item = items.Single(); + + item = await client.ResolveCodeLens(item); + item.Command.Name.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_Partial_CancellationToken() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCodeLens((codeLensParams, observer, token) => { + var a = new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + (codeLens, token) => { + codeLens.Command.Name = "resolved"; + return Task.FromResult(codeLens); + }, + + new CodeLensRegistrationOptions() { + }); + }); + + var item = await client.RequestCodeLens(new CodeLensParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveCodeLens(item); + item.Command.Name.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCodeLens((codeLensParams) => { + return Task.FromResult(new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + })); + }, + (codeLens) => { + codeLens.Command.Name = "resolved"; + return Task.FromResult(codeLens); + }, + + new CodeLensRegistrationOptions() { + }); + }); + + var items = await client.RequestCodeLens(new CodeLensParams()); + + var item = items.Single(); + + item = await client.ResolveCodeLens(item); + item.Command.Name.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_Partial() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCodeLens((codeLensParams, observer) => { + var a = new CodeLensContainer(new CodeLens() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + (codeLens) => { + codeLens.Command.Name = "resolved"; + return Task.FromResult(codeLens); + }, + + new CodeLensRegistrationOptions() { + }); + }); + + var item = await client.RequestCodeLens(new CodeLensParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveCodeLens(item); + item.Command.Name.Should().Be("resolved"); + } + + class Data + { + public string Name { get; set; } + public Guid Id { get; set; } + public Nested Child { get; set; } + + } + + class Nested + { + public DateTimeOffset Date { get; set; } + } + } +} diff --git a/test/Lsp.Tests/Integration/TypedCompletionTests.cs b/test/Lsp.Tests/Integration/TypedCompletionTests.cs new file mode 100644 index 000000000..2cbe30617 --- /dev/null +++ b/test/Lsp.Tests/Integration/TypedCompletionTests.cs @@ -0,0 +1,580 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Reactive.Threading.Tasks; +using System.Threading.Tasks; +using FluentAssertions; +using Newtonsoft.Json.Linq; +using NSubstitute; +using OmniSharp.Extensions.JsonRpc.Testing; +using OmniSharp.Extensions.LanguageProtocol.Testing; +using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Document; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using Serilog.Events; +using Xunit; +using Xunit.Abstractions; + +namespace Lsp.Tests.Integration +{ + public class TypedCompletionTests : LanguageProtocolTestBase + { + + public TypedCompletionTests(ITestOutputHelper outputHelper) : base(new JsonRpcTestOptions().ConfigureForXUnit(outputHelper, LogEventLevel.Verbose)) + { + } + + [Fact] + public async Task Should_Aggregate_With_All_Related_Handlers() + { + var (client, server) = await Initialize( + options => { }, options => { + var identifier = Substitute.For(); + identifier.GetTextDocumentAttributes(Arg.Any()).Returns( + call => new TextDocumentAttributes(call.ArgAt(0), "file", "csharp")); + options.AddTextDocumentIdentifier(identifier); + + options.OnCompletion((codeLensParams) => { + return Task.FromResult(new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "data-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + })); + }, + (codeLens) => { + codeLens.Command.Name = "resolved-a"; + return Task.FromResult(codeLens); + }, + + new CompletionRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + }); + + options.OnCompletion((codeLensParams) => { + return Task.FromResult(new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "nested-b", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Nested() { + Date = DateTimeOffset.Now + } + })); + }, + (codeLens) => { + codeLens.Command.Name = "resolved-b"; + return Task.FromResult(codeLens); + }, + + new CompletionRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + }); + + options.OnCompletion((codeLensParams) => { + return Task.FromResult(new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "no-data-c", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + } + })); + }, + (codeLens) => { + codeLens.Command.Name = "resolved-c"; + return Task.FromResult(codeLens); + }, + + new CompletionRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + }); + + options.OnCompletion((codeLensParams) => { + return Task.FromResult(new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "not-included", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + } + })); + }, + (codeLens) => { + codeLens.Command.Name = "resolved-d"; + return Task.FromResult(codeLens); + }, + + new CompletionRegistrationOptions() { + DocumentSelector = DocumentSelector.ForLanguage("vb") + }); + }); + + var codeLens = await client.RequestCompletion(new CompletionParams() { + TextDocument = new TextDocumentIdentifier("/some/path/file.cs"), + }); + + var lens = codeLens.ToArray(); + + var responses = await Task.WhenAll(lens.Select(z => client.ResolveCompletion(z))); + responses.Select(z => z.Command.Name).Should().Contain("resolved-a", "resolved-b", "resolved-c"); + responses.Select(z => z.Command.Name).Should().NotContain("resolved-d"); + lens.Length.Should().Be(3); + } + + [Fact] + public async Task Should_Resolve_With_Data_Capability() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCompletion((completionParams, capability, token) => { + return Task.FromResult(new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + })); + }, + (completionItem, capability, token) => { + AssertionExtensions.Should((Guid) completionItem.Data.Id).NotBeEmpty(); + AssertionExtensions.Should((object) completionItem.Data.Child).NotBeNull(); + AssertionExtensions.Should((string) completionItem.Data.Name).Be("name"); + completionItem.Detail = "resolved"; + return Task.FromResult>(completionItem); + }, + + new CompletionRegistrationOptions() { + }); + }); + + var items = await client.RequestCompletion(new CompletionParams()); + + var item = items.Items.Single(); + + item = await client.ResolveCompletion(item); + item.Detail.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_With_Partial_Data_Capability() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCompletion((completionParams, observer, capability, token) => { + var a = new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + (completionItem, capability, token) => { + completionItem.Data.Id.Should().NotBeEmpty(); + completionItem.Data.Child.Should().NotBeNull(); + completionItem.Data.Name.Should().Be("name"); + completionItem.Detail = "resolved"; + return Task.FromResult(completionItem); + }, + + new CompletionRegistrationOptions() { + }); + }); + + var item = await client.RequestCompletion(new CompletionParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveCompletion(item); + item.Detail.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_With_Data_CancellationToken() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCompletion((completionParams, token) => { + return Task.FromResult(new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + })); + }, + (completionItem, token) => { + completionItem.Data.Id.Should().NotBeEmpty(); + completionItem.Data.Child.Should().NotBeNull(); + completionItem.Data.Name.Should().Be("name"); + completionItem.Detail = "resolved"; + return Task.FromResult(completionItem); + }, + + new CompletionRegistrationOptions() { + }); + }); + + var items = await client.RequestCompletion(new CompletionParams()); + + var item = items.Items.Single(); + + item = await client.ResolveCompletion(item); + item.Detail.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_With_Partial_Data_CancellationToken() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCompletion((completionParams, observer, token) => { + var a = new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + (completionItem, token) => { + completionItem.Data.Id.Should().NotBeEmpty(); + completionItem.Data.Child.Should().NotBeNull(); + completionItem.Data.Name.Should().Be("name"); + completionItem.Detail = "resolved"; + return Task.FromResult(completionItem); + }, + + new CompletionRegistrationOptions() { + }); + }); + + var item = await client.RequestCompletion(new CompletionParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveCompletion(item); + item.Detail.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_With_Data() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCompletion((completionParams) => { + return Task.FromResult(new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + })); + }, + (completionItem) => { + completionItem.Data.Id.Should().NotBeEmpty(); + completionItem.Data.Child.Should().NotBeNull(); + completionItem.Data.Name.Should().Be("name"); + completionItem.Detail = "resolved"; + return Task.FromResult(completionItem); + }, + + new CompletionRegistrationOptions() { + }); + }); + + var items = await client.RequestCompletion(new CompletionParams()); + + var item = items.Items.Single(); + + item = await client.ResolveCompletion(item); + item.Detail.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_With_Partial_Data() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCompletion((completionParams, observer) => { + var a = new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + (completionItem) => { + completionItem.Data.Id.Should().NotBeEmpty(); + completionItem.Data.Child.Should().NotBeNull(); + completionItem.Data.Name.Should().Be("name"); + completionItem.Detail = "resolved"; + return Task.FromResult(completionItem); + }, + + new CompletionRegistrationOptions() { + }); + }); + + var item = await client.RequestCompletion(new CompletionParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveCompletion(item); + item.Detail.Should().Be("resolved"); + } + + + [Fact] + public async Task Should_Resolve_Capability() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCompletion((completionParams, capability, token) => { + return Task.FromResult(new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + } + })); + }, + (completionItem, capability, token) => { + completionItem.Detail = "resolved"; + return Task.FromResult(completionItem); + }, + new CompletionRegistrationOptions() { + }); + }); + + var items = await client.RequestCompletion(new CompletionParams()); + + var item = items.Items.Single(); + + item = await client.ResolveCompletion(item); + item.Detail.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_Partial_Capability() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCompletion((completionParams, observer, capability, token) => { + var a = new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + + (completionItem, capability, token) => { + completionItem.Detail = "resolved"; + return Task.FromResult(completionItem); + }, + new CompletionRegistrationOptions() { + }); + }); + + var item = await client.RequestCompletion(new CompletionParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveCompletion(item); + item.Detail.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_CancellationToken() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCompletion((completionParams, token) => { + return Task.FromResult(new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + })); + }, + (completionItem, token) => { + completionItem.Detail = "resolved"; + return Task.FromResult(completionItem); + }, + + new CompletionRegistrationOptions() { + }); + }); + + var items = await client.RequestCompletion(new CompletionParams()); + + var item = items.Items.Single(); + + item = await client.ResolveCompletion(item); + item.Detail.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_Partial_CancellationToken() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCompletion((completionParams, observer, token) => { + var a = new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + (completionItem, token) => { + completionItem.Detail = "resolved"; + return Task.FromResult(completionItem); + }, + + new CompletionRegistrationOptions() { + }); + }); + + var item = await client.RequestCompletion(new CompletionParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveCompletion(item); + item.Detail.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCompletion((completionParams) => { + return Task.FromResult(new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + })); + }, + (completionItem) => { + completionItem.Detail = "resolved"; + return Task.FromResult(completionItem); + }, + + new CompletionRegistrationOptions() { + }); + }); + + var items = await client.RequestCompletion(new CompletionParams()); + + var item = items.Items.Single(); + + item = await client.ResolveCompletion(item); + item.Detail.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_Partial() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnCompletion((completionParams, observer) => { + var a = new CompletionList(new CompletionItem() { + Command = new Command() { + Name = "execute-a", + Arguments = JArray.FromObject(new object[] { 1, "2", false }) + }, + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + (completionItem) => { + completionItem.Detail = "resolved"; + return Task.FromResult(completionItem); + }, + + new CompletionRegistrationOptions() { + }); + }); + + var item = await client.RequestCompletion(new CompletionParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveCompletion(item); + item.Detail.Should().Be("resolved"); + } + + class Data + { + public string Name { get; set; } + public Guid Id { get; set; } + public Nested Child { get; set; } + + } + + class Nested + { + public DateTimeOffset Date { get; set; } + } + } +} diff --git a/test/Lsp.Tests/Integration/TypedDocumentLinkTests.cs b/test/Lsp.Tests/Integration/TypedDocumentLinkTests.cs new file mode 100644 index 000000000..9a3fe894f --- /dev/null +++ b/test/Lsp.Tests/Integration/TypedDocumentLinkTests.cs @@ -0,0 +1,533 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Reactive.Threading.Tasks; +using System.Threading.Tasks; +using FluentAssertions; +using Newtonsoft.Json.Linq; +using NSubstitute; +using OmniSharp.Extensions.JsonRpc.Testing; +using OmniSharp.Extensions.LanguageProtocol.Testing; +using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Document; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using Serilog.Events; +using Xunit; +using Xunit.Abstractions; + +namespace Lsp.Tests.Integration +{ + public class TypedDocumentLinkTests : LanguageProtocolTestBase + { + + public TypedDocumentLinkTests(ITestOutputHelper outputHelper) : base(new JsonRpcTestOptions().ConfigureForXUnit(outputHelper, LogEventLevel.Verbose)) + { + } + + [Fact] + public async Task Should_Aggregate_With_All_Related_Handlers() + { + + var (client, server) = await Initialize( + options => { }, options => { + var identifier = Substitute.For(); + identifier.GetTextDocumentAttributes(Arg.Any()).Returns( + call => new TextDocumentAttributes(call.ArgAt(0), "file", "csharp")); + options.AddTextDocumentIdentifier(identifier); + + options.OnDocumentLink((codeLensParams) => { + return Task.FromResult(new DocumentLinkContainer(new DocumentLink() { + Tooltip = "data-a", + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + })); + }, + (codeLens) => { + codeLens.Tooltip = "resolved-a"; + return Task.FromResult(codeLens); + }, + + new DocumentLinkRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + }); + + options.OnDocumentLink((codeLensParams) => { + return Task.FromResult(new DocumentLinkContainer(new DocumentLink() { + Tooltip = "nested-b", + Data = new Nested() { + Date = DateTimeOffset.Now + } + })); + }, + (codeLens) => { + codeLens.Tooltip = "resolved-b"; + return Task.FromResult(codeLens); + }, + + new DocumentLinkRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + }); + + options.OnDocumentLink((codeLensParams) => { + return Task.FromResult(new DocumentLinkContainer(new DocumentLink() { + Tooltip = "no-data-c", + })); + }, + (codeLens) => { + codeLens.Tooltip = "resolved-c"; + return Task.FromResult(codeLens); + }, + + new DocumentLinkRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + }); + + options.OnDocumentLink((codeLensParams) => { + return Task.FromResult(new DocumentLinkContainer(new DocumentLink() { + Tooltip = "not-included", + })); + }, + (codeLens) => { + codeLens.Tooltip = "resolved-d"; + return Task.FromResult(codeLens); + }, + + new DocumentLinkRegistrationOptions() { + DocumentSelector = DocumentSelector.ForLanguage("vb") + }); + }); + + var codeLens = await client.RequestDocumentLink(new DocumentLinkParams() { + TextDocument = new TextDocumentIdentifier("/some/path/file.cs"), + }); + + var lens = codeLens.ToArray(); + + var responses = await Task.WhenAll(lens.Select(z => client.ResolveDocumentLink(z))); + responses.Select(z => z.Tooltip).Should().Contain("resolved-a", "resolved-b", "resolved-c"); + responses.Select(z => z.Tooltip).Should().NotContain("resolved-d"); + lens.Length.Should().Be(3); + } + + [Fact] + public async Task Should_Resolve_With_Data_Capability() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnDocumentLink((documentLinkParams, capability, token) => { + return Task.FromResult(new DocumentLinkContainer(new DocumentLink() { + Tooltip = "execute-a", + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + })); + }, + (documentLink, capability, token) => { + documentLink.Data.Id.Should().NotBeEmpty(); + documentLink.Data.Child.Should().NotBeNull(); + documentLink.Data.Name.Should().Be("name"); + documentLink.Tooltip = "resolved"; + return Task.FromResult(documentLink); + }, + + new DocumentLinkRegistrationOptions() { + }); + }); + + var items = await client.RequestDocumentLink(new DocumentLinkParams()); + + var item = items.Single(); + + item = await client.ResolveDocumentLink(item); + item.Tooltip.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_With_Partial_Data_Capability() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnDocumentLink((documentLinkParams, observer, capability, token) => { + var a = new DocumentLinkContainer(new DocumentLink() { + Tooltip = "execute-a", + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + (documentLink, capability, token) => { + documentLink.Data.Id.Should().NotBeEmpty(); + documentLink.Data.Child.Should().NotBeNull(); + documentLink.Data.Name.Should().Be("name"); + documentLink.Tooltip = "resolved"; + return Task.FromResult(documentLink); + }, + + new DocumentLinkRegistrationOptions() { + }); + }); + + var item = await client.RequestDocumentLink(new DocumentLinkParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveDocumentLink(item); + item.Tooltip.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_With_Data_CancellationToken() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnDocumentLink((documentLinkParams, token) => { + return Task.FromResult(new DocumentLinkContainer(new DocumentLink() { + Tooltip = "execute-a", + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + })); + }, + (documentLink, token) => { + documentLink.Data.Id.Should().NotBeEmpty(); + documentLink.Data.Child.Should().NotBeNull(); + documentLink.Data.Name.Should().Be("name"); + documentLink.Tooltip = "resolved"; + return Task.FromResult(documentLink); + }, + + new DocumentLinkRegistrationOptions() { + }); + }); + + var items = await client.RequestDocumentLink(new DocumentLinkParams()); + + var item = items.Single(); + + item = await client.ResolveDocumentLink(item); + item.Tooltip.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_With_Partial_Data_CancellationToken() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnDocumentLink((documentLinkParams, observer, token) => { + var a = new DocumentLinkContainer(new DocumentLink() { + Tooltip = "execute-a", + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + (documentLink, token) => { + documentLink.Data.Id.Should().NotBeEmpty(); + documentLink.Data.Child.Should().NotBeNull(); + documentLink.Data.Name.Should().Be("name"); + documentLink.Tooltip = "resolved"; + return Task.FromResult(documentLink); + }, + + new DocumentLinkRegistrationOptions() { + }); + }); + + var item = await client.RequestDocumentLink(new DocumentLinkParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveDocumentLink(item); + item.Tooltip.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_With_Data() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnDocumentLink((documentLinkParams) => { + return Task.FromResult(new DocumentLinkContainer(new DocumentLink() { + Tooltip = "execute-a", + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + })); + }, + (documentLink) => { + documentLink.Data.Id.Should().NotBeEmpty(); + documentLink.Data.Child.Should().NotBeNull(); + documentLink.Data.Name.Should().Be("name"); + documentLink.Tooltip = "resolved"; + return Task.FromResult(documentLink); + }, + + new DocumentLinkRegistrationOptions() { + }); + }); + + var items = await client.RequestDocumentLink(new DocumentLinkParams()); + + var item = items.Single(); + + item = await client.ResolveDocumentLink(item); + item.Tooltip.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_With_Partial_Data() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnDocumentLink((documentLinkParams, observer) => { + var a = new DocumentLinkContainer(new DocumentLink() { + Tooltip = "execute-a", + Data = new Data() { + Child = new Nested() { + Date = DateTimeOffset.MinValue + }, + Id = Guid.NewGuid(), + Name = "name" + } + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + (documentLink) => { + documentLink.Data.Id.Should().NotBeEmpty(); + documentLink.Data.Child.Should().NotBeNull(); + documentLink.Data.Name.Should().Be("name"); + documentLink.Tooltip = "resolved"; + return Task.FromResult(documentLink); + }, + + new DocumentLinkRegistrationOptions() { + }); + }); + + var item = await client.RequestDocumentLink(new DocumentLinkParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveDocumentLink(item); + item.Tooltip.Should().Be("resolved"); + } + + + [Fact] + public async Task Should_Resolve_Capability() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnDocumentLink((documentLinkParams, capability, token) => { + return Task.FromResult(new DocumentLinkContainer(new DocumentLink() { + Tooltip = "execute-a", + })); + }, + (documentLink, capability, token) => { + documentLink.Tooltip = "resolved"; + return Task.FromResult(documentLink); + }, + new DocumentLinkRegistrationOptions() { + }); + }); + + var items = await client.RequestDocumentLink(new DocumentLinkParams()); + + var item = items.Single(); + + item = await client.ResolveDocumentLink(item); + item.Tooltip.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_Partial_Capability() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnDocumentLink((documentLinkParams, observer, capability, token) => { + var a = new DocumentLinkContainer(new DocumentLink() { + Tooltip = "execute-a", + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + + (documentLink, capability, token) => { + documentLink.Tooltip = "resolved"; + return Task.FromResult(documentLink); + }, + new DocumentLinkRegistrationOptions() { + }); + }); + + var item = await client.RequestDocumentLink(new DocumentLinkParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveDocumentLink(item); + item.Tooltip.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_CancellationToken() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnDocumentLink((documentLinkParams, token) => { + return Task.FromResult(new DocumentLinkContainer(new DocumentLink() { + Tooltip = "execute-a", + })); + }, + (documentLink, token) => { + documentLink.Tooltip = "resolved"; + return Task.FromResult(documentLink); + }, + + new DocumentLinkRegistrationOptions() { + }); + }); + + var items = await client.RequestDocumentLink(new DocumentLinkParams()); + + var item = items.Single(); + + item = await client.ResolveDocumentLink(item); + item.Tooltip.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_Partial_CancellationToken() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnDocumentLink((documentLinkParams, observer, token) => { + var a = new DocumentLinkContainer(new DocumentLink() { + Tooltip = "execute-a", + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + (documentLink, token) => { + documentLink.Tooltip = "resolved"; + return Task.FromResult(documentLink); + }, + + new DocumentLinkRegistrationOptions() { + }); + }); + + var item = await client.RequestDocumentLink(new DocumentLinkParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveDocumentLink(item); + item.Tooltip.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnDocumentLink((documentLinkParams) => { + return Task.FromResult(new DocumentLinkContainer(new DocumentLink() { + Tooltip = "execute-a", + })); + }, + (documentLink) => { + documentLink.Tooltip = "resolved"; + return Task.FromResult(documentLink); + }, + + new DocumentLinkRegistrationOptions() { + }); + }); + + var items = await client.RequestDocumentLink(new DocumentLinkParams()); + + var item = items.Single(); + + item = await client.ResolveDocumentLink(item); + item.Tooltip.Should().Be("resolved"); + } + + [Fact] + public async Task Should_Resolve_Partial() + { + + var (client, server) = await Initialize( + options => { }, options => { + options.OnDocumentLink((documentLinkParams, observer) => { + var a = new DocumentLinkContainer(new DocumentLink() { + Tooltip = "execute-a", + }); + + observer.OnNext(a); + observer.OnCompleted(); + }, + (documentLink) => { + documentLink.Tooltip = "resolved"; + return Task.FromResult(documentLink); + }, + + new DocumentLinkRegistrationOptions() { + }); + }); + + var item = await client.RequestDocumentLink(new DocumentLinkParams()).SelectMany(z => z).Take(1).ToTask(CancellationToken); + + item = await client.ResolveDocumentLink(item); + item.Tooltip.Should().Be("resolved"); + } + + class Data + { + public string Name { get; set; } + public Guid Id { get; set; } + public Nested Child { get; set; } + + } + + class Nested + { + public DateTimeOffset Date { get; set; } + } + } +} diff --git a/test/Lsp.Tests/LspRequestRouterTests.cs b/test/Lsp.Tests/LspRequestRouterTests.cs index b58c82bb0..fb7b24d68 100644 --- a/test/Lsp.Tests/LspRequestRouterTests.cs +++ b/test/Lsp.Tests/LspRequestRouterTests.cs @@ -112,7 +112,7 @@ public async Task ShouldRouteToCorrect_Notification() var request = new Notification(TextDocumentNames.DidSave, JObject.Parse(JsonConvert.SerializeObject(@params, new Serializer(ClientVersion.Lsp3).Settings))); - await mediator.RouteNotification(mediator.GetDescriptor(request), request, CancellationToken.None); + await mediator.RouteNotification(mediator.GetDescriptors(request), request, CancellationToken.None); await textDocumentSyncHandler.Received(1) .Handle(Arg.Any(), Arg.Any()); @@ -146,7 +146,7 @@ public async Task ShouldRouteToCorrect_Notification_WithManyHandlers() var request = new Notification(TextDocumentNames.DidSave, JObject.Parse(JsonConvert.SerializeObject(@params, new Serializer(ClientVersion.Lsp3).Settings))); - await mediator.RouteNotification(mediator.GetDescriptor(request), request, CancellationToken.None); + await mediator.RouteNotification(mediator.GetDescriptors(request), request, CancellationToken.None); await textDocumentSyncHandler.Received(0) .Handle(Arg.Any(), Arg.Any()); @@ -184,7 +184,7 @@ public async Task ShouldRouteToCorrect_Request() var request = new Request(id, TextDocumentNames.CodeAction, JObject.Parse(JsonConvert.SerializeObject(@params, new Serializer(ClientVersion.Lsp3).Settings))); - await mediator.RouteRequest(mediator.GetDescriptor(request), request, CancellationToken.None); + await mediator.RouteRequest(mediator.GetDescriptors(request), request, CancellationToken.None); await codeActionHandler.Received(1).Handle(Arg.Any(), Arg.Any()); } @@ -236,7 +236,7 @@ public async Task ShouldRouteToCorrect_Request_WithManyHandlers() var request = new Request(id, TextDocumentNames.CodeAction, JObject.Parse(JsonConvert.SerializeObject(@params, new Serializer(ClientVersion.Lsp3).Settings))); - await mediator.RouteRequest(mediator.GetDescriptor(request), request, CancellationToken.None); + await mediator.RouteRequest(mediator.GetDescriptors(request), request, CancellationToken.None); await codeActionHandler.Received(0).Handle(Arg.Any(), Arg.Any()); await codeActionDelegate.Received(1).Invoke(Arg.Any(), Arg.Any()); @@ -285,7 +285,7 @@ public async Task ShouldRouteToCorrect_Request_WithManyHandlers_CodeLensHandler( var request = new Request(id, TextDocumentNames.CodeLens, JObject.Parse(JsonConvert.SerializeObject(@params, new Serializer(ClientVersion.Lsp3).Settings))); - await mediator.RouteRequest(mediator.GetDescriptor(request), request, CancellationToken.None); + await mediator.RouteRequest(mediator.GetDescriptors(request), request, CancellationToken.None); await codeActionHandler2.Received(0).Handle(Arg.Any(), Arg.Any()); await codeActionHandler.Received(1).Handle(Arg.Any(), Arg.Any()); @@ -309,7 +309,7 @@ public async Task ShouldRouteTo_CorrectRequestWhenGivenNullParams() var id = Guid.NewGuid().ToString(); var request = new Request(id, GeneralNames.Shutdown, new JObject()); - await mediator.RouteRequest(mediator.GetDescriptor(request), request, CancellationToken.None); + await mediator.RouteRequest(mediator.GetDescriptors(request), request, CancellationToken.None); await handler.Received(1).Handle(Arg.Any(), Arg.Any()); } diff --git a/test/Lsp.Tests/Matchers/ResolveCommandMatcherTests.cs b/test/Lsp.Tests/Matchers/ResolveCommandMatcherTests.cs index 811018cc9..1c780b36c 100644 --- a/test/Lsp.Tests/Matchers/ResolveCommandMatcherTests.cs +++ b/test/Lsp.Tests/Matchers/ResolveCommandMatcherTests.cs @@ -21,6 +21,8 @@ namespace Lsp.Tests.Matchers { public class ResolveCommandMatcherTests : AutoTestBase { + private readonly Guid TrueId = Guid.NewGuid(); + private readonly Guid FalseId = Guid.NewGuid(); public ResolveCommandMatcherTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } @@ -87,12 +89,12 @@ public void Should_Return_CodeLensResolve_Descriptor() var handlerMatcher = AutoSubstitute.Resolve(); var resolveHandler = Substitute.For(); var resolveHandler2 = Substitute.For(); - resolveHandler.CanResolve(Arg.Any()).Returns(false); - resolveHandler2.CanResolve(Arg.Any()).Returns(true); + resolveHandler.Id.Returns(FalseId); + resolveHandler2.Id.Returns(TrueId); // When var result = handlerMatcher.FindHandler(new CodeLens() { - Data = JToken.FromObject(new {handlerType = typeof(ICodeLensResolveHandler).FullName, data = new {a = 1}}) + Data = JObject.FromObject(new Dictionary() { [ResolveCommandMatcher.PrivateHandlerId] = TrueId, ["a"] = 1}) }, new List { new LspHandlerDescriptor(TextDocumentNames.CodeLensResolve, @@ -133,7 +135,7 @@ public void Should_Handle_Null_Data() // Given var handlerMatcher = AutoSubstitute.Resolve(); var resolveHandler = Substitute.For(); - resolveHandler.CanResolve(Arg.Any()).Returns(true); + resolveHandler.Id.Returns(TrueId); // When var result = handlerMatcher.FindHandler(new CompletionItem() { }, @@ -154,41 +156,7 @@ public void Should_Handle_Null_Data() .ToArray(); // Then - result.Should().NotBeNullOrEmpty(); - result.OfType().Should().Contain(x => x.Handler == resolveHandler); - } - - [Fact] - public void Should_Handle_Simple_Json_Data() - { - // Given - var handlerMatcher = AutoSubstitute.Resolve(); - var resolveHandler = Substitute.For(); - resolveHandler.CanResolve(Arg.Any()).Returns(true); - - // When - var result = handlerMatcher.FindHandler(new CompletionItem() { - Data = new Uri("file:///c%3A/Users/mb/src/gh/Cake.Json/src/Cake.Json/Namespaces.cs") - }, - new List { - new LspHandlerDescriptor(TextDocumentNames.CompletionResolve, - "Key", - resolveHandler, - resolveHandler.GetType(), - typeof(CompletionItem), - null, - null, - () => false, - null, - null, - () => { }, - Substitute.For()), - }) - .ToArray(); - - // Then - result.Should().NotBeNullOrEmpty(); - result.OfType().Should().Contain(x => x.Handler == resolveHandler); + result.Should().BeNullOrEmpty(); } [Fact] @@ -198,12 +166,12 @@ public void Should_Return_CompletionResolve_Descriptor() var handlerMatcher = AutoSubstitute.Resolve(); var resolveHandler = Substitute.For(); var resolveHandler2 = Substitute.For(); - resolveHandler.CanResolve(Arg.Any()).Returns(false); - resolveHandler2.CanResolve(Arg.Any()).Returns(true); + resolveHandler.Id.Returns(FalseId); + resolveHandler2.Id.Returns(TrueId); // When var result = handlerMatcher.FindHandler(new CompletionItem() { - Data = JToken.FromObject(new {handlerType = typeof(ICompletionResolveHandler).FullName, data = new {a = 1}}) + Data = JObject.FromObject(new Dictionary() { [ResolveCommandMatcher.PrivateHandlerId] = TrueId, ["a"] = 1}) }, new List { new LspHandlerDescriptor(TextDocumentNames.CompletionResolve, @@ -238,110 +206,6 @@ public void Should_Return_CompletionResolve_Descriptor() result.OfType().Should().Contain(x => x.Handler == resolveHandler2); } - [Fact] - public void Should_Deal_WithHandlers_That_Not_Also_Resolvers() - { - // Given - var handlerMatcher = AutoSubstitute.Resolve(); - var resolveHandler = Substitute.For(); - resolveHandler.CanResolve(Arg.Any()).Returns(false); - var resolveHandler2 = Substitute.For(new Type[] { - typeof(ICompletionHandler), - typeof(ICompletionResolveHandler) - }, new object[0]) as IJsonRpcHandler; - (resolveHandler2 as ICompletionResolveHandler).CanResolve(Arg.Any()).Returns(true); - (resolveHandler2 as ICompletionHandler).GetRegistrationOptions().Returns( - new CompletionRegistrationOptions() { - DocumentSelector = DocumentSelector.ForLanguage("csharp") - }); - - // When - var result = handlerMatcher.FindHandler(new CompletionItem() { - Data = new JObject() - }, - new List { - new LspHandlerDescriptor(TextDocumentNames.CompletionResolve, - "Key", - resolveHandler, - resolveHandler.GetType(), - typeof(CompletionItem), - null, - null, - () => false, - null, - null, - () => { }, - Substitute.For()), - new LspHandlerDescriptor(TextDocumentNames.CompletionResolve, - "Key2", - resolveHandler2 as IJsonRpcHandler, - typeof(ICompletionResolveHandler), - typeof(CompletionItem), - null, - null, - () => false, - null, - null, - () => { }, - Substitute.For()), - }) - .ToArray(); - - // Then - result.Should().NotBeNullOrEmpty(); - result.OfType().Should().Contain(x => x.Handler == resolveHandler2); - } - - [Fact] - public void Should_Deal_WithHandlers_That_Not_Also_Resolvers2() - { - // Given - var handlerMatcher = AutoSubstitute.Resolve(); - var resolveHandler = Substitute.For(); - resolveHandler.CanResolve(Arg.Any()).Returns(true); - var resolveHandler2 = Substitute.For(new Type[] { - typeof(ICompletionHandler), - typeof(ICompletionResolveHandler) - }, new object[0]); - (resolveHandler2 as ICompletionResolveHandler).CanResolve(Arg.Any()).Returns(false); - - // When - var result = handlerMatcher.FindHandler(new CompletionItem() { - Data = new JObject() - }, - new List { - new LspHandlerDescriptor(TextDocumentNames.CompletionResolve, - "Key", - resolveHandler, - resolveHandler.GetType(), - typeof(CompletionItem), - null, - null, - () => false, - null, - null, - () => { }, - Substitute.For()), - new LspHandlerDescriptor(TextDocumentNames.CompletionResolve, - "Key2", - resolveHandler2 as IJsonRpcHandler, - typeof(ICompletionResolveHandler), - typeof(CompletionItem), - null, - null, - () => false, - null, - null, - () => { }, - Substitute.For()), - }) - .ToArray(); - - // Then - result.Should().NotBeNullOrEmpty(); - result.OfType().Should().Contain(x => x.Handler == resolveHandler); - } - [Fact] public async Task Should_Update_CompletionItems_With_HandlerType() { @@ -350,7 +214,7 @@ public async Task Should_Update_CompletionItems_With_HandlerType() typeof(ICompletionHandler), typeof(ICompletionResolveHandler) }, new object[0]); - (resolveHandler as ICompletionResolveHandler).CanResolve(Arg.Any()).Returns(true); + (resolveHandler as ICompletionResolveHandler).Id.Returns(TrueId); var descriptor = new LspHandlerDescriptor( TextDocumentNames.Completion, "Key", @@ -382,9 +246,8 @@ public async Task Should_Update_CompletionItems_With_HandlerType() response.Should().BeEquivalentTo(list); (response as CompletionList).Items.Should().Contain(item); var responseItem = (response as CompletionList).Items.First(); - responseItem.Data[ResolveCommandMatcher.PrivateHandlerTypeName].Value().Should().NotBeNullOrEmpty(); - responseItem.Data[ResolveCommandMatcher.PrivateHandlerKey].Value().Should().NotBeNullOrEmpty(); - responseItem.Data["data"]["hello"].Value().Should().Be("world"); + responseItem.Data[ResolveCommandMatcher.PrivateHandlerId].Value().Should().Be(TrueId); + responseItem.Data["hello"].Value().Should().Be("world"); } [Fact] @@ -395,7 +258,7 @@ public async Task Should_Update_CodeLensContainer_With_HandlerType() typeof(ICodeLensHandler), typeof(ICodeLensResolveHandler) }, new object[0]); - (resolveHandler as ICodeLensResolveHandler).CanResolve(Arg.Any()).Returns(true); + (resolveHandler as ICodeLensResolveHandler).Id.Returns(TrueId); var descriptor = new LspHandlerDescriptor( TextDocumentNames.CodeLens, "Key", @@ -427,9 +290,8 @@ public async Task Should_Update_CodeLensContainer_With_HandlerType() response.Should().BeEquivalentTo(list); (response as CodeLensContainer).Should().Contain(item); var responseItem = (response as CodeLensContainer).First(); - responseItem.Data[ResolveCommandMatcher.PrivateHandlerTypeName].Value().Should().NotBeNullOrEmpty(); - responseItem.Data[ResolveCommandMatcher.PrivateHandlerKey].Value().Should().NotBeNullOrEmpty(); - responseItem.Data["data"]["hello"].Value().Should().Be("world"); + responseItem.Data[ResolveCommandMatcher.PrivateHandlerId].Value().Should().Be(TrueId); + responseItem.Data["hello"].Value().Should().Be("world"); } [Fact] @@ -440,7 +302,7 @@ public async Task Should_Update_CodeLens_Removing_HandlerType() typeof(ICodeLensHandler), typeof(ICodeLensResolveHandler) }, new object[0]); - (resolveHandler as ICodeLensResolveHandler).CanResolve(Arg.Any()).Returns(true); + (resolveHandler as ICodeLensResolveHandler).Id.Returns(TrueId); var descriptor = new LspHandlerDescriptor( TextDocumentNames.CodeLensResolve, "Key", @@ -459,16 +321,16 @@ public async Task Should_Update_CodeLens_Removing_HandlerType() Substitute.For>>()); var item = new CodeLens() { - Data = JObject.FromObject(new {data = new {hello = "world"}}) + Data = JObject.FromObject(new {hello = "world"}) }; - item.Data[ResolveCommandMatcher.PrivateHandlerTypeName] = resolveHandler.GetType().FullName; + item.Data[ResolveCommandMatcher.PrivateHandlerId] = Guid.Empty; // When var response = await handlerMatcher.Handle(item, CancellationToken.None, () => Task.FromResult(item)); // Then response.Should().BeEquivalentTo(item); - item.Data?[ResolveCommandMatcher.PrivateHandlerTypeName].Should().BeNull(); + item.Data?[ResolveCommandMatcher.PrivateHandlerId].Value().Should().BeEmpty(); item.Data["hello"].Value().Should().Be("world"); } } diff --git a/test/Lsp.Tests/MediatorTestsRequestHandlerOfTRequest.cs b/test/Lsp.Tests/MediatorTestsRequestHandlerOfTRequest.cs deleted file mode 100644 index c76ebf136..000000000 --- a/test/Lsp.Tests/MediatorTestsRequestHandlerOfTRequest.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using FluentAssertions; -using MediatR; -using Microsoft.Extensions.DependencyInjection; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using NSubstitute; -using OmniSharp.Extensions.JsonRpc; -using OmniSharp.Extensions.JsonRpc.Server; -using Xunit; -using Xunit.Abstractions; -using Xunit.Sdk; -using OmniSharp.Extensions.LanguageServer.Protocol.Models; -using ISerializer = OmniSharp.Extensions.LanguageServer.Protocol.Serialization.ISerializer; -using Serializer = OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Serializer; -using System.Collections.Generic; -using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; -using OmniSharp.Extensions.LanguageServer.Protocol.Document; -using OmniSharp.Extensions.LanguageServer.Protocol.Shared; -using OmniSharp.Extensions.LanguageServer.Protocol.Workspace; -using OmniSharp.Extensions.LanguageServer.Shared; - -namespace Lsp.Tests -{ - public class MediatorTestsRequestHandlerOfTRequest : AutoTestBase - { - public MediatorTestsRequestHandlerOfTRequest(ITestOutputHelper testOutputHelper) : base(testOutputHelper) - { - Services - .AddJsonRpcMediatR(new[] { typeof(LspRequestRouterTests).Assembly }) - .AddSingleton(new Serializer(ClientVersion.Lsp3)); - } - - [Fact] - public async Task RequestsCancellation() - { - var executeCommandHandler = Substitute.For(); - executeCommandHandler - .Handle(Arg.Any(), Arg.Any()) - .Returns(async (c) => { - await Task.Delay(1000, c.Arg()); - throw new XunitException("Task was not cancelled in time!"); - return Unit.Value; - }); - - var collection = new SharedHandlerCollection(SupportedCapabilitiesFixture.AlwaysTrue, new TextDocumentIdentifiers()) { executeCommandHandler }; - AutoSubstitute.Provide(collection); - AutoSubstitute.Provide>(collection); - var mediator = AutoSubstitute.Resolve(); - - var id = Guid.NewGuid().ToString(); - var @params = new ExecuteCommandParams() { Command = "123" }; - var request = new Request(id, "workspace/executeCommand", JObject.Parse(JsonConvert.SerializeObject(@params, new Serializer(ClientVersion.Lsp3).Settings))); - var cts = new CancellationTokenSource(); - cts.Cancel(); - - var response = ((IRequestRouter)mediator).RouteRequest(mediator.GetDescriptor(request), request, cts.Token); - Func action = () => ((IRequestRouter) mediator).RouteRequest(mediator.GetDescriptor(request), request, cts.Token); - await action.Should().ThrowAsync(); - } - } -} diff --git a/test/Lsp.Tests/MediatorTestsRequestHandlerOfTRequestTResponse.cs b/test/Lsp.Tests/MediatorTestsRequestHandlerOfTRequestTResponse.cs index f2523220d..c85ba846a 100644 --- a/test/Lsp.Tests/MediatorTestsRequestHandlerOfTRequestTResponse.cs +++ b/test/Lsp.Tests/MediatorTestsRequestHandlerOfTRequestTResponse.cs @@ -70,8 +70,8 @@ public async Task RequestsCancellation() var cts = new CancellationTokenSource(); cts.Cancel(); - var response = ((IRequestRouter)mediator).RouteRequest(mediator.GetDescriptor(request), request, cts.Token); - Func action = () => ((IRequestRouter) mediator).RouteRequest(mediator.GetDescriptor(request), request, cts.Token); + var response = ((IRequestRouter)mediator).RouteRequest(mediator.GetDescriptors(request), request, cts.Token); + Func action = () => ((IRequestRouter) mediator).RouteRequest(mediator.GetDescriptors(request), request, cts.Token); await action.Should().ThrowAsync(); } } diff --git a/test/Lsp.Tests/TextDocumentSyncHandlerExtensions.cs b/test/Lsp.Tests/TextDocumentSyncHandlerExtensions.cs index d1e4acf4e..d5279af5c 100644 --- a/test/Lsp.Tests/TextDocumentSyncHandlerExtensions.cs +++ b/test/Lsp.Tests/TextDocumentSyncHandlerExtensions.cs @@ -19,7 +19,7 @@ public static ITextDocumentSyncHandler With(this ITextDocumentSyncHandler handle ((IDidCloseTextDocumentHandler)handler).GetRegistrationOptions().Returns(new TextDocumentRegistrationOptions() { DocumentSelector = documentSelector }); ((IDidSaveTextDocumentHandler)handler).GetRegistrationOptions().Returns(new TextDocumentSaveRegistrationOptions() { DocumentSelector = documentSelector }); ((ITextDocumentIdentifier) handler).GetTextDocumentAttributes(Arg.Any()) - .Returns((info) => new TextDocumentAttributes(info.Arg(), language)); + .Returns(c => new TextDocumentAttributes(c.Arg(), language)); handler .GetTextDocumentAttributes(Arg.Is(x => documentSelector.IsMatch(new TextDocumentAttributes(x, language))))