Skip to content

Commit 6703457

Browse files
Added support for custom handlers that can replace / work side-by-side with existing handlers (#309)
* Added support for class based handlers to HandlerTypeDescriptorHelper/LspHandlerTypeDescriptorHelper. Added unit test demonstrating a custom params / handler pair for Execute Command Made the OptionalAttribute's public Added IExecuteCommandParams to allow for custom command execution handlers to work correctly. * fix project file
1 parent 4d53a6e commit 6703457

17 files changed

+301
-141
lines changed

src/Client/LanguageClientRegistrationManager.cs

+10-10
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,16 @@ public void RegisterCapabilities(ServerCapabilities serverCapabilities)
6565
serverCapabilities
6666
))
6767
{
68-
var descriptor = LspHandlerTypeDescriptorHelper.GetHandlerTypeForRegistrationOptions(registrationOptions);
69-
if (descriptor == null)
68+
var method = LspHandlerTypeDescriptorHelper.GetMethodForRegistrationOptions(registrationOptions);
69+
if (method == null)
7070
{
71-
_logger.LogWarning("Unable to find handler type descriptor for the given {@RegistrationOptions}", registrationOptions);
71+
_logger.LogWarning("Unable to find method for given {@RegistrationOptions}", registrationOptions);
7272
continue;
7373
}
7474

7575
var reg = new Registration {
7676
Id = registrationOptions.Id,
77-
Method = descriptor.Method,
77+
Method = method,
7878
RegisterOptions = registrationOptions
7979
};
8080
_registrations.AddOrUpdate(registrationOptions.Id, x => reg, (a, b) => reg);
@@ -91,16 +91,16 @@ public void RegisterCapabilities(ServerCapabilities serverCapabilities)
9191
.Workspace
9292
))
9393
{
94-
var descriptor = LspHandlerTypeDescriptorHelper.GetHandlerTypeForRegistrationOptions(registrationOptions);
95-
if (descriptor == null)
94+
var method = LspHandlerTypeDescriptorHelper.GetMethodForRegistrationOptions(registrationOptions);
95+
if (method == null)
9696
{
9797
// TODO: Log this
9898
continue;
9999
}
100100

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

118118
private void Register(Registration registration)
119119
{
120-
var typeDescriptor = LspHandlerTypeDescriptorHelper.GetHandlerTypeDescriptor(registration.Method);
121-
if (typeDescriptor == null)
120+
var registrationType = LspHandlerTypeDescriptorHelper.GetRegistrationType(registration.Method);
121+
if (registrationType == null)
122122
{
123123
_registrations.AddOrUpdate(registration.Id, x => registration, (a, b) => registration);
124124
return;
@@ -128,7 +128,7 @@ private void Register(Registration registration)
128128
Id = registration.Id,
129129
Method = registration.Method,
130130
RegisterOptions = registration.RegisterOptions is JToken token
131-
? token.ToObject(typeDescriptor.RegistrationType, _serializer.JsonSerializer)
131+
? token.ToObject(registrationType, _serializer.JsonSerializer)
132132
: registration.RegisterOptions
133133
};
134134
_registrations.AddOrUpdate(deserializedRegistration.Id, x => deserializedRegistration, (a, b) => deserializedRegistration);

src/Dap.Protocol/Requests/IAttachHandler.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public abstract class AttachHandlerBase<T> : IAttachHandler<T> where T : AttachR
2222
public abstract Task<AttachResponse> Handle(T request, CancellationToken cancellationToken);
2323
}
2424

25-
public abstract class AttachHandler : AttachHandlerBase<AttachRequestArguments>
25+
public abstract class AttachHandler : AttachHandlerBase<AttachRequestArguments>, IAttachHandler
2626
{
2727
}
2828
}

src/Dap.Protocol/Requests/ILaunchHandler.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public abstract class LaunchHandlerBase<T> : ILaunchHandler<T> where T : LaunchR
2222
public abstract Task<LaunchResponse> Handle(T request, CancellationToken cancellationToken);
2323
}
2424

25-
public abstract class LaunchHandler : LaunchHandlerBase<LaunchRequestArguments>
25+
public abstract class LaunchHandler : LaunchHandlerBase<LaunchRequestArguments>, ILaunchHandler
2626
{
2727
}
2828
}

src/Dap.Protocol/Serialization/OptionalAttribute.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace OmniSharp.Extensions.DebugAdapter.Protocol.Serialization
44
{
55
[AttributeUsage(AttributeTargets.Property)]
6-
internal class OptionalAttribute : Attribute
6+
public class OptionalAttribute : Attribute
77
{
88
}
99
}

src/Dap.Shared/DebugAdapterHandlerCollection.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ private CompositeDisposable AddHandler(IJsonRpcHandler handler, JsonRpcHandlerOp
122122

123123
private HandlerDescriptor GetDescriptor(string method, Type handlerType, IJsonRpcHandler handler, JsonRpcHandlerOptions options)
124124
{
125-
var typeDescriptor = HandlerTypeDescriptorHelper.GetHandlerTypeDescriptor(method);
125+
var typeDescriptor = HandlerTypeDescriptorHelper.GetHandlerTypeDescriptor(handlerType);
126126
var @interface = HandlerTypeDescriptorHelper.GetHandlerInterface(handlerType);
127127

128128
return GetDescriptor(method, handlerType, handler, options, typeDescriptor, @interface);

src/JsonRpc/HandlerTypeDescriptor.cs

+40-5
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,22 @@
66

77
namespace OmniSharp.Extensions.JsonRpc
88
{
9-
[DebuggerDisplay("{" + nameof(Method) + "}")]
10-
internal class HandlerTypeDescriptor : IHandlerTypeDescriptor
9+
[DebuggerDisplay("{ToString()}")]
10+
internal class HandlerTypeDescriptor : IHandlerTypeDescriptor, IEquatable<HandlerTypeDescriptor>
1111
{
1212
public HandlerTypeDescriptor(Type handlerType)
1313
{
1414
var method = MethodAttribute.From(handlerType);
1515
Method = method.Method;
1616
Direction = method.Direction;
17-
if (handlerType.IsGenericTypeDefinition)
17+
if (handlerType.IsGenericTypeDefinition && handlerType.IsPublic)
1818
{
19-
handlerType = handlerType.MakeGenericType(handlerType.GetTypeInfo().GenericTypeParameters[0].GetGenericParameterConstraints()[0]);
19+
var parameter = handlerType.GetTypeInfo().GenericTypeParameters[0];
20+
var constraints = parameter.GetGenericParameterConstraints();
21+
if (constraints.Length == 1)
22+
{
23+
handlerType = handlerType.MakeGenericType(handlerType.GetTypeInfo().GenericTypeParameters[0].GetGenericParameterConstraints()[0]);
24+
}
2025
}
2126

2227
HandlerType = handlerType;
@@ -74,6 +79,36 @@ public HandlerTypeDescriptor(Type handlerType)
7479
public Type ParamsType { get; }
7580
public bool HasResponseType { get; }
7681
public Type ResponseType { get; }
77-
public override string ToString() => $"{Method}";
82+
public override string ToString() => $"{Method}:{HandlerType.FullName}";
83+
84+
public bool Equals(HandlerTypeDescriptor other)
85+
{
86+
if (ReferenceEquals(null, other)) return false;
87+
if (ReferenceEquals(this, other)) return true;
88+
return Method == other.Method && HandlerType.Equals(other.HandlerType) && InterfaceType.Equals(other.InterfaceType);
89+
}
90+
91+
public override bool Equals(object obj)
92+
{
93+
if (ReferenceEquals(null, obj)) return false;
94+
if (ReferenceEquals(this, obj)) return true;
95+
if (obj.GetType() != this.GetType()) return false;
96+
return Equals((HandlerTypeDescriptor) obj);
97+
}
98+
99+
public override int GetHashCode()
100+
{
101+
unchecked
102+
{
103+
var hashCode = Method.GetHashCode();
104+
hashCode = ( hashCode * 397 ) ^ HandlerType.GetHashCode();
105+
hashCode = ( hashCode * 397 ) ^ InterfaceType.GetHashCode();
106+
return hashCode;
107+
}
108+
}
109+
110+
public static bool operator ==(HandlerTypeDescriptor left, HandlerTypeDescriptor right) => Equals(left, right);
111+
112+
public static bool operator !=(HandlerTypeDescriptor left, HandlerTypeDescriptor right) => !Equals(left, right);
78113
}
79114
}

src/JsonRpc/HandlerTypeDescriptorHelper.cs

+13-20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Concurrent;
3+
using System.Collections.Generic;
34
using System.Collections.Immutable;
45
using System.Linq;
56
using System.Reflection;
@@ -12,7 +13,7 @@ public static class HandlerTypeDescriptorHelper
1213
private static readonly ConcurrentDictionary<Type, string> MethodNames =
1314
new ConcurrentDictionary<Type, string>();
1415

15-
internal static readonly ImmutableSortedDictionary<string, IHandlerTypeDescriptor> KnownHandlers;
16+
internal static readonly ILookup<string, IHandlerTypeDescriptor> KnownHandlers;
1617

1718
static HandlerTypeDescriptorHelper()
1819
{
@@ -31,43 +32,38 @@ static HandlerTypeDescriptorHelper()
3132
}
3233
}
3334
)
34-
.Where(z => z.IsInterface && typeof(IJsonRpcHandler).IsAssignableFrom(z))
35+
.Where(z => (z.IsInterface || (z.IsClass && !z.IsAbstract)) && typeof(IJsonRpcHandler).IsAssignableFrom(z))
3536
.Where(z => MethodAttribute.From(z) != null)
3637
.Where(z => !z.Name.EndsWith("Manager")) // Manager interfaces are generally specializations around the handlers
3738
.Select(GetMethodType)
3839
.Distinct()
3940
.ToLookup(x => MethodAttribute.From(x).Method)
40-
.Select(x => new HandlerTypeDescriptor(x.First()) as IHandlerTypeDescriptor)
41-
.ToImmutableSortedDictionary(x => x.Method, x => x, StringComparer.Ordinal);
41+
.SelectMany(x => x.Select(z => new HandlerTypeDescriptor(z) as IHandlerTypeDescriptor))
42+
.ToLookup(x => x.Method, StringComparer.Ordinal);
4243
}
4344
catch (Exception e)
4445
{
4546
throw new AggregateException("Failed", e);
4647
}
4748
}
4849

49-
public static IHandlerTypeDescriptor GetHandlerTypeDescriptor(string method) => KnownHandlers.TryGetValue(method, out var descriptor) ? descriptor : null;
50-
51-
public static IHandlerTypeDescriptor GetHandlerTypeDescriptor<T>() =>
52-
KnownHandlers.Values.FirstOrDefault(x => x.InterfaceType == typeof(T)) ??
53-
GetHandlerTypeDescriptor(GetMethodName(typeof(T)));
50+
public static IHandlerTypeDescriptor GetHandlerTypeDescriptor<T>() => GetHandlerTypeDescriptor(typeof(T));
5451

5552
public static IHandlerTypeDescriptor GetHandlerTypeDescriptor(Type type)
5653
{
57-
var @default = KnownHandlers.Values.FirstOrDefault(x => x.InterfaceType == type);
54+
var @default = KnownHandlers
55+
.SelectMany(g => g)
56+
.FirstOrDefault(x => x.InterfaceType == type || x.HandlerType == type || x.ParamsType == type);
5857
if (@default != null)
5958
{
6059
return @default;
6160
}
6261

6362
var methodName = GetMethodName(type);
64-
if (string.IsNullOrWhiteSpace(methodName)) return null;
65-
return GetHandlerTypeDescriptor(methodName);
63+
return string.IsNullOrWhiteSpace(methodName) ? null : KnownHandlers[methodName].FirstOrDefault();
6664
}
6765

68-
public static string GetMethodName<T>()
69-
where T : IJsonRpcHandler =>
70-
GetMethodName(typeof(T));
66+
public static string GetMethodName<T>() where T : IJsonRpcHandler => GetMethodName(typeof(T));
7167

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

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

81-
var handler = KnownHandlers.Values.FirstOrDefault(
82-
z =>
83-
z.InterfaceType == type || z.HandlerType == type || z.ParamsType == type
84-
);
77+
var handler = KnownHandlers.SelectMany(z => z)
78+
.FirstOrDefault(z => z.InterfaceType == type || z.HandlerType == type || z.ParamsType == type);
8579
if (handler != null)
8680
{
8781
return handler.Method;
8882
}
8983

90-
9184
// TODO: Log unknown method name
9285
if (attribute is null)
9386
{

0 commit comments

Comments
 (0)