Skip to content

Commit 69f9d6b

Browse files
authored
Merge pull request #53 from tintoy/feature/handler-payload-deserialization
Move payload deserialisation out of handlers
2 parents c601b27 + 768e63f commit 69f9d6b

18 files changed

+261
-41
lines changed

src/Client/Dispatcher/LspDispatcher.cs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Threading;
55
using System.Threading.Tasks;
66
using Newtonsoft.Json.Linq;
7+
using OmniSharp.Extensions.JsonRpc;
78
using OmniSharp.Extensions.LanguageServer.Client.Handlers;
89

910
namespace OmniSharp.Extensions.LanguageServer.Client.Dispatcher
@@ -21,10 +22,22 @@ public class LspDispatcher
2122
/// <summary>
2223
/// Create a new <see cref="LspDispatcher"/>.
2324
/// </summary>
24-
public LspDispatcher()
25+
/// <param name="serializer">
26+
/// The JSON serialiser for notification / request / response payloads.
27+
/// </param>
28+
public LspDispatcher(ISerializer serializer)
2529
{
30+
if (serializer == null)
31+
throw new ArgumentNullException(nameof(serializer));
32+
33+
Serializer = serializer;
2634
}
2735

36+
/// <summary>
37+
/// The JSON serialiser to use for notification / request / response payloads.
38+
/// </summary>
39+
public ISerializer Serializer { get; set; }
40+
2841
/// <summary>
2942
/// Register a handler invoker.
3043
/// </summary>
@@ -92,7 +105,9 @@ public async Task<bool> TryHandleNotification(string method, JObject notificatio
92105

93106
if (_handlers.TryGetValue(method, out IHandler handler) && handler is IInvokeNotificationHandler notificationHandler)
94107
{
95-
await notificationHandler.Invoke(notification);
108+
object notificationPayload = DeserializePayload(notificationHandler.PayloadType, notification);
109+
110+
await notificationHandler.Invoke(notificationPayload);
96111

97112
return true;
98113
}
@@ -121,9 +136,36 @@ public Task<object> TryHandleRequest(string method, JObject request, Cancellatio
121136
throw new ArgumentException($"Argument cannot be null, empty, or entirely composed of whitespace: {nameof(method)}.", nameof(method));
122137

123138
if (_handlers.TryGetValue(method, out IHandler handler) && handler is IInvokeRequestHandler requestHandler)
124-
return requestHandler.Invoke(request, cancellationToken);
139+
{
140+
object requestPayload = DeserializePayload(requestHandler.PayloadType, request);
141+
142+
return requestHandler.Invoke(requestPayload, cancellationToken);
143+
}
125144

126145
return null;
127146
}
147+
148+
/// <summary>
149+
/// Deserialise a notification / request payload from JSON.
150+
/// </summary>
151+
/// <param name="payloadType">
152+
/// The payload's CLR type.
153+
/// </param>
154+
/// <param name="payload">
155+
/// JSON representing the payload.
156+
/// </param>
157+
/// <returns>
158+
/// The deserialised payload (if one is present and expected).
159+
/// </returns>
160+
object DeserializePayload(Type payloadType, JObject payload)
161+
{
162+
if (payloadType == null)
163+
throw new ArgumentNullException(nameof(payloadType));
164+
165+
if (payloadType == null || payload == null)
166+
return null;
167+
168+
return payload.ToObject(payloadType, Serializer.JsonSerializer);
169+
}
128170
}
129171
}

src/Client/Handlers/DelegateEmptyNotificationHandler.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ public DelegateEmptyNotificationHandler(string method, NotificationHandler handl
3232
/// </summary>
3333
public NotificationHandler Handler { get; }
3434

35+
/// <summary>
36+
/// The expected CLR type of the notification payload (<c>null</c>, since the handler does not use the request payload).
37+
/// </summary>
38+
public override Type PayloadType => null;
39+
3540
/// <summary>
3641
/// Invoke the handler.
3742
/// </summary>

src/Client/Handlers/DelegateHandler.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,10 @@ protected DelegateHandler(string method)
2626
/// The name of the method handled by the handler.
2727
/// </summary>
2828
public string Method { get; }
29+
30+
/// <summary>
31+
/// The expected CLR type of the request / notification payload (if any; <c>null</c> if the handler does not use the request payload).
32+
/// </summary>
33+
public abstract Type PayloadType { get; }
2934
}
3035
}

src/Client/Handlers/DelegateNotificationHandler.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ public DelegateNotificationHandler(string method, NotificationHandler<TNotificat
3838
/// </summary>
3939
public NotificationHandler<TNotification> Handler { get; }
4040

41+
/// <summary>
42+
/// The expected CLR type of the notification payload.
43+
/// </summary>
44+
public override Type PayloadType => typeof(TNotification);
45+
4146
/// <summary>
4247
/// Invoke the handler.
4348
/// </summary>
@@ -47,12 +52,12 @@ public DelegateNotificationHandler(string method, NotificationHandler<TNotificat
4752
/// <returns>
4853
/// A <see cref="Task"/> representing the operation.
4954
/// </returns>
50-
public async Task Invoke(JObject notification)
55+
public async Task Invoke(object notification)
5156
{
5257
await Task.Yield();
5358

5459
Handler(
55-
notification != null ? notification.ToObject<TNotification>(Serializer.Instance.JsonSerializer /* Fix me: this is ugly */) : default(TNotification)
60+
(TNotification)notification
5661
);
5762
}
5863
}

src/Client/Handlers/DelegateRequestHandler.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ public DelegateRequestHandler(string method, RequestHandler<TRequest> handler)
3939
/// </summary>
4040
public RequestHandler<TRequest> Handler { get; }
4141

42+
/// <summary>
43+
/// The expected CLR type of the request payload.
44+
/// </summary>
45+
public override Type PayloadType => typeof(TRequest);
46+
4247
/// <summary>
4348
/// Invoke the handler.
4449
/// </summary>
@@ -51,10 +56,10 @@ public DelegateRequestHandler(string method, RequestHandler<TRequest> handler)
5156
/// <returns>
5257
/// A <see cref="Task{TResult}"/> representing the operation.
5358
/// </returns>
54-
public async Task<object> Invoke(JObject request, CancellationToken cancellationToken)
59+
public async Task<object> Invoke(object request, CancellationToken cancellationToken)
5560
{
5661
await Handler(
57-
request != null ? request.ToObject<TRequest>(Serializer.Instance.JsonSerializer /* Fix me: this is ugly */) : default(TRequest),
62+
(TRequest)request,
5863
cancellationToken
5964
);
6065

src/Client/Handlers/DelegateRequestResponseHandler.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ public DelegateRequestResponseHandler(string method, RequestHandler<TRequest, TR
4242
/// </summary>
4343
public RequestHandler<TRequest, TResponse> Handler { get; }
4444

45+
/// <summary>
46+
/// The expected CLR type of the request payload.
47+
/// </summary>
48+
public override Type PayloadType => typeof(TRequest);
49+
4550
/// <summary>
4651
/// Invoke the handler.
4752
/// </summary>
@@ -54,10 +59,10 @@ public DelegateRequestResponseHandler(string method, RequestHandler<TRequest, TR
5459
/// <returns>
5560
/// A <see cref="Task{TResult}"/> representing the operation.
5661
/// </returns>
57-
public async Task<object> Invoke(JObject request, CancellationToken cancellationToken)
62+
public async Task<object> Invoke(object request, CancellationToken cancellationToken)
5863
{
5964
return await Handler(
60-
request != null ? request.ToObject<TRequest>(Serializer.Instance.JsonSerializer /* Fix me: this is ugly */) : default(TRequest),
65+
(TRequest)request,
6166
cancellationToken
6267
);
6368
}

src/Client/Handlers/DynamicRegistrationHandler.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Threading;
23
using System.Threading.Tasks;
34
using Newtonsoft.Json.Linq;
@@ -31,6 +32,11 @@ public DynamicRegistrationHandler()
3132
/// </summary>
3233
public string Method => "client/registerCapability";
3334

35+
/// <summary>
36+
/// The expected CLR type of the request / notification payload (if any; <c>null</c> if the handler does not use the request payload).
37+
/// </summary>
38+
public Type PayloadType => null;
39+
3440
/// <summary>
3541
/// Invoke the handler.
3642
/// </summary>
@@ -43,7 +49,7 @@ public DynamicRegistrationHandler()
4349
/// <returns>
4450
/// A <see cref="Task{TResult}"/> representing the operation.
4551
/// </returns>
46-
public Task<object> Invoke(JObject request, CancellationToken cancellationToken)
52+
public Task<object> Invoke(object request, CancellationToken cancellationToken)
4753
{
4854
// For now, we don't really support dynamic registration but OmniSharp's implementation sends a request even when dynamic registrations are not supported.
4955

src/Client/Handlers/IHandler.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
namespace OmniSharp.Extensions.LanguageServer.Client.Handlers
1+
using System;
2+
3+
namespace OmniSharp.Extensions.LanguageServer.Client.Handlers
24
{
35
/// <summary>
46
/// Represents a client-side message handler.
@@ -9,5 +11,10 @@ public interface IHandler
911
/// The name of the method handled by the handler.
1012
/// </summary>
1113
string Method { get; }
14+
15+
/// <summary>
16+
/// The expected CLR type of the request / notification payload (if any; <c>null</c> if the handler does not use the request body).
17+
/// </summary>
18+
Type PayloadType { get; }
1219
}
1320
}

src/Client/Handlers/IInvokeNotificationHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ public interface IInvokeNotificationHandler
1818
/// <returns>
1919
/// A <see cref="Task"/> representing the operation.
2020
/// </returns>
21-
Task Invoke(JObject notification);
21+
Task Invoke(object notification);
2222
}
2323
}

src/Client/Handlers/IInvokeRequestHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ public interface IInvokeRequestHandler
2222
/// <returns>
2323
/// A <see cref="Task{TResult}"/> representing the operation.
2424
/// </returns>
25-
Task<object> Invoke(JObject request, CancellationToken cancellationToken);
25+
Task<object> Invoke(object request, CancellationToken cancellationToken);
2626
}
2727
}

0 commit comments

Comments
 (0)