Skip to content

Added support for custom handlers that can replace / work side-by-side with existing handlers #309

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/Client/LanguageClientRegistrationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,16 @@ public void RegisterCapabilities(ServerCapabilities serverCapabilities)
serverCapabilities
))
{
var descriptor = LspHandlerTypeDescriptorHelper.GetHandlerTypeForRegistrationOptions(registrationOptions);
if (descriptor == null)
var method = LspHandlerTypeDescriptorHelper.GetMethodForRegistrationOptions(registrationOptions);
if (method == null)
{
_logger.LogWarning("Unable to find handler type descriptor for the given {@RegistrationOptions}", registrationOptions);
_logger.LogWarning("Unable to find method for given {@RegistrationOptions}", registrationOptions);
continue;
}

var reg = new Registration {
Id = registrationOptions.Id,
Method = descriptor.Method,
Method = method,
RegisterOptions = registrationOptions
};
_registrations.AddOrUpdate(registrationOptions.Id, x => reg, (a, b) => reg);
Expand All @@ -91,16 +91,16 @@ public void RegisterCapabilities(ServerCapabilities serverCapabilities)
.Workspace
))
{
var descriptor = LspHandlerTypeDescriptorHelper.GetHandlerTypeForRegistrationOptions(registrationOptions);
if (descriptor == null)
var method = LspHandlerTypeDescriptorHelper.GetMethodForRegistrationOptions(registrationOptions);
if (method == null)
{
// TODO: Log this
continue;
}

var reg = new Registration {
Id = registrationOptions.Id,
Method = descriptor.Method,
Method = method,
RegisterOptions = registrationOptions
};
_registrations.AddOrUpdate(registrationOptions.Id, x => reg, (a, b) => reg);
Expand All @@ -117,8 +117,8 @@ private void Register(params Registration[] registrations)

private void Register(Registration registration)
{
var typeDescriptor = LspHandlerTypeDescriptorHelper.GetHandlerTypeDescriptor(registration.Method);
if (typeDescriptor == null)
var registrationType = LspHandlerTypeDescriptorHelper.GetRegistrationType(registration.Method);
if (registrationType == null)
{
_registrations.AddOrUpdate(registration.Id, x => registration, (a, b) => registration);
return;
Expand All @@ -128,7 +128,7 @@ private void Register(Registration registration)
Id = registration.Id,
Method = registration.Method,
RegisterOptions = registration.RegisterOptions is JToken token
? token.ToObject(typeDescriptor.RegistrationType, _serializer.JsonSerializer)
? token.ToObject(registrationType, _serializer.JsonSerializer)
: registration.RegisterOptions
};
_registrations.AddOrUpdate(deserializedRegistration.Id, x => deserializedRegistration, (a, b) => deserializedRegistration);
Expand Down
2 changes: 1 addition & 1 deletion src/Dap.Protocol/Requests/IAttachHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public abstract class AttachHandlerBase<T> : IAttachHandler<T> where T : AttachR
public abstract Task<AttachResponse> Handle(T request, CancellationToken cancellationToken);
}

public abstract class AttachHandler : AttachHandlerBase<AttachRequestArguments>
public abstract class AttachHandler : AttachHandlerBase<AttachRequestArguments>, IAttachHandler
{
}
}
2 changes: 1 addition & 1 deletion src/Dap.Protocol/Requests/ILaunchHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public abstract class LaunchHandlerBase<T> : ILaunchHandler<T> where T : LaunchR
public abstract Task<LaunchResponse> Handle(T request, CancellationToken cancellationToken);
}

public abstract class LaunchHandler : LaunchHandlerBase<LaunchRequestArguments>
public abstract class LaunchHandler : LaunchHandlerBase<LaunchRequestArguments>, ILaunchHandler
{
}
}
2 changes: 1 addition & 1 deletion src/Dap.Protocol/Serialization/OptionalAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace OmniSharp.Extensions.DebugAdapter.Protocol.Serialization
{
[AttributeUsage(AttributeTargets.Property)]
internal class OptionalAttribute : Attribute
public class OptionalAttribute : Attribute
{
}
}
2 changes: 1 addition & 1 deletion src/Dap.Shared/DebugAdapterHandlerCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ private CompositeDisposable AddHandler(IJsonRpcHandler handler, JsonRpcHandlerOp

private HandlerDescriptor GetDescriptor(string method, Type handlerType, IJsonRpcHandler handler, JsonRpcHandlerOptions options)
{
var typeDescriptor = HandlerTypeDescriptorHelper.GetHandlerTypeDescriptor(method);
var typeDescriptor = HandlerTypeDescriptorHelper.GetHandlerTypeDescriptor(handlerType);
var @interface = HandlerTypeDescriptorHelper.GetHandlerInterface(handlerType);

return GetDescriptor(method, handlerType, handler, options, typeDescriptor, @interface);
Expand Down
45 changes: 40 additions & 5 deletions src/JsonRpc/HandlerTypeDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,22 @@

namespace OmniSharp.Extensions.JsonRpc
{
[DebuggerDisplay("{" + nameof(Method) + "}")]
internal class HandlerTypeDescriptor : IHandlerTypeDescriptor
[DebuggerDisplay("{ToString()}")]
internal class HandlerTypeDescriptor : IHandlerTypeDescriptor, IEquatable<HandlerTypeDescriptor>
{
public HandlerTypeDescriptor(Type handlerType)
{
var method = MethodAttribute.From(handlerType);
Method = method.Method;
Direction = method.Direction;
if (handlerType.IsGenericTypeDefinition)
if (handlerType.IsGenericTypeDefinition && handlerType.IsPublic)
{
handlerType = handlerType.MakeGenericType(handlerType.GetTypeInfo().GenericTypeParameters[0].GetGenericParameterConstraints()[0]);
var parameter = handlerType.GetTypeInfo().GenericTypeParameters[0];
var constraints = parameter.GetGenericParameterConstraints();
if (constraints.Length == 1)
{
handlerType = handlerType.MakeGenericType(handlerType.GetTypeInfo().GenericTypeParameters[0].GetGenericParameterConstraints()[0]);
}
}

HandlerType = handlerType;
Expand Down Expand Up @@ -74,6 +79,36 @@ public HandlerTypeDescriptor(Type handlerType)
public Type ParamsType { get; }
public bool HasResponseType { get; }
public Type ResponseType { get; }
public override string ToString() => $"{Method}";
public override string ToString() => $"{Method}:{HandlerType.FullName}";

public bool Equals(HandlerTypeDescriptor other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Method == other.Method && HandlerType.Equals(other.HandlerType) && InterfaceType.Equals(other.InterfaceType);
}

public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((HandlerTypeDescriptor) obj);
}

public override int GetHashCode()
{
unchecked
{
var hashCode = Method.GetHashCode();
hashCode = ( hashCode * 397 ) ^ HandlerType.GetHashCode();
hashCode = ( hashCode * 397 ) ^ InterfaceType.GetHashCode();
return hashCode;
}
}

public static bool operator ==(HandlerTypeDescriptor left, HandlerTypeDescriptor right) => Equals(left, right);

public static bool operator !=(HandlerTypeDescriptor left, HandlerTypeDescriptor right) => !Equals(left, right);
}
}
33 changes: 13 additions & 20 deletions src/JsonRpc/HandlerTypeDescriptorHelper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
Expand All @@ -12,7 +13,7 @@ public static class HandlerTypeDescriptorHelper
private static readonly ConcurrentDictionary<Type, string> MethodNames =
new ConcurrentDictionary<Type, string>();

internal static readonly ImmutableSortedDictionary<string, IHandlerTypeDescriptor> KnownHandlers;
internal static readonly ILookup<string, IHandlerTypeDescriptor> KnownHandlers;

static HandlerTypeDescriptorHelper()
{
Expand All @@ -31,43 +32,38 @@ static HandlerTypeDescriptorHelper()
}
}
)
.Where(z => z.IsInterface && typeof(IJsonRpcHandler).IsAssignableFrom(z))
.Where(z => (z.IsInterface || (z.IsClass && !z.IsAbstract)) && typeof(IJsonRpcHandler).IsAssignableFrom(z))
.Where(z => MethodAttribute.From(z) != null)
.Where(z => !z.Name.EndsWith("Manager")) // Manager interfaces are generally specializations around the handlers
.Select(GetMethodType)
.Distinct()
.ToLookup(x => MethodAttribute.From(x).Method)
.Select(x => new HandlerTypeDescriptor(x.First()) as IHandlerTypeDescriptor)
.ToImmutableSortedDictionary(x => x.Method, x => x, StringComparer.Ordinal);
.SelectMany(x => x.Select(z => new HandlerTypeDescriptor(z) as IHandlerTypeDescriptor))
.ToLookup(x => x.Method, StringComparer.Ordinal);
}
catch (Exception e)
{
throw new AggregateException("Failed", e);
}
}

public static IHandlerTypeDescriptor GetHandlerTypeDescriptor(string method) => KnownHandlers.TryGetValue(method, out var descriptor) ? descriptor : null;

public static IHandlerTypeDescriptor GetHandlerTypeDescriptor<T>() =>
KnownHandlers.Values.FirstOrDefault(x => x.InterfaceType == typeof(T)) ??
GetHandlerTypeDescriptor(GetMethodName(typeof(T)));
public static IHandlerTypeDescriptor GetHandlerTypeDescriptor<T>() => GetHandlerTypeDescriptor(typeof(T));

public static IHandlerTypeDescriptor GetHandlerTypeDescriptor(Type type)
{
var @default = KnownHandlers.Values.FirstOrDefault(x => x.InterfaceType == type);
var @default = KnownHandlers
.SelectMany(g => g)
.FirstOrDefault(x => x.InterfaceType == type || x.HandlerType == type || x.ParamsType == type);
if (@default != null)
{
return @default;
}

var methodName = GetMethodName(type);
if (string.IsNullOrWhiteSpace(methodName)) return null;
return GetHandlerTypeDescriptor(methodName);
return string.IsNullOrWhiteSpace(methodName) ? null : KnownHandlers[methodName].FirstOrDefault();
}

public static string GetMethodName<T>()
where T : IJsonRpcHandler =>
GetMethodName(typeof(T));
public static string GetMethodName<T>() where T : IJsonRpcHandler => GetMethodName(typeof(T));

public static bool IsMethodName(string name, params Type[] types) => types.Any(z => GetMethodName(z).Equals(name));

Expand All @@ -78,16 +74,13 @@ public static string GetMethodName(Type type)
// Custom method
var attribute = MethodAttribute.From(type);

var handler = KnownHandlers.Values.FirstOrDefault(
z =>
z.InterfaceType == type || z.HandlerType == type || z.ParamsType == type
);
var handler = KnownHandlers.SelectMany(z => z)
.FirstOrDefault(z => z.InterfaceType == type || z.HandlerType == type || z.ParamsType == type);
if (handler != null)
{
return handler.Method;
}


// TODO: Log unknown method name
if (attribute is null)
{
Expand Down
Loading