Skip to content

Commit fa70801

Browse files
fixed semantic tokens (RAINBOWS BACK BABY!). Added ability to link rpc methods together, so that if you need to support cases where the method has changed, you can now.
1 parent cfc55d3 commit fa70801

26 files changed

+425
-518
lines changed

sample/SampleServer/Program.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
using System;
2+
using System.Diagnostics;
23
using System.Threading.Tasks;
34
using Microsoft.Extensions.Configuration;
45
using Microsoft.Extensions.DependencyInjection;
56
using Microsoft.Extensions.Logging;
67
using Newtonsoft.Json.Linq;
8+
using OmniSharp.Extensions.JsonRpc;
9+
using OmniSharp.Extensions.LanguageServer.Protocol;
710
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
11+
using OmniSharp.Extensions.LanguageServer.Protocol.Models.Proposals;
812
using OmniSharp.Extensions.LanguageServer.Server;
913
using Serilog;
1014

@@ -37,8 +41,10 @@ static async Task MainAsync(string[] args)
3741

3842
var server = await LanguageServer.From(options =>
3943
options
44+
.WithInput(Console.OpenStandardInput())
45+
.WithOutput(Console.OpenStandardOutput())
4046
.ConfigureLogging(x => x
41-
.AddSerilog()
47+
.AddSerilog(Log.Logger)
4248
.AddLanguageProtocolLogging()
4349
.SetMinimumLevel(LogLevel.Debug))
4450
.WithHandler<TextDocumentHandler>()
@@ -47,6 +53,9 @@ static async Task MainAsync(string[] args)
4753
.WithHandler<MyWorkspaceSymbolsHandler>()
4854
.WithHandler<MyDocumentSymbolHandler>()
4955
.WithHandler<SemanticTokensHandler>()
56+
// Linking here because the spec changed but vscode is behind
57+
.WithLink(TextDocumentNames.SemanticTokensFull, "textDocument/semanticTokens")
58+
.WithLink(TextDocumentNames.SemanticTokensFullDelta, "textDocument/semanticTokens/edits")
5059
.WithServices(x => x.AddLogging(b => b.SetMinimumLevel(LogLevel.Trace)))
5160
.WithServices(services => {
5261
services.AddSingleton(provider => {

src/Client/LanguageClient.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ internal LanguageClient(LanguageClientOptions options) : base(options)
200200
var serviceIdentifiers = _serviceProvider.GetServices<ITextDocumentIdentifier>().ToArray();
201201
_disposable.Add(_textDocumentIdentifiers.Add(serviceIdentifiers));
202202
_disposable.Add(_collection.Add(serviceHandlers));
203+
options.AddLinks(_collection);
203204
}
204205

205206
public ITextDocumentLanguageClient TextDocument { get; }

src/Client/RegistrationManager.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,23 @@ public RegistrationManager(ISerializer serializer, ILogger<RegistrationManager>
3636

3737
Task<Unit> IRequestHandler<RegistrationParams, Unit>.Handle(RegistrationParams request, CancellationToken cancellationToken)
3838
{
39-
Register(request.Registrations.ToArray());
39+
lock (this)
40+
{
41+
Register(request.Registrations.ToArray());
42+
}
43+
4044
_registrationSubject.OnNext(_registrations.Values);
4145
return Unit.Task;
4246
}
4347

4448
Task<Unit> IRequestHandler<UnregistrationParams, Unit>.Handle(UnregistrationParams request, CancellationToken cancellationToken)
4549
{
46-
foreach (var item in request.Unregisterations ?? new UnregistrationContainer())
50+
lock (this)
4751
{
48-
_registrations.TryRemove(item.Id, out _);
52+
foreach (var item in request.Unregisterations ?? new UnregistrationContainer())
53+
{
54+
_registrations.TryRemove(item.Id, out _);
55+
}
4956
}
5057

5158
_registrationSubject.OnNext(_registrations.Values);

src/JsonRpc.Testing/AggregateSettler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public Task SettleNext()
1919
{
2020
return _settlers.ToObservable()
2121
.Select(z => z.Settle())
22-
.Switch()
22+
.Merge()
2323
.Take(1)
2424
//.Amb(Observable.Timer(_waitTime + _waitTime).Select(z => Unit.Value))
2525
.ToTask();

src/JsonRpc.Testing/Settler.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ public Settler(TimeSpan waitTime, TimeSpan timeout, CancellationToken cancellati
2727
acc += next;
2828
return acc;
2929
})
30-
.Select(z => z <= 0 ? Observable.Timer(waitTime).Select(_ => Unit.Default).Timeout(timeout, Observable.Return(Unit.Default)) : Observable.Never<Unit>())
31-
.Switch()
3230
.Replay(1)
33-
.RefCount();
31+
.RefCount()
32+
.Select(z => z <= 0 ? Observable.Timer(waitTime).Select(_ => Unit.Default).Timeout(timeout, Observable.Return(Unit.Default)) : Observable.Never<Unit>())
33+
.Switch();
3434
_requester = subject;
3535
}
3636

@@ -43,7 +43,7 @@ public Task SettleNext()
4343

4444
public IObservable<Unit> Settle()
4545
{
46-
return _settle;
46+
return _settle.Timeout(_timeout).Catch<Unit, Exception>(_ => Observable.Empty<Unit>());
4747
}
4848

4949
void IRequestSettler.OnStartRequest()

src/JsonRpc/CompositeHandlersManager.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public IDisposable Add(string method, IJsonRpcHandler handler, JsonRpcHandlerOpt
2727
return result;
2828
}
2929

30+
public IDisposable AddLink(string sourceMethod, string destinationMethod) => _parent.AddLink(sourceMethod, destinationMethod);
31+
3032
public CompositeDisposable GetDisposable() => _compositeDisposable;
3133
}
3234
}

src/JsonRpc/HandlerCollection.cs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99

1010
namespace OmniSharp.Extensions.JsonRpc
1111
{
12+
13+
1214
public class HandlerCollection : IEnumerable<IHandlerDescriptor>, IHandlersManager
1315
{
14-
internal readonly List<HandlerInstance> _handlers = new List<HandlerInstance>();
16+
internal readonly List<IHandlerDescriptor> _handlers = new List<IHandlerDescriptor>();
1517

1618
public HandlerCollection() { }
1719

@@ -70,6 +72,41 @@ public void Dispose()
7072
}
7173
}
7274

75+
[DebuggerDisplay("{Method}")]
76+
internal class LinkedHandler : IHandlerDescriptor, IDisposable
77+
{
78+
private readonly IHandlerDescriptor _descriptor;
79+
private readonly Action _disposeAction;
80+
81+
public LinkedHandler(string method, IHandlerDescriptor descriptor, Action disposeAction)
82+
{
83+
_descriptor = descriptor;
84+
_disposeAction = disposeAction;
85+
Method = method;
86+
}
87+
public string Method { get; }
88+
public Type HandlerType => _descriptor.HandlerType;
89+
90+
public Type ImplementationType => _descriptor.ImplementationType;
91+
92+
public Type Params => _descriptor.Params;
93+
94+
public Type Response => _descriptor.Response;
95+
96+
public bool HasReturnType => _descriptor.HasReturnType;
97+
98+
public bool IsDelegatingHandler => _descriptor.IsDelegatingHandler;
99+
100+
public IJsonRpcHandler Handler => _descriptor.Handler;
101+
102+
public RequestProcessType? RequestProcessType => _descriptor.RequestProcessType;
103+
104+
public void Dispose()
105+
{
106+
_disposeAction();
107+
}
108+
}
109+
73110
public IEnumerator<IHandlerDescriptor> GetEnumerator()
74111
{
75112
return _handlers.GetEnumerator();
@@ -82,8 +119,11 @@ IEnumerator IEnumerable.GetEnumerator()
82119

83120
private void Remove(IJsonRpcHandler handler)
84121
{
85-
var i = _handlers.Find(instance => instance.Handler == handler);
86-
if (i != null) _handlers.Remove(i);
122+
var handlers = _handlers.FindAll(instance => instance.Handler == handler);
123+
foreach (var item in handlers)
124+
{
125+
_handlers.Remove(item);
126+
}
87127
}
88128

89129
public IDisposable Add(params IJsonRpcHandler[] handlers)
@@ -132,6 +172,14 @@ public IDisposable Add(string method, IJsonRpcHandler handler, JsonRpcHandlerOpt
132172
return h;
133173
}
134174

175+
public IDisposable AddLink(string sourceMethod, string destinationMethod)
176+
{
177+
var source = _handlers.Find(z => z.Method == sourceMethod);
178+
var h = new LinkedHandler(destinationMethod, source, () => _handlers.RemoveAll(z => z.Method == destinationMethod));
179+
_handlers.Add(h);
180+
return h;
181+
}
182+
135183
private static readonly Type[] HandlerTypes = {
136184
typeof(IJsonRpcNotificationHandler),
137185
typeof(IJsonRpcNotificationHandler<>),

src/JsonRpc/IHandlersManager.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ public interface IHandlersManager
66
{
77
IDisposable Add(IJsonRpcHandler handler, JsonRpcHandlerOptions options);
88
IDisposable Add(string method, IJsonRpcHandler handler, JsonRpcHandlerOptions options);
9+
IDisposable AddLink(string sourceMethod, string destinationMethod);
910
}
1011
}

src/JsonRpc/JsonRpcOptionsRegistryBase.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
using System;
2+
using System.Collections.Generic;
23
using Microsoft.Extensions.DependencyInjection;
34

45
namespace OmniSharp.Extensions.JsonRpc
56
{
67
public abstract class JsonRpcOptionsRegistryBase<T> : JsonRpcCommonMethodsBase<T> where T : IJsonRpcHandlerRegistry<T>
78
{
89
public IServiceCollection Services { get; set; } = new ServiceCollection();
10+
protected internal HashSet<(string source, string destination)> Links= new HashSet<(string source, string destination)>();
11+
12+
public void AddLinks(IHandlersManager handlersManager)
13+
{
14+
foreach (var link in Links)
15+
{
16+
handlersManager.AddLink(link.source, link.destination);
17+
}
18+
}
919

1020
#region AddHandler
1121

src/JsonRpc/JsonRpcServer.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ internal JsonRpcServer(JsonRpcServerOptions options) : base(options)
9898
);
9999
_disposable.Add(_connection);
100100
_collection.Add(_serviceProvider.GetRequiredService<IEnumerable<IJsonRpcHandler>>().ToArray());
101+
options.AddLinks(_collection);
101102
}
102103

103104
private async Task Initialize(CancellationToken cancellationToken)

src/JsonRpc/JsonRpcServerOptionsBase.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,5 +139,11 @@ public T WithMaximumRequestTimeout(TimeSpan maximumRequestTimeout)
139139
MaximumRequestTimeout = maximumRequestTimeout;
140140
return (T)(object) this;
141141
}
142+
143+
public T WithLink(string source, string destination)
144+
{
145+
Links.Add((source, destination));
146+
return (T)(object) this;
147+
}
142148
}
143149
}

src/Protocol/AbstractHandlers.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ async Task<TResponse> IRequestHandler<TItem, TResponse>.Handle(TItem request,
6868
}
6969

7070
var subject = new AsyncSubject<TItem>();
71+
// in the event nothing is emitted...
72+
subject.OnNext(default);
7173
Handle(request, subject, cancellationToken);
7274
return _factory(await subject);
7375
}

src/Protocol/Client/IRegisterCapabilityHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace OmniSharp.Extensions.LanguageServer.Protocol.Client
1010
{
11-
[Serial, Method(ClientNames.RegisterCapability, Direction.ServerToClient)]
11+
[Parallel, Method(ClientNames.RegisterCapability, Direction.ServerToClient)]
1212
[GenerateHandlerMethods, GenerateRequestMethods(typeof(IClientLanguageServer), typeof(ILanguageServer))]
1313
public interface IRegisterCapabilityHandler : IJsonRpcRequestHandler<RegistrationParams>
1414
{

src/Protocol/Client/IUnregisterCapabilityHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace OmniSharp.Extensions.LanguageServer.Protocol.Client
1010
{
11-
[Serial, Method(ClientNames.UnregisterCapability, Direction.ServerToClient)]
11+
[Parallel, Method(ClientNames.UnregisterCapability, Direction.ServerToClient)]
1212
[GenerateHandlerMethods, GenerateRequestMethods(typeof(IClientLanguageServer), typeof(ILanguageServer))]
1313
public interface IUnregisterCapabilityHandler : IJsonRpcRequestHandler<UnregistrationParams> { }
1414

src/Protocol/Window/ILogMessageHandler.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,31 @@ public abstract class LogMessageHandler : ILogMessageHandler
1919

2020
public static partial class LogMessageExtensions
2121
{
22+
public static void Log(this ILanguageServer mediator, LogMessageParams @params)
23+
{
24+
mediator.LogMessage(@params);
25+
}
26+
27+
public static void LogError(this ILanguageServer mediator, string message)
28+
{
29+
mediator.LogMessage(new LogMessageParams() { Type = MessageType.Error, Message = message });
30+
}
31+
32+
public static void Log(this ILanguageServer mediator, string message)
33+
{
34+
mediator.LogMessage(new LogMessageParams() { Type = MessageType.Log, Message = message });
35+
}
36+
37+
public static void LogWarning(this ILanguageServer mediator, string message)
38+
{
39+
mediator.LogMessage(new LogMessageParams() { Type = MessageType.Warning, Message = message });
40+
}
41+
42+
public static void LogInfo(this ILanguageServer mediator, string message)
43+
{
44+
mediator.LogMessage(new LogMessageParams() { Type = MessageType.Info, Message = message });
45+
}
46+
2247
public static void Log(this IWindowLanguageServer mediator, LogMessageParams @params)
2348
{
2449
mediator.LogMessage(@params);

src/Server/LanguageServer.cs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
using OmniSharp.Extensions.LanguageServer.Protocol.Server.Capabilities;
3232
using OmniSharp.Extensions.LanguageServer.Protocol.Server.WorkDone;
3333
using OmniSharp.Extensions.LanguageServer.Protocol.Shared;
34+
using OmniSharp.Extensions.LanguageServer.Protocol.Window;
3435
using OmniSharp.Extensions.LanguageServer.Protocol.Workspace;
3536
using OmniSharp.Extensions.LanguageServer.Server.Configuration;
3637
using OmniSharp.Extensions.LanguageServer.Server.Logging;
@@ -85,7 +86,7 @@ public static LanguageServer PreInit(Action<LanguageServerOptions> optionsAction
8586

8687
public static async Task<LanguageServer> From(LanguageServerOptions options, CancellationToken token)
8788
{
88-
var server = (LanguageServer)PreInit(options);
89+
var server = (LanguageServer) PreInit(options);
8990
await server.Initialize(token);
9091

9192
return server;
@@ -199,6 +200,7 @@ internal LanguageServer(LanguageServerOptions options) : base(options)
199200
var serviceIdentifiers = _serviceProvider.GetServices<ITextDocumentIdentifier>().ToArray();
200201
_disposable.Add(_textDocumentIdentifiers.Add(serviceIdentifiers));
201202
_disposable.Add(_collection.Add(serviceHandlers));
203+
options.AddLinks(_collection);
202204
}
203205
}
204206

@@ -420,7 +422,7 @@ async Task<InitializeResult> IRequestHandler<InitializeParams, InitializeResult>
420422
OpenClose = _collection.ContainsHandler(typeof(IDidOpenTextDocumentHandler)) ||
421423
_collection.ContainsHandler(typeof(IDidCloseTextDocumentHandler)),
422424
Save = _collection.ContainsHandler(typeof(IDidSaveTextDocumentHandler))
423-
? new SaveOptions() { IncludeText = true /* TODO: Make configurable */}
425+
? new SaveOptions() {IncludeText = true /* TODO: Make configurable */}
424426
: null,
425427
WillSave = _collection.ContainsHandler(typeof(IWillSaveTextDocumentHandler)),
426428
WillSaveWaitUntil = _collection.ContainsHandler(typeof(IWillSaveWaitUntilTextDocumentHandler))
@@ -476,12 +478,23 @@ private async Task DynamicallyRegisterHandlers(Registration[] registrations)
476478
if (registrations.Length == 0)
477479
return; // No dynamic registrations supported by client.
478480

479-
var @params = new RegistrationParams() { Registrations = registrations };
480-
481481
await _initializeComplete;
482482

483-
await Client.RegisterCapability(@params);
483+
// Some clients (read: vscode) will throw errors on the first unhandled registration it encounters (and never registers the rest)
484+
// This is not really desirable as if we send a registration they don't support... that shouldn't break any unprocessed registrations
485+
// So in order to fix this we just send each registration along as separate requests all at once.
486+
await registrations
487+
.ToObservable()
488+
.Select(z => Observable.FromAsync(ct => Client.RegisterCapability(new RegistrationParams() {Registrations = new RegistrationContainer(z)}, ct))
489+
.Catch<System.Reactive.Unit, Exception>(_ => {
490+
this.LogWarning($"Unable to dynamically register capability '{z.Method}' perhaps it is not supported?");
491+
return Observable.Empty<System.Reactive.Unit>();
492+
})
493+
)
494+
.Merge()
495+
.ToTask();
484496
}
497+
485498
public IObservable<InitializeResult> Start => _initializeComplete.AsObservable();
486499

487500
public Task<InitializeResult> WasStarted => _initializeComplete.ToTask();
@@ -497,6 +510,7 @@ public void Dispose()
497510

498511
protected override IResponseRouter ResponseRouter => _responseRouter;
499512
protected override IHandlersManager HandlersManager => _collection;
513+
500514
public IDisposable Register(Action<ILanguageServerRegistry> registryAction)
501515
{
502516
var manager = new CompositeHandlersManager(_collection);
@@ -550,16 +564,17 @@ private IDisposable RegisterHandlers(IDisposable handlerDisposable)
550564
{
551565
return new CompositeDisposable(lsp, RegisterHandlers(lsp.Descriptors));
552566
}
567+
553568
if (!(handlerDisposable is CompositeDisposable cd)) return Disposable.Empty;
554569
cd.Add(RegisterHandlers(cd.OfType<LspHandlerDescriptorDisposable>().SelectMany(z => z.Descriptors)));
555570
return cd;
556571
}
557-
558572
}
559573

560574
class LangaugeServerRegistry : InterimLanguageProtocolRegistry<ILanguageServerRegistry>, ILanguageServerRegistry
561575
{
562-
public LangaugeServerRegistry(IServiceProvider serviceProvider, CompositeHandlersManager handlersManager, TextDocumentIdentifiers textDocumentIdentifiers) : base(serviceProvider, handlersManager, textDocumentIdentifiers)
576+
public LangaugeServerRegistry(IServiceProvider serviceProvider, CompositeHandlersManager handlersManager, TextDocumentIdentifiers textDocumentIdentifiers) : base(
577+
serviceProvider, handlersManager, textDocumentIdentifiers)
563578
{
564579
}
565580
}

0 commit comments

Comments
 (0)