diff --git a/sample/SampleServer/TextDocumentHandler.cs b/sample/SampleServer/TextDocumentHandler.cs index 0b0fd54df..24df82dd5 100644 --- a/sample/SampleServer/TextDocumentHandler.cs +++ b/sample/SampleServer/TextDocumentHandler.cs @@ -100,9 +100,9 @@ TextDocumentSaveRegistrationOptions IRegistration GetTextDocumentAttributes(DocumentUri uri) { - return new TextDocumentAttributes(uri, "csharp"); + yield return new TextDocumentAttributes(uri, "csharp"); } } diff --git a/src/JsonRpc.Testing/IRequestSettler.cs b/src/JsonRpc.Testing/IRequestSettler.cs index df34e935f..9bb02a39b 100644 --- a/src/JsonRpc.Testing/IRequestSettler.cs +++ b/src/JsonRpc.Testing/IRequestSettler.cs @@ -5,4 +5,4 @@ public interface IRequestSettler void OnStartRequest(); void OnEndRequest(); } -} +} \ No newline at end of file diff --git a/src/JsonRpc/IRequestRouter.cs b/src/JsonRpc/IRequestRouter.cs index 58f3fedf9..6a3f5da2e 100644 --- a/src/JsonRpc/IRequestRouter.cs +++ b/src/JsonRpc/IRequestRouter.cs @@ -1,4 +1,7 @@ using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using OmniSharp.Extensions.JsonRpc.Server; @@ -10,11 +13,38 @@ public interface IRequestRouter IServiceProvider ServiceProvider { get; } } + public interface IRequestDescriptor : IEnumerable + { + TDescriptor Default { get; } + } + + class RequestDescriptor : IRequestDescriptor + { + private IEnumerable _descriptors; + + public RequestDescriptor(IEnumerable descriptors) + { + var enumerable = descriptors as TDescriptor[] ?? descriptors.ToArray(); + _descriptors = enumerable; + Default = enumerable.FirstOrDefault(); + } + + public IEnumerator GetEnumerator() => _descriptors.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable) _descriptors).GetEnumerator(); + + public TDescriptor Default { get; } + } + 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 GetDescriptor(Notification notification); + IRequestDescriptor GetDescriptor(Request request); + Task RouteNotification(IRequestDescriptor descriptors, Notification notification, CancellationToken token); + Task RouteRequest(IRequestDescriptor descriptors, Request request, CancellationToken token); + } + + interface IAggregateResults + { + object AggregateResults(IEnumerable values); } } diff --git a/src/JsonRpc/InputHandler.cs b/src/JsonRpc/InputHandler.cs index a511e605d..6676e6074 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; @@ -411,14 +411,14 @@ private void HandleRequest(in ReadOnlySequence request) { // _logger.LogDebug("Handling Request {Method} {ResponseId}", item.Request.Method, item.Request.Id); var descriptor = _requestRouter.GetDescriptor(item.Request); - if (descriptor is null) + 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)); } @@ -446,7 +446,7 @@ private void HandleRequest(in ReadOnlySequence request) // _logger.LogDebug("Handling Request {Method}", item.Notification.Method); var descriptor = _requestRouter.GetDescriptor(item.Notification); - if (descriptor is null) + 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,7 +465,7 @@ private void HandleRequest(in ReadOnlySequence request) } } - private SchedulerDelegate RouteRequest(IHandlerDescriptor descriptor, Request request) + private SchedulerDelegate RouteRequest(IRequestDescriptor descriptor, Request request) { // start request, create cts, etc var cts = new CancellationTokenSource(); @@ -521,7 +521,7 @@ private SchedulerDelegate RouteRequest(IHandlerDescriptor descriptor, Request re }); } - private SchedulerDelegate RouteNotification(IHandlerDescriptor descriptor, Notification notification) + private SchedulerDelegate RouteNotification(IRequestDescriptor descriptor, Notification notification) { return (contentModifiedToken, scheduler) => // ITS A RACE! diff --git a/src/JsonRpc/NotificationHandler.cs b/src/JsonRpc/NotificationHandler.cs index 31d1262a0..b0bb234c9 100644 --- a/src/JsonRpc/NotificationHandler.cs +++ b/src/JsonRpc/NotificationHandler.cs @@ -5,12 +5,12 @@ namespace OmniSharp.Extensions.JsonRpc { - public static class NotificationHandler - { + public static class NotificationHandler { + public static DelegatingHandlers.Notification For(Action handler) where TParams : IRequest { - return new DelegatingHandlers.Notification(handler); + return new DelegatingHandlers.Notification( handler); } public static DelegatingHandlers.Notification For(Func handler) @@ -18,7 +18,6 @@ public static DelegatingHandlers.Notification For(Func(handler); } - public static DelegatingHandlers.Notification For(Action handler) where TParams : IRequest { @@ -31,4 +30,4 @@ public static DelegatingHandlers.Notification For(Func((p, ct) => handler(p)); } } -} +} \ No newline at end of file diff --git a/src/JsonRpc/RequestRouter.cs b/src/JsonRpc/RequestRouter.cs index 456fa7491..cb18e6aeb 100644 --- a/src/JsonRpc/RequestRouter.cs +++ b/src/JsonRpc/RequestRouter.cs @@ -22,17 +22,17 @@ public IDisposable Add(IJsonRpcHandler handler) return _collection.Add(handler); } - private IHandlerDescriptor FindDescriptor(IMethodWithParams instance) + private IRequestDescriptor FindDescriptor(IMethodWithParams instance) { - return _collection.FirstOrDefault(x => x.Method == instance.Method); + return new RequestDescriptor(_collection.Where(x => x.Method == instance.Method)); } - public override IHandlerDescriptor GetDescriptor(Notification notification) + public override IRequestDescriptor GetDescriptor(Notification notification) { return FindDescriptor(notification); } - public override IHandlerDescriptor GetDescriptor(Request request) + public override IRequestDescriptor GetDescriptor(Request request) { return FindDescriptor(request); } diff --git a/src/JsonRpc/RequestRouterBase.cs b/src/JsonRpc/RequestRouterBase.cs index db0d6ca75..af76ab61b 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,48 @@ 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) - { - await HandleNotification(mediator, descriptor, EmptyRequest.Instance, token); - } - else + await Task.WhenAll(descriptors.Select(descriptor => InnerRouteNotification(descriptor))); + + async Task InnerRouteNotification(TDescriptor descriptor) { - _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); + _logger.LogDebug("Converting params for Notification {Method} to {Type}", notification.Method, descriptor.Params.FullName); + object @params; + if (descriptor.IsDelegatingHandler) + { + // new DelegatingRequest(); + var o = notification.Params?.ToObject(descriptor.Params.GetGenericArguments()[0], _serializer.JsonSerializer); + @params = Activator.CreateInstance(descriptor.Params, new object[] {o}); + } + 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,7 +80,27 @@ public virtual async Task RouteRequest(TDescriptor descriptor, Re new KeyValuePair("Method", request.Method), new KeyValuePair("Params", request.Params?.ToString()) }); - using var scope = _serviceScopeFactory.CreateScope(); + + if (typeof(IAggregateResults).IsAssignableFrom(descriptors.Default.Response)) + { + var responses = await Task.WhenAll(descriptors.Select(InnerRouteRequest)); + var errorResponse = responses.FirstOrDefault(x => x.IsError); + if (errorResponse.IsError) return errorResponse; + if (responses.Length == 1) + { + return responses[0]; + } + + if (!(responses[0].Value is OutgoingResponse or)) throw new NotSupportedException("Unsupported response type"); + if (!(or.Result is IAggregateResults ar)) throw new NotSupportedException("Unsupported result type"); + return new OutgoingResponse(request.Id, ar.AggregateResults(responses.Skip(1).Select(z => z.Value).OfType().Select(z => z.Result)), request); + } + + return await InnerRouteRequest(descriptors.Default); + + async Task InnerRouteRequest(TDescriptor descriptor) + { + using var scope = _serviceScopeFactory.CreateScope(); var context = scope.ServiceProvider.GetRequiredService(); context.Descriptor = descriptor; var mediator = scope.ServiceProvider.GetRequiredService(); @@ -132,12 +159,12 @@ public virtual async Task RouteRequest(TDescriptor descriptor, Re _logger.LogTrace("Response value was {Type}", responseValue?.GetType().FullName); } - return new JsonRpc.Client.OutgoingResponse(request.Id, responseValue, request); + } } - public abstract TDescriptor GetDescriptor(Notification notification); - public abstract TDescriptor GetDescriptor(Request request); + public abstract IRequestDescriptor GetDescriptor(Notification notification); + public abstract IRequestDescriptor GetDescriptor(Request request); private static readonly MethodInfo SendRequestUnit = typeof(RequestRouterBase) .GetMethods(BindingFlags.NonPublic | BindingFlags.Static) diff --git a/src/Protocol/Client/Capabilities/CodeLensCapability.cs b/src/Protocol/Client/Capabilities/CodeLensCapability.cs index f7d22e0c9..e5947ddc6 100644 --- a/src/Protocol/Client/Capabilities/CodeLensCapability.cs +++ b/src/Protocol/Client/Capabilities/CodeLensCapability.cs @@ -1,6 +1,7 @@ using OmniSharp.Extensions.LanguageServer.Protocol.Document; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; namespace OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities { - public class CodeLensCapability : DynamicCapability, ConnectedCapability { } + public class CodeLensCapability : DynamicCapability, ConnectedCapability> { } } diff --git a/src/Protocol/Client/Capabilities/CompletionCapability.cs b/src/Protocol/Client/Capabilities/CompletionCapability.cs index 896c5359c..418ec38fc 100644 --- a/src/Protocol/Client/Capabilities/CompletionCapability.cs +++ b/src/Protocol/Client/Capabilities/CompletionCapability.cs @@ -1,9 +1,10 @@ using OmniSharp.Extensions.LanguageServer.Protocol.Document; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; namespace OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities { - public class CompletionCapability : DynamicCapability, ConnectedCapability + public class CompletionCapability : DynamicCapability, ConnectedCapability> { /// /// The client supports the following `CompletionItem` specific diff --git a/src/Protocol/Client/Capabilities/DocumentLinkCapability.cs b/src/Protocol/Client/Capabilities/DocumentLinkCapability.cs index 2c13d8572..993523be4 100644 --- a/src/Protocol/Client/Capabilities/DocumentLinkCapability.cs +++ b/src/Protocol/Client/Capabilities/DocumentLinkCapability.cs @@ -1,9 +1,10 @@ using OmniSharp.Extensions.LanguageServer.Protocol.Document; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; namespace OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities { - public class DocumentLinkCapability : DynamicCapability, ConnectedCapability + public class DocumentLinkCapability : DynamicCapability, ConnectedCapability> { /// /// Whether the client support the `tooltip` property on `DocumentLink`. diff --git a/src/Protocol/DelegatingHandlers.cs b/src/Protocol/DelegatingHandlers.cs index 9fc124343..c8fb222aa 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.NewGuid(), (a, c, ct) => handler(a, c), registrationOptions) + { + } + + public Request(Func> handler, TRegistrationOptions registrationOptions) : + this(Guid.NewGuid(), 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; } @@ -49,89 +59,72 @@ Task IRequestHandler. void ICapability.SetCapability(TCapability capability) => _capability = capability; } - public sealed class CanBeResolved : + public sealed class CanBeResolved : IRegistration, ICapability, - ICanBeResolvedHandler - where TItem : ICanBeResolved, IRequest + ICanBeResolvedHandler + where TItem : ICanBeResolved, IRequest where TRegistrationOptions : class, new() where TCapability : ICapability + where TData : CanBeResolvedData { - 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 : + public sealed class CanBeResolved : IRegistration, - ICanBeResolvedHandler - where TItem : ICanBeResolved, IRequest + ICanBeResolvedHandler + where TItem : ICanBeResolved, IRequest where TRegistrationOptions : class, new() + where TData : CanBeResolvedData { - 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 +132,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.NewGuid(), (a, c, ct) => handler(a, c), registrationOptions) { } - public Request( - Func handler, - TRegistrationOptions registrationOptions) + public Request(Func handler, TRegistrationOptions registrationOptions) : + this(Guid.NewGuid(), 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 +170,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.NewGuid(), (a, ct) => handler(a), registrationOptions) + { + } + + public RequestRegistration(Func> handler, TRegistrationOptions registrationOptions) : + this(Guid.NewGuid(), 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.NewGuid(), (a, ct) => handler(a), registrationOptions) { } - public RequestRegistration( - Func handler, - TRegistrationOptions registrationOptions) + public RequestRegistration(Func handler, TRegistrationOptions registrationOptions): + this(Guid.NewGuid(), 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 +253,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.NewGuid(), (a, c, ct) => handler(a, c)) + { + } + + public RequestCapability(Func> handler): + this(Guid.NewGuid(), 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 +293,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.NewGuid(), handler) + { + } - public RequestCapability( - Func handler) : this((a, c, ct) => handler(a, c)) + public RequestCapability(Func handler): + this(Guid.NewGuid(), 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 +336,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 +348,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.NewGuid(), (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.NewGuid(), 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 +397,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 +407,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.NewGuid(), (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.NewGuid(), 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 +454,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 +464,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.NewGuid(), 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 +509,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.NewGuid(), (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.NewGuid(), 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 +564,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 +576,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.NewGuid(), (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.NewGuid(), 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 +630,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 +640,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.NewGuid(), (p, o, ct) => handler(p, o), registrationOptions, progressManager, factory) + { + } + + public PartialResults(Action>, CancellationToken> handler, TRegistrationOptions registrationOptions, IProgressManager progressManager, Func, TResponse> factory): + this(Guid.NewGuid(), 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 +692,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 +702,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.NewGuid(), (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.NewGuid(), 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 +753,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.NewGuid(), (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.NewGuid(), 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 +814,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 +823,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.NewGuid(), (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.NewGuid(), (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.NewGuid(), (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.NewGuid(), 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 +880,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.NewGuid(), (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.NewGuid(), (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.NewGuid(), (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.NewGuid(), 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 +942,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.NewGuid(), (request, capability, ct) => { handler(request, capability); return Task.CompletedTask; }) + { + } + + public NotificationCapability(Func handler) : + this(Guid.NewGuid(), (request, capability, ct) => handler(request, capability)) + { + } + + public NotificationCapability(Func handler) : + this(Guid.NewGuid(), 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 653fb732c..55aedeb78 100644 --- a/src/Protocol/Document/ICodeLensHandler.cs +++ b/src/Protocol/Document/ICodeLensHandler.cs @@ -14,18 +14,21 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Document { [Parallel, Method(TextDocumentNames.CodeLens, Direction.ClientToServer)] - public interface ICodeLensHandler : IJsonRpcRequestHandler, IRegistration, ICapability + public interface ICodeLensHandler : IJsonRpcRequestHandler, CodeLensContainer>, IRegistration, + ICapability where TData : CanBeResolvedData { } [Parallel, Method(TextDocumentNames.CodeLensResolve, Direction.ClientToServer)] - public interface ICodeLensResolveHandler : ICanBeResolvedHandler + public interface ICodeLensResolveHandler : ICanBeResolvedHandler, TData> where TData : CanBeResolvedData { } - public abstract class CodeLensHandler : ICodeLensHandler, ICodeLensResolveHandler + public abstract class CodeLensHandler : ICodeLensHandler, ICodeLensResolveHandler + where TData : CanBeResolvedData { private readonly CodeLensRegistrationOptions _options; + private readonly Guid _id = Guid.NewGuid(); public CodeLensHandler(CodeLensRegistrationOptions registrationOptions) { @@ -33,259 +36,289 @@ public CodeLensHandler(CodeLensRegistrationOptions registrationOptions) } 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 abstract Task> Handle(CodeLensParams request, CancellationToken cancellationToken); + public abstract Task> Handle(CodeLens request, CancellationToken cancellationToken); public virtual void SetCapability(CodeLensCapability capability) => Capability = capability; protected CodeLensCapability Capability { get; private set; } + Guid ICanBeIdentifiedHandler.Id => _id; } public static class CodeLensExtensions { - public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, - Func> handler, - CodeLensRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Func, CodeLensCapability, CancellationToken, Task>> handler, + CodeLensRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CodeLensRegistrationOptions(); return registry.AddHandler(TextDocumentNames.CodeLens, - new LanguageProtocolDelegatingHandlers.Request, CodeLensContainer, CodeLensCapability, CodeLensRegistrationOptions>( handler, registrationOptions)); } - public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, - Func> handler, - Func canResolve, - Func> resolveHandler, - CodeLensRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Func, CodeLensCapability, CancellationToken, Task>> handler, + Func, CodeLensCapability, CancellationToken, Task>> resolveHandler, + CodeLensRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CodeLensRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, cap, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link, cap, token) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); - return registry.AddHandler(TextDocumentNames.CodeLens, - new LanguageProtocolDelegatingHandlers.Request( + return registry + .AddHandler(TextDocumentNames.CodeLens, + new LanguageProtocolDelegatingHandlers.Request, CodeLensContainer, CodeLensCapability, CodeLensRegistrationOptions>( + id, handler, - registrationOptions)) + registrationOptions) + ) .AddHandler(TextDocumentNames.CodeLensResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, CodeLensCapability, CodeLensRegistrationOptions, TData>( + id, resolveHandler, - canResolve, - registrationOptions)) + registrationOptions) + ) ; } - public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, - Func> handler, - CodeLensRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Func, CancellationToken, Task>> handler, + CodeLensRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CodeLensRegistrationOptions(); return registry.AddHandler(TextDocumentNames.CodeLens, - new LanguageProtocolDelegatingHandlers.RequestRegistration, CodeLensContainer, CodeLensRegistrationOptions>( handler, registrationOptions)); } - public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, - Func> handler, - Func canResolve, - Func> resolveHandler, - CodeLensRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Func, CancellationToken, Task>> handler, + Func, CancellationToken, Task>> resolveHandler, + CodeLensRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CodeLensRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link, token) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); - return registry.AddHandler(TextDocumentNames.CodeLens, - new LanguageProtocolDelegatingHandlers.RequestRegistration( + return registry + .AddHandler(TextDocumentNames.CodeLens, + new LanguageProtocolDelegatingHandlers.RequestRegistration, CodeLensContainer, CodeLensRegistrationOptions>( + id, handler, - registrationOptions)) + registrationOptions) + ) .AddHandler(TextDocumentNames.CodeLensResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, CodeLensRegistrationOptions, TData>( + id, resolveHandler, - canResolve, - registrationOptions)) + registrationOptions) + ) ; } - public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, - Func> handler, - CodeLensRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Func, Task>> handler, + CodeLensRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CodeLensRegistrationOptions(); return registry.AddHandler(TextDocumentNames.CodeLens, - new LanguageProtocolDelegatingHandlers.RequestRegistration, CodeLensContainer, CodeLensRegistrationOptions>( handler, registrationOptions)); } - public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, - Func> handler, - Func canResolve, - Func> resolveHandler, - CodeLensRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Func, Task>> handler, + Func, Task>> resolveHandler, + CodeLensRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CodeLensRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); - return registry.AddHandler(TextDocumentNames.CodeLens, - new LanguageProtocolDelegatingHandlers.RequestRegistration, CodeLensContainer, CodeLensRegistrationOptions>( + id, handler, - registrationOptions)) + registrationOptions) + ) .AddHandler(TextDocumentNames.CodeLensResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, CodeLensRegistrationOptions, TData>( + id, resolveHandler, - canResolve, - registrationOptions)) + registrationOptions) + ) ; } - public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, - Action>, CodeLensCapability, CancellationToken> handler, - CodeLensRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Action, IObserver>>, CodeLensCapability, CancellationToken> handler, + CodeLensRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CodeLensRegistrationOptions(); return registry.AddHandler(TextDocumentNames.CodeLens, - _ => new LanguageProtocolDelegatingHandlers.PartialResults new LanguageProtocolDelegatingHandlers.PartialResults, CodeLensContainer, CodeLens, CodeLensCapability, CodeLensRegistrationOptions>( handler, registrationOptions, _.GetRequiredService(), - x => new CodeLensContainer(x))); + x => new CodeLensContainer(x))); } - public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, - Action>, CodeLensCapability, CancellationToken> handler, - Func canResolve, - Func> resolveHandler, - CodeLensRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Action, IObserver>>, CodeLensCapability, CancellationToken> handler, + Func, CodeLensCapability, CancellationToken, Task>> resolveHandler, + CodeLensRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CodeLensRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, cap, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link, cap, token) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); return - registry.AddHandler(TextDocumentNames.CodeLens, - _ => new LanguageProtocolDelegatingHandlers.PartialResults( + registry + .AddHandler(TextDocumentNames.CodeLens, + _ => new LanguageProtocolDelegatingHandlers.PartialResults, CodeLensContainer, CodeLens, CodeLensCapability, CodeLensRegistrationOptions>( + id, handler, registrationOptions, _.GetRequiredService(), - x => new CodeLensContainer(x))) + x => new CodeLensContainer(x)) + ) .AddHandler(TextDocumentNames.CodeLensResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, CodeLensCapability, CodeLensRegistrationOptions, TData>( + id, resolveHandler, - canResolve, - registrationOptions)) + registrationOptions) + ) ; } - public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, - Action>, CancellationToken> handler, - CodeLensRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Action, IObserver>>, CancellationToken> handler, + CodeLensRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CodeLensRegistrationOptions(); return registry.AddHandler(TextDocumentNames.CodeLens, - _ => new LanguageProtocolDelegatingHandlers.PartialResults new LanguageProtocolDelegatingHandlers.PartialResults, CodeLensContainer, CodeLens, CodeLensRegistrationOptions>( handler, registrationOptions, _.GetRequiredService(), - x => new CodeLensContainer(x))); + x => new CodeLensContainer(x))); } - public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, - Action>, CancellationToken> handler, - Func canResolve, - Func> resolveHandler, - CodeLensRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Action, IObserver>>, CancellationToken> handler, + Func, CancellationToken, Task>> resolveHandler, + CodeLensRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CodeLensRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link, token) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); - return registry.AddHandler(TextDocumentNames.CodeLens, - _ => new LanguageProtocolDelegatingHandlers.PartialResults( + return registry + .AddHandler(TextDocumentNames.CodeLens, + _ => new LanguageProtocolDelegatingHandlers.PartialResults, CodeLensContainer, CodeLens, CodeLensRegistrationOptions>( + id, handler, registrationOptions, _.GetRequiredService(), - x => new CodeLensContainer(x))) + x => new CodeLensContainer(x)) + ) .AddHandler(TextDocumentNames.CodeLensResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, CodeLensRegistrationOptions, TData>( + id, resolveHandler, - canResolve, - registrationOptions)) + registrationOptions) + ) ; } - public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, - Action>> handler, - CodeLensRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Action, IObserver>>> handler, + CodeLensRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CodeLensRegistrationOptions(); return registry.AddHandler(TextDocumentNames.CodeLens, - _ => new LanguageProtocolDelegatingHandlers.PartialResults new LanguageProtocolDelegatingHandlers.PartialResults, CodeLensContainer, CodeLens, CodeLensRegistrationOptions>( handler, registrationOptions, _.GetRequiredService(), - x => new CodeLensContainer(x))); + x => new CodeLensContainer(x))); } - public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, - Action>> handler, - Func canResolve, - Func> resolveHandler, - CodeLensRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCodeLens(this ILanguageServerRegistry registry, + Action, IObserver>>> handler, + Func, Task>> resolveHandler, + CodeLensRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CodeLensRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); - return registry.AddHandler(TextDocumentNames.CodeLens, - _ => new LanguageProtocolDelegatingHandlers.PartialResults( + return registry + .AddHandler(TextDocumentNames.CodeLens, + _ => new LanguageProtocolDelegatingHandlers.PartialResults, CodeLensContainer, CodeLens, CodeLensRegistrationOptions>( + id, handler, registrationOptions, _.GetRequiredService(), - x => new CodeLensContainer(x))) + x => new CodeLensContainer(x)) + ) .AddHandler(TextDocumentNames.CodeLensResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, CodeLensRegistrationOptions, TData>( + id, resolveHandler, - canResolve, - registrationOptions)) + registrationOptions) + ) ; } - public static IRequestProgressObservable, CodeLensContainer> RequestCodeLens( + public static IRequestProgressObservable>, CodeLensContainer> RequestCodeLens( this ITextDocumentLanguageClient mediator, - CodeLensParams @params, + CodeLensParams @params, CancellationToken cancellationToken = default) { - return mediator.ProgressManager.MonitorUntil(@params, x => new CodeLensContainer(x), cancellationToken); + return mediator.ProgressManager.MonitorUntil(@params, x => new CodeLensContainer(x), cancellationToken); } - public static Task ResolveCodeLens(this ITextDocumentLanguageClient mediator, CodeLens @params, CancellationToken cancellationToken = default) + public static IRequestProgressObservable>, CodeLensContainer> RequestCodeLens( + this ITextDocumentLanguageClient mediator, + CodeLensParams @params, + CancellationToken cancellationToken = default) + where TData : CanBeResolvedData + { + return mediator.ProgressManager.MonitorUntil(@params, x => new CodeLensContainer(x), cancellationToken); + } + + public static Task> ResolveCodeLens(this ITextDocumentLanguageClient mediator, CodeLens @params, + CancellationToken cancellationToken = default) + { + return mediator.SendRequest(@params, cancellationToken); + } + + public static Task> ResolveCodeLens(this ITextDocumentLanguageClient mediator, CodeLens @params, + CancellationToken cancellationToken = default) + where TData : CanBeResolvedData { return mediator.SendRequest(@params, cancellationToken); } diff --git a/src/Protocol/Document/ICompletionHandler.cs b/src/Protocol/Document/ICompletionHandler.cs index 0131434b7..3ae08d1a6 100644 --- a/src/Protocol/Document/ICompletionHandler.cs +++ b/src/Protocol/Document/ICompletionHandler.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json.Linq; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Protocol.Client; using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities; @@ -14,18 +15,20 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Document { [Parallel, Method(TextDocumentNames.Completion, Direction.ClientToServer)] - public interface ICompletionHandler : IJsonRpcRequestHandler, IRegistration, ICapability + public interface ICompletionHandler : IJsonRpcRequestHandler, CompletionList>, IRegistration, + ICapability where TData : CanBeResolvedData { } [Parallel, Method(TextDocumentNames.CompletionResolve, Direction.ClientToServer)] - public interface ICompletionResolveHandler : ICanBeResolvedHandler + public interface ICompletionResolveHandler : ICanBeResolvedHandler, TData> where TData : CanBeResolvedData { } - public abstract class CompletionHandler : ICompletionHandler, ICompletionResolveHandler + public abstract class CompletionHandler : ICompletionHandler, ICompletionResolveHandler where TData : CanBeResolvedData { private readonly CompletionRegistrationOptions _options; + private readonly Guid _id = Guid.NewGuid(); public CompletionHandler(CompletionRegistrationOptions registrationOptions) { @@ -33,258 +36,269 @@ public CompletionHandler(CompletionRegistrationOptions registrationOptions) } 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 abstract Task> Handle(CompletionParams request, CancellationToken cancellationToken); + public abstract Task> Handle(CompletionItem request, CancellationToken cancellationToken); public virtual void SetCapability(CompletionCapability capability) => Capability = capability; protected CompletionCapability Capability { get; private set; } + Guid ICanBeIdentifiedHandler.Id => _id; } public static class CompletionExtensions { - public static ILanguageServerRegistry OnCompletion( + public static ILanguageServerRegistry OnCompletion( this ILanguageServerRegistry registry, - Func> handler, - CompletionRegistrationOptions registrationOptions) + Func, CompletionCapability, CancellationToken, Task>> handler, + CompletionRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CompletionRegistrationOptions(); return registry.AddHandler(TextDocumentNames.Completion, - new LanguageProtocolDelegatingHandlers.Request, CompletionList, CompletionCapability, CompletionRegistrationOptions>( handler, registrationOptions)); } - public static ILanguageServerRegistry OnCompletion( + public static ILanguageServerRegistry OnCompletion( this ILanguageServerRegistry registry, - Func> handler, - Func canResolve, - Func> resolveHandler, - CompletionRegistrationOptions registrationOptions) + Func, CompletionCapability, CancellationToken, Task>> handler, + Func, CompletionCapability, CancellationToken, Task>> resolveHandler, + CompletionRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CompletionRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, cap, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link, cap, token) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); return registry .AddHandler(TextDocumentNames.Completion, - new LanguageProtocolDelegatingHandlers.Request( + new LanguageProtocolDelegatingHandlers.Request, CompletionList, CompletionCapability, CompletionRegistrationOptions>( + id, handler, registrationOptions) ) .AddHandler(TextDocumentNames.CompletionResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, CompletionCapability, CompletionRegistrationOptions, TData>( + id, resolveHandler, - canResolve, registrationOptions)); } - public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, - Func> handler, - CompletionRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Func, CancellationToken, Task>> handler, + CompletionRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CompletionRegistrationOptions(); return registry.AddHandler(TextDocumentNames.Completion, - new LanguageProtocolDelegatingHandlers.RequestRegistration, CompletionList, CompletionRegistrationOptions>( handler, registrationOptions)); } - public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, - Func> handler, - Func canResolve, - Func> resolveHandler, - CompletionRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Func, CancellationToken, Task>> handler, + Func, CancellationToken, Task>> resolveHandler, + CompletionRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CompletionRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link, token) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); return registry .AddHandler(TextDocumentNames.Completion, - new LanguageProtocolDelegatingHandlers.RequestRegistration( + new LanguageProtocolDelegatingHandlers.RequestRegistration, CompletionList, CompletionRegistrationOptions>( + id, handler, registrationOptions)) .AddHandler(TextDocumentNames.CompletionResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, CompletionRegistrationOptions, TData>( + id, resolveHandler, - canResolve, registrationOptions)); } - public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, - Func> handler, - CompletionRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Func, Task>> handler, + CompletionRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CompletionRegistrationOptions(); return registry.AddHandler(TextDocumentNames.Completion, - new LanguageProtocolDelegatingHandlers.RequestRegistration, CompletionList, CompletionRegistrationOptions>( handler, registrationOptions)); } - public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, - Func> handler, - Func canResolve, - Func> resolveHandler, - CompletionRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Func, Task>> handler, + Func, Task>> resolveHandler, + CompletionRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CompletionRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); return registry .AddHandler(TextDocumentNames.Completion, - new LanguageProtocolDelegatingHandlers.RequestRegistration( + new LanguageProtocolDelegatingHandlers.RequestRegistration, CompletionList, CompletionRegistrationOptions>( + id, handler, registrationOptions) ) .AddHandler(TextDocumentNames.CompletionResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, CompletionRegistrationOptions, TData>( + id, resolveHandler, - canResolve, registrationOptions)); } - public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, - Action>, CompletionCapability, CancellationToken> handler, - CompletionRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Action, IObserver>>, CompletionCapability, CancellationToken> handler, + CompletionRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CompletionRegistrationOptions(); return registry.AddHandler(TextDocumentNames.Completion, - _ => new LanguageProtocolDelegatingHandlers.PartialResults new LanguageProtocolDelegatingHandlers.PartialResults, CompletionList, CompletionItem, CompletionCapability, CompletionRegistrationOptions>( handler, registrationOptions, _.GetRequiredService(), - x => new CompletionList(x))); + x => new CompletionList(x))); } - public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, - Action>, CompletionCapability, CancellationToken> handler, - Func canResolve, - Func> resolveHandler, - CompletionRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Action, IObserver>>, CompletionCapability, CancellationToken> handler, + Func, CompletionCapability, CancellationToken, Task>> resolveHandler, + CompletionRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CompletionRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, cap, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link, cap, token) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.Completion, - _ => new LanguageProtocolDelegatingHandlers.PartialResults( + _ => new LanguageProtocolDelegatingHandlers.PartialResults, CompletionList, CompletionItem, CompletionCapability, CompletionRegistrationOptions>( + id, handler, registrationOptions, _.GetRequiredService(), - x => new CompletionList(x)) + x => new CompletionList(x)) ) .AddHandler(TextDocumentNames.CompletionResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, CompletionCapability, CompletionRegistrationOptions, TData>( + id, resolveHandler, - canResolve, registrationOptions)); } - public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, - Action>, CancellationToken> handler, - CompletionRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Action, IObserver>>, CancellationToken> handler, + CompletionRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CompletionRegistrationOptions(); return registry.AddHandler(TextDocumentNames.Completion, - _ => new LanguageProtocolDelegatingHandlers.PartialResults( + _ => new LanguageProtocolDelegatingHandlers.PartialResults, CompletionList, CompletionItem, CompletionRegistrationOptions>( handler, registrationOptions, _.GetRequiredService(), - x => new CompletionList(x))); + x => new CompletionList(x))); } - public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, - Action>, CancellationToken> handler, - Func canResolve, - Func> resolveHandler, - CompletionRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Action, IObserver>>, CancellationToken> handler, + Func, CancellationToken, Task>> resolveHandler, + CompletionRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CompletionRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link, token) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.Completion, - _ => new LanguageProtocolDelegatingHandlers.PartialResults( + _ => new LanguageProtocolDelegatingHandlers.PartialResults, CompletionList, CompletionItem, CompletionRegistrationOptions>( + id, handler, registrationOptions, _.GetRequiredService(), - x => new CompletionList(x))) + x => new CompletionList(x))) .AddHandler(TextDocumentNames.CompletionResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, CompletionRegistrationOptions, TData>( + id, resolveHandler, - canResolve, registrationOptions)); } - public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, - Action>> handler, - CompletionRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Action, IObserver>>> handler, + CompletionRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CompletionRegistrationOptions(); return registry.AddHandler(TextDocumentNames.Completion, - _ => new LanguageProtocolDelegatingHandlers.PartialResults new LanguageProtocolDelegatingHandlers.PartialResults, CompletionList, CompletionItem, CompletionRegistrationOptions>( handler, registrationOptions, _.GetRequiredService(), - x => new CompletionList(x))); + x => new CompletionList(x))); } - public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, - Action>> handler, - Func canResolve, - Func> resolveHandler, - CompletionRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnCompletion(this ILanguageServerRegistry registry, + Action, IObserver>>> handler, + Func, Task>> resolveHandler, + CompletionRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new CompletionRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.Completion, - _ => new LanguageProtocolDelegatingHandlers.PartialResults new LanguageProtocolDelegatingHandlers.PartialResults, CompletionList, CompletionItem, CompletionRegistrationOptions>( + id, handler, registrationOptions, _.GetRequiredService(), - x => new CompletionList(x))) + x => new CompletionList(x))) .AddHandler(TextDocumentNames.CompletionResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, CompletionRegistrationOptions, TData>( + id, resolveHandler, - canResolve, registrationOptions)); } - public static IRequestProgressObservable, CompletionList> RequestCompletion(this ITextDocumentLanguageClient mediator, CompletionParams @params, + public static IRequestProgressObservable>, CompletionList> RequestCompletion( + this ITextDocumentLanguageClient mediator, CompletionParams @params, + CancellationToken cancellationToken = default) + { + return mediator.ProgressManager.MonitorUntil(@params, x => new CompletionList(x), cancellationToken); + } + + public static Task> ResolveCompletion(this ITextDocumentLanguageClient mediator, CompletionItem @params, + CancellationToken cancellationToken = default) + { + return mediator.SendRequest(@params, cancellationToken); + } + + public static IRequestProgressObservable>, CompletionList> RequestCompletion(this ITextDocumentLanguageClient mediator, + CompletionParams @params, CancellationToken cancellationToken = default) + where TData : CanBeResolvedData { - return mediator.ProgressManager.MonitorUntil(@params, x => new CompletionList(x), cancellationToken); + return mediator.ProgressManager.MonitorUntil(@params, x => new CompletionList(x), cancellationToken); } - public static Task ResolveCompletion(this ITextDocumentLanguageClient mediator, CompletionItem @params, CancellationToken cancellationToken = default) + public static Task> ResolveCompletion(this ITextDocumentLanguageClient mediator, CompletionItem @params, + CancellationToken cancellationToken = default) + where TData : CanBeResolvedData { return mediator.SendRequest(@params, cancellationToken); } diff --git a/src/Protocol/Document/IDocumentLinkHandler.cs b/src/Protocol/Document/IDocumentLinkHandler.cs index dbf84fdce..10ff48cab 100644 --- a/src/Protocol/Document/IDocumentLinkHandler.cs +++ b/src/Protocol/Document/IDocumentLinkHandler.cs @@ -14,19 +14,20 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Document { [Parallel, Method(TextDocumentNames.DocumentLink, Direction.ClientToServer)] - public interface IDocumentLinkHandler : IJsonRpcRequestHandler, - IRegistration, ICapability + public interface IDocumentLinkHandler : IJsonRpcRequestHandler, DocumentLinkContainer>, + IRegistration, ICapability where TData : CanBeResolvedData { } [Parallel, Method(TextDocumentNames.DocumentLinkResolve, Direction.ClientToServer)] - public interface IDocumentLinkResolveHandler : ICanBeResolvedHandler + public interface IDocumentLinkResolveHandler : ICanBeResolvedHandler, TData> where TData : CanBeResolvedData { } - public abstract class DocumentLinkHandler : IDocumentLinkHandler, IDocumentLinkResolveHandler + public abstract class DocumentLinkHandler : IDocumentLinkHandler, IDocumentLinkResolveHandler where TData : CanBeResolvedData { private readonly DocumentLinkRegistrationOptions _options; + private readonly Guid _id = Guid.NewGuid(); public DocumentLinkHandler(DocumentLinkRegistrationOptions registrationOptions) { @@ -35,257 +36,275 @@ public DocumentLinkHandler(DocumentLinkRegistrationOptions registrationOptions) public DocumentLinkRegistrationOptions GetRegistrationOptions() => _options; - public abstract Task Handle(DocumentLinkParams request, CancellationToken cancellationToken); + public abstract Task> Handle(DocumentLinkParams request, CancellationToken cancellationToken); - public abstract Task Handle(DocumentLink request, CancellationToken cancellationToken); - public abstract bool CanResolve(DocumentLink value); + public abstract Task> Handle(DocumentLink request, CancellationToken cancellationToken); public virtual void SetCapability(DocumentLinkCapability capability) => Capability = capability; protected DocumentLinkCapability Capability { get; private set; } + Guid ICanBeIdentifiedHandler.Id => _id; } public static class DocumentLinkExtensions { -public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, - Func> handler, - DocumentLinkRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Func, DocumentLinkCapability, CancellationToken, Task>> handler, + DocumentLinkRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new DocumentLinkRegistrationOptions(); return registry.AddHandler(TextDocumentNames.DocumentLink, - new LanguageProtocolDelegatingHandlers.Request, DocumentLinkContainer, DocumentLinkCapability, DocumentLinkRegistrationOptions>( handler, registrationOptions)); } -public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, - Func> handler, - Func canResolve, - Func> resolveHandler, - DocumentLinkRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Func, DocumentLinkCapability, CancellationToken, Task>> handler, + Func, DocumentLinkCapability, CancellationToken, Task>> resolveHandler, + DocumentLinkRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new DocumentLinkRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, cap, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link, cap, token) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.DocumentLink, - new LanguageProtocolDelegatingHandlers.Request, DocumentLinkContainer, DocumentLinkCapability, DocumentLinkRegistrationOptions>( + id, handler, registrationOptions)) .AddHandler(TextDocumentNames.DocumentLinkResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( - resolveHandler, - canResolve, - registrationOptions)) - ; + new LanguageProtocolDelegatingHandlers.CanBeResolved, DocumentLinkCapability, DocumentLinkRegistrationOptions, TData>( + id, + resolveHandler, registrationOptions + )); } -public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, - Func> handler, - DocumentLinkRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Func, CancellationToken, Task>> handler, + DocumentLinkRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new DocumentLinkRegistrationOptions(); return registry.AddHandler(TextDocumentNames.DocumentLink, - new LanguageProtocolDelegatingHandlers.RequestRegistration, DocumentLinkContainer, DocumentLinkRegistrationOptions>( handler, registrationOptions)); } -public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, - Func> handler, - Func canResolve, - Func> resolveHandler, - DocumentLinkRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Func, CancellationToken, Task>> handler, + Func, CancellationToken, Task>> resolveHandler, + DocumentLinkRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new DocumentLinkRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link, token) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.DocumentLink, - new LanguageProtocolDelegatingHandlers.RequestRegistration, DocumentLinkContainer, DocumentLinkRegistrationOptions>( + id, handler, registrationOptions)).AddHandler(TextDocumentNames.DocumentLinkResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, DocumentLinkRegistrationOptions, TData>( + id, resolveHandler, - canResolve, registrationOptions)) ; } -public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, - Func> handler, - DocumentLinkRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Func, Task>> handler, + DocumentLinkRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new DocumentLinkRegistrationOptions(); return registry.AddHandler(TextDocumentNames.DocumentLink, - new LanguageProtocolDelegatingHandlers.RequestRegistration, DocumentLinkContainer, DocumentLinkRegistrationOptions>( handler, registrationOptions)); } -public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, - Func> handler, - Func canResolve, - Func> resolveHandler, - DocumentLinkRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Func, Task>> handler, + Func, Task>> resolveHandler, + DocumentLinkRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new DocumentLinkRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.DocumentLink, - new LanguageProtocolDelegatingHandlers.RequestRegistration, DocumentLinkContainer, DocumentLinkRegistrationOptions>( + id, handler, registrationOptions)) .AddHandler(TextDocumentNames.DocumentLinkResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, DocumentLinkRegistrationOptions, TData>( + id, resolveHandler, - canResolve, registrationOptions)) ; } -public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, - Action>, DocumentLinkCapability, CancellationToken> handler, - DocumentLinkRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Action, IObserver>>, DocumentLinkCapability, CancellationToken> handler, + DocumentLinkRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new DocumentLinkRegistrationOptions(); return registry.AddHandler(TextDocumentNames.DocumentLink, - _ => new LanguageProtocolDelegatingHandlers.PartialResults new LanguageProtocolDelegatingHandlers.PartialResults, DocumentLinkContainer, DocumentLink, DocumentLinkCapability, DocumentLinkRegistrationOptions>( handler, registrationOptions, _.GetRequiredService(), - x => new DocumentLinkContainer(x))); + x => new DocumentLinkContainer(x))); } -public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, - Action>, DocumentLinkCapability, CancellationToken> handler, - Func canResolve, - Func> resolveHandler, - DocumentLinkRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Action, IObserver>>, DocumentLinkCapability, CancellationToken> handler, + Func, DocumentLinkCapability, CancellationToken, Task>> resolveHandler, + DocumentLinkRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new DocumentLinkRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, cap, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link, cap, token) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.DocumentLink, - _ => new LanguageProtocolDelegatingHandlers.PartialResults new LanguageProtocolDelegatingHandlers.PartialResults, DocumentLinkContainer, DocumentLink, + DocumentLinkCapability, DocumentLinkRegistrationOptions>( + id, handler, registrationOptions, _.GetRequiredService(), - x => new DocumentLinkContainer(x))) + x => new DocumentLinkContainer(x))) .AddHandler(TextDocumentNames.DocumentLinkResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, DocumentLinkCapability, DocumentLinkRegistrationOptions, TData>( + id, resolveHandler, - canResolve, registrationOptions)) ; } -public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, - Action>, CancellationToken> handler, - DocumentLinkRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Action, IObserver>>, CancellationToken> handler, + DocumentLinkRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new DocumentLinkRegistrationOptions(); return registry.AddHandler(TextDocumentNames.DocumentLink, - _ => new LanguageProtocolDelegatingHandlers.PartialResults new LanguageProtocolDelegatingHandlers.PartialResults, DocumentLinkContainer, DocumentLink, DocumentLinkRegistrationOptions>( handler, registrationOptions, _.GetRequiredService(), - x => new DocumentLinkContainer(x))); + x => new DocumentLinkContainer(x))); } -public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, - Action>, CancellationToken> handler, - Func canResolve, - Func> resolveHandler, - DocumentLinkRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Action, IObserver>>, CancellationToken> handler, + Func, CancellationToken, Task>> resolveHandler, + DocumentLinkRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new DocumentLinkRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link, token) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link, token) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.DocumentLink, - _ => new LanguageProtocolDelegatingHandlers.PartialResults new LanguageProtocolDelegatingHandlers.PartialResults, DocumentLinkContainer, DocumentLink, DocumentLinkRegistrationOptions>( + id, handler, registrationOptions, _.GetRequiredService(), - x => new DocumentLinkContainer(x))) + x => new DocumentLinkContainer(x))) .AddHandler(TextDocumentNames.DocumentLinkResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, DocumentLinkRegistrationOptions, TData>( + id, resolveHandler, - canResolve, registrationOptions)) ; } -public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, - Action>> handler, - DocumentLinkRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Action, IObserver>>> handler, + DocumentLinkRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new DocumentLinkRegistrationOptions(); return registry.AddHandler(TextDocumentNames.DocumentLink, - _ => new LanguageProtocolDelegatingHandlers.PartialResults new LanguageProtocolDelegatingHandlers.PartialResults, DocumentLinkContainer, DocumentLink, DocumentLinkRegistrationOptions>( handler, registrationOptions, _.GetRequiredService(), - x => new DocumentLinkContainer(x))); + x => new DocumentLinkContainer(x))); } -public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, - Action>> handler, - Func canResolve, - Func> resolveHandler, - DocumentLinkRegistrationOptions registrationOptions) + public static ILanguageServerRegistry OnDocumentLink(this ILanguageServerRegistry registry, + Action, IObserver>>> handler, + Func, Task>> resolveHandler, + DocumentLinkRegistrationOptions registrationOptions) where TData : CanBeResolvedData { registrationOptions ??= new DocumentLinkRegistrationOptions(); - registrationOptions.ResolveProvider = canResolve != null && resolveHandler != null; - canResolve ??= item => registrationOptions.ResolveProvider; - resolveHandler ??= (link) => Task.FromException(new NotImplementedException()); + registrationOptions.ResolveProvider = resolveHandler != null; + resolveHandler ??= (link) => Task.FromException>(new NotImplementedException()); + var id = Guid.NewGuid(); return registry.AddHandler(TextDocumentNames.DocumentLink, - _ => new LanguageProtocolDelegatingHandlers.PartialResults new LanguageProtocolDelegatingHandlers.PartialResults, DocumentLinkContainer, DocumentLink, DocumentLinkRegistrationOptions>( + id, handler, registrationOptions, _.GetRequiredService(), - x => new DocumentLinkContainer(x))) + x => new DocumentLinkContainer(x))) .AddHandler(TextDocumentNames.DocumentLinkResolve, - new LanguageProtocolDelegatingHandlers.CanBeResolved( + new LanguageProtocolDelegatingHandlers.CanBeResolved, DocumentLinkRegistrationOptions, TData>( + id, resolveHandler, - canResolve, registrationOptions)) ; } - public static IRequestProgressObservable, DocumentLinkContainer> RequestDocumentLink(this ITextDocumentLanguageClient mediator, - DocumentLinkParams @params, + public static IRequestProgressObservable>, DocumentLinkContainer> RequestDocumentLink( + this ITextDocumentLanguageClient mediator, + DocumentLinkParams @params, CancellationToken cancellationToken = default) { - return mediator.ProgressManager.MonitorUntil(@params, x => new DocumentLinkContainer(x), cancellationToken); + return mediator.ProgressManager.MonitorUntil(@params, x => new DocumentLinkContainer(x), cancellationToken); } - public static Task ResolveDocumentLink(this ITextDocumentLanguageClient mediator, DocumentLink @params, CancellationToken cancellationToken = default) + public static Task> ResolveDocumentLink(this ITextDocumentLanguageClient mediator, DocumentLink @params, + CancellationToken cancellationToken = default) + { + return mediator.SendRequest(@params, cancellationToken); + } + + public static IRequestProgressObservable>, DocumentLinkContainer> RequestDocumentLink( + this ITextDocumentLanguageClient mediator, + DocumentLinkParams @params, + CancellationToken cancellationToken = default) + where TData : CanBeResolvedData + { + return mediator.ProgressManager.MonitorUntil(@params, x => new DocumentLinkContainer(x), cancellationToken); + } + + public static Task> ResolveDocumentLink(this ITextDocumentLanguageClient mediator, DocumentLink @params, + CancellationToken cancellationToken = default) + where TData : CanBeResolvedData { return mediator.SendRequest(@params, cancellationToken); } diff --git a/src/Protocol/Document/ITextDocumentIdentifier.cs b/src/Protocol/Document/ITextDocumentIdentifier.cs index fdcf61ea8..c17bec66e 100644 --- a/src/Protocol/Document/ITextDocumentIdentifier.cs +++ b/src/Protocol/Document/ITextDocumentIdentifier.cs @@ -1,7 +1,9 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Reactive.Disposables; +using Minimatch; namespace OmniSharp.Extensions.LanguageServer.Protocol.Document { @@ -12,20 +14,104 @@ public interface ITextDocumentIdentifier /// /// /// - TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri); + IEnumerable GetTextDocumentAttributes(DocumentUri uri); } public abstract class TextDocumentIdentifierBase : ITextDocumentIdentifier { - public TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri) + public IEnumerable GetTextDocumentAttributes(DocumentUri uri) { - var (languageId, schema) = GetAttributes(uri); - return new TextDocumentAttributes(uri, languageId, schema); + if (GetAttributes(uri, out var languageId, out var scheme)) + { + yield return new TextDocumentAttributes(uri, languageId, scheme); + } } - protected abstract (string languageId, string schema) GetAttributes(DocumentUri uri); + protected abstract bool GetAttributes(DocumentUri uri, out string languageId, out string scheme); } + public class WellKnownLanguageIdentifier : TextDocumentIdentifierBase + { + private readonly string _languageId; + private readonly Minimatcher[] _minimatchers; + public WellKnownLanguageIdentifier( + string name, + string languageId, + params string[] filePatterns) + { + Name = name; + _languageId = languageId; + _minimatchers = filePatterns.Select(z => new Minimatcher(z)).ToArray(); + } + + public string Name { get; } + + protected override bool GetAttributes(DocumentUri uri, out string languageId, out string scheme) + { + languageId = _languageId; + scheme = uri.Scheme; + return _minimatchers.Any(z => z.IsMatch(uri.ToString())); + } + } + + public static class WellKnownLanguages + { + public static readonly WellKnownLanguageIdentifier ABAP = new WellKnownLanguageIdentifier("ABAP", "abap", "**/*.abap"); + public static readonly WellKnownLanguageIdentifier WindowsBatch = new WellKnownLanguageIdentifier("Windows Bat", "bat", "**/*.{bat,cmd}"); + public static readonly WellKnownLanguageIdentifier BibTeX = new WellKnownLanguageIdentifier("BibTeX", "bibtex", "**/*.bib"); + public static readonly WellKnownLanguageIdentifier Clojure = new WellKnownLanguageIdentifier("Clojure", "clojure", "**/*.{clj,cljs,cljc,ed}"); + public static readonly WellKnownLanguageIdentifier CoffeeScript = new WellKnownLanguageIdentifier("Coffeescript", "coffeescript", "**/*.{coffee,litcoffee}"); + public static readonly WellKnownLanguageIdentifier C = new WellKnownLanguageIdentifier("C", "c", "**/*.{c,h}"); + public static readonly WellKnownLanguageIdentifier CPlusPlus = new WellKnownLanguageIdentifier("C++", "cpp", "**/*.{c,cc,cpp,cxx,c++,h,hh,hpp,hxx,h++}"); + public static readonly WellKnownLanguageIdentifier CSharp = new WellKnownLanguageIdentifier("C#", "csharp", "**/*.{cs,csi,csx,cake}"); + public static readonly WellKnownLanguageIdentifier CSS = new WellKnownLanguageIdentifier("CSS", "css", "**/*.css"); + public static readonly WellKnownLanguageIdentifier Diff = new WellKnownLanguageIdentifier("Diff", "diff", "**/*.diff"); + public static readonly WellKnownLanguageIdentifier Dart = new WellKnownLanguageIdentifier("Dart", "dart", "**/*.dart"); + public static readonly WellKnownLanguageIdentifier Dockerfile = new WellKnownLanguageIdentifier("Dockerfile", "dockerfile", "**/*.dockerfile"); + public static readonly WellKnownLanguageIdentifier Elixir = new WellKnownLanguageIdentifier("Elixir", "elixir", "**/*.{ex,exs}"); + public static readonly WellKnownLanguageIdentifier Erlang = new WellKnownLanguageIdentifier("Erlang", "erlang", "**/*.{erl,hrl}"); + public static readonly WellKnownLanguageIdentifier FSharp = new WellKnownLanguageIdentifier("F#", "fsharp", "**/*.{fs,fsi,fsx,fsscript}"); + public static readonly WellKnownLanguageIdentifier Go = new WellKnownLanguageIdentifier("Go", "go", "**/*.go"); + public static readonly WellKnownLanguageIdentifier Groovy = new WellKnownLanguageIdentifier("Groovy", "groovy", "**/*.{groovy,gvy,gy,gsh}"); + public static readonly WellKnownLanguageIdentifier Handlebars = new WellKnownLanguageIdentifier("Handlebars", "handlebars", "**/*.{handlebars,hbs,mustache}"); + public static readonly WellKnownLanguageIdentifier HTML = new WellKnownLanguageIdentifier("HTML", "html", "**/*.{html,htm}"); + public static readonly WellKnownLanguageIdentifier Ini = new WellKnownLanguageIdentifier("Ini", "ini", "**/*.ini"); + public static readonly WellKnownLanguageIdentifier Java = new WellKnownLanguageIdentifier("Java", "java", "**/*.{java,class}"); + public static readonly WellKnownLanguageIdentifier JavaScript = new WellKnownLanguageIdentifier("JavaScript", "javascript", "**/*.{js,mjs}"); + public static readonly WellKnownLanguageIdentifier JavaScriptReact = new WellKnownLanguageIdentifier("JavaScript React", "javascriptreact", "**/*.jsx"); + public static readonly WellKnownLanguageIdentifier JSON = new WellKnownLanguageIdentifier("JSON", "json", "**/*.json"); + public static readonly WellKnownLanguageIdentifier LaTeX = new WellKnownLanguageIdentifier("TeX", "latex", "**/*.tex"); + public static readonly WellKnownLanguageIdentifier Less = new WellKnownLanguageIdentifier("Less", "less", "**/*.less"); + public static readonly WellKnownLanguageIdentifier Lua = new WellKnownLanguageIdentifier("Lua", "lua", "**/*.lua"); + public static readonly WellKnownLanguageIdentifier Markdown = new WellKnownLanguageIdentifier("Markdown", "markdown", "**/*.{markdown,mdown,mkdn,mkd,md}"); + public static readonly WellKnownLanguageIdentifier ObjectiveC = new WellKnownLanguageIdentifier("Objective-C", "objective-c", "**/*.{h,m,mm,M}"); + public static readonly WellKnownLanguageIdentifier ObjectiveCPlusPlus = new WellKnownLanguageIdentifier("Objective-C++", "objective-cpp", "**/*.{h,mm}"); + public static readonly WellKnownLanguageIdentifier Perl = new WellKnownLanguageIdentifier("Perl", "perl", "**/*.{plx,pl,pm,xs,t,pod}"); + public static readonly WellKnownLanguageIdentifier Raku = new WellKnownLanguageIdentifier("Raku", "raku", "**/*.{p6,pm6,pod6,t6,raku,rakumod,rakudoc,rakutest}"); + public static readonly WellKnownLanguageIdentifier PHP = new WellKnownLanguageIdentifier("PHP", "php", "**/*.{php,phtml,php3,php4,php5,php7,phps,php-s,pht,pha}"); + public static readonly WellKnownLanguageIdentifier PowerShell = new WellKnownLanguageIdentifier("PowerShell", "powershell", "**/*.{ps1,psd1,psm1}"); + public static readonly WellKnownLanguageIdentifier Pug = new WellKnownLanguageIdentifier("Pug", "jade", "**/*.{pug}"); + public static readonly WellKnownLanguageIdentifier Python = new WellKnownLanguageIdentifier("Python", "python", "**/*.{py,pyi,pyc,pyd,pyo,pyw,pyz}"); + public static readonly WellKnownLanguageIdentifier R = new WellKnownLanguageIdentifier("R", "r", "**/*.{r,rdata,rds,rda}"); + public static readonly WellKnownLanguageIdentifier Razor = new WellKnownLanguageIdentifier("Razor", "razor", "**/*.{razor,cshtml}"); + public static readonly WellKnownLanguageIdentifier Ruby = new WellKnownLanguageIdentifier("Ruby", "ruby", "**/*.rb"); + public static readonly WellKnownLanguageIdentifier Rust = new WellKnownLanguageIdentifier("Rust", "rust", "**/*.{rs,rlib}"); + public static readonly WellKnownLanguageIdentifier SCSS = new WellKnownLanguageIdentifier("SCSS", "scss", "**/*.{scss,sass}"); + public static readonly WellKnownLanguageIdentifier Scala = new WellKnownLanguageIdentifier("Scala", "scala", "**/*.{scala,sc}"); + public static readonly WellKnownLanguageIdentifier SQL = new WellKnownLanguageIdentifier("SQL", "sql", "**/*.sql"); + public static readonly WellKnownLanguageIdentifier Swift = new WellKnownLanguageIdentifier("Swift", "swift", "**/*.swift"); + public static readonly WellKnownLanguageIdentifier TypeScript = new WellKnownLanguageIdentifier("TypeScript", "typescript", "**/*.ts"); + public static readonly WellKnownLanguageIdentifier TypeScriptReact = new WellKnownLanguageIdentifier("TypeScript React", "typescriptreact", "**/*.tsx"); + public static readonly WellKnownLanguageIdentifier TeX = new WellKnownLanguageIdentifier("TeX", "tex", "**/*.tex"); + public static readonly WellKnownLanguageIdentifier VisualBasic = new WellKnownLanguageIdentifier("Visual Basic", "vb", "**/*.vb"); + public static readonly WellKnownLanguageIdentifier XML = new WellKnownLanguageIdentifier("XML", "xml", "**/*.xml"); + public static readonly WellKnownLanguageIdentifier XSL = new WellKnownLanguageIdentifier("XSL", "xsl", "**/*.xsl"); + public static readonly WellKnownLanguageIdentifier YAML = new WellKnownLanguageIdentifier("YAML", "yaml", "**/*.{yaml,yml}"); + } + + + + public class TextDocumentIdentifiers : IEnumerable { private readonly HashSet _textDocumentIdentifiers = new HashSet(); diff --git a/src/Protocol/Document/ITextDocumentSyncHandler.cs b/src/Protocol/Document/ITextDocumentSyncHandler.cs index bed599868..0c8d1559d 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; @@ -38,7 +39,7 @@ TextDocumentSaveRegistrationOptions IRegistration. GetRegistrationOptions() => _changeOptions; - public abstract TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri); + public abstract IEnumerable GetTextDocumentAttributes(DocumentUri uri); public abstract Task Handle(DidOpenTextDocumentParams request, CancellationToken cancellationToken); public abstract Task Handle(DidChangeTextDocumentParams request, CancellationToken cancellationToken); public abstract Task Handle(DidSaveTextDocumentParams request, CancellationToken cancellationToken); @@ -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, @@ -258,8 +259,10 @@ public override async Task Handle(DidCloseTextDocumentParams request, return Unit.Value; } - public override TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri) => - _getTextDocumentAttributes.Invoke(uri); + public override IEnumerable GetTextDocumentAttributes(DocumentUri uri) + { + yield return _getTextDocumentAttributes.Invoke(uri); + } public override void SetCapability(SynchronizationCapability capability) { diff --git a/src/Protocol/Models/CodeLens.cs b/src/Protocol/Models/CodeLens.cs index 8308189fd..593e4ad9e 100644 --- a/src/Protocol/Models/CodeLens.cs +++ b/src/Protocol/Models/CodeLens.cs @@ -5,6 +5,34 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { + /// + /// 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. + /// + public interface ICodeLens : ICanBeResolved where TData : CanBeResolvedData + { + /// + /// The range in which this code lens is valid. Should only span a single line. + /// + Range Range { get; } + + /// + /// The command this code lens represents. + /// + [Optional] + Command Command { get; } + + /// + /// A data entry field that is preserved on a code lens item between + /// a code lens and a code lens resolve request. + /// + [Optional] + TData Data { get; } + } + /// /// 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. @@ -13,8 +41,23 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Models /// reasons the creation of a code lens and resolving should be done in two stages. /// [Method(TextDocumentNames.CodeLensResolve, Direction.ClientToServer)] - public class CodeLens : ICanBeResolved, IRequest + public class CodeLens : ICodeLens, IRequest> + where TData : CanBeResolvedData { + /// + /// Used for aggregating results when completion is supported by multiple handlers + /// + public static CodeLens From(ICodeLens item) + { + return item is CodeLens cl + ? cl + : new CodeLens() { + Command = item.Command, + Data = item.Data, + Range = item.Range + }; + } + /// /// The range in which this code lens is valid. Should only span a single line. /// @@ -31,6 +74,6 @@ public class CodeLens : ICanBeResolved, IRequest /// a code lens and a code lens resolve request. /// [Optional] - public JToken Data { get; set; } + public TData Data { get; set; } } } diff --git a/src/Protocol/Models/CodeLensContainer.cs b/src/Protocol/Models/CodeLensContainer.cs index 8f31f89b5..fe29c5883 100644 --- a/src/Protocol/Models/CodeLensContainer.cs +++ b/src/Protocol/Models/CodeLensContainer.cs @@ -1,36 +1,48 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using OmniSharp.Extensions.JsonRpc; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { - public class CodeLensContainer : Container + public class CodeLensContainer : Container>, IAggregateResults + where TData : CanBeResolvedData { - public CodeLensContainer() : this(Enumerable.Empty()) + public CodeLensContainer() : this(Enumerable.Empty>()) { } - public CodeLensContainer(IEnumerable items) : base(items) + public CodeLensContainer(IEnumerable> items) : base(items) { } - public CodeLensContainer(params CodeLens[] items) : base(items) + public CodeLensContainer(params CodeLens[] items) : base(items) { } - public static implicit operator CodeLensContainer(CodeLens[] items) + public static implicit operator CodeLensContainer(CodeLens[] items) { - return new CodeLensContainer(items); + return new CodeLensContainer(items); } - public static implicit operator CodeLensContainer(Collection items) + public static implicit operator CodeLensContainer(Collection> items) { - return new CodeLensContainer(items); + return new CodeLensContainer(items); } - public static implicit operator CodeLensContainer(List items) + public static implicit operator CodeLensContainer(List> items) { - return new CodeLensContainer(items); + return new CodeLensContainer(items); + } + + object IAggregateResults.AggregateResults(IEnumerable values) + { + return new CodeLensContainer( + values.Cast>() + .SelectMany(z => z.OfType>()) + .Concat(this) + .Select(CodeLens.From) + ); } } } diff --git a/src/Protocol/Models/CodeLensParams.cs b/src/Protocol/Models/CodeLensParams.cs index 45e560b0a..0e45c08a7 100644 --- a/src/Protocol/Models/CodeLensParams.cs +++ b/src/Protocol/Models/CodeLensParams.cs @@ -1,10 +1,11 @@ +using Newtonsoft.Json.Linq; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { [Method(TextDocumentNames.CodeLens, Direction.ClientToServer)] - public class CodeLensParams : ITextDocumentIdentifierParams, IWorkDoneProgressParams, IPartialItemsRequest + public class CodeLensParams : ITextDocumentIdentifierParams, IWorkDoneProgressParams, IPartialItemsRequest, CodeLens> where TData : CanBeResolvedData { /// /// The document to request code lens for. diff --git a/src/Protocol/Models/CompletionItem.cs b/src/Protocol/Models/CompletionItem.cs index 5f2bcaa0c..8c4edda71 100644 --- a/src/Protocol/Models/CompletionItem.cs +++ b/src/Protocol/Models/CompletionItem.cs @@ -5,9 +5,162 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { + public interface ICompletionItem : ICanBeResolved + where TData : CanBeResolvedData + { + /// + /// The label of this completion item. By default + /// also the text that is inserted when selecting + /// this completion. + /// + public string Label { get; } + + /// + /// The kind of this completion item. Based of the kind + /// an icon is chosen by the editor. + /// + [Optional] + public CompletionItemKind Kind { get; } + + /// + /// Tags for this completion item. + /// + /// @since 3.15.0 + /// + public Container Tags { get; } + + /// + /// A human-readable string with additional information + /// about this item, like type or symbol information. + /// + [Optional] + public string Detail { get; } + + /// + /// A human-readable string that represents a doc-comment. + /// + [Optional] + public StringOrMarkupContent Documentation { get; } + + /// + /// Indicates if this item is deprecated. + /// + [Optional] + public bool Deprecated { get; } + + /// + /// Select this item when showing. + /// + /// *Note* that only one completion item can be selected and that the + /// tool / client decides which item that is. The rule is that the *first* + /// item of those that match best is selected. + /// + [Optional] + public bool Preselect { get; } + + /// + /// A string that shoud be used when comparing this item + /// with other items. When `falsy` the label is used. + /// + [Optional] + public string SortText { get; } + + /// + /// A string that should be used when filtering a set of + /// completion items. When `falsy` the label is used. + /// + + [Optional] + public string FilterText { get; } + + /// + /// A string that should be inserted a document when selecting + /// this completion. When `falsy` the label is used. + /// + + [Optional] + public string InsertText { get; } + + /// + /// The format of the insert text. The format applies to both the `insertText` property + /// and the `newText` property of a provided `textEdit`. + /// + [Optional] + public InsertTextFormat InsertTextFormat { get; } + + /// + /// An edit which is applied to a document when selecting this completion. When an edit is provided the value of + /// `insertText` is ignored. + /// + /// *Note:* The range of the edit must be a single line range and it must contain the position at which completion + /// has been requested. + /// + [Optional] + public TextEdit TextEdit { get; } + + /// + /// An optional array of additional text edits that are applied when + /// selecting this completion. Edits must not overlap with the main edit + /// nor with themselves. + /// + [Optional] + public TextEditContainer AdditionalTextEdits { get; } + + /// + /// An optional set of characters that when pressed while this completion is active will accept it first and + /// then type that character. *Note* that all commit characters should have `length=1` and that superfluous + /// characters will be ignored. + /// + [Optional] + public Container CommitCharacters { get; } + + /// + /// An optional command that is executed/// after* inserting this completion./// Note* that + /// additional modifications to the current document should be described with the + /// additionalTextEdits-property. + /// + [Optional] + public Command Command { get; } + + /// + /// An data entry field that is preserved on a completion item between + /// a completion and a completion resolve request. + /// + [Optional] + public TData Data { get; } + } + [Method(TextDocumentNames.CompletionResolve, Direction.ClientToServer)] - public class CompletionItem : ICanBeResolved, IRequest + public class CompletionItem : ICompletionItem, IRequest> + where TData : CanBeResolvedData { + /// + /// Used for aggregating results when completion is supported by multiple handlers + /// + public static CompletionItem From(ICompletionItem item) + { + return item is CompletionItem cl + ? cl + : new CompletionItem() { + Command = item.Command, + Data = item.Data, + Deprecated = item.Deprecated, + Detail = item.Detail, + Documentation = item.Documentation, + Kind = item.Kind, + Label = item.Label, + Preselect = item.Preselect, + Tags = item.Tags, + CommitCharacters = item.CommitCharacters, + FilterText = item.FilterText, + InsertText = item.InsertText, + SortText = item.SortText, + TextEdit = item.TextEdit, + AdditionalTextEdits = item.AdditionalTextEdits, + InsertTextFormat = item.InsertTextFormat + }; + } + /// /// The label of this completion item. By default /// also the text that is inserted when selecting @@ -127,6 +280,6 @@ public class CompletionItem : ICanBeResolved, IRequest /// a completion and a completion resolve request. /// [Optional] - public JToken Data { get; set; } + public TData Data { get; set; } } } diff --git a/src/Protocol/Models/CompletionList.cs b/src/Protocol/Models/CompletionList.cs index 29817dc51..23403bb3f 100644 --- a/src/Protocol/Models/CompletionList.cs +++ b/src/Protocol/Models/CompletionList.cs @@ -1,29 +1,55 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using Newtonsoft.Json.Linq; +using OmniSharp.Extensions.JsonRpc; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { + interface ICompletionList + { + /// + /// This list it not complete. Further typing should result in recomputing + /// this list. + /// + public bool IsIncomplete { get; } + + /// + /// The completion items. + /// + public IEnumerable Items { get; } + } + /// /// Represents a collection of [completion items](#CompletionItem) to be presented /// in the editor. /// - public class CompletionList : Container + public class CompletionList : Container>, ICompletionList, IAggregateResults + where TData : CanBeResolvedData { - public CompletionList() : base(Enumerable.Empty()) { } - public CompletionList(bool isIncomplete) : base(Enumerable.Empty()) + 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) + 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) + public CompletionList(params CompletionItem[] items) : base(items) + { + } + + public CompletionList(bool isIncomplete, params CompletionItem[] items) : base(items) { IsIncomplete = isIncomplete; } @@ -37,24 +63,88 @@ public CompletionList(bool isIncomplete, params CompletionItem[] items) : base(i /// /// The completion items. /// - public IEnumerable Items => this; + public IEnumerable> Items => this; + + IEnumerable ICompletionList.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(); + } + + object IAggregateResults.AggregateResults(IEnumerable values) + { + return new CompletionList( + values + .Cast>() + .SelectMany(z => z.OfType>()) + .Concat(this) + .Select(CompletionItem.From) + ); + } + } + + /// + /// Represents a collection of [completion items](#CompletionItem) to be presented + /// in the editor. + /// + public class CompletionList : CompletionList + { + public CompletionList() : base(Enumerable.Empty>()) + { + } + + public CompletionList(bool isIncomplete) : base(isIncomplete) + { + } + + public CompletionList(IEnumerable> items) : base(items) + { + } + + public CompletionList(IEnumerable> items, bool isIncomplete) : base(items, isIncomplete) + { + } + + public CompletionList(params CompletionItem[] items) : base(items) + { + } + + public CompletionList(bool isIncomplete, params CompletionItem[] items) : base(isIncomplete, items) + { + } - public static implicit operator CompletionList(CompletionItem[] items) + public static implicit operator CompletionList(CompletionItem[] items) { return new CompletionList(items); } - public static implicit operator CompletionList(Collection items) + public static implicit operator CompletionList(Collection> items) { return new CompletionList(items); } - public static implicit operator CompletionList(List items) + public static implicit operator CompletionList(List> items) { return new CompletionList(items); } - public static implicit operator CompletionItem[] (CompletionList list) + public static implicit operator CompletionItem[](CompletionList list) { return list.ToArray(); } diff --git a/src/Protocol/Models/CompletionParams.cs b/src/Protocol/Models/CompletionParams.cs index 38c368da9..3f907febf 100644 --- a/src/Protocol/Models/CompletionParams.cs +++ b/src/Protocol/Models/CompletionParams.cs @@ -1,10 +1,11 @@ +using Newtonsoft.Json.Linq; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { [Method(TextDocumentNames.Completion, Direction.ClientToServer)] - public class CompletionParams : WorkDoneTextDocumentPositionParams, IPartialItemsRequest + public class CompletionParams : WorkDoneTextDocumentPositionParams, IPartialItemsRequest, CompletionItem> where TData : CanBeResolvedData { /// /// The completion context. This is only available it the client specifies to send diff --git a/src/Protocol/Models/DocumentLink.cs b/src/Protocol/Models/DocumentLink.cs index cbc2c1d5f..7e90ddff3 100644 --- a/src/Protocol/Models/DocumentLink.cs +++ b/src/Protocol/Models/DocumentLink.cs @@ -5,13 +5,65 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { + /// + /// 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 interface IDocumentLink : ICanBeResolved where TData : CanBeResolvedData + { + /// + /// The range this link applies to. + /// + public Range Range { get; } + + /// + /// The uri this link points to. If missing a resolve request is sent later. + /// + [Optional] + public DocumentUri Target { get; } + + /// + /// A data entry field that is preserved on a document link between a + /// DocumentLinkRequest and a DocumentLinkResolveRequest. + /// + [Optional] + public TData Data { get; } + + /// + /// The tooltip text when you hover over this link. + /// + /// If a tooltip is provided, is will be displayed in a string that includes instructions on how to + /// trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary depending on OS, + /// user settings, and localization. + /// + /// @since 3.15.0 + /// + [Optional] + public string Tooltip { get; } + } + /// /// 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. /// [Method(TextDocumentNames.DocumentLinkResolve, Direction.ClientToServer)] - public class DocumentLink : ICanBeResolved, IRequest + public class DocumentLink : IDocumentLink, IRequest> where TData : CanBeResolvedData { + /// + /// Used for aggregating results when completion is supported by multiple handlers + /// + public static DocumentLink From(IDocumentLink item) + { + return item is DocumentLink cl + ? cl + : new DocumentLink() { + Data = item.Data, + Range = item.Range, + Target = item.Target, + Tooltip = item.Tooltip + }; + } + /// /// The range this link applies to. /// @@ -28,7 +80,7 @@ public class DocumentLink : ICanBeResolved, IRequest /// DocumentLinkRequest and a DocumentLinkResolveRequest. /// [Optional] - public JToken Data { get; set; } + public TData Data { get; set; } /// /// The tooltip text when you hover over this link. diff --git a/src/Protocol/Models/DocumentLinkContainer.cs b/src/Protocol/Models/DocumentLinkContainer.cs index e8b2f6caf..7d24fdd9b 100644 --- a/src/Protocol/Models/DocumentLinkContainer.cs +++ b/src/Protocol/Models/DocumentLinkContainer.cs @@ -1,36 +1,49 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using OmniSharp.Extensions.JsonRpc; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { - public class DocumentLinkContainer : Container + public class DocumentLinkContainer : Container>, IAggregateResults + where TData : CanBeResolvedData { - public DocumentLinkContainer() : this(Enumerable.Empty()) + public DocumentLinkContainer() : this(Enumerable.Empty>()) { } - public DocumentLinkContainer(IEnumerable items) : base(items) + public DocumentLinkContainer(IEnumerable> items) : base(items) { } - public DocumentLinkContainer(params DocumentLink[] items) : base(items) + public DocumentLinkContainer(params DocumentLink[] items) : base(items) { } - public static implicit operator DocumentLinkContainer(DocumentLink[] items) + public static implicit operator DocumentLinkContainer(DocumentLink[] items) { - return new DocumentLinkContainer(items); + return new DocumentLinkContainer(items); } - public static implicit operator DocumentLinkContainer(Collection items) + public static implicit operator DocumentLinkContainer(Collection> items) { - return new DocumentLinkContainer(items); + return new DocumentLinkContainer(items); } - public static implicit operator DocumentLinkContainer(List items) + public static implicit operator DocumentLinkContainer(List> items) { - return new DocumentLinkContainer(items); + return new DocumentLinkContainer(items); + } + + object IAggregateResults.AggregateResults(IEnumerable values) + { + return new DocumentLinkContainer( + values + .Cast>() + .SelectMany(z => z.OfType>()) + .Concat(this) + .Select(DocumentLink.From) + ); } } } diff --git a/src/Protocol/Models/DocumentLinkParams.cs b/src/Protocol/Models/DocumentLinkParams.cs index 92d77c2a0..86518bd0e 100644 --- a/src/Protocol/Models/DocumentLinkParams.cs +++ b/src/Protocol/Models/DocumentLinkParams.cs @@ -1,10 +1,11 @@ +using Newtonsoft.Json.Linq; using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { [Method(TextDocumentNames.DocumentLink, Direction.ClientToServer)] - public class DocumentLinkParams : ITextDocumentIdentifierParams, IPartialItemsRequest, IWorkDoneProgressParams + public class DocumentLinkParams : ITextDocumentIdentifierParams, IPartialItemsRequest, DocumentLink>, IWorkDoneProgressParams where TData : CanBeResolvedData { /// /// The document to provide document links for. diff --git a/src/Protocol/Models/ICanBeIdentifiedHandler.cs b/src/Protocol/Models/ICanBeIdentifiedHandler.cs new file mode 100644 index 000000000..33cf56a40 --- /dev/null +++ b/src/Protocol/Models/ICanBeIdentifiedHandler.cs @@ -0,0 +1,22 @@ +using System; +using MediatR; +using OmniSharp.Extensions.JsonRpc; + +namespace OmniSharp.Extensions.LanguageServer.Protocol.Models +{ + /// + /// Common interface for types that support resolution. + /// + /// + public interface ICanBeResolvedHandler : IJsonRpcRequestHandler, ICanBeIdentifiedHandler + where T : ICanBeResolved, IRequest + where TData : CanBeResolvedData + { + + } + + public interface ICanBeIdentifiedHandler + { + Guid Id { get; } + } +} diff --git a/src/Protocol/Models/ICanBeResolved.cs b/src/Protocol/Models/ICanBeResolved.cs index 27c2e864e..2375a34ed 100644 --- a/src/Protocol/Models/ICanBeResolved.cs +++ b/src/Protocol/Models/ICanBeResolved.cs @@ -1,3 +1,6 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OmniSharp.Extensions.LanguageServer.Protocol.Serialization; @@ -6,12 +9,31 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Models /// /// Common interface for types that support resolution. /// - public interface ICanBeResolved + public interface ICanBeResolved + where T : CanBeResolvedData { /// - /// A data entry field that is preserved for resolve requests + /// A data entry field that is preserved for resolve requests~ /// [Optional] - JToken Data { get; set; } + T Data { get; } + } + + public class CanBeResolvedData + { + [Newtonsoft.Json.JsonProperty(PropertyName = "$$___handler___$$", NullValueHandling = NullValueHandling.Ignore)] + internal Guid? handler; + [JsonExtensionData] + internal IDictionary data { get; set; } = new Dictionary(); + } + + public sealed class ResolvedData : CanBeResolvedData + { + [JsonIgnore] + public IDictionary Data + { + get => data; + set => data = value; + } } } diff --git a/src/Protocol/Models/ICanBeResolvedHandler.cs b/src/Protocol/Models/ICanBeResolvedHandler.cs deleted file mode 100644 index d4085b0ed..000000000 --- a/src/Protocol/Models/ICanBeResolvedHandler.cs +++ /dev/null @@ -1,20 +0,0 @@ -using MediatR; -using OmniSharp.Extensions.JsonRpc; - -namespace OmniSharp.Extensions.LanguageServer.Protocol.Models -{ - /// - /// Common interface for types that support resolution. - /// - /// - public interface ICanBeResolvedHandler : IJsonRpcRequestHandler - where T : ICanBeResolved, IRequest - { - /// - /// Method that determines if a handler can be used to resolve this one - /// - /// - /// - bool CanResolve(T value); - } -} diff --git a/src/Protocol/Serialization/ContractResolver.cs b/src/Protocol/Serialization/ContractResolver.cs index dad2f64ad..21dc973fe 100644 --- a/src/Protocol/Serialization/ContractResolver.cs +++ b/src/Protocol/Serialization/ContractResolver.cs @@ -80,7 +80,7 @@ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerializ property.ValueProvider = new SupportsValueProvider(property.ValueProvider); } - if (property.DeclaringType == typeof(CompletionItem)) + if (property.DeclaringType.IsGenericType && property.DeclaringType.GetGenericTypeDefinition() == typeof(CompletionItem<>)) { if (property.PropertyType == typeof(CompletionItemKind)) { diff --git a/src/Protocol/Serialization/Converters/CompletionListConverter.cs b/src/Protocol/Serialization/Converters/CompletionListConverter.cs index 440c9cde8..4f6e84a9c 100644 --- a/src/Protocol/Serialization/Converters/CompletionListConverter.cs +++ b/src/Protocol/Serialization/Converters/CompletionListConverter.cs @@ -7,10 +7,16 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters { - class CompletionListConverter : JsonConverter + class CompletionListConverter : JsonConverter { - public override void WriteJson(JsonWriter writer, CompletionList value, JsonSerializer serializer) + public override bool CanConvert(Type objectType) { + return typeof(ICompletionList).IsAssignableFrom(objectType); + } + + public override void WriteJson(JsonWriter writer, object v, JsonSerializer serializer) + { + if (!(v is ICompletionList value)) throw new NotSupportedException("could not convert item to ICompletionList"); if (!value.IsIncomplete) { serializer.Serialize(writer, value.Items.ToArray()); @@ -31,19 +37,36 @@ public override void WriteJson(JsonWriter writer, CompletionList value, JsonSeri writer.WriteEndObject(); } - public override CompletionList ReadJson(JsonReader reader, Type objectType, CompletionList existingValue, bool hasExistingValue, JsonSerializer serializer) + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { + Type innerType; + Type enumerableType; + Type returnType; + if (objectType == typeof(CompletionList)) + { + innerType = typeof(ResolvedData); + enumerableType = typeof(IEnumerable<>).MakeGenericType(typeof(CompletionItem<>).MakeGenericType(innerType)); + returnType = typeof(CompletionList); + } + else + { + innerType = objectType.GetGenericArguments()[0]; + enumerableType = typeof(IEnumerable<>).MakeGenericType(typeof(CompletionItem<>).MakeGenericType(innerType)); + returnType = typeof(CompletionList<>).MakeGenericType(innerType); + + } if (reader.TokenType == JsonToken.StartArray) { - var array = JArray.Load(reader).ToObject>(serializer); - return new CompletionList(array); + var array = JArray.Load(reader).ToObject(enumerableType, serializer); + return Activator.CreateInstance(returnType, array); } var result = JObject.Load(reader); - var items = result["items"].ToObject>(serializer); - return new CompletionList(items, result["isIncomplete"].Value()); + var items = result["items"].ToObject(enumerableType, serializer); + return Activator.CreateInstance(returnType, new object[] { items, result["isIncomplete"].Value() }); } public override bool CanRead => true; + public override bool CanWrite => true; } } diff --git a/src/Protocol/Server/Capabilities/CodeLensOptions.cs b/src/Protocol/Server/Capabilities/CodeLensOptions.cs index 692ce6ef0..baf191363 100644 --- a/src/Protocol/Server/Capabilities/CodeLensOptions.cs +++ b/src/Protocol/Server/Capabilities/CodeLensOptions.cs @@ -21,7 +21,7 @@ public class CodeLensOptions : WorkDoneProgressOptions, ICodeLensOptions public static CodeLensOptions Of(ICodeLensOptions options, IEnumerable descriptors) { return new CodeLensOptions() { - ResolveProvider = options.ResolveProvider || descriptors.Any(z => z.HandlerType == typeof(ICodeLensResolveHandler)), + ResolveProvider = options.ResolveProvider || descriptors.Any(z => z.HandlerType.IsGenericType && z.HandlerType.GetGenericTypeDefinition() == typeof(ICodeLensResolveHandler<>)), WorkDoneProgress = options.WorkDoneProgress }; } diff --git a/src/Protocol/Server/Capabilities/CompletionOptions.cs b/src/Protocol/Server/Capabilities/CompletionOptions.cs index 572bb6b62..020241097 100644 --- a/src/Protocol/Server/Capabilities/CompletionOptions.cs +++ b/src/Protocol/Server/Capabilities/CompletionOptions.cs @@ -46,7 +46,7 @@ public static CompletionOptions Of(ICompletionOptions options, IEnumerable z.HandlerType == typeof(ICompletionResolveHandler)), + ResolveProvider = options.ResolveProvider || descriptors.Any(z => z.HandlerType.IsGenericType && z.HandlerType.GetGenericTypeDefinition() == typeof(ICompletionResolveHandler<>)), AllCommitCharacters = options.AllCommitCharacters, TriggerCharacters = options.TriggerCharacters, WorkDoneProgress = options.WorkDoneProgress, diff --git a/src/Protocol/Server/Capabilities/DocumentLinkOptions.cs b/src/Protocol/Server/Capabilities/DocumentLinkOptions.cs index 8e6cae866..8e9c0c7ae 100644 --- a/src/Protocol/Server/Capabilities/DocumentLinkOptions.cs +++ b/src/Protocol/Server/Capabilities/DocumentLinkOptions.cs @@ -21,7 +21,7 @@ public class DocumentLinkOptions : WorkDoneProgressOptions, IDocumentLinkOptions public static DocumentLinkOptions Of(IDocumentLinkOptions options, IEnumerable descriptors) { return new DocumentLinkOptions() { - ResolveProvider = options.ResolveProvider || descriptors.Any(z => z.HandlerType == typeof(IDocumentLinkResolveHandler)) , + ResolveProvider = options.ResolveProvider || descriptors.Any(z => z.HandlerType.IsGenericType && z.HandlerType.GetGenericTypeDefinition() == typeof(IDocumentLinkResolveHandler<>)) , WorkDoneProgress = options.WorkDoneProgress, }; } diff --git a/src/Protocol/Shared/ILspHandlerDescriptor.cs b/src/Protocol/Shared/ILspHandlerDescriptor.cs index 52a30ad7b..8e18ca7be 100644 --- a/src/Protocol/Shared/ILspHandlerDescriptor.cs +++ b/src/Protocol/Shared/ILspHandlerDescriptor.cs @@ -17,5 +17,7 @@ public interface ILspHandlerDescriptor : IHandlerDescriptor Type CanBeResolvedHandlerType { get; } OnServerStartedDelegate OnServerStartedDelegate { get; } OnClientStartedDelegate OnClientStartedDelegate { get; } + bool CanBeResolved { get; } + Type CanBeResolvedDataType { get; } } } diff --git a/src/Protocol/Shared/LspHandlerDescriptor.cs b/src/Protocol/Shared/LspHandlerDescriptor.cs index ef6f6a0f4..5098ce8fa 100644 --- a/src/Protocol/Shared/LspHandlerDescriptor.cs +++ b/src/Protocol/Shared/LspHandlerDescriptor.cs @@ -50,7 +50,13 @@ public LspHandlerDescriptor( // If multiple are implemented this behavior is unknown CanBeResolvedHandlerType = handler.GetType().GetTypeInfo() .ImplementedInterfaces - .FirstOrDefault(x => x.GetTypeInfo().IsGenericType && x.GetTypeInfo().GetGenericTypeDefinition() == typeof(ICanBeResolvedHandler<>)); + .FirstOrDefault(x => x.GetTypeInfo().IsGenericType && x.GetTypeInfo().GetGenericTypeDefinition() == typeof(ICanBeResolvedHandler<,>)); + CanBeResolvedDataType = CanBeResolvedHandlerType?.GetGenericArguments()[1] ?? (@params.IsGenericType && typeof(CanBeResolvedData).IsAssignableFrom(@params.GetGenericArguments()[0]) ? @params.GetGenericArguments()[0] : null); + CanBeResolved = CanBeResolvedHandlerType != null || CanBeResolvedDataType != null; + // if (CanBeResolved && !@params.IsGenericType && @params?.BaseType.IsGenericType == true) + // { + // Params = @params.BaseType; + // } HasReturnType = HandlerType.GetInterfaces().Any(@interface => @interface.IsGenericType && @@ -91,6 +97,8 @@ public LspHandlerDescriptor( public Type CapabilityType { get; } public OnServerStartedDelegate OnServerStartedDelegate { get; } public OnClientStartedDelegate OnClientStartedDelegate { get; } + public bool CanBeResolved { get; } + public Type CanBeResolvedDataType { get; } public string Method { get; } public string Key { get; } diff --git a/src/Server/Matchers/ResolveCommandMatcher.cs b/src/Server/Matchers/ResolveCommandMatcher.cs index 62a6121f9..49dbbd5eb 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,6 @@ namespace OmniSharp.Extensions.LanguageServer.Server.Matchers public class ResolveCommandMatcher : IHandlerMatcher { private readonly ILogger _logger; - internal static string PrivateHandlerTypeName = "$$___handlerType___$$"; - internal static string PrivateHandlerKey = "$$___handlerKey___$$"; public ResolveCommandMatcher(ILogger logger) { @@ -29,34 +28,11 @@ public ResolveCommandMatcher(ILogger logger) /// public IEnumerable FindHandler(object parameters, IEnumerable descriptors) { - if (parameters is ICanBeResolved canBeResolved) + if (parameters is ICanBeResolved canBeResolved) { - string handlerType = null; - string handlerKey = null; - if (canBeResolved.Data != null && canBeResolved.Data.Type == JTokenType.Object) + var handlerName = canBeResolved.Data?.handler; + if (handlerName == null || handlerName == Guid.Empty) { - handlerType = canBeResolved.Data?[PrivateHandlerTypeName]?.ToString(); - handlerKey = canBeResolved.Data?[PrivateHandlerKey]?.ToString(); - } - - 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) - { - var method = CanResolveMethod - .MakeGenericMethod(descriptor.Params); - if ((bool)method.Invoke(null, new[] { descriptor.Handler, parameters })) - { - yield return descriptor; - yield break; - } - } - } - var descriptor2 = descriptors.FirstOrDefault(); _logger.LogTrace( "Resolve {Method} was called, but data did not have handle type defined. Using Handler {HandlerType}", @@ -72,23 +48,14 @@ public IEnumerable FindHandler(object parameters, IEnumer _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)) + if (!(descriptor.Handler is ICanBeIdentifiedHandler handler)) continue; + + if (handler.Id == handlerName) { 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 d1c636dc3..559eda8b7 100644 --- a/src/Server/Matchers/TextDocumentMatcher.cs +++ b/src/Server/Matchers/TextDocumentMatcher.cs @@ -57,7 +57,7 @@ public IEnumerable FindHandler(object parameters, IEnumer private List GetTextDocumentAttributes(DocumentUri uri) { return _textDocumentIdentifiers - .Select(x => x.GetTextDocumentAttributes(uri)) + .SelectMany(x => x.GetTextDocumentAttributes(uri)) .Where(x => x != null) .Distinct() .ToList(); diff --git a/src/Server/Pipelines/ResolveCommandPipeline.cs b/src/Server/Pipelines/ResolveCommandPipeline.cs index ab1c0bc91..1651351ab 100644 --- a/src/Server/Pipelines/ResolveCommandPipeline.cs +++ b/src/Server/Pipelines/ResolveCommandPipeline.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -26,40 +28,27 @@ 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(); // Only pin the handler type, if we know the source handler (codelens) is also the resolver. - if (_descriptor is LspHandlerDescriptor handlerDescriptor && - response is IEnumerable canBeResolveds && - _descriptor?.CanBeResolvedHandlerType?.GetTypeInfo().IsAssignableFrom(_descriptor.ImplementationType) == true) + if (_descriptor is LspHandlerDescriptor handlerDescriptor && handlerDescriptor.CanBeResolved && response is IEnumerable values) { + PropertyInfo propertyInfo = null; _logger.LogTrace( "Updating Resolve items with wrapped data for {Method}:{Handler}", _descriptor.Method, _descriptor.ImplementationType.FullName); - foreach (var item in canBeResolveds) + foreach (var item in values) { - // 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; + if (!(item is ICanBeResolved value)) continue; + if (value.Data == null) + { + propertyInfo ??= item.GetType().GetProperty(nameof(value.Data)); + propertyInfo.SetValue(item, Activator.CreateInstance(propertyInfo.PropertyType)); + } + + value.Data.handler = _descriptor.Handler is ICanBeIdentifiedHandler resolved ? resolved.Id : Guid.Empty; } } diff --git a/src/Shared/LspRequestRouter.cs b/src/Shared/LspRequestRouter.cs index 80d794ec1..c09361f69 100644 --- a/src/Shared/LspRequestRouter.cs +++ b/src/Shared/LspRequestRouter.cs @@ -16,7 +16,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, @@ -28,25 +28,25 @@ 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 GetDescriptor(Notification notification) { return FindDescriptor(notification); } - public override ILspHandlerDescriptor GetDescriptor(Request request) + public override IRequestDescriptor GetDescriptor(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); var descriptor = _collection.FirstOrDefault(x => x.Method == method); @@ -57,27 +57,28 @@ private ILspHandlerDescriptor FindDescriptor(string method, JToken @params) return null; } - 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(); - if (lspHandlerDescriptors.Count == 1) return descriptor; + if (lspHandlerDescriptors.Count == 1) return new RequestDescriptor(lspHandlerDescriptors); var paramsValue = @params.ToObject(descriptor.Params, _serializer.JsonSerializer); - return _handlerMatchers.SelectMany(strat => strat.FindHandler(paramsValue, lspHandlerDescriptors)).FirstOrDefault() ?? descriptor; + var matchingDescriptors = _handlerMatchers.SelectMany(strat => strat.FindHandler(paramsValue, lspHandlerDescriptors)).ToArray(); + return matchingDescriptors.Length == 0 ? new RequestDescriptor(new [] { descriptor }) : new RequestDescriptor(matchingDescriptors); } - IHandlerDescriptor IRequestRouter.GetDescriptor(Notification notification) => GetDescriptor(notification); - IHandlerDescriptor IRequestRouter.GetDescriptor(Request request) => GetDescriptor(request); + IRequestDescriptor IRequestRouter.GetDescriptor(Notification notification) => GetDescriptor(notification); + IRequestDescriptor IRequestRouter.GetDescriptor(Request request) => GetDescriptor(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 a583010b5..425d7fa26 100644 --- a/src/Shared/SharedHandlerCollection.cs +++ b/src/Shared/SharedHandlerCollection.cs @@ -203,7 +203,7 @@ private LspHandlerDescriptor GetDescriptor(string method, Type handlerType, IJso // In some scenarios, users will implement both the main handler and the resolve handler to the same class // This allows us to get a key for those interfaces so we can register many resolve handlers // and then route those resolve requests to the correct handler - if (handler.GetType().GetTypeInfo().ImplementedInterfaces.Any(x => x.GetTypeInfo().IsGenericType && x.GetTypeInfo().GetGenericTypeDefinition() == typeof(ICanBeResolvedHandler<>))) + if (handler.GetType().GetTypeInfo().ImplementedInterfaces.Any(x => x.GetTypeInfo().IsGenericType && x.GetTypeInfo().GetGenericTypeDefinition() == typeof(ICanBeResolvedHandler<,>))) { key = handlerRegistration?.GetRegistrationOptions()?.DocumentSelector ?? key; } @@ -211,6 +211,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 ?? @@ -244,7 +249,20 @@ public bool ContainsHandler(Type type) public bool ContainsHandler(TypeInfo typeInfo) { - return this.Any(z => z.HandlerType.GetTypeInfo().IsAssignableFrom(typeInfo) || z.ImplementationType.GetTypeInfo().IsAssignableFrom(typeInfo)); + if (typeInfo.IsGenericType && typeof(CanBeResolvedData).IsAssignableFrom(typeInfo.GenericTypeArguments[0])) + { + var type = typeInfo.GenericTypeArguments[0]; + var typeDefinition = typeInfo.GetGenericTypeDefinition(); + return this.Any(z => + z.Params.IsGenericType && + type.IsAssignableFrom(z.CanBeResolvedDataType) && + typeDefinition.MakeGenericType(z.CanBeResolvedDataType).GetInterfaces().Any(x => z.HandlerType.IsAssignableFrom(x)) + ); + } + return this.Any(z => + z.HandlerType.GetTypeInfo().IsAssignableFrom(typeInfo) + || z.ImplementationType.GetTypeInfo().IsAssignableFrom(typeInfo) + ); } private static object GetRegistration(IRegistration registration) diff --git a/test/Client.Tests/ClientTests.cs b/test/Client.Tests/ClientTests.cs index 4777e53b6..6f4ef6ccf 100644 --- a/test/Client.Tests/ClientTests.cs +++ b/test/Client.Tests/ClientTests.cs @@ -108,8 +108,8 @@ public async Task Completions_Success() const int column = 5; var expectedDocumentPath = AbsoluteDocumentPath; var expectedDocumentUri = DocumentUri.FromFileSystemPath(expectedDocumentPath); - var expectedCompletionItems = new CompletionItem[] { - new CompletionItem { + var expectedCompletionItems = new CompletionItem[] { + new CompletionItem { Kind = CompletionItemKind.Class, Label = "Class1", TextEdit = new TextEdit { @@ -135,7 +135,7 @@ public async Task Completions_Success() }); }, server => { - server.OnCompletion((request, cancellationToken) => { + server.OnCompletion((request, cancellationToken) => { Assert.NotNull(request.TextDocument); Assert.Equal(expectedDocumentUri, request.TextDocument.Uri); @@ -143,14 +143,14 @@ public async Task Completions_Success() Assert.Equal(line, request.Position.Line); Assert.Equal(column, request.Position.Character); - return Task.FromResult(new CompletionList( + return Task.FromResult(new CompletionList( expectedCompletionItems, isIncomplete: true )); }, new CompletionRegistrationOptions()); }); - var actualCompletions = await client.TextDocument.RequestCompletion(new CompletionParams() { + var actualCompletions = await client.TextDocument.RequestCompletion(new CompletionParams() { TextDocument = AbsoluteDocumentPath, Position = (line, column), }, CancellationToken); 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/Lsp.Tests/CompletionItemKindTests.cs b/test/Lsp.Tests/CompletionItemKindTests.cs index 01f63a8eb..197fc692b 100644 --- a/test/Lsp.Tests/CompletionItemKindTests.cs +++ b/test/Lsp.Tests/CompletionItemKindTests.cs @@ -13,11 +13,11 @@ public class CompletionItemKindTests public void DefaultBehavior_Should_Only_Support_InitialCompletionItemKinds() { var serializer = new Serializer(); - var json = serializer.SerializeObject(new CompletionItem() { + var json = serializer.SerializeObject(new CompletionItem() { Kind = CompletionItemKind.Event }); - var result = serializer.DeserializeObject(json); + var result = serializer.DeserializeObject>(json); result.Kind.Should().Be(CompletionItemKind.Event); } @@ -25,11 +25,11 @@ public void DefaultBehavior_Should_Only_Support_InitialCompletionItemKinds() public void DefaultBehavior_Should_Only_Support_InitialCompletionItemTags() { var serializer = new Serializer(); - var json = serializer.SerializeObject(new CompletionItem() { + var json = serializer.SerializeObject(new CompletionItem() { Tags = new Container(CompletionItemTag.Deprecated) }); - var result = serializer.DeserializeObject(json); + var result = serializer.DeserializeObject>(json); result.Tags.Should().Contain(CompletionItemTag.Deprecated); } @@ -48,11 +48,11 @@ public void CustomBehavior_When_CompletionItemKinds_Defined_By_Client() } }); - var json = serializer.SerializeObject(new CompletionItem() { + var json = serializer.SerializeObject(new CompletionItem() { Kind = CompletionItemKind.Event }); - var result = serializer.DeserializeObject(json); + var result = serializer.DeserializeObject>(json); result.Kind.Should().Be(CompletionItemKind.Class); } @@ -73,11 +73,11 @@ public void CustomBehavior_When_CompletionItemTags_Defined_By_Client() } }); - var json = serializer.SerializeObject(new CompletionItem() { + var json = serializer.SerializeObject(new CompletionItem() { Tags = new Container(CompletionItemTag.Deprecated) }); - var result = serializer.DeserializeObject(json); + var result = serializer.DeserializeObject>(json); result.Tags.Should().BeEmpty(); } } diff --git a/test/Lsp.Tests/FoundationTests.cs b/test/Lsp.Tests/FoundationTests.cs index 942030833..6a1511995 100644 --- a/test/Lsp.Tests/FoundationTests.cs +++ b/test/Lsp.Tests/FoundationTests.cs @@ -397,7 +397,7 @@ public class ParamsShouldHaveMethodAttributeData : TheoryData { public ParamsShouldHaveMethodAttributeData() { - foreach (var type in typeof(CompletionParams).Assembly.ExportedTypes.Where(z => + foreach (var type in typeof(CompletionParams).Assembly.ExportedTypes.Where(z => z.IsClass && !z.IsAbstract && z.GetInterfaces().Any(z => z.IsGenericType && typeof(IRequest<>).IsAssignableFrom(z.GetGenericTypeDefinition())))) { Add(type); @@ -409,7 +409,7 @@ public class HandlersShouldHaveMethodAttributeData : TheoryData { public HandlersShouldHaveMethodAttributeData() { - foreach (var type in typeof(CompletionParams).Assembly.ExportedTypes.Where(z => z.IsInterface && typeof(IJsonRpcHandler).IsAssignableFrom(z) && !z.IsGenericType) + foreach (var type in typeof(CompletionParams).Assembly.ExportedTypes.Where(z => z.IsInterface && typeof(IJsonRpcHandler).IsAssignableFrom(z) && !z.IsGenericType) .Except(new[] {typeof(ITextDocumentSyncHandler)})) { Add(type); @@ -421,7 +421,7 @@ public class TypeHandlerData : TheoryData { public TypeHandlerData() { - foreach (var type in typeof(CompletionParams).Assembly.ExportedTypes.Where(z => z.IsInterface && typeof(IJsonRpcHandler).IsAssignableFrom(z) && !z.IsGenericType) + foreach (var type in typeof(CompletionParams).Assembly.ExportedTypes.Where(z => z.IsInterface && typeof(IJsonRpcHandler).IsAssignableFrom(z) && !z.IsGenericType) .Except(new[] {typeof(ITextDocumentSyncHandler)})) { Add(LspHandlerTypeDescriptorHelper.GetHandlerTypeDescriptor(type)); @@ -439,7 +439,7 @@ public class TypeHandlerExtensionData : TheoryData).Assembly.ExportedTypes .Where(z => z.IsInterface && typeof(IJsonRpcHandler).IsAssignableFrom(z) && !z.IsGenericType) .Except(new[] {typeof(ITextDocumentSyncHandler)})) { diff --git a/test/Lsp.Tests/HandlerResolverTests.cs b/test/Lsp.Tests/HandlerResolverTests.cs index 05acef73c..451c255a8 100644 --- a/test/Lsp.Tests/HandlerResolverTests.cs +++ b/test/Lsp.Tests/HandlerResolverTests.cs @@ -194,8 +194,8 @@ public void Should_DealWithClassesThatImplementMultipleHandlers_WithoutConflicti [Fact] public void Should_DealWithClassesThatImplementMultipleHandlers_BySettingKeyAccordingly() { - var codeLensHandler = Substitute.For(new Type[] { typeof(ICodeLensHandler), typeof(ICodeLensResolveHandler) }, new object[0]); - ((ICodeLensHandler)codeLensHandler).GetRegistrationOptions() + var codeLensHandler = Substitute.For(new Type[] { typeof(ICodeLensHandler), typeof(ICodeLensResolveHandler) }, new object[0]); + ((ICodeLensHandler)codeLensHandler).GetRegistrationOptions() .Returns(new CodeLensRegistrationOptions() { DocumentSelector = new DocumentSelector(DocumentFilter.ForLanguage("foo")) }); @@ -209,24 +209,24 @@ public void Should_DealWithClassesThatImplementMultipleHandlers_BySettingKeyAcco public static IEnumerable Should_DealWithClassesThatImplementMultipleHandlers_WithoutConflictingRegistrations_Data() { - var codeLensHandler = Substitute.For(new Type[] { typeof(ICodeLensHandler), typeof(ICodeLensResolveHandler) }, new object[0]); - ((ICodeLensHandler)codeLensHandler).GetRegistrationOptions() + var codeLensHandler = Substitute.For(new Type[] { typeof(ICodeLensHandler), typeof(ICodeLensResolveHandler) }, new object[0]); + ((ICodeLensHandler)codeLensHandler).GetRegistrationOptions() .Returns(new CodeLensRegistrationOptions() { DocumentSelector = new DocumentSelector() { } }); yield return new object[] { TextDocumentNames.CodeLensResolve, codeLensHandler }; - var documentLinkHandler = Substitute.For(new Type[] { typeof(IDocumentLinkHandler), typeof(IDocumentLinkResolveHandler) }, new object[0]); - ((IDocumentLinkHandler)documentLinkHandler).GetRegistrationOptions() + var documentLinkHandler = Substitute.For(new Type[] { typeof(IDocumentLinkHandler), typeof(IDocumentLinkResolveHandler) }, new object[0]); + ((IDocumentLinkHandler)documentLinkHandler).GetRegistrationOptions() .Returns(new DocumentLinkRegistrationOptions() { DocumentSelector = new DocumentSelector() { } }); yield return new object[] { TextDocumentNames.DocumentLinkResolve, documentLinkHandler }; - var completionHandler = Substitute.For(new Type[] { typeof(ICompletionHandler), typeof(ICompletionResolveHandler) }, new object[0]); - ((ICompletionHandler)completionHandler).GetRegistrationOptions() + var completionHandler = Substitute.For(new Type[] { typeof(ICompletionHandler), typeof(ICompletionResolveHandler) }, new object[0]); + ((ICompletionHandler)completionHandler).GetRegistrationOptions() .Returns(new CompletionRegistrationOptions() { DocumentSelector = new DocumentSelector() { } }); diff --git a/test/Lsp.Tests/Integration/DynamicRegistrationTests.cs b/test/Lsp.Tests/Integration/DynamicRegistrationTests.cs index 7194c4fad..11acd9d0b 100644 --- a/test/Lsp.Tests/Integration/DynamicRegistrationTests.cs +++ b/test/Lsp.Tests/Integration/DynamicRegistrationTests.cs @@ -51,8 +51,8 @@ public async Task Should_Register_Dynamically_While_Server_Is_Running() await Events.SettleNext(); server.Register(x => x - .OnCompletion( - (@params, token) => Task.FromResult(new CompletionList()), + .OnCompletion( + async (@params, token) => new CompletionList(), registrationOptions: new CompletionRegistrationOptions() { DocumentSelector = DocumentSelector.ForLanguage("vb") }) @@ -74,8 +74,8 @@ public async Task Should_Unregister_Dynamically_While_Server_Is_Running() await Events.SettleNext(); - var disposable = server.Register(x => x.OnCompletion( - (@params, token) => Task.FromResult(new CompletionList()), + var disposable = server.Register(x => x.OnCompletion( + async (@params, token) => new CompletionList(), registrationOptions: new CompletionRegistrationOptions() { DocumentSelector = DocumentSelector.ForLanguage("vb") }) @@ -209,8 +209,8 @@ private void ConfigureClient(LanguageClientOptions options) private void ConfigureServer(LanguageServerOptions options) { - options.OnCompletion( - (@params, token) => Task.FromResult(new CompletionList()), + options.OnCompletion( + async (@params, token) => Task.FromResult(new CompletionList()), registrationOptions: new CompletionRegistrationOptions() { DocumentSelector = DocumentSelector.ForLanguage("csharp"), ResolveProvider = false, diff --git a/test/Lsp.Tests/Integration/Handlers/CodeLensHandlerTests.cs b/test/Lsp.Tests/Integration/Handlers/CodeLensHandlerTests.cs new file mode 100644 index 000000000..13ad609d5 --- /dev/null +++ b/test/Lsp.Tests/Integration/Handlers/CodeLensHandlerTests.cs @@ -0,0 +1,654 @@ +using System; +using System.Linq; +using System.Threading; +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.Client; +using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Document; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using OmniSharp.Extensions.LanguageServer.Server; +using Xunit; +using Xunit.Abstractions; +using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range; + +namespace Lsp.Tests.Integration.Handlers +{ + public class CodeLensHandlerTests : LanguageProtocolTestBase + { + public CodeLensHandlerTests(ITestOutputHelper outputHelper) : base(new JsonRpcTestOptions().ConfigureForXUnit(outputHelper)) + { + } + + [Fact] + public async Task Should_Return_And_Resolve_CodeLens() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.OnCodeLens( + async (request, capability, ct) => { + return new CodeLensContainer(new CodeLens() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }); + }, + async (request, capability, ct) => { + request.Data.Position.Should().BeEquivalentTo(new Position(2, 2)); + request.Data.Range.Should().BeEquivalentTo(new Range(new Position(2, 2), new Position(2, 3))); + return new CodeLens() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + Range = request.Range + }; + }, + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForLanguage("csharp") + } + ); + }); + + var codeLens = await client.TextDocument.RequestCodeLens(new CodeLensParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + codeLens.Should().HaveCount(3); + + var cl = codeLens.Skip(1).First(); + cl.Command.Should().BeNull(); + cl.Data.Data.Should().ContainKeys("position", "range"); + + var resolved = await client.TextDocument.ResolveCodeLens(cl, CancellationToken); + resolved.Command.Should().NotBeNull(); + resolved.Data.Data.Should().ContainKeys("position", "range"); + } + + [Fact] + public async Task Should_Return_And_Resolve_CodeLens_With_Known_Model() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.OnCodeLens( + async (request, capability, ct) => { + return new CodeLensContainer(new CodeLens() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }); + }, + async (request, capability, ct) => { + request.Data.Position.Should().BeEquivalentTo(new Position(2, 2)); + request.Data.Range.Should().BeEquivalentTo(new Range(new Position(2, 2), new Position(2, 3))); + request.Data.Position = new Position(3, 3); + return new CodeLens() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + Range = request.Range + }; + }, + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForLanguage("csharp") + } + ); + }); + + var codeLens = await client.TextDocument.RequestCodeLens(new CodeLensParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + codeLens.Should().HaveCount(3); + + var cl = codeLens.Skip(1).First(); + cl.Command.Should().BeNull(); + cl.Data.Position.Should().BeEquivalentTo(new Position(2, 2)); + + var resolved = await client.TextDocument.ResolveCodeLens(cl, CancellationToken); + resolved.Command.Should().NotBeNull(); + resolved.Data.Position.Should().BeEquivalentTo(new Position(3, 3)); + } + + [Fact] + public async Task Should_Return_And_Resolve_CodeLens_With_Different_Data_Models() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.AddTextDocumentIdentifier(WellKnownLanguages.CSharp, WellKnownLanguages.VisualBasic); + options.OnCodeLens( + async (request, capability, ct) => { + return new CodeLensContainer(new CodeLens() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }); + }, + async (request, capability, ct) => { + request.Data.Position.Should().BeEquivalentTo(new Position(2, 2)); + request.Data.Range.Should().BeEquivalentTo(new Range(new Position(2, 2), new Position(2, 3))); + request.Data.Position = new Position(3, 3); + return new CodeLens() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + Range = request.Range + }; + }, + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + } + ); + options.OnCodeLens( + async (request, capability, ct) => { + return new CodeLensContainer(new CodeLens() { + Data = new DataSecond() { + Id = "1", + } + }, + new CodeLens() { + Data = new DataSecond() { + Id = "2", + } + }, + new CodeLens() { + Data = new DataSecond() { + Id = "3", + } + }); + }, + async (request, capability, ct) => { + request.Data.Id.Should().Be("2"); + + return new CodeLens() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + Range = request.Range + }; + }, + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.vb") + } + ); + }); + + var codeLensCs = await client.TextDocument.RequestCodeLens(new CodeLensParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + var codeLensVb = await client.TextDocument.RequestCodeLens(new CodeLensParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.vb")) + }, CancellationToken); + + codeLensCs.Should().HaveCount(3); + codeLensVb.Should().HaveCount(3); + codeLensCs.Should().NotBeEquivalentTo(codeLensVb); + + var clCs = codeLensCs.Skip(1).First(); + clCs.Command.Should().BeNull(); + clCs.Data.Data.Should().ContainKeys("position", "range"); + + var clVb = codeLensVb.Skip(1).First(); + clVb.Command.Should().BeNull(); + clVb.Data.Data.Should().ContainKeys("id"); + + var resolvedCs = await client.TextDocument.ResolveCodeLens(clCs, CancellationToken); + resolvedCs.Command.Should().NotBeNull(); + resolvedCs.Should().BeEquivalentTo(clCs, x => x.Excluding(x => x.Command)); + + var resolvedVb = await client.TextDocument.ResolveCodeLens(clVb, CancellationToken); + resolvedVb.Command.Should().NotBeNull(); + resolvedVb.Should().BeEquivalentTo(clVb, x => x.Excluding(x => x.Command)); + } + + + [Fact] + public async Task Should_Return_And_Aggregate_CodeLens_With_Different_Data_Models_But_Same_Selector() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.AddTextDocumentIdentifier(WellKnownLanguages.CSharp, WellKnownLanguages.VisualBasic); + options.OnCodeLens( + async (request, capability, ct) => { + return new CodeLensContainer(new CodeLens() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }); + }, + async (request, capability, ct) => { + request.Data.Position.Should().BeEquivalentTo(new Position(2, 2)); + request.Data.Range.Should().BeEquivalentTo(new Range(new Position(2, 2), new Position(2, 3))); + request.Data.Position = new Position(3, 3); + + return new CodeLens() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + Range = request.Range + }; + }, + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + } + ); + options.OnCodeLens( + async (request, capability, ct) => { + return new CodeLensContainer(new CodeLens() { + Data = new DataSecond() { + Id = "1", + } + }, + new CodeLens() { + Data = new DataSecond() { + Id = "2", + } + }, + new CodeLens() { + Data = new DataSecond() { + Id = "3", + } + }); + }, + async (request, capability, ct) => { + request.Data.Id.Should().Be("2"); + return new CodeLens() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + Range = request.Range + }; + }, + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + } + ); + }); + + var codeLensCs = await client.TextDocument.RequestCodeLens(new CodeLensParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + codeLensCs.Should().HaveCount(6); + + codeLensCs.Should().Match(x => x.Any(z => z.Data.Data.Keys.Contains("position") && z.Data.Data.Keys.Contains("range"))); + codeLensCs.Should().Match(x => x.Any(z => z.Data.Data.Keys.Contains("id"))); + } + + + [Fact] + public async Task Should_Return_And_Resolve_CodeLens_Using_ClassHandler() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.WithHandler(new DataCodeLensHandler(new CodeLensContainer(new CodeLens() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }), + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForLanguage("csharp") + } + )); + }); + + var codeLens = await client.TextDocument.RequestCodeLens(new CodeLensParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + codeLens.Should().HaveCount(3); + + var cl = codeLens.Skip(1).First(); + cl.Command.Should().BeNull(); + cl.Data.Data.Should().ContainKeys("position", "range"); + + var resolved = await client.TextDocument.ResolveCodeLens(cl, CancellationToken); + resolved.Command.Should().NotBeNull(); + resolved.Data.Data.Should().ContainKeys("position", "range"); + } + + [Fact] + public async Task Should_Return_And_Resolve_CodeLens_With_Known_Model_ClassHandler() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.WithHandler(new DataCodeLensHandler(new CodeLensContainer(new CodeLens() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }), + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForLanguage("csharp") + } + )); + }); + + var codeLens = await client.TextDocument.RequestCodeLens(new CodeLensParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + codeLens.Should().HaveCount(3); + + var cl = codeLens.Skip(1).First(); + cl.Command.Should().BeNull(); + cl.Data.Position.Should().BeEquivalentTo(new Position(2, 2)); + + var resolved = await client.TextDocument.ResolveCodeLens(cl, CancellationToken); + resolved.Command.Should().NotBeNull(); + resolved.Data.Position.Should().BeEquivalentTo(new Position(3, 3)); + } + + [Fact] + public async Task Should_Return_And_Resolve_CodeLens_With_Different_Data_Models_ClassHandler() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.AddTextDocumentIdentifier(WellKnownLanguages.CSharp, WellKnownLanguages.VisualBasic); + + options.WithHandler(new DataCodeLensHandler(new CodeLensContainer(new CodeLens() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }), + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + } + )); + options.WithHandler(new DataSecondCodeLensHandler(new CodeLensContainer(new CodeLens() { + Data = new DataSecond() { + Id = "1", + } + }, + new CodeLens() { + Data = new DataSecond() { + Id = "2", + } + }, + new CodeLens() { + Data = new DataSecond() { + Id = "3", + } + }), + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.vb") + } + )); + }); + + var codeLensCs = await client.TextDocument.RequestCodeLens(new CodeLensParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + var codeLensVb = await client.TextDocument.RequestCodeLens(new CodeLensParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.vb")) + }, CancellationToken); + + codeLensCs.Should().HaveCount(3); + codeLensVb.Should().HaveCount(3); + codeLensCs.Should().NotBeEquivalentTo(codeLensVb); + + var clCs = codeLensCs.Skip(1).First(); + clCs.Command.Should().BeNull(); + clCs.Data.Data.Should().ContainKeys("position", "range"); + + var clVb = codeLensVb.Skip(1).First(); + clVb.Command.Should().BeNull(); + clVb.Data.Data.Should().ContainKeys("id"); + + var resolvedCs = await client.TextDocument.ResolveCodeLens(clCs, CancellationToken); + resolvedCs.Command.Should().NotBeNull(); + resolvedCs.Should().BeEquivalentTo(clCs, x => x.Excluding(x => x.Command)); + + var resolvedVb = await client.TextDocument.ResolveCodeLens(clVb, CancellationToken); + resolvedVb.Command.Should().NotBeNull(); + resolvedVb.Should().BeEquivalentTo(clVb, x => x.Excluding(x => x.Command)); + } + + + [Fact] + public async Task Should_Return_And_Aggregate_CodeLens_With_Different_Data_Models_But_Same_Selector_ClassHandler() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.AddTextDocumentIdentifier(WellKnownLanguages.CSharp, WellKnownLanguages.VisualBasic); + + options.WithHandler(new DataCodeLensHandler(new CodeLensContainer(new CodeLens() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new CodeLens() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }), + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + } + )); + options.WithHandler(new DataSecondCodeLensHandler(new CodeLensContainer(new CodeLens() { + Data = new DataSecond() { + Id = "1", + } + }, + new CodeLens() { + Data = new DataSecond() { + Id = "2", + } + }, + new CodeLens() { + Data = new DataSecond() { + Id = "3", + } + }), + new CodeLensRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + } + )); + }); + + var codeLensCs = await client.TextDocument.RequestCodeLens(new CodeLensParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + codeLensCs.Should().HaveCount(6); + + codeLensCs.Should().Match(x => x.Any(z => z.Data.Data.Keys.Contains("position") && z.Data.Data.Keys.Contains("range"))); + codeLensCs.Should().Match(x => x.Any(z => z.Data.Data.Keys.Contains("id"))); + } + + // custom data + // resolve custom data + // resolve as resolved data + + // add two different types and negotiate with resolve + + private void ConfigureClient(LanguageClientOptions options) + { + } + + class Data : CanBeResolvedData + { + public Position Position { get; set; } + public Range Range { get; set; } + } + + class DataCodeLensHandler : CodeLensHandler + { + private readonly CodeLensContainer _container; + + public DataCodeLensHandler(CodeLensContainer container, CodeLensRegistrationOptions registrationOptions) : base(registrationOptions) + { + _container = container; + registrationOptions.ResolveProvider = true; + } + + public override async Task> Handle(CodeLensParams request, CancellationToken cancellationToken) => _container; + + public override async Task> Handle(CodeLens request, CancellationToken cancellationToken) + { + request.Data.Position.Should().BeEquivalentTo(new Position(2, 2)); + request.Data.Range.Should().BeEquivalentTo(new Range(new Position(2, 2), new Position(2, 3))); + request.Data.Position = new Position(3, 3); + + return new CodeLens() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + Range = request.Range + }; + } + } + + class DataSecondCodeLensHandler : CodeLensHandler + { + private readonly CodeLensContainer _container; + + public DataSecondCodeLensHandler(CodeLensContainer container, CodeLensRegistrationOptions registrationOptions) : base(registrationOptions) + { + _container = container; + registrationOptions.ResolveProvider = true; + } + + public override async Task> Handle(CodeLensParams request, CancellationToken cancellationToken) => _container; + + public override async Task> Handle(CodeLens request, CancellationToken cancellationToken) + { + request.Data.Id.Should().Be("2"); + return new CodeLens() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + Range = request.Range + }; + } + } + + class DataSecond : CanBeResolvedData + { + public string Id { get; set; } + } + + class ClientData : CanBeResolvedData + { + public Position Position { get; set; } + } + } +} diff --git a/test/Lsp.Tests/Integration/Handlers/CompletionHandlerTests.cs b/test/Lsp.Tests/Integration/Handlers/CompletionHandlerTests.cs new file mode 100644 index 000000000..b077e1d34 --- /dev/null +++ b/test/Lsp.Tests/Integration/Handlers/CompletionHandlerTests.cs @@ -0,0 +1,642 @@ +using System.Linq; +using System.Threading; +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.Client; +using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Document; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using Xunit; +using Xunit.Abstractions; + +namespace Lsp.Tests.Integration.Handlers +{ + public class CompletionHandlerTests : LanguageProtocolTestBase + { + public CompletionHandlerTests(ITestOutputHelper outputHelper) : base(new JsonRpcTestOptions().ConfigureForXUnit(outputHelper)) + { + } + + [Fact] + public async Task Should_Return_And_Resolve_Completion() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.OnCompletion( + async (request, capability, ct) => { + return new CompletionList(new CompletionItem() { + Data = new Data() { + Position = new Position(1,1), + Range = new Range(new Position(1,1), new Position(1,2)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(2,2), + Range = new Range(new Position(2,2), new Position(2,3)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(3,3), + Range = new Range(new Position(3,3), new Position(3,4)) + } + }); + }, + async (request, capability, ct) => { + AssertionExtensions.Should((object) request.Data.Position).BeEquivalentTo(new Position(2,2)); + AssertionExtensions.Should((object) request.Data.Range).BeEquivalentTo(new Range(new Position(2,2), new Position(2,3))); + return new CompletionItem() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + }; + }, + new CompletionRegistrationOptions() { + DocumentSelector =DocumentSelector.ForLanguage("csharp") + } + ); + }); + + var Completion = await client.TextDocument.RequestCompletion(new CompletionParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + Completion.Should().HaveCount(3); + + var cl = Completion.Skip(1).First(); + cl.Command.Should().BeNull(); + cl.Data.Data.Should().ContainKeys("position", "range"); + + var resolved = await client.TextDocument.ResolveCompletion(cl, CancellationToken); + resolved.Command.Should().NotBeNull(); + resolved.Data.Data.Should().ContainKeys("position", "range"); + } + + [Fact] + public async Task Should_Return_And_Resolve_Completion_With_Known_Model() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.OnCompletion( + async (request, capability, ct) => { + return new CompletionList(new CompletionItem() { + Data = new Data() { + Position = new Position(1,1), + Range = new Range(new Position(1,1), new Position(1,2)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(2,2), + Range = new Range(new Position(2,2), new Position(2,3)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(3,3), + Range = new Range(new Position(3,3), new Position(3,4)) + } + }); + }, + async (request, capability, ct) => { + request.Data.Position.Should().BeEquivalentTo(new Position(2,2)); + request.Data.Range.Should().BeEquivalentTo(new Range(new Position(2,2), new Position(2,3))); + request.Data.Position = new Position(3, 3); + return new CompletionItem() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + }; + }, + new CompletionRegistrationOptions() { + DocumentSelector =DocumentSelector.ForLanguage("csharp") + } + ); + }); + + var Completion = await client.TextDocument.RequestCompletion(new CompletionParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + Completion.Should().HaveCount(3); + + var cl = Completion.Skip(1).First(); + cl.Command.Should().BeNull(); + cl.Data.Position.Should().BeEquivalentTo(new Position(2,2)); + + var resolved = await client.TextDocument.ResolveCompletion(cl, CancellationToken); + resolved.Command.Should().NotBeNull(); + resolved.Data.Position.Should().BeEquivalentTo(new Position(3,3)); + } + + [Fact] + public async Task Should_Return_And_Resolve_Completion_With_Different_Data_Models() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.AddTextDocumentIdentifier(WellKnownLanguages.CSharp, WellKnownLanguages.VisualBasic); + options.OnCompletion( + async (request, capability, ct) => { + return new CompletionList(new CompletionItem() { + Data = new Data() { + Position = new Position(1,1), + Range = new Range(new Position(1,1), new Position(1,2)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(2,2), + Range = new Range(new Position(2,2), new Position(2,3)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(3,3), + Range = new Range(new Position(3,3), new Position(3,4)) + } + }); + }, + async (request, capability, ct) => { + request.Data.Position.Should().BeEquivalentTo(new Position(2,2)); + request.Data.Range.Should().BeEquivalentTo(new Range(new Position(2,2), new Position(2,3))); + request.Data.Position = new Position(3, 3); + return new CompletionItem() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + }; + }, + new CompletionRegistrationOptions() { + DocumentSelector =DocumentSelector.ForPattern("**/*.cs") + } + ); + options.OnCompletion( + async (request, capability, ct) => { + return new CompletionList(new CompletionItem() { + Data = new DataSecond() { + Id = "1", + } + }, + new CompletionItem() { + Data = new DataSecond() { + Id = "2", + } + }, + new CompletionItem() { + Data = new DataSecond() { + Id = "3", + } + }); + }, + async (request, capability, ct) => { + request.Data.Id.Should().Be("2"); + + return new CompletionItem() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + }; + }, + new CompletionRegistrationOptions() { + DocumentSelector =DocumentSelector.ForPattern("**/*.vb") + } + ); + }); + + var CompletionCs = await client.TextDocument.RequestCompletion(new CompletionParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + var CompletionVb = await client.TextDocument.RequestCompletion(new CompletionParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.vb")) + }, CancellationToken); + + CompletionCs.Should().HaveCount(3); + CompletionVb.Should().HaveCount(3); + CompletionCs.Should().NotBeEquivalentTo(CompletionVb); + + var clCs = CompletionCs.Skip(1).First(); + clCs.Command.Should().BeNull(); + clCs.Data.Data.Should().ContainKeys("position", "range"); + + var clVb = CompletionVb.Skip(1).First(); + clVb.Command.Should().BeNull(); + clVb.Data.Data.Should().ContainKeys("id"); + + var resolvedCs = await client.TextDocument.ResolveCompletion(clCs, CancellationToken); + resolvedCs.Command.Should().NotBeNull(); + resolvedCs.Should().BeEquivalentTo(clCs, x => x.Excluding(x => x.Command)); + + var resolvedVb = await client.TextDocument.ResolveCompletion(clVb, CancellationToken); + resolvedVb.Command.Should().NotBeNull(); + resolvedVb.Should().BeEquivalentTo(clVb, x => x.Excluding(x => x.Command)); + } + + + [Fact] + public async Task Should_Return_And_Aggregate_Completion_With_Different_Data_Models_But_Same_Selector() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.AddTextDocumentIdentifier(WellKnownLanguages.CSharp, WellKnownLanguages.VisualBasic); + options.OnCompletion( + async (request, capability, ct) => { + return new CompletionList(new CompletionItem() { + Data = new Data() { + Position = new Position(1,1), + Range = new Range(new Position(1,1), new Position(1,2)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(2,2), + Range = new Range(new Position(2,2), new Position(2,3)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(3,3), + Range = new Range(new Position(3,3), new Position(3,4)) + } + }); + }, + async (request, capability, ct) => { + request.Data.Position.Should().BeEquivalentTo(new Position(2,2)); + request.Data.Range.Should().BeEquivalentTo(new Range(new Position(2,2), new Position(2,3))); + request.Data.Position = new Position(3, 3); + + return new CompletionItem() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + }; + }, + new CompletionRegistrationOptions() { + DocumentSelector =DocumentSelector.ForPattern("**/*.cs") + } + ); + options.OnCompletion( + async (request, capability, ct) => { + return new CompletionList(new CompletionItem() { + Data = new DataSecond() { + Id = "1", + } + }, + new CompletionItem() { + Data = new DataSecond() { + Id = "2", + } + }, + new CompletionItem() { + Data = new DataSecond() { + Id = "3", + } + }); + }, + async (request, capability, ct) => { + request.Data.Id.Should().Be("2"); + return new CompletionItem() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + }; + }, + new CompletionRegistrationOptions() { + DocumentSelector =DocumentSelector.ForPattern("**/*.cs") + } + ); + }); + + var CompletionCs = await client.TextDocument.RequestCompletion(new CompletionParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + CompletionCs.Should().HaveCount(6); + + CompletionCs.Should().Match(x => x.Any(z => z.Data.Data.Keys.Contains("position") && z.Data.Data.Keys.Contains("range"))); + CompletionCs.Should().Match(x => x.Any(z => z.Data.Data.Keys.Contains("id"))); + } + + + [Fact] + public async Task Should_Return_And_Resolve_Completion_Using_ClassHandler() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.WithHandler(new DataCompletionHandler(new CompletionList(new CompletionItem() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }), + new CompletionRegistrationOptions() { + DocumentSelector = DocumentSelector.ForLanguage("csharp") + } + )); + }); + + var Completion = await client.TextDocument.RequestCompletion(new CompletionParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + Completion.Should().HaveCount(3); + + var cl = Completion.Skip(1).First(); + cl.Command.Should().BeNull(); + cl.Data.Data.Should().ContainKeys("position", "range"); + + var resolved = await client.TextDocument.ResolveCompletion(cl, CancellationToken); + resolved.Command.Should().NotBeNull(); + resolved.Data.Data.Should().ContainKeys("position", "range"); + } + + [Fact] + public async Task Should_Return_And_Resolve_Completion_With_Known_Model_ClassHandler() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.WithHandler(new DataCompletionHandler(new CompletionList(new CompletionItem() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }), + new CompletionRegistrationOptions() { + DocumentSelector = DocumentSelector.ForLanguage("csharp") + } + )); + }); + + var Completion = await client.TextDocument.RequestCompletion(new CompletionParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + Completion.Should().HaveCount(3); + + var cl = Completion.Skip(1).First(); + cl.Command.Should().BeNull(); + cl.Data.Position.Should().BeEquivalentTo(new Position(2, 2)); + + var resolved = await client.TextDocument.ResolveCompletion(cl, CancellationToken); + resolved.Command.Should().NotBeNull(); + resolved.Data.Position.Should().BeEquivalentTo(new Position(3, 3)); + } + + [Fact] + public async Task Should_Return_And_Resolve_Completion_With_Different_Data_Models_ClassHandler() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.AddTextDocumentIdentifier(WellKnownLanguages.CSharp, WellKnownLanguages.VisualBasic); + + options.WithHandler(new DataCompletionHandler(new CompletionList(new CompletionItem() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }), + new CompletionRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + } + )); + options.WithHandler(new DataSecondCompletionHandler(new CompletionList(new CompletionItem() { + Data = new DataSecond() { + Id = "1", + } + }, + new CompletionItem() { + Data = new DataSecond() { + Id = "2", + } + }, + new CompletionItem() { + Data = new DataSecond() { + Id = "3", + } + }), + new CompletionRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.vb") + } + )); + }); + + var CompletionCs = await client.TextDocument.RequestCompletion(new CompletionParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + var CompletionVb = await client.TextDocument.RequestCompletion(new CompletionParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.vb")) + }, CancellationToken); + + CompletionCs.Should().HaveCount(3); + CompletionVb.Should().HaveCount(3); + CompletionCs.Should().NotBeEquivalentTo(CompletionVb); + + var clCs = CompletionCs.Skip(1).First(); + clCs.Command.Should().BeNull(); + clCs.Data.Data.Should().ContainKeys("position", "range"); + + var clVb = CompletionVb.Skip(1).First(); + clVb.Command.Should().BeNull(); + clVb.Data.Data.Should().ContainKeys("id"); + + var resolvedCs = await client.TextDocument.ResolveCompletion(clCs, CancellationToken); + resolvedCs.Command.Should().NotBeNull(); + resolvedCs.Should().BeEquivalentTo(clCs, x => x.Excluding(x => x.Command)); + + var resolvedVb = await client.TextDocument.ResolveCompletion(clVb, CancellationToken); + resolvedVb.Command.Should().NotBeNull(); + resolvedVb.Should().BeEquivalentTo(clVb, x => x.Excluding(x => x.Command)); + } + + + [Fact] + public async Task Should_Return_And_Aggregate_Completion_With_Different_Data_Models_But_Same_Selector_ClassHandler() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.AddTextDocumentIdentifier(WellKnownLanguages.CSharp, WellKnownLanguages.VisualBasic); + + options.WithHandler(new DataCompletionHandler(new CompletionList(new CompletionItem() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new CompletionItem() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }), + new CompletionRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + } + )); + options.WithHandler(new DataSecondCompletionHandler(new CompletionList(new CompletionItem() { + Data = new DataSecond() { + Id = "1", + } + }, + new CompletionItem() { + Data = new DataSecond() { + Id = "2", + } + }, + new CompletionItem() { + Data = new DataSecond() { + Id = "3", + } + }), + new CompletionRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + } + )); + }); + + var CompletionCs = await client.TextDocument.RequestCompletion(new CompletionParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + CompletionCs.Should().HaveCount(6); + + CompletionCs.Should().Match(x => x.Any(z => z.Data.Data.Keys.Contains("position") && z.Data.Data.Keys.Contains("range"))); + CompletionCs.Should().Match(x => x.Any(z => z.Data.Data.Keys.Contains("id"))); + } + + // custom data + // resolve custom data + // resolve as resolved data + + // add two different types and negotiate with resolve + + private void ConfigureClient(LanguageClientOptions options) + { + } + + class Data : CanBeResolvedData + { + public Position Position { get; set; } + public Range Range { get; set; } + } + + class DataSecond : CanBeResolvedData + { + public string Id { get; set; } + } + + class DataCompletionHandler : CompletionHandler + { + private readonly CompletionList _container; + + public DataCompletionHandler(CompletionList container, CompletionRegistrationOptions registrationOptions) : base(registrationOptions) + { + _container = container; + registrationOptions.ResolveProvider = true; + } + + public override async Task> Handle(CompletionParams request, CancellationToken cancellationToken) => _container; + + public override async Task> Handle(CompletionItem request, CancellationToken cancellationToken) + { + request.Data.Position.Should().BeEquivalentTo(new Position(2,2)); + request.Data.Range.Should().BeEquivalentTo(new Range(new Position(2,2), new Position(2,3))); + request.Data.Position = new Position(3, 3); + return new CompletionItem() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + }; + } + } + + class DataSecondCompletionHandler : CompletionHandler + { + private readonly CompletionList _container; + + public DataSecondCompletionHandler(CompletionList container, CompletionRegistrationOptions registrationOptions) : base(registrationOptions) + { + _container = container; + registrationOptions.ResolveProvider = true; + } + + public override async Task> Handle(CompletionParams request, CancellationToken cancellationToken) => _container; + + public override async Task> Handle(CompletionItem request, CancellationToken cancellationToken) + { + request.Data.Id.Should().Be("2"); + return new CompletionItem() { + Command = new Command() { + Arguments = new JArray(), + Name = "my command", + Title = "My Command" + }, + Data = request.Data, + }; + } + } + + class ClientData : CanBeResolvedData + { + public Position Position { get; set; } + } + } +} diff --git a/test/Lsp.Tests/Integration/Handlers/DocumentLinkHandlerTests.cs b/test/Lsp.Tests/Integration/Handlers/DocumentLinkHandlerTests.cs new file mode 100644 index 000000000..54f10f12a --- /dev/null +++ b/test/Lsp.Tests/Integration/Handlers/DocumentLinkHandlerTests.cs @@ -0,0 +1,642 @@ +using System.Linq; +using System.Threading; +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.Client; +using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Document; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using Xunit; +using Xunit.Abstractions; + +namespace Lsp.Tests.Integration.Handlers +{ + public class DocumentLinkHandlerTests : LanguageProtocolTestBase + { + public DocumentLinkHandlerTests(ITestOutputHelper outputHelper) : base(new JsonRpcTestOptions().ConfigureForXUnit(outputHelper)) + { + } + + [Fact] + public async Task Should_Return_And_Resolve_DocumentLink() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.OnDocumentLink( + async (request, capability, ct) => { + return new DocumentLinkContainer(new DocumentLink() { + Data = new Data() { + Position = new Position(1,1), + Range = new Range(new Position(1,1), new Position(1,2)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(2,2), + Range = new Range(new Position(2,2), new Position(2,3)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(3,3), + Range = new Range(new Position(3,3), new Position(3,4)) + } + }); + }, + async (request, capability, ct) => { + AssertionExtensions.Should((object) request.Data.Position).BeEquivalentTo(new Position(2,2)); + AssertionExtensions.Should((object) request.Data.Range).BeEquivalentTo(new Range(new Position(2,2), new Position(2,3))); + return new DocumentLink() { + Target = DocumentUri.File("/some/path"), + Tooltip = "Tooltip", + Data = request.Data, + Range = request.Range + }; + }, + new DocumentLinkRegistrationOptions() { + DocumentSelector =DocumentSelector.ForLanguage("csharp") + } + ); + }); + + var documentLink = await client.TextDocument.RequestDocumentLink(new DocumentLinkParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + documentLink.Should().HaveCount(3); + + var cl = documentLink.Skip(1).First(); + cl.Tooltip.Should().BeNull(); + cl.Target.Should().BeNull(); + cl.Data.Data.Should().ContainKeys("position", "range"); + + var resolved = await client.TextDocument.ResolveDocumentLink(cl, CancellationToken); + resolved.Tooltip.Should().NotBeNull(); + resolved.Target.Should().NotBeNull(); + resolved.Data.Data.Should().ContainKeys("position", "range"); + } + + [Fact] + public async Task Should_Return_And_Resolve_DocumentLink_With_Known_Model() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.OnDocumentLink( + async (request, capability, ct) => { + return new DocumentLinkContainer(new DocumentLink() { + Data = new Data() { + Position = new Position(1,1), + Range = new Range(new Position(1,1), new Position(1,2)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(2,2), + Range = new Range(new Position(2,2), new Position(2,3)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(3,3), + Range = new Range(new Position(3,3), new Position(3,4)) + } + }); + }, + async (request, capability, ct) => { + request.Data.Position.Should().BeEquivalentTo(new Position(2,2)); + request.Data.Range.Should().BeEquivalentTo(new Range(new Position(2,2), new Position(2,3))); + request.Data.Position = new Position(3, 3); + return new DocumentLink() { + Target = DocumentUri.File("/some/path"), + Tooltip = "Tooltip", + Data = request.Data, + Range = request.Range + }; + }, + new DocumentLinkRegistrationOptions() { + DocumentSelector =DocumentSelector.ForLanguage("csharp") + } + ); + }); + + var documentLink = await client.TextDocument.RequestDocumentLink(new DocumentLinkParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + documentLink.Should().HaveCount(3); + + var cl = documentLink.Skip(1).First(); + cl.Tooltip.Should().BeNull(); + cl.Target.Should().BeNull(); + cl.Data.Position.Should().BeEquivalentTo(new Position(2,2)); + + var resolved = await client.TextDocument.ResolveDocumentLink(cl, CancellationToken); + resolved.Tooltip.Should().NotBeNull(); + resolved.Target.Should().NotBeNull(); + resolved.Data.Position.Should().BeEquivalentTo(new Position(3,3)); + } + + [Fact] + public async Task Should_Return_And_Resolve_DocumentLink_With_Different_Data_Models() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.AddTextDocumentIdentifier(WellKnownLanguages.CSharp, WellKnownLanguages.VisualBasic); + options.OnDocumentLink( + async (request, capability, ct) => { + return new DocumentLinkContainer(new DocumentLink() { + Data = new Data() { + Position = new Position(1,1), + Range = new Range(new Position(1,1), new Position(1,2)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(2,2), + Range = new Range(new Position(2,2), new Position(2,3)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(3,3), + Range = new Range(new Position(3,3), new Position(3,4)) + } + }); + }, + async (request, capability, ct) => { + request.Data.Position.Should().BeEquivalentTo(new Position(2,2)); + request.Data.Range.Should().BeEquivalentTo(new Range(new Position(2,2), new Position(2,3))); + request.Data.Position = new Position(3, 3); + return new DocumentLink() { + Target = DocumentUri.File("/some/path"), + Tooltip = "Tooltip", + Data = request.Data, + Range = request.Range + }; + }, + new DocumentLinkRegistrationOptions() { + DocumentSelector =DocumentSelector.ForPattern("**/*.cs") + } + ); + options.OnDocumentLink( + async (request, capability, ct) => { + return new DocumentLinkContainer(new DocumentLink() { + Data = new DataSecond() { + Id = "1", + } + }, + new DocumentLink() { + Data = new DataSecond() { + Id = "2", + } + }, + new DocumentLink() { + Data = new DataSecond() { + Id = "3", + } + }); + }, + async (request, capability, ct) => { + request.Data.Id.Should().Be("2"); + + return new DocumentLink() { + Target = DocumentUri.File("/some/path"), + Tooltip = "Tooltip", + Data = request.Data, + Range = request.Range + }; + }, + new DocumentLinkRegistrationOptions() { + DocumentSelector =DocumentSelector.ForPattern("**/*.vb") + } + ); + }); + + var documentLinkCs = await client.TextDocument.RequestDocumentLink(new DocumentLinkParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + var documentLinkVb = await client.TextDocument.RequestDocumentLink(new DocumentLinkParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.vb")) + }, CancellationToken); + + documentLinkCs.Should().HaveCount(3); + documentLinkVb.Should().HaveCount(3); + documentLinkCs.Should().NotBeEquivalentTo(documentLinkVb); + + var clCs = documentLinkCs.Skip(1).First(); + clCs.Tooltip.Should().BeNull(); + clCs.Target.Should().BeNull(); + clCs.Data.Data.Should().ContainKeys("position", "range"); + + var clVb = documentLinkVb.Skip(1).First(); + clVb.Tooltip.Should().BeNull(); + clVb.Target.Should().BeNull(); + clVb.Data.Data.Should().ContainKeys("id"); + + var resolvedCs = await client.TextDocument.ResolveDocumentLink(clCs, CancellationToken); + resolvedCs.Tooltip.Should().NotBeNull(); + resolvedCs.Target.Should().NotBeNull(); + resolvedCs.Should().BeEquivalentTo(clCs, x => x.Excluding(x => x.Tooltip).Excluding(x => x.Target)); + + var resolvedVb = await client.TextDocument.ResolveDocumentLink(clVb, CancellationToken); + resolvedVb.Tooltip.Should().NotBeNull(); + resolvedVb.Target.Should().NotBeNull(); + resolvedVb.Should().BeEquivalentTo(clVb, x => x.Excluding(x => x.Tooltip).Excluding(x => x.Target)); + } + + + [Fact] + public async Task Should_Return_And_Aggregate_DocumentLink_With_Different_Data_Models_But_Same_Selector() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.AddTextDocumentIdentifier(WellKnownLanguages.CSharp, WellKnownLanguages.VisualBasic); + options.OnDocumentLink( + async (request, capability, ct) => { + return new DocumentLinkContainer(new DocumentLink() { + Data = new Data() { + Position = new Position(1,1), + Range = new Range(new Position(1,1), new Position(1,2)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(2,2), + Range = new Range(new Position(2,2), new Position(2,3)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(3,3), + Range = new Range(new Position(3,3), new Position(3,4)) + } + }); + }, + async (request, capability, ct) => { + request.Data.Position.Should().BeEquivalentTo(new Position(2,2)); + request.Data.Range.Should().BeEquivalentTo(new Range(new Position(2,2), new Position(2,3))); + request.Data.Position = new Position(3, 3); + + return new DocumentLink() { + Target = DocumentUri.File("/some/path"), + Tooltip = "Tooltip", + Data = request.Data, + Range = request.Range + }; + }, + new DocumentLinkRegistrationOptions() { + DocumentSelector =DocumentSelector.ForPattern("**/*.cs") + } + ); + options.OnDocumentLink( + async (request, capability, ct) => { + return new DocumentLinkContainer(new DocumentLink() { + Data = new DataSecond() { + Id = "1", + } + }, + new DocumentLink() { + Data = new DataSecond() { + Id = "2", + } + }, + new DocumentLink() { + Data = new DataSecond() { + Id = "3", + } + }); + }, + async (request, capability, ct) => { + request.Data.Id.Should().Be("2"); + return new DocumentLink() { + Target = DocumentUri.File("/some/path"), + Tooltip = "Tooltip", + Data = request.Data, + Range = request.Range + }; + }, + new DocumentLinkRegistrationOptions() { + DocumentSelector =DocumentSelector.ForPattern("**/*.cs") + } + ); + }); + + var documentLinkCs = await client.TextDocument.RequestDocumentLink(new DocumentLinkParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + documentLinkCs.Should().HaveCount(6); + + documentLinkCs.Should().Match(x => x.Any(z => z.Data.Data.Keys.Contains("position") && z.Data.Data.Keys.Contains("range"))); + documentLinkCs.Should().Match(x => x.Any(z => z.Data.Data.Keys.Contains("id"))); + } + + + [Fact] + public async Task Should_Return_And_Resolve_DocumentLink_Using_ClassHandler() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.WithHandler(new DataDocumentLinkHandler(new DocumentLinkContainer(new DocumentLink() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }), + new DocumentLinkRegistrationOptions() { + DocumentSelector = DocumentSelector.ForLanguage("csharp") + } + )); + }); + + var documentLink = await client.TextDocument.RequestDocumentLink(new DocumentLinkParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + documentLink.Should().HaveCount(3); + + var cl = documentLink.Skip(1).First(); + cl.Tooltip.Should().BeNull(); + cl.Target.Should().BeNull(); + cl.Data.Data.Should().ContainKeys("position", "range"); + + var resolved = await client.TextDocument.ResolveDocumentLink(cl, CancellationToken); + resolved.Tooltip.Should().NotBeNull(); + resolved.Target.Should().NotBeNull(); + resolved.Data.Data.Should().ContainKeys("position", "range"); + } + + [Fact] + public async Task Should_Return_And_Resolve_DocumentLink_With_Known_Model_ClassHandler() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.WithHandler(new DataDocumentLinkHandler(new DocumentLinkContainer(new DocumentLink() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }), + new DocumentLinkRegistrationOptions() { + DocumentSelector = DocumentSelector.ForLanguage("csharp") + } + )); + }); + + var documentLink = await client.TextDocument.RequestDocumentLink(new DocumentLinkParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + documentLink.Should().HaveCount(3); + + var cl = documentLink.Skip(1).First(); + cl.Tooltip.Should().BeNull(); + cl.Target.Should().BeNull(); + cl.Data.Position.Should().BeEquivalentTo(new Position(2, 2)); + + var resolved = await client.TextDocument.ResolveDocumentLink(cl, CancellationToken); + resolved.Tooltip.Should().NotBeNull(); + resolved.Target.Should().NotBeNull(); + resolved.Data.Position.Should().BeEquivalentTo(new Position(3, 3)); + } + + [Fact] + public async Task Should_Return_And_Resolve_DocumentLink_With_Different_Data_Models_ClassHandler() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.AddTextDocumentIdentifier(WellKnownLanguages.CSharp, WellKnownLanguages.VisualBasic); + + options.WithHandler(new DataDocumentLinkHandler(new DocumentLinkContainer(new DocumentLink() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }), + new DocumentLinkRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + } + )); + options.WithHandler(new DataSecondDocumentLinkHandler(new DocumentLinkContainer(new DocumentLink() { + Data = new DataSecond() { + Id = "1", + } + }, + new DocumentLink() { + Data = new DataSecond() { + Id = "2", + } + }, + new DocumentLink() { + Data = new DataSecond() { + Id = "3", + } + }), + new DocumentLinkRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.vb") + } + )); + }); + + var documentLinkCs = await client.TextDocument.RequestDocumentLink(new DocumentLinkParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + var documentLinkVb = await client.TextDocument.RequestDocumentLink(new DocumentLinkParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.vb")) + }, CancellationToken); + + documentLinkCs.Should().HaveCount(3); + documentLinkVb.Should().HaveCount(3); + documentLinkCs.Should().NotBeEquivalentTo(documentLinkVb); + + var clCs = documentLinkCs.Skip(1).First(); + clCs.Target.Should().BeNull(); + clCs.Tooltip.Should().BeNull(); + clCs.Data.Data.Should().ContainKeys("position", "range"); + + var clVb = documentLinkVb.Skip(1).First(); + clVb.Target.Should().BeNull(); + clVb.Tooltip.Should().BeNull(); + clVb.Data.Data.Should().ContainKeys("id"); + + var resolvedCs = await client.TextDocument.ResolveDocumentLink(clCs, CancellationToken); + resolvedCs.Target.Should().NotBeNull(); + resolvedCs.Tooltip.Should().NotBeNull(); + resolvedCs.Should().BeEquivalentTo(clCs, x => x.Excluding(x => x.Target).Excluding(x => x.Tooltip)); + + var resolvedVb = await client.TextDocument.ResolveDocumentLink(clVb, CancellationToken); + resolvedVb.Target.Should().NotBeNull(); + resolvedVb.Tooltip.Should().NotBeNull(); + resolvedVb.Should().BeEquivalentTo(clVb, x => x.Excluding(x => x.Target).Excluding(x => x.Tooltip)); + } + + + [Fact] + public async Task Should_Return_And_Aggregate_DocumentLink_With_Different_Data_Models_But_Same_Selector_ClassHandler() + { + var (client, server) = await Initialize(ConfigureClient, options => { + options.AddTextDocumentIdentifier(WellKnownLanguages.CSharp, WellKnownLanguages.VisualBasic); + + options.WithHandler(new DataDocumentLinkHandler(new DocumentLinkContainer(new DocumentLink() { + Data = new Data() { + Position = new Position(1, 1), + Range = new Range(new Position(1, 1), new Position(1, 2)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(2, 2), + Range = new Range(new Position(2, 2), new Position(2, 3)) + } + }, + new DocumentLink() { + Data = new Data() { + Position = new Position(3, 3), + Range = new Range(new Position(3, 3), new Position(3, 4)) + } + }), + new DocumentLinkRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + } + )); + options.WithHandler(new DataSecondDocumentLinkHandler(new DocumentLinkContainer(new DocumentLink() { + Data = new DataSecond() { + Id = "1", + } + }, + new DocumentLink() { + Data = new DataSecond() { + Id = "2", + } + }, + new DocumentLink() { + Data = new DataSecond() { + Id = "3", + } + }), + new DocumentLinkRegistrationOptions() { + DocumentSelector = DocumentSelector.ForPattern("**/*.cs") + } + )); + }); + + var documentLinkCs = await client.TextDocument.RequestDocumentLink(new DocumentLinkParams() { + TextDocument = new TextDocumentIdentifier(DocumentUri.File("/some/path/file.cs")) + }, CancellationToken); + + documentLinkCs.Should().HaveCount(6); + + documentLinkCs.Should().Match(x => x.Any(z => z.Data.Data.Keys.Contains("position") && z.Data.Data.Keys.Contains("range"))); + documentLinkCs.Should().Match(x => x.Any(z => z.Data.Data.Keys.Contains("id"))); + } + + // custom data + // resolve custom data + // resolve as resolved data + + // add two different types and negotiate with resolve + + private void ConfigureClient(LanguageClientOptions options) + { + } + + class Data : CanBeResolvedData + { + public Position Position { get; set; } + public Range Range { get; set; } + } + + class DataSecond : CanBeResolvedData + { + public string Id { get; set; } + } + + class DataDocumentLinkHandler : DocumentLinkHandler + { + private readonly DocumentLinkContainer _container; + + public DataDocumentLinkHandler(DocumentLinkContainer container, DocumentLinkRegistrationOptions registrationOptions) : base(registrationOptions) + { + _container = container; + registrationOptions.ResolveProvider = true; + } + + public override async Task> Handle(DocumentLinkParams request, CancellationToken cancellationToken) => _container; + + public override async Task> Handle(DocumentLink request, CancellationToken cancellationToken) + { + request.Data.Position.Should().BeEquivalentTo(new Position(2,2)); + request.Data.Range.Should().BeEquivalentTo(new Range(new Position(2,2), new Position(2,3))); + request.Data.Position = new Position(3, 3); + return new DocumentLink() { + Target = DocumentUri.File("/some/path"), + Tooltip = "Tooltip", + Data = request.Data, + Range = request.Range + }; + } + } + + class DataSecondDocumentLinkHandler : DocumentLinkHandler + { + private readonly DocumentLinkContainer _container; + + public DataSecondDocumentLinkHandler(DocumentLinkContainer container, DocumentLinkRegistrationOptions registrationOptions) : base(registrationOptions) + { + _container = container; + registrationOptions.ResolveProvider = true; + } + + public override async Task> Handle(DocumentLinkParams request, CancellationToken cancellationToken) => _container; + + public override async Task> Handle(DocumentLink request, CancellationToken cancellationToken) + { + request.Data.Id.Should().Be("2"); + return new DocumentLink() { + Target = DocumentUri.File("/some/path"), + Tooltip = "Tooltip", + Data = request.Data, + Range = request.Range + }; + } + } + + class ClientData : CanBeResolvedData + { + public Position Position { get; set; } + } + } +} diff --git a/test/Lsp.Tests/Integration/PartialItemTests.cs b/test/Lsp.Tests/Integration/PartialItemTests.cs index 982131af0..2a7161a56 100644 --- a/test/Lsp.Tests/Integration/PartialItemTests.cs +++ b/test/Lsp.Tests/Integration/PartialItemTests.cs @@ -34,7 +34,7 @@ public PartialItemTests(ITestOutputHelper outputHelper) : base(new JsonRpcTestOp public async Task Should_Behave_Like_A_Task() { var (client, server) = await Initialize(ConfigureClient, ConfigureServerWithDelegateCodeLens); - var result = await client.TextDocument.RequestCodeLens(new CodeLensParams() { + var result = await client.TextDocument.RequestCodeLens(new CodeLensParams() { TextDocument = new TextDocumentIdentifier(@"c:\test.cs") }, CancellationToken); @@ -47,8 +47,8 @@ public async Task Should_Behave_Like_An_Observable() { var (client, server) = await Initialize(ConfigureClient, ConfigureServerWithDelegateCodeLens); - var items = new List(); - await client.TextDocument.RequestCodeLens(new CodeLensParams() { + var items = new List>(); + await client.TextDocument.RequestCodeLens(new CodeLensParams() { TextDocument = new TextDocumentIdentifier(@"c:\test.cs") }, CancellationToken).ForEachAsync(x => items.AddRange(x)); @@ -61,11 +61,11 @@ public async Task Should_Behave_Like_An_Observable_With_WorkDone() { var (client, server) = await Initialize(ConfigureClient, ConfigureServerWithClassCodeLens); - var items = new List(); + var items = new List>(); var work = new List(); client.TextDocument .ObserveWorkDone( - new CodeLensParams() {TextDocument = new TextDocumentIdentifier(@"c:\test.cs")}, + new CodeLensParams() {TextDocument = new TextDocumentIdentifier(@"c:\test.cs")}, (client, request) => CodeLensExtensions.RequestCodeLens(client, request, CancellationToken), Observer.Create(z => work.Add(z)) ).Subscribe(x => items.AddRange(x)); @@ -87,23 +87,23 @@ private void ConfigureClient(LanguageClientOptions options) private void ConfigureServerWithDelegateCodeLens(LanguageServerOptions options) { - options.OnCodeLens((@params, observer, capability, cancellationToken) => { + options.OnCodeLens((@params, observer, capability, cancellationToken) => { observer.OnNext(new [] { - new CodeLens() { + new CodeLens() { Command = new Command() { Name = "CodeLens 1" } }, }); observer.OnNext(new [] { - new CodeLens() { + new CodeLens() { Command = new Command() { Name = "CodeLens 2" } }, }); observer.OnNext(new [] { - new CodeLens() { + new CodeLens() { Command = new Command() { Name = "CodeLens 3" } @@ -120,7 +120,7 @@ private void ConfigureServerWithClassCodeLens(LanguageServerOptions options) options.AddHandler(); } - class InnerCodeLensHandler : CodeLensHandler + class InnerCodeLensHandler : CodeLensHandler { private readonly IServerWorkDoneManager _workDoneManager; private readonly IProgressManager _progressManager; @@ -131,7 +131,7 @@ public InnerCodeLensHandler(IServerWorkDoneManager workDoneManager, IProgressMan _progressManager = progressManager; } - public override async Task Handle(CodeLensParams request, CancellationToken cancellationToken) + public override async Task> Handle(CodeLensParams request, CancellationToken cancellationToken) { var partial = _progressManager.For(request, cancellationToken); var workDone = _workDoneManager.For(request, new WorkDoneProgressBegin() { @@ -145,7 +145,7 @@ public override async Task Handle(CodeLensParams request, Can }); partial.OnNext(new [] { - new CodeLens() { + new CodeLens() { Command = new Command() { Name = "CodeLens 1" } @@ -157,7 +157,7 @@ public override async Task Handle(CodeLensParams request, Can }); partial.OnNext(new [] { - new CodeLens() { + new CodeLens() { Command = new Command() { Name = "CodeLens 2" } @@ -169,7 +169,7 @@ public override async Task Handle(CodeLensParams request, Can }); partial.OnNext(new [] { - new CodeLens() { + new CodeLens() { Command = new Command() { Name = "CodeLens 3" } @@ -181,7 +181,7 @@ public override async Task Handle(CodeLensParams request, Can }); partial.OnNext(new [] { - new CodeLens() { + new CodeLens() { Command = new Command() { Name = "CodeLens 4" } @@ -196,12 +196,10 @@ public override async Task Handle(CodeLensParams request, Can await Task.Yield(); - return new CodeLensContainer(); + return new CodeLensContainer(); } - public override Task Handle(CodeLens request, CancellationToken cancellationToken) => Task.FromResult(request); - - public override bool CanResolve(CodeLens value) => true; + public override Task> Handle(CodeLens request, CancellationToken cancellationToken) => Task.FromResult(request); } } diff --git a/test/Lsp.Tests/Integration/RequestCancellationTests.cs b/test/Lsp.Tests/Integration/RequestCancellationTests.cs index 34d80a98d..6fb4b47f1 100644 --- a/test/Lsp.Tests/Integration/RequestCancellationTests.cs +++ b/test/Lsp.Tests/Integration/RequestCancellationTests.cs @@ -36,7 +36,7 @@ public async Task Should_Cancel_Pending_Requests() var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(10)); CancellationToken.Register(cts.Cancel); - Func> action = () => client.TextDocument.RequestCompletion(new CompletionParams() { + Func>> action = () => client.TextDocument.RequestCompletion(new CompletionParams() { TextDocument = "/a/file.cs" }, cts.Token).AsTask(); action.Should().Throw(); @@ -47,7 +47,7 @@ public async Task Should_Abandon_Pending_Requests_For_Text_Changes() { var (client, server) = await Initialize(ConfigureClient, ConfigureServer); - var request1 = client.TextDocument.RequestCompletion(new CompletionParams() { + var request1 = client.TextDocument.RequestCompletion(new CompletionParams() { TextDocument = "/a/file.cs" }, CancellationToken).AsTask(); @@ -71,7 +71,7 @@ public async Task Should_Cancel_Requests_After_Timeout() x.WithMaximumRequestTimeout(TimeSpan.FromMilliseconds(500)); }); - Func action = () => client.TextDocument.RequestCompletion(new CompletionParams() { + Func action = () => client.TextDocument.RequestCompletion(new CompletionParams() { TextDocument = "/a/file.cs" }, CancellationToken).AsTask(); action.Should().Throw(); @@ -85,7 +85,7 @@ public async Task Should_Cancel_Requests_After_Timeout_without_Content_Modified( x.WithContentModifiedSupport(false).WithMaximumRequestTimeout(TimeSpan.FromMilliseconds(500)); }); - Func action = () => client.TextDocument.RequestCompletion(new CompletionParams() { + Func action = () => client.TextDocument.RequestCompletion(new CompletionParams() { TextDocument = "/a/file.cs" }, CancellationToken).AsTask(); action.Should().Throw(); @@ -135,7 +135,7 @@ private void ConfigureClient(LanguageClientOptions options) private void ConfigureServer(LanguageServerOptions options) { options.WithContentModifiedSupport(true); - options.OnCompletion(async (x, ct) => { + options.OnCompletion(async (x, ct) => { await Task.Delay(50000, ct); return new CompletionList(); }, new CompletionRegistrationOptions()); diff --git a/test/Lsp.Tests/LspRequestRouterTests.cs b/test/Lsp.Tests/LspRequestRouterTests.cs index 6e345983e..0e14614d5 100644 --- a/test/Lsp.Tests/LspRequestRouterTests.cs +++ b/test/Lsp.Tests/LspRequestRouterTests.cs @@ -86,7 +86,6 @@ public LspRequestRouterTests(ITestOutputHelper testOutputHelper) : base(testOutp Services .AddJsonRpcMediatR(new[] {typeof(LspRequestRouterTests).Assembly}) .AddSingleton(new Serializer(ClientVersion.Lsp3)); - Services.AddTransient(); } [Fact] @@ -102,6 +101,7 @@ public async Task ShouldRouteToCorrect_Notification() {textDocumentSyncHandler}; AutoSubstitute.Provide(collection); AutoSubstitute.Provide>(collection); + AutoSubstitute.Provide(AutoSubstitute.Resolve()); var mediator = AutoSubstitute.Resolve(); var @params = new DidSaveTextDocumentParams() { @@ -136,6 +136,7 @@ public async Task ShouldRouteToCorrect_Notification_WithManyHandlers() {textDocumentSyncHandler, textDocumentSyncHandler2}; AutoSubstitute.Provide(collection); AutoSubstitute.Provide>(collection); + AutoSubstitute.Provide(AutoSubstitute.Resolve()); var mediator = AutoSubstitute.Resolve(); var @params = new DidSaveTextDocumentParams() { @@ -173,6 +174,7 @@ public async Task ShouldRouteToCorrect_Request() {textDocumentSyncHandler, codeActionHandler}; AutoSubstitute.Provide(collection); AutoSubstitute.Provide>(collection); + AutoSubstitute.Provide(AutoSubstitute.Resolve()); var mediator = AutoSubstitute.Resolve(); var id = Guid.NewGuid().ToString(); @@ -225,6 +227,7 @@ public async Task ShouldRouteToCorrect_Request_WithManyHandlers() handlerCollection.Add(registry.Handlers); AutoSubstitute.Provide(handlerCollection); AutoSubstitute.Provide>(handlerCollection); + AutoSubstitute.Provide(AutoSubstitute.Resolve()); var mediator = AutoSubstitute.Resolve(); var id = Guid.NewGuid().ToString(); @@ -253,29 +256,30 @@ public async Task ShouldRouteToCorrect_Request_WithManyHandlers_CodeLensHandler( textDocumentSyncHandler2.Handle(Arg.Any(), Arg.Any()) .Returns(Unit.Value); - var codeActionHandler = Substitute.For(); + var codeActionHandler = Substitute.For>(); codeActionHandler.GetRegistrationOptions().Returns(new CodeLensRegistrationOptions() {DocumentSelector = DocumentSelector.ForPattern("**/*.cs")}); codeActionHandler - .Handle(Arg.Any(), Arg.Any()) - .Returns(new CodeLensContainer()); + .Handle(Arg.Any>(), Arg.Any()) + .Returns(new CodeLensContainer()); - var codeActionHandler2 = Substitute.For(); + var codeActionHandler2 = Substitute.For>(); codeActionHandler2.GetRegistrationOptions().Returns(new CodeLensRegistrationOptions() {DocumentSelector = DocumentSelector.ForPattern("**/*.cake")}); codeActionHandler2 - .Handle(Arg.Any(), Arg.Any()) - .Returns(new CodeLensContainer()); + .Handle(Arg.Any>(), Arg.Any()) + .Returns(new CodeLensContainer()); var collection = new SharedHandlerCollection(SupportedCapabilitiesFixture.AlwaysTrue, new TextDocumentIdentifiers()) {textDocumentSyncHandler, textDocumentSyncHandler2, codeActionHandler, codeActionHandler2}; AutoSubstitute.Provide(collection); AutoSubstitute.Provide>(collection); + AutoSubstitute.Provide(AutoSubstitute.Resolve()); var mediator = AutoSubstitute.Resolve(); var id = Guid.NewGuid().ToString(); - var @params = new CodeLensParams() { + var @params = new CodeLensParams() { TextDocument = new TextDocumentIdentifier(new Uri("file:///c:/test/123.cs")) }; @@ -284,8 +288,8 @@ public async Task ShouldRouteToCorrect_Request_WithManyHandlers_CodeLensHandler( await mediator.RouteRequest(mediator.GetDescriptor(request), request, CancellationToken.None); - await codeActionHandler2.Received(0).Handle(Arg.Any(), Arg.Any()); - await codeActionHandler.Received(1).Handle(Arg.Any(), Arg.Any()); + await codeActionHandler2.Received(0).Handle(Arg.Any>(), Arg.Any()); + await codeActionHandler.Received(1).Handle(Arg.Any>(), Arg.Any()); } [Fact] @@ -301,6 +305,7 @@ public async Task ShouldRouteTo_CorrectRequestWhenGivenNullParams() {handler}; AutoSubstitute.Provide(collection); AutoSubstitute.Provide>(collection); + AutoSubstitute.Provide(AutoSubstitute.Resolve()); var mediator = AutoSubstitute.Resolve(); var id = Guid.NewGuid().ToString(); diff --git a/test/Lsp.Tests/Matchers/ResolveCommandMatcherTests.cs b/test/Lsp.Tests/Matchers/ResolveCommandMatcherTests.cs index 86abc8b4e..936fcc039 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) { } @@ -57,25 +59,25 @@ public void Should_Return_Empty_Descriptor() public void Should_Not_Throw_Given_Another_Descriptor() { // Given - var resolveHandler = Substitute.For(); + var resolveHandler = Substitute.For>(); var handlerDescriptor = new LspHandlerDescriptor( TextDocumentNames.CodeLens, "Key", resolveHandler, resolveHandler.GetType(), - typeof(CodeLensParams), + typeof(CodeLensParams), null, null, () => false, null, null, () => { }); - var handlerMatcher = new ResolveCommandPipeline( + var handlerMatcher = new ResolveCommandPipeline, CodeLensContainer>( new RequestContext() {Descriptor = handlerDescriptor}, - LoggerFactory.CreateLogger>()); + LoggerFactory.CreateLogger, CodeLensContainer>>()); // When - Func a = async () => await handlerMatcher.Handle(new CodeLensParams(), CancellationToken.None, () => Task.FromResult(new CodeLensContainer())); + Func a = async () => await handlerMatcher.Handle(new CodeLensParams(), CancellationToken.None, () => Task.FromResult(new CodeLensContainer())); a.Should().NotThrow(); } @@ -84,21 +86,21 @@ public void Should_Return_CodeLensResolve_Descriptor() { // Given var handlerMatcher = AutoSubstitute.Resolve(); - var resolveHandler = Substitute.For(); - var resolveHandler2 = Substitute.For(); - resolveHandler.CanResolve(Arg.Any()).Returns(false); - resolveHandler2.CanResolve(Arg.Any()).Returns(true); + var resolveHandler = Substitute.For>(); + var resolveHandler2 = Substitute.For>(); + 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}}) + var result = handlerMatcher.FindHandler(new CodeLens() { + Data = new ResolvedData() { handler = TrueId, Data = new Dictionary() { ["a"] = 1 }} }, new List { new LspHandlerDescriptor(TextDocumentNames.CodeLensResolve, "Key", resolveHandler, resolveHandler.GetType(), - typeof(CodeLens), + typeof(CodeLens), null, null, () => false, @@ -108,8 +110,8 @@ public void Should_Return_CodeLensResolve_Descriptor() new LspHandlerDescriptor(TextDocumentNames.CodeLensResolve, "Key2", resolveHandler2, - typeof(ICodeLensResolveHandler), - typeof(CodeLens), + typeof(ICodeLensResolveHandler), + typeof(CodeLens), null, null, () => false, @@ -129,49 +131,17 @@ public void Should_Handle_Null_Data() { // Given var handlerMatcher = AutoSubstitute.Resolve(); - var resolveHandler = Substitute.For(); - resolveHandler.CanResolve(Arg.Any()).Returns(true); + var resolveHandler = Substitute.For>(); + resolveHandler.Id.Returns(TrueId); // When - var result = handlerMatcher.FindHandler(new CompletionItem() { }, + var result = handlerMatcher.FindHandler(new CompletionItem() { }, new List { new LspHandlerDescriptor(TextDocumentNames.CompletionResolve, "Key", resolveHandler, resolveHandler.GetType(), - typeof(CompletionItem), - null, - null, - () => false, - null, - null, - () => { }), - }) - .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), + typeof(CompletionItem), null, null, () => false, @@ -191,21 +161,21 @@ public void Should_Return_CompletionResolve_Descriptor() { // Given var handlerMatcher = AutoSubstitute.Resolve(); - var resolveHandler = Substitute.For(); - var resolveHandler2 = Substitute.For(); - resolveHandler.CanResolve(Arg.Any()).Returns(false); - resolveHandler2.CanResolve(Arg.Any()).Returns(true); + var resolveHandler = Substitute.For>(); + var resolveHandler2 = Substitute.For>(); + 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}}) + var result = handlerMatcher.FindHandler(new CompletionItem() { + Data = new ResolvedData() { handler = TrueId, Data = new Dictionary() { ["a"] = 1 }} }, new List { new LspHandlerDescriptor(TextDocumentNames.CompletionResolve, "Key", resolveHandler, resolveHandler.GetType(), - typeof(CompletionItem), + typeof(CompletionItem), null, null, () => false, @@ -215,60 +185,8 @@ public void Should_Return_CompletionResolve_Descriptor() new LspHandlerDescriptor(TextDocumentNames.CompletionResolve, "Key2", resolveHandler2, - typeof(ICompletionResolveHandler), - typeof(CompletionItem), - null, - null, - () => false, - null, - null, - () => { }), - }) - .ToArray(); - - // Then - result.Should().NotBeNullOrEmpty(); - 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, - () => { }), - new LspHandlerDescriptor(TextDocumentNames.CompletionResolve, - "Key2", - resolveHandler2 as IJsonRpcHandler, - typeof(ICompletionResolveHandler), - typeof(CompletionItem), + typeof(ICompletionResolveHandler), + typeof(CompletionItem), null, null, () => false, @@ -283,96 +201,47 @@ public void Should_Deal_WithHandlers_That_Not_Also_Resolvers() 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, - () => { }), - new LspHandlerDescriptor(TextDocumentNames.CompletionResolve, - "Key2", - resolveHandler2 as IJsonRpcHandler, - typeof(ICompletionResolveHandler), - typeof(CompletionItem), - null, - null, - () => false, - null, - null, - () => { }), - }) - .ToArray(); - - // Then - result.Should().NotBeNullOrEmpty(); - result.OfType().Should().Contain(x => x.Handler == resolveHandler); - } - [Fact] public async Task Should_Update_CompletionItems_With_HandlerType() { // Given var resolveHandler = Substitute.For(new Type[] { - typeof(ICompletionHandler), - typeof(ICompletionResolveHandler) + 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", resolveHandler as IJsonRpcHandler, resolveHandler.GetType(), - typeof(CompletionParams), + typeof(CompletionParams), null, null, () => false, null, null, () => { }); - var handlerMatcher = new ResolveCommandPipeline( + var handlerMatcher = new ResolveCommandPipeline, CompletionList>( new RequestContext() {Descriptor = descriptor}, - Substitute.For>>()); + Substitute.For, CompletionList>>>()); - var item = new CompletionItem() { - Data = JObject.FromObject(new {hello = "world"}) + var item = new CompletionItem() { + Data = new ResolvedData() { Data = new Dictionary() { ["hello"] = "world"}} }; var list = new CompletionList(new[] {item}); - (list is IEnumerable).Should().BeTrue(); + (list is IEnumerable>).Should().BeTrue(); // When - var response = await handlerMatcher.Handle(new CompletionParams(), CancellationToken.None, () => Task.FromResult(list)); + var response = await handlerMatcher.Handle(new CompletionParams(), CancellationToken.None, () => Task.FromResult(list)); // Then 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.handler.Should().Be(TrueId); + responseItem.Data.Data["hello"].Value().Should().Be("world"); } [Fact] @@ -380,43 +249,42 @@ public async Task Should_Update_CodeLensContainer_With_HandlerType() { // Given var resolveHandler = Substitute.For(new Type[] { - typeof(ICodeLensHandler), - typeof(ICodeLensResolveHandler) + 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", resolveHandler as IJsonRpcHandler, resolveHandler.GetType(), - typeof(CodeLensParams), + typeof(CodeLensParams), null, null, () => false, null, null, () => { }); - var handlerMatcher = new ResolveCommandPipeline( + var handlerMatcher = new ResolveCommandPipeline, CodeLensContainer>( new RequestContext() {Descriptor = descriptor}, - Substitute.For>>()); + Substitute.For, CodeLensContainer>>>()); - var item = new CodeLens() { - Data = JObject.FromObject(new {hello = "world"}) + var item = new CodeLens() { + Data = new ResolvedData() { Data = new Dictionary() { ["hello"] = "world"}} }; - var list = new CodeLensContainer(new[] {item}); + var list = new CodeLensContainer(new[] {item}); - (list is IEnumerable).Should().BeTrue(); + (list is IEnumerable>).Should().BeTrue(); // When - var response = await handlerMatcher.Handle(new CodeLensParams(), CancellationToken.None, () => Task.FromResult(list)); + var response = await handlerMatcher.Handle(new CodeLensParams(), CancellationToken.None, () => Task.FromResult(list)); // Then 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"); + (response as CodeLensContainer).Should().Contain(item); + var responseItem = (response as CodeLensContainer).First(); + responseItem.Data.handler.Should().Be(TrueId); + responseItem.Data.Data["hello"].Value().Should().Be("world"); } [Fact] @@ -424,38 +292,38 @@ public async Task Should_Update_CodeLens_Removing_HandlerType() { // Given var resolveHandler = Substitute.For(new Type[] { - typeof(ICodeLensHandler), - typeof(ICodeLensResolveHandler) + 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", resolveHandler as IJsonRpcHandler, resolveHandler.GetType(), - typeof(CodeLens), + typeof(CodeLens), null, null, () => false, null, null, () => { }); - var handlerMatcher = new ResolveCommandPipeline( + var handlerMatcher = new ResolveCommandPipeline, CodeLens>( new RequestContext() {Descriptor = descriptor}, - Substitute.For>>()); + Substitute.For, CodeLens>>>()); - var item = new CodeLens() { - Data = JObject.FromObject(new {data = new {hello = "world"}}) + var item = new CodeLens() { + Data = new ResolvedData() { Data = new Dictionary() { ["hello"] = "world"}} }; - item.Data[ResolveCommandMatcher.PrivateHandlerTypeName] = resolveHandler.GetType().FullName; + item.Data.handler = TrueId; // 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["hello"].Value().Should().Be("world"); + item.Data.Data["hello"].Value().Should().Be("world"); } } } diff --git a/test/Lsp.Tests/Matchers/TextDocumentMatcherTests.cs b/test/Lsp.Tests/Matchers/TextDocumentMatcherTests.cs index 8f765bfd6..a6d67ef87 100644 --- a/test/Lsp.Tests/Matchers/TextDocumentMatcherTests.cs +++ b/test/Lsp.Tests/Matchers/TextDocumentMatcherTests.cs @@ -194,14 +194,14 @@ public void Should_Return_Code_Lens_Descriptor() AutoSubstitute.Provide>(collection); var handlerMatcher = AutoSubstitute.Resolve(); - var codeLensHandler = Substitute.For(new Type[] { typeof(ICodeLensHandler), typeof(ICodeLensResolveHandler) }, new object[0]) as ICodeLensHandler; + var codeLensHandler = Substitute.For(new Type[] { typeof(ICodeLensHandler), typeof(ICodeLensResolveHandler) }, new object[0]) as ICodeLensHandler; codeLensHandler.GetRegistrationOptions() .Returns(new CodeLensRegistrationOptions() { DocumentSelector = new DocumentSelector(new DocumentFilter { Pattern = "**/*.cs" }) }); - var codeLensHandler2 = Substitute.For(new Type[] { typeof(ICodeLensHandler), typeof(ICodeLensResolveHandler) }, new object[0]) as ICodeLensHandler; + var codeLensHandler2 = Substitute.For(new Type[] { typeof(ICodeLensHandler), typeof(ICodeLensResolveHandler) }, new object[0]) as ICodeLensHandler; codeLensHandler2.GetRegistrationOptions() .Returns(new CodeLensRegistrationOptions() { @@ -210,7 +210,7 @@ public void Should_Return_Code_Lens_Descriptor() collection.Add(codeLensHandler, codeLensHandler2); // When - var result = handlerMatcher.FindHandler(new CodeLensParams() + var result = handlerMatcher.FindHandler(new CodeLensParams() { TextDocument = new VersionedTextDocumentIdentifier { Uri = new Uri("file:///abc/123/d.cs"), Version = 1 } }, diff --git a/test/Lsp.Tests/Models/CodeLensParamsTests.cs b/test/Lsp.Tests/Models/CodeLensParamsTests.cs index fe1588375..985779266 100644 --- a/test/Lsp.Tests/Models/CodeLensParamsTests.cs +++ b/test/Lsp.Tests/Models/CodeLensParamsTests.cs @@ -13,21 +13,21 @@ public class CodeLensParamsTests [Theory, JsonFixture] public void SimpleTest(string expected) { - var model = new CodeLensParams() { + var model = new CodeLensParams() { TextDocument = new TextDocumentIdentifier(new Uri("file:///abc/123/d.cs")), }; var result = Fixture.SerializeObject(model); result.Should().Be(expected); - var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject(expected); + var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject>(expected); deresult.Should().BeEquivalentTo(model); } [Theory, JsonFixture] public void NonStandardCharactersTest(string expected) { - var model = new CodeLensParams() { + var model = new CodeLensParams() { // UNC path with Chinese character for tree. TextDocument = new TextDocumentIdentifier(DocumentUri.FromFileSystemPath("\\\\abc\\123\\树.cs")), }; @@ -35,7 +35,7 @@ public void NonStandardCharactersTest(string expected) result.Should().Be(expected); - var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject(expected); + var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject>(expected); deresult.Should().BeEquivalentTo(model); } } diff --git a/test/Lsp.Tests/Models/CodeLensTests.cs b/test/Lsp.Tests/Models/CodeLensTests.cs index 844252cd0..581d9df65 100644 --- a/test/Lsp.Tests/Models/CodeLensTests.cs +++ b/test/Lsp.Tests/Models/CodeLensTests.cs @@ -14,16 +14,18 @@ public class CodeLensTests [Theory, JsonFixture] public void SimpleTest(string expected) { - var model = new CodeLens() { + var model = new CodeLens() { Command = new Command() { Arguments = new JArray { 1, "2", true }, Name = "abc", Title = "Cool story bro" }, - Data = JObject.FromObject(new Dictionary() - { - { "somethingCool" , 1 } - }), + Data = new ResolvedData { + Data = new Dictionary() + { + { "somethingCool" , 1 } + }, + }, Range = new Range(new Position(1, 2), new Position(2, 3)), }; var result = Fixture.SerializeObject(model); @@ -31,7 +33,7 @@ public void SimpleTest(string expected) result.Should().Be(expected); // TODO: Come back and fix this... - var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject(expected); + var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject>(expected); deresult.Should().BeEquivalentTo(model); } } diff --git a/test/Lsp.Tests/Models/CompletionItemTests.cs b/test/Lsp.Tests/Models/CompletionItemTests.cs index 7d56a8424..969862027 100644 --- a/test/Lsp.Tests/Models/CompletionItemTests.cs +++ b/test/Lsp.Tests/Models/CompletionItemTests.cs @@ -11,7 +11,7 @@ public class CompletionItemTests [Theory, JsonFixture] public void SimpleTest(string expected) { - var model = new CompletionItem() + var model = new CompletionItem() { Kind = CompletionItemKind.Text, CommitCharacters = new[] { ";", "/", "." }, @@ -25,7 +25,7 @@ public void SimpleTest(string expected) result.Should().Be(expected); - var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject(expected); + var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject>(expected); deresult.Should().BeEquivalentTo(model); } } diff --git a/test/Lsp.Tests/Models/CompletionListTests.cs b/test/Lsp.Tests/Models/CompletionListTests.cs index 26646d794..0de0bd6f1 100644 --- a/test/Lsp.Tests/Models/CompletionListTests.cs +++ b/test/Lsp.Tests/Models/CompletionListTests.cs @@ -12,7 +12,7 @@ public class CompletionListTests [Theory, JsonFixture] public void SimpleTest(string expected) { - var model = new CompletionList(new List() { new CompletionItem() + var model = new CompletionList(new List>() { new CompletionItem() { Kind = CompletionItemKind.Class, Detail = "details" @@ -28,8 +28,8 @@ public void SimpleTest(string expected) [Theory, JsonFixture] public void ComplexTest(string expected) { - var model = new CompletionList(new List() { - new CompletionItem() + var model = new CompletionList(new List>() { + new CompletionItem() { Kind = CompletionItemKind.Class, Detail = "details" diff --git a/test/Lsp.Tests/Models/DocumentLinkParamsTests.cs b/test/Lsp.Tests/Models/DocumentLinkParamsTests.cs index aac1dd948..95ca85260 100644 --- a/test/Lsp.Tests/Models/DocumentLinkParamsTests.cs +++ b/test/Lsp.Tests/Models/DocumentLinkParamsTests.cs @@ -12,14 +12,14 @@ public class DocumentLinkParamsTests [Theory, JsonFixture] public void SimpleTest(string expected) { - var model = new DocumentLinkParams() { + var model = new DocumentLinkParams() { TextDocument = new TextDocumentIdentifier(new Uri("file:///abc/123.cs")) }; var result = Fixture.SerializeObject(model); result.Should().Be(expected); - var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject(expected); + var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject>(expected); deresult.Should().BeEquivalentTo(model); } } diff --git a/test/Lsp.Tests/Models/DocumentLinkTests.cs b/test/Lsp.Tests/Models/DocumentLinkTests.cs index 09dd8f83c..fd27c457c 100644 --- a/test/Lsp.Tests/Models/DocumentLinkTests.cs +++ b/test/Lsp.Tests/Models/DocumentLinkTests.cs @@ -13,7 +13,7 @@ public class DocumentLinkTests [Theory, JsonFixture] public void SimpleTest(string expected) { - var model = new DocumentLink() { + var model = new DocumentLink() { Range = new Range(new Position(1, 2), new Position(3, 4)), Target = new Uri("file:///abc/123.cs") }; @@ -21,7 +21,7 @@ public void SimpleTest(string expected) result.Should().Be(expected); - var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject(expected); + var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject>(expected); deresult.Should().BeEquivalentTo(model); } } diff --git a/test/Lsp.Tests/RequestProcessIdentifierTests.cs b/test/Lsp.Tests/RequestProcessIdentifierTests.cs index 14dc4121d..91bab7cf2 100644 --- a/test/Lsp.Tests/RequestProcessIdentifierTests.cs +++ b/test/Lsp.Tests/RequestProcessIdentifierTests.cs @@ -4,6 +4,7 @@ using OmniSharp.Extensions.JsonRpc; using OmniSharp.Extensions.LanguageServer.Protocol.Document; using OmniSharp.Extensions.LanguageServer.Protocol.General; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; using OmniSharp.Extensions.LanguageServer.Protocol.Workspace; using OmniSharp.Extensions.LanguageServer.Shared; using Xunit; @@ -33,13 +34,13 @@ public void ShouldIdentifyAs_Default() [Theory] [InlineData(typeof(ICodeActionHandler))] - [InlineData(typeof(ICodeLensHandler))] - [InlineData(typeof(ICodeLensResolveHandler))] + [InlineData(typeof(ICodeLensHandler))] + [InlineData(typeof(ICodeLensResolveHandler))] [InlineData(typeof(IDefinitionHandler))] [InlineData(typeof(IDidCloseTextDocumentHandler))] [InlineData(typeof(IDocumentHighlightHandler))] - [InlineData(typeof(IDocumentLinkHandler))] - [InlineData(typeof(IDocumentLinkResolveHandler))] + [InlineData(typeof(IDocumentLinkHandler))] + [InlineData(typeof(IDocumentLinkResolveHandler))] [InlineData(typeof(IDocumentSymbolHandler))] [InlineData(typeof(IWorkspaceSymbolsHandler))] [InlineData(typeof(IWillSaveTextDocumentHandler))] diff --git a/test/Lsp.Tests/TextDocumentSyncHandlerExtensions.cs b/test/Lsp.Tests/TextDocumentSyncHandlerExtensions.cs index d1e4acf4e..989ac15cd 100644 --- a/test/Lsp.Tests/TextDocumentSyncHandlerExtensions.cs +++ b/test/Lsp.Tests/TextDocumentSyncHandlerExtensions.cs @@ -19,11 +19,11 @@ 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((info) => new [] { new TextDocumentAttributes(info.Arg(), language) }); handler .GetTextDocumentAttributes(Arg.Is(x => documentSelector.IsMatch(new TextDocumentAttributes(x, language)))) - .Returns(c => new TextDocumentAttributes(c.Arg(), language)); + .Returns(c => new [] { new TextDocumentAttributes(c.Arg(), language) }); return handler; }