Skip to content

Commit 6eeee3e

Browse files
Added workspace folder tests (#279)
* Added workspace folder tests, normalized initilaize/initilaized/startup handling * DAP doesn't support dynamic registration... whoops
1 parent d24a9c2 commit 6eeee3e

File tree

57 files changed

+1121
-169
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1121
-169
lines changed

sample/SampleServer/Program.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ static async Task MainAsync(string[] args)
9797
});
9898
workDone.OnCompleted();
9999
})
100-
.OnStarted(async (languageServer, result, token) => {
100+
.OnStarted(async (languageServer, token) => {
101101
using var manager = await languageServer.WorkDoneManager.Create(new WorkDoneProgressBegin() { Title = "Doing some work..." });
102102

103103
manager.OnNext(new WorkDoneProgressReport() { Message = "doing things..." });

src/Client/LanguageClient.cs

+76-22
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ public class LanguageClient : JsonRpcServerBase, ILanguageClient
4343
// private readonly IEnumerable<InitializedDelegate> _initializedDelegates;
4444
private readonly IEnumerable<OnLanguageClientStartedDelegate> _startedDelegates;
4545
private readonly IEnumerable<IOnLanguageClientStarted> _startedHandlers;
46+
private readonly IEnumerable<OnLanguageClientInitializeDelegate> _initializeDelegates;
47+
private readonly IEnumerable<IOnLanguageClientInitialize> _initializeHandlers;
48+
private readonly IEnumerable<OnLanguageClientInitializedDelegate> _initializedDelegates;
49+
private readonly IEnumerable<IOnLanguageClientInitialized> _initializedHandlers;
4650
private readonly IResponseRouter _responseRouter;
4751
private readonly ISubject<InitializeResult> _initializeComplete = new AsyncSubject<InitializeResult>();
4852
private readonly CompositeDisposable _disposable = new CompositeDisposable();
@@ -55,6 +59,7 @@ public class LanguageClient : JsonRpcServerBase, ILanguageClient
5559
private readonly ClientCapabilities _clientCapabilities;
5660
private readonly LanguageProtocolSettingsBag _settingsBag;
5761
private bool _started;
62+
private readonly int? _concurrency;
5863

5964
internal static IContainer CreateContainer(LanguageClientOptions options, IServiceProvider outerServiceProvider) =>
6065
JsonRpcServerContainer.Create(outerServiceProvider)
@@ -70,7 +75,9 @@ public static LanguageClient Create(Action<LanguageClientOptions> optionsAction,
7075
return Create(options, outerServiceProvider);
7176
}
7277

73-
public static LanguageClient Create(LanguageClientOptions options, IServiceProvider outerServiceProvider) => CreateContainer(options, outerServiceProvider).Resolve<LanguageClient>();
78+
public static LanguageClient Create(LanguageClientOptions options, IServiceProvider outerServiceProvider) =>
79+
CreateContainer(options, outerServiceProvider).Resolve<LanguageClient>();
80+
7481
public static Task<LanguageClient> From(LanguageClientOptions options) => From(options, null, CancellationToken.None);
7582
public static Task<LanguageClient> From(Action<LanguageClientOptions> optionsAction) => From(optionsAction, null, CancellationToken.None);
7683
public static Task<LanguageClient> From(LanguageClientOptions options, CancellationToken cancellationToken) => From(options, null, cancellationToken);
@@ -142,8 +149,9 @@ internal LanguageClient(
142149
IProgressManager progressManager,
143150
IClientWorkDoneManager clientWorkDoneManager,
144151
IRegistrationManager registrationManager,
145-
ILanguageClientWorkspaceFoldersManager languageClientWorkspaceFoldersManager
146-
) : base(handlerCollection, responseRouter)
152+
ILanguageClientWorkspaceFoldersManager languageClientWorkspaceFoldersManager, IEnumerable<OnLanguageClientInitializeDelegate> initializeDelegates,
153+
IEnumerable<IOnLanguageClientInitialize> initializeHandlers, IEnumerable<OnLanguageClientInitializedDelegate> initializedDelegates,
154+
IEnumerable<IOnLanguageClientInitialized> initializedHandlers) : base(handlerCollection, responseRouter)
147155
{
148156
_connection = connection;
149157
_capabilities = capabilities;
@@ -165,6 +173,11 @@ ILanguageClientWorkspaceFoldersManager languageClientWorkspaceFoldersManager
165173
WorkDoneManager = clientWorkDoneManager;
166174
RegistrationManager = registrationManager;
167175
WorkspaceFoldersManager = languageClientWorkspaceFoldersManager;
176+
_initializeDelegates = initializeDelegates;
177+
_initializeHandlers = initializeHandlers;
178+
_initializedDelegates = initializedDelegates;
179+
_initializedHandlers = initializedHandlers;
180+
_concurrency = options.Value.Concurrency;
168181

169182
// We need to at least create Window here in case any handler does loggin in their constructor
170183
TextDocument = textDocumentLanguageClient;
@@ -209,34 +222,55 @@ public async Task Initialize(CancellationToken token)
209222
WorkspaceFolders = new Container<WorkspaceFolder>(WorkspaceFoldersManager.CurrentWorkspaceFolders),
210223
InitializationOptions = _initializationOptions
211224
};
225+
212226
RegisterCapabilities(@params.Capabilities);
227+
213228
WorkDoneManager.Initialize(@params.Capabilities.Window);
214229

215230
ClientSettings = @params;
216231

232+
await LanguageProtocolEventingHelper.Run(
233+
_initializeDelegates,
234+
(handler, ct) => handler(this, @params, ct),
235+
_initializeHandlers.Union(_collection.Select(z => z.Handler).OfType<IOnLanguageClientInitialize>()),
236+
(handler, ct) => handler.OnInitialize(this, @params, ct),
237+
_concurrency,
238+
token
239+
);
240+
217241
_connection.Open();
218242
var serverParams = await this.RequestLanguageProtocolInitialize(ClientSettings, token);
219243
_receiver.Initialized();
220244

221-
var tasks =
222-
_startedDelegates.Select(handler => handler(this, serverParams, token))
223-
.Concat(_startedHandlers
224-
.Union(_collection.Select(z => z.Handler).OfType<IOnLanguageClientStarted>())
225-
.Select(handler => handler.OnStarted(this, serverParams, token))
226-
)
227-
.ToArray();
228-
if (tasks.Length > 0)
229-
{
230-
await Task.WhenAll(tasks);
231-
}
232-
233245
ServerSettings = serverParams;
246+
247+
await LanguageProtocolEventingHelper.Run(
248+
_initializedDelegates,
249+
(handler, ct) => handler(this, @params, serverParams, ct),
250+
_initializedHandlers.Union(_collection.Select(z => z.Handler).OfType<IOnLanguageClientInitialized>()),
251+
(handler, ct) => handler.OnInitialized(this, @params, serverParams, ct),
252+
_concurrency,
253+
token
254+
);
255+
256+
// post init
257+
234258
if (_collection.ContainsHandler(typeof(IRegisterCapabilityHandler)))
235259
RegistrationManager.RegisterCapabilities(serverParams.Capabilities);
236260

261+
await LanguageProtocolEventingHelper.Run(
262+
_startedDelegates,
263+
(handler, ct) => handler(this, ct),
264+
_startedHandlers.Union(_collection.Select(z => z.Handler).OfType<IOnLanguageClientStarted>()),
265+
(handler, ct) => handler.OnStarted(this, ct),
266+
_concurrency,
267+
token
268+
);
269+
270+
_started = true;
271+
237272
// TODO: pull supported fields and add any static registrations to the registration manager
238273
this.SendLanguageProtocolInitialized(new InitializedParams());
239-
_started = true;
240274
}
241275

242276
private void RegisterCapabilities(ClientCapabilities capabilities)
@@ -347,14 +381,34 @@ public IDisposable Register(Action<ILanguageClientRegistry> registryAction)
347381
var result = manager.GetDisposable();
348382
if (_started)
349383
{
350-
foreach (var item in result.OfType<LspHandlerDescriptorDisposable>())
351-
foreach (var descriptor in item.Descriptors)
384+
static IEnumerable<T> GetUniqueHandlers<T>(CompositeDisposable disposable)
385+
{
386+
return disposable.OfType<ILspHandlerDescriptor>()
387+
.Select(z => z.Handler)
388+
.OfType<T>()
389+
.Concat(disposable.OfType<CompositeDisposable>().SelectMany(GetUniqueHandlers<T>))
390+
.Concat(disposable.OfType<LspHandlerDescriptorDisposable>().SelectMany(GetLspHandlers<T>))
391+
.Distinct();
392+
}
393+
static IEnumerable<T> GetLspHandlers<T>(LspHandlerDescriptorDisposable disposable)
352394
{
353-
if (descriptor.Handler is IOnLanguageClientStarted clientStarted)
354-
{
355-
Observable.FromAsync((ct) => clientStarted.OnStarted(this, ServerSettings, ct)).Subscribe();
356-
}
395+
return disposable.Descriptors
396+
.Select(z => z.Handler)
397+
.OfType<T>()
398+
.Distinct();
357399
}
400+
401+
Observable.Concat(
402+
GetUniqueHandlers<IOnLanguageClientInitialize>(result)
403+
.Select(handler => Observable.FromAsync((ct) => handler.OnInitialize(this, ClientSettings, ct)))
404+
.Merge(),
405+
GetUniqueHandlers<IOnLanguageClientInitialized>(result)
406+
.Select(handler => Observable.FromAsync((ct) => handler.OnInitialized(this, ClientSettings, ServerSettings, ct)))
407+
.Merge(),
408+
GetUniqueHandlers<IOnLanguageClientStarted>(result)
409+
.Select(handler => Observable.FromAsync((ct) => handler.OnStarted(this, ct)))
410+
.Merge()
411+
).Subscribe();
358412
}
359413

360414
return result;

src/Client/LanguageClientOptionsExtensions.cs

+14-2
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,20 @@ public static LanguageClientOptions WithClientCapabilities(this LanguageClientOp
9191
return options;
9292
}
9393

94-
public static LanguageClientOptions OnStarted(this LanguageClientOptions options,
95-
OnLanguageClientStartedDelegate @delegate)
94+
public static LanguageClientOptions OnInitialize(this LanguageClientOptions options, OnLanguageClientInitializeDelegate @delegate)
95+
{
96+
options.Services.AddSingleton(@delegate);
97+
return options;
98+
}
99+
100+
101+
public static LanguageClientOptions OnInitialized(this LanguageClientOptions options, OnLanguageClientInitializedDelegate @delegate)
102+
{
103+
options.Services.AddSingleton(@delegate);
104+
return options;
105+
}
106+
107+
public static LanguageClientOptions OnStarted(this LanguageClientOptions options, OnLanguageClientStartedDelegate @delegate)
96108
{
97109
options.Services.AddSingleton(@delegate);
98110
return options;

src/Dap.Client/DebugAdapterClient.cs

+62-11
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using DryIoc;
1212
using MediatR;
1313
using Microsoft.Extensions.Logging;
14+
using Microsoft.Extensions.Options;
1415
using OmniSharp.Extensions.DebugAdapter.Protocol;
1516
using OmniSharp.Extensions.DebugAdapter.Protocol.Client;
1617
using OmniSharp.Extensions.DebugAdapter.Protocol.Events;
@@ -22,16 +23,23 @@
2223

2324
namespace OmniSharp.Extensions.DebugAdapter.Client
2425
{
25-
public class DebugAdapterClient : JsonRpcServerBase, IDebugAdapterClient, IInitializedHandler
26+
public class DebugAdapterClient : JsonRpcServerBase, IDebugAdapterClient, IDebugAdapterInitializedHandler
2627
{
2728
private readonly DebugAdapterSettingsBag _settingsBag;
2829
private readonly DebugAdapterHandlerCollection _collection;
30+
private readonly IEnumerable<OnDebugAdapterClientInitializeDelegate> _initializeDelegates;
31+
private readonly IEnumerable<IOnDebugAdapterClientInitialize> _initializeHandlers;
32+
private readonly IEnumerable<OnDebugAdapterClientInitializedDelegate> _initializedDelegates;
33+
private readonly IEnumerable<IOnDebugAdapterClientInitialized> _initializedHandlers;
2934
private readonly IEnumerable<OnDebugAdapterClientStartedDelegate> _startedDelegates;
35+
private readonly IEnumerable<IOnDebugAdapterClientStarted> _startedHandlers;
3036
private readonly CompositeDisposable _disposable = new CompositeDisposable();
3137
private readonly Connection _connection;
3238
private readonly DapReceiver _receiver;
3339
private readonly IServiceProvider _serviceProvider;
3440
private readonly ISubject<InitializedEvent> _initializedComplete = new AsyncSubject<InitializedEvent>();
41+
private bool _started;
42+
private readonly int? _concurrency;
3543

3644
internal static IContainer CreateContainer(DebugAdapterClientOptions options, IServiceProvider outerServiceProvider) =>
3745
JsonRpcServerContainer.Create(outerServiceProvider)
@@ -69,6 +77,7 @@ public static async Task<DebugAdapterClient> From(DebugAdapterClientOptions opti
6977
}
7078

7179
internal DebugAdapterClient(
80+
IOptions<DebugAdapterClientOptions> options,
7281
InitializeRequestArguments clientSettings,
7382
DebugAdapterSettingsBag settingsBag,
7483
DebugAdapterHandlerCollection collection,
@@ -77,8 +86,12 @@ internal DebugAdapterClient(
7786
IResponseRouter responseRouter,
7887
IServiceProvider serviceProvider,
7988
IDebugAdapterClientProgressManager debugAdapterClientProgressManager,
80-
Connection connection
81-
) : base(collection, responseRouter)
89+
Connection connection,
90+
IEnumerable<OnDebugAdapterClientInitializeDelegate> initializeDelegates,
91+
IEnumerable<IOnDebugAdapterClientInitialize> initializeHandlers,
92+
IEnumerable<OnDebugAdapterClientInitializedDelegate> initializedDelegates,
93+
IEnumerable<IOnDebugAdapterClientInitialized> initializedHandlers,
94+
IEnumerable<IOnDebugAdapterClientStarted> startedHandlers) : base(collection, responseRouter)
8295
{
8396
_settingsBag = settingsBag;
8497
ClientSettings = clientSettings;
@@ -88,33 +101,71 @@ Connection connection
88101
_serviceProvider = serviceProvider;
89102
ProgressManager = debugAdapterClientProgressManager;
90103
_connection = connection;
104+
_initializeDelegates = initializeDelegates;
105+
_initializeHandlers = initializeHandlers;
106+
_initializedDelegates = initializedDelegates;
107+
_initializedHandlers = initializedHandlers;
108+
_startedHandlers = startedHandlers;
109+
_concurrency = options.Value.Concurrency;
110+
91111
_disposable.Add(collection.Add(this));
92112
}
93113

94114
public async Task Initialize(CancellationToken token)
95115
{
116+
await DebugAdapterEventingHelper.Run(
117+
_initializeDelegates,
118+
(handler, ct) => handler(this, ClientSettings, ct),
119+
_initializeHandlers.Union(_collection.Select(z => z.Handler).OfType<IOnDebugAdapterClientInitialize>()),
120+
(handler, ct) => handler.OnInitialize(this, ClientSettings, ct),
121+
_concurrency,
122+
token
123+
);
124+
96125
RegisterCapabilities(ClientSettings);
97126

98127
_connection.Open();
99-
var serverParams = await this.RequestInitialize(ClientSettings, token);
128+
var serverParams = await this.RequestDebugAdapterInitialize(ClientSettings, token);
100129

101130
ServerSettings = serverParams;
102131
_receiver.Initialized();
103132

133+
await DebugAdapterEventingHelper.Run(
134+
_initializedDelegates,
135+
(handler, ct) => handler(this, ClientSettings, ServerSettings, ct),
136+
_initializedHandlers.Union(_collection.Select(z => z.Handler).OfType<IOnDebugAdapterClientInitialized>()),
137+
(handler, ct) => handler.OnInitialized(this, ClientSettings, ServerSettings, ct),
138+
_concurrency,
139+
token
140+
);
141+
104142
await _initializedComplete.ToTask(token);
105143

106-
await _startedDelegates.Select(@delegate => Observable.FromAsync(() => @delegate(this, serverParams, token)))
107-
.ToObservable()
108-
.Merge()
109-
.LastOrDefaultAsync()
110-
.ToTask(token);
144+
await DebugAdapterEventingHelper.Run(
145+
_startedDelegates,
146+
(handler, ct) => handler(this, ct),
147+
_startedHandlers.Union(_collection.Select(z => z.Handler).OfType<IOnDebugAdapterClientStarted>()),
148+
(handler, ct) => handler.OnStarted(this, ct),
149+
_concurrency,
150+
token
151+
);
152+
_started = true;
111153
}
112154

113-
Task<Unit> IRequestHandler<InitializedEvent, Unit>.Handle(InitializedEvent request, CancellationToken cancellationToken)
155+
async Task<Unit> IRequestHandler<InitializedEvent, Unit>.Handle(InitializedEvent request, CancellationToken cancellationToken)
114156
{
157+
await DebugAdapterEventingHelper.Run(
158+
_initializedDelegates,
159+
(handler, ct) => handler(this, ClientSettings, ServerSettings, ct),
160+
_initializedHandlers.Union(_collection.Select(z => z.Handler).OfType<IOnDebugAdapterClientInitialized>()),
161+
(handler, ct) => handler.OnInitialized(this, ClientSettings, ServerSettings, ct),
162+
_concurrency,
163+
cancellationToken
164+
);
165+
115166
_initializedComplete.OnNext(request);
116167
_initializedComplete.OnCompleted();
117-
return Unit.Task;
168+
return Unit.Value;
118169
}
119170

120171
private void RegisterCapabilities(InitializeRequestArguments capabilities)

src/Dap.Client/DebugAdapterClientOptionsExtensions.cs

+14-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Microsoft.Extensions.Logging;
55
using OmniSharp.Extensions.DebugAdapter.Protocol;
66
using OmniSharp.Extensions.DebugAdapter.Protocol.Client;
7+
using OmniSharp.Extensions.DebugAdapter.Protocol.Server;
78
using OmniSharp.Extensions.JsonRpc;
89

910
namespace OmniSharp.Extensions.DebugAdapter.Client
@@ -23,8 +24,19 @@ public static DebugAdapterClientOptions WithRequestProcessIdentifier(this DebugA
2324
return options;
2425
}
2526

26-
public static DebugAdapterClientOptions OnStarted(this DebugAdapterClientOptions options,
27-
OnDebugAdapterClientStartedDelegate @delegate)
27+
public static DebugAdapterClientOptions OnInitialize(this DebugAdapterClientOptions options, OnDebugAdapterClientInitializeDelegate @delegate)
28+
{
29+
options.Services.AddSingleton(@delegate);
30+
return options;
31+
}
32+
33+
public static DebugAdapterClientOptions OnInitialized(this DebugAdapterClientOptions options, OnDebugAdapterClientInitializedDelegate initializedDelegate)
34+
{
35+
options.Services.AddSingleton(initializedDelegate);
36+
return options;
37+
}
38+
39+
public static DebugAdapterClientOptions OnStarted(this DebugAdapterClientOptions options, OnDebugAdapterClientStartedDelegate @delegate)
2840
{
2941
options.Services.AddSingleton(@delegate);
3042
return options;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System.Threading;
2+
using System.Threading.Tasks;
3+
using OmniSharp.Extensions.DebugAdapter.Protocol.Requests;
4+
using OmniSharp.Extensions.JsonRpc;
5+
6+
namespace OmniSharp.Extensions.DebugAdapter.Protocol.Client
7+
{
8+
/// <summary>
9+
/// Gives your class or handler an opportunity to interact with
10+
/// the <see cref="InitializeRequestArguments"/> before it is sent to the server
11+
/// </summary>
12+
public interface IOnDebugAdapterClientInitialize : IEventingHandler
13+
{
14+
Task OnInitialize(IDebugAdapterClient client, InitializeRequestArguments request, CancellationToken cancellationToken);
15+
}
16+
}

0 commit comments

Comments
 (0)