Skip to content

Commit 383d6c2

Browse files
Fixed an issue where the application would stackoverflow if you tried to resolve the LanguageServer while it was being constructed (#338)
1 parent b048463 commit 383d6c2

File tree

68 files changed

+1535
-362
lines changed

Some content is hidden

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

68 files changed

+1535
-362
lines changed
+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using DryIoc;
5+
using OmniSharp.Extensions.JsonRpc;
6+
using OmniSharp.Extensions.LanguageServer.Protocol;
7+
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
8+
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
9+
using OmniSharp.Extensions.LanguageServer.Protocol.Progress;
10+
11+
namespace OmniSharp.Extensions.LanguageServer.Client
12+
{
13+
internal class DefaultLanguageClientFacade : LanguageProtocolProxy, ILanguageClientFacade, IOnLanguageClientStarted
14+
{
15+
private readonly Lazy<ITextDocumentLanguageClient> _textDocument;
16+
private readonly Lazy<IClientLanguageClient> _client;
17+
private readonly Lazy<IGeneralLanguageClient> _general;
18+
private readonly Lazy<IWindowLanguageClient> _window;
19+
private readonly Lazy<IWorkspaceLanguageClient> _workspace;
20+
private readonly Lazy<IHandlersManager> _handlersManager;
21+
private readonly TextDocumentIdentifiers _textDocumentIdentifiers;
22+
private readonly IInsanceHasStarted _insanceHasStarted;
23+
private ILanguageClient _languageClient;
24+
25+
public DefaultLanguageClientFacade(
26+
IResponseRouter requestRouter,
27+
IResolverContext resolverContext,
28+
IProgressManager progressManager,
29+
ILanguageProtocolSettings languageProtocolSettings,
30+
Lazy<ITextDocumentLanguageClient> textDocument,
31+
Lazy<IClientLanguageClient> client,
32+
Lazy<IGeneralLanguageClient> general,
33+
Lazy<IWindowLanguageClient> window,
34+
Lazy<IWorkspaceLanguageClient> workspace,
35+
Lazy<IHandlersManager> handlersManager,
36+
TextDocumentIdentifiers textDocumentIdentifiers,
37+
IInsanceHasStarted insanceHasStarted
38+
) : base(requestRouter, resolverContext, progressManager, languageProtocolSettings)
39+
{
40+
_textDocument = textDocument;
41+
_client = client;
42+
_general = general;
43+
_window = window;
44+
_workspace = workspace;
45+
_handlersManager = handlersManager;
46+
_textDocumentIdentifiers = textDocumentIdentifiers;
47+
_insanceHasStarted = insanceHasStarted;
48+
}
49+
50+
public ITextDocumentLanguageClient TextDocument => _textDocument.Value;
51+
public IClientLanguageClient Client => _client.Value;
52+
public IGeneralLanguageClient General => _general.Value;
53+
public IWindowLanguageClient Window => _window.Value;
54+
public IWorkspaceLanguageClient Workspace => _workspace.Value;
55+
56+
public IDisposable Register(Action<ILanguageClientRegistry> registryAction)
57+
{
58+
var manager = new CompositeHandlersManager(_handlersManager.Value);
59+
registryAction(new LangaugeClientRegistry(ResolverContext, manager, _textDocumentIdentifiers));
60+
var result = manager.GetDisposable();
61+
if (_insanceHasStarted.Started)
62+
{
63+
if (_languageClient == null) throw new NotSupportedException("Language client has not yet started... you shouldn't be here.");
64+
LanguageClientHelpers.InitHandlers(_languageClient, result);
65+
}
66+
67+
return result;
68+
}
69+
70+
Task IOnLanguageClientStarted.OnStarted(ILanguageClient client, CancellationToken cancellationToken)
71+
{
72+
_languageClient = client;
73+
return Task.CompletedTask;
74+
}
75+
}
76+
}

src/Client/LangaugeClientRegistry.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using DryIoc;
23
using OmniSharp.Extensions.JsonRpc;
34
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
45
using OmniSharp.Extensions.LanguageServer.Protocol.Document;
@@ -8,8 +9,8 @@ namespace OmniSharp.Extensions.LanguageServer.Client
89
{
910
internal class LangaugeClientRegistry : InterimLanguageProtocolRegistry<ILanguageClientRegistry>, ILanguageClientRegistry
1011
{
11-
public LangaugeClientRegistry(IServiceProvider serviceProvider, CompositeHandlersManager handlersManager, TextDocumentIdentifiers textDocumentIdentifiers) : base(
12-
serviceProvider, handlersManager, textDocumentIdentifiers
12+
public LangaugeClientRegistry(IResolverContext resolverContext, CompositeHandlersManager handlersManager, TextDocumentIdentifiers textDocumentIdentifiers) : base(
13+
resolverContext, handlersManager, textDocumentIdentifiers
1314
)
1415
{
1516
}

src/Client/LanguageClient.cs

+11-37
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
using OmniSharp.Extensions.LanguageServer.Protocol.General;
2222
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
2323
using OmniSharp.Extensions.LanguageServer.Protocol.Progress;
24-
using OmniSharp.Extensions.LanguageServer.Protocol.Shared;
2524
using OmniSharp.Extensions.LanguageServer.Protocol.Workspace;
2625
using OmniSharp.Extensions.LanguageServer.Shared;
2726

@@ -45,6 +44,7 @@ public class LanguageClient : JsonRpcServerBase, ILanguageClient
4544
private readonly IEnumerable<OnLanguageClientInitializedDelegate> _initializedDelegates;
4645
private readonly IEnumerable<IOnLanguageClientInitialized> _initializedHandlers;
4746
private readonly ISerializer _serializer;
47+
private readonly InstanceHasStarted _instanceHasStarted;
4848
private readonly IResponseRouter _responseRouter;
4949
private readonly ISubject<InitializeResult> _initializeComplete = new AsyncSubject<InitializeResult>();
5050
private readonly CompositeDisposable _disposable = new CompositeDisposable();
@@ -56,8 +56,8 @@ public class LanguageClient : JsonRpcServerBase, ILanguageClient
5656
private readonly InitializeTrace _trace;
5757
private readonly ClientCapabilities _clientCapabilities;
5858
private readonly LanguageProtocolSettingsBag _settingsBag;
59-
private bool _started;
6059
private readonly int? _concurrency;
60+
private readonly IResolverContext _resolverContext;
6161

6262
internal static IContainer CreateContainer(LanguageClientOptions options, IServiceProvider outerServiceProvider) =>
6363
JsonRpcServerContainer.Create(outerServiceProvider)
@@ -127,7 +127,7 @@ internal LanguageClient(
127127
ClientCapabilities clientCapabilities,
128128
ILspClientReceiver lspClientReceiver,
129129
TextDocumentIdentifiers textDocumentIdentifiers,
130-
IServiceProvider serviceProvider,
130+
IResolverContext resolverContext,
131131
IEnumerable<OnLanguageClientStartedDelegate> startedDelegates,
132132
IEnumerable<IOnLanguageClientStarted> startedHandlers,
133133
ITextDocumentLanguageClient textDocumentLanguageClient,
@@ -144,7 +144,8 @@ internal LanguageClient(
144144
ILanguageClientWorkspaceFoldersManager languageClientWorkspaceFoldersManager, IEnumerable<OnLanguageClientInitializeDelegate> initializeDelegates,
145145
IEnumerable<IOnLanguageClientInitialize> initializeHandlers, IEnumerable<OnLanguageClientInitializedDelegate> initializedDelegates,
146146
IEnumerable<IOnLanguageClientInitialized> initializedHandlers,
147-
ISerializer serializer
147+
ISerializer serializer,
148+
InstanceHasStarted instanceHasStarted
148149
) : base(handlerCollection, responseRouter)
149150
{
150151
_connection = connection;
@@ -160,7 +161,7 @@ ISerializer serializer
160161
_initializationOptions = options.Value.InitializationOptions;
161162
_settingsBag = languageProtocolSettingsBag;
162163
_collection = handlerCollection;
163-
Services = serviceProvider;
164+
Services = _resolverContext = resolverContext;
164165

165166
_responseRouter = responseRouter;
166167
ProgressManager = progressManager;
@@ -172,6 +173,7 @@ ISerializer serializer
172173
_initializedDelegates = initializedDelegates;
173174
_initializedHandlers = initializedHandlers;
174175
_serializer = serializer;
176+
_instanceHasStarted = instanceHasStarted;
175177
_concurrency = options.Value.Concurrency;
176178

177179
// We need to at least create Window here in case any handler does loggin in their constructor
@@ -290,7 +292,7 @@ await LanguageProtocolEventingHelper.Run(
290292
token
291293
);
292294

293-
_started = true;
295+
_instanceHasStarted.Started = true;
294296

295297
// TODO: pull supported fields and add any static registrations to the registration manager
296298
this.SendLanguageProtocolInitialized(new InitializedParams());
@@ -397,39 +399,11 @@ public void Dispose()
397399
public IDisposable Register(Action<ILanguageClientRegistry> registryAction)
398400
{
399401
var manager = new CompositeHandlersManager(_collection);
400-
registryAction(new LangaugeClientRegistry(Services, manager, _textDocumentIdentifiers));
402+
registryAction(new LangaugeClientRegistry(_resolverContext, manager, _textDocumentIdentifiers));
401403
var result = manager.GetDisposable();
402-
if (_started)
404+
if (_instanceHasStarted.Started)
403405
{
404-
static IEnumerable<T> GetUniqueHandlers<T>(CompositeDisposable disposable)
405-
{
406-
return disposable.OfType<ILspHandlerDescriptor>()
407-
.Select(z => z.Handler)
408-
.OfType<T>()
409-
.Concat(disposable.OfType<CompositeDisposable>().SelectMany(GetUniqueHandlers<T>))
410-
.Concat(disposable.OfType<LspHandlerDescriptorDisposable>().SelectMany(GetLspHandlers<T>))
411-
.Distinct();
412-
}
413-
414-
static IEnumerable<T> GetLspHandlers<T>(LspHandlerDescriptorDisposable disposable)
415-
{
416-
return disposable.Descriptors
417-
.Select(z => z.Handler)
418-
.OfType<T>()
419-
.Distinct();
420-
}
421-
422-
Observable.Concat(
423-
GetUniqueHandlers<IOnLanguageClientInitialize>(result)
424-
.Select(handler => Observable.FromAsync(ct => handler.OnInitialize(this, ClientSettings, ct)))
425-
.Merge(),
426-
GetUniqueHandlers<IOnLanguageClientInitialized>(result)
427-
.Select(handler => Observable.FromAsync(ct => handler.OnInitialized(this, ClientSettings, ServerSettings, ct)))
428-
.Merge(),
429-
GetUniqueHandlers<IOnLanguageClientStarted>(result)
430-
.Select(handler => Observable.FromAsync(ct => handler.OnStarted(this, ct)))
431-
.Merge()
432-
).Subscribe();
406+
LanguageClientHelpers.InitHandlers(this, result);
433407
}
434408

435409
return result;

src/Client/LanguageClientHelpers.cs

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reactive.Disposables;
5+
using System.Reactive.Linq;
6+
using OmniSharp.Extensions.LanguageServer.Protocol.Client;
7+
using OmniSharp.Extensions.LanguageServer.Protocol.Shared;
8+
using OmniSharp.Extensions.LanguageServer.Shared;
9+
10+
namespace OmniSharp.Extensions.LanguageServer.Client
11+
{
12+
static class LanguageClientHelpers
13+
{
14+
15+
static IEnumerable<T> GetUniqueHandlers<T>(CompositeDisposable disposable)
16+
{
17+
return disposable.OfType<ILspHandlerDescriptor>()
18+
.Select(z => z.Handler)
19+
.OfType<T>()
20+
.Concat(disposable.OfType<CompositeDisposable>().SelectMany(GetUniqueHandlers<T>))
21+
.Concat(disposable.OfType<LspHandlerDescriptorDisposable>().SelectMany(GetLspHandlers<T>))
22+
.Distinct();
23+
}
24+
25+
static IEnumerable<T> GetLspHandlers<T>(LspHandlerDescriptorDisposable disposable)
26+
{
27+
return disposable.Descriptors
28+
.Select(z => z.Handler)
29+
.OfType<T>()
30+
.Distinct();
31+
}
32+
33+
internal static void InitHandlers(ILanguageClient client, CompositeDisposable result)
34+
{
35+
Observable.Concat(
36+
GetUniqueHandlers<IOnLanguageClientInitialize>(result)
37+
.Select(handler => Observable.FromAsync(ct => handler.OnInitialize(client, client.ClientSettings, ct)))
38+
.Merge(),
39+
GetUniqueHandlers<IOnLanguageClientInitialized>(result)
40+
.Select(handler => Observable.FromAsync(ct => handler.OnInitialized(client, client.ClientSettings, client.ServerSettings, ct)))
41+
.Merge(),
42+
GetUniqueHandlers<IOnLanguageClientStarted>(result)
43+
.Select(handler => Observable.FromAsync(ct => handler.OnStarted(client, ct)))
44+
.Merge()
45+
).Subscribe();
46+
}
47+
}
48+
}

src/Client/LanguageClientServiceCollectionExtensions.cs

+10-2
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,18 @@ internal static IContainer AddLanguageClientInternals(this IContainer container,
4040
container.RegisterMany<GeneralLanguageClient>(serviceTypeCondition: type => type.Name.Contains(nameof(GeneralLanguageClient)), reuse: Reuse.Singleton);
4141
container.RegisterMany<WindowLanguageClient>(serviceTypeCondition: type => type.Name.Contains(nameof(WindowLanguageClient)), reuse: Reuse.Singleton);
4242
container.RegisterMany<WorkspaceLanguageClient>(serviceTypeCondition: type => type.Name.Contains(nameof(WorkspaceLanguageClient)), reuse: Reuse.Singleton);
43-
container.RegisterMany<DefaultLanguageClientFacade>(serviceTypeCondition: type => type.Name.Contains("LanguageClientFacade"), reuse: Reuse.Singleton);
43+
container.RegisterMany<DefaultLanguageClientFacade>(
44+
serviceTypeCondition: type => type.IsClass || !type.Name.Contains("Proxy") && typeof(DefaultLanguageClientFacade).GetInterfaces()
45+
.Except(typeof(DefaultLanguageClientFacade).BaseType!.GetInterfaces()).Any(z => type == z),
46+
reuse: Reuse.Singleton
47+
);
4448
container.RegisterInstance<IOptionsFactory<LanguageClientOptions>>(new ValueOptionsFactory<LanguageClientOptions>(options));
4549

46-
container.RegisterMany<LanguageClient>(serviceTypeCondition: type => type == typeof(ILanguageClient) || type == typeof(LanguageClient), reuse: Reuse.Singleton);
50+
container.RegisterMany<LanguageClient>(
51+
serviceTypeCondition: type => type == typeof(ILanguageClient) || type == typeof(LanguageClient),
52+
reuse: Reuse.Singleton,
53+
setup: Setup.With(condition: req => req.IsResolutionRoot || req.Container.Resolve<IInsanceHasStarted>().Started)
54+
);
4755

4856
container.RegisterInstance(
4957
options.ClientInfo ?? new ClientInfo {

src/Dap.Client/DebugAdapterClient.cs

+6-3
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ public class DebugAdapterClient : JsonRpcServerBase, IDebugAdapterClient, IDebug
2828
private readonly IEnumerable<IOnDebugAdapterClientInitialized> _initializedHandlers;
2929
private readonly IEnumerable<OnDebugAdapterClientStartedDelegate> _startedDelegates;
3030
private readonly IEnumerable<IOnDebugAdapterClientStarted> _startedHandlers;
31+
private readonly InstanceHasStarted _instanceHasStarted;
3132
private readonly CompositeDisposable _disposable = new CompositeDisposable();
3233
private readonly Connection _connection;
3334
private readonly DapReceiver _receiver;
3435
private readonly IServiceProvider _serviceProvider;
3536
private readonly ISubject<InitializedEvent> _initializedComplete = new AsyncSubject<InitializedEvent>();
36-
private bool _started;
3737
private readonly int? _concurrency;
3838

3939
internal static IContainer CreateContainer(DebugAdapterClientOptions options, IServiceProvider outerServiceProvider) =>
@@ -95,7 +95,8 @@ internal DebugAdapterClient(
9595
IEnumerable<IOnDebugAdapterClientInitialize> initializeHandlers,
9696
IEnumerable<OnDebugAdapterClientInitializedDelegate> initializedDelegates,
9797
IEnumerable<IOnDebugAdapterClientInitialized> initializedHandlers,
98-
IEnumerable<IOnDebugAdapterClientStarted> startedHandlers
98+
IEnumerable<IOnDebugAdapterClientStarted> startedHandlers,
99+
InstanceHasStarted instanceHasStarted
99100
) : base(collection, responseRouter)
100101
{
101102
_settingsBag = settingsBag;
@@ -111,6 +112,7 @@ IEnumerable<IOnDebugAdapterClientStarted> startedHandlers
111112
_initializedDelegates = initializedDelegates;
112113
_initializedHandlers = initializedHandlers;
113114
_startedHandlers = startedHandlers;
115+
_instanceHasStarted = instanceHasStarted;
114116
_concurrency = options.Value.Concurrency;
115117

116118
_disposable.Add(collection.Add(this));
@@ -154,7 +156,8 @@ await DebugAdapterEventingHelper.Run(
154156
_concurrency,
155157
token
156158
);
157-
_started = true;
159+
160+
_instanceHasStarted.Started = true;
158161
}
159162

160163
async Task<Unit> IRequestHandler<InitializedEvent, Unit>.Handle(InitializedEvent request, CancellationToken cancellationToken)

src/Dap.Client/DebugAdapterClientServiceCollectionExtensions.cs

+7-5
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@ internal static IContainer AddDebugAdapterClientInternals(this IContainer contai
2929

3030
container.RegisterInstance<IOptionsFactory<DebugAdapterClientOptions>>(new ValueOptionsFactory<DebugAdapterClientOptions>(options));
3131

32-
container.RegisterMany<DebugAdapterClient>(
33-
serviceTypeCondition: type => type == typeof(IDebugAdapterClient) || type == typeof(DebugAdapterClient), reuse: Reuse.Singleton
34-
);
35-
3632
container.RegisterInstance(
3733
new InitializeRequestArguments {
3834
Locale = options.Locale,
@@ -53,7 +49,13 @@ internal static IContainer AddDebugAdapterClientInternals(this IContainer contai
5349

5450
container.RegisterMany<DebugAdapterClientProgressManager>(nonPublicServiceTypes: true, reuse: Reuse.Singleton);
5551
container.RegisterMany<DebugAdapterClient>(
56-
serviceTypeCondition: type => type == typeof(IDebugAdapterClient) || type == typeof(DebugAdapterClient), reuse: Reuse.Singleton
52+
serviceTypeCondition: type => type == typeof(IDebugAdapterClient) || type == typeof(DebugAdapterClient),
53+
reuse: Reuse.Singleton,
54+
setup: Setup.With(condition: req => req.IsResolutionRoot || req.Container.Resolve<IInsanceHasStarted>().Started)
55+
);
56+
container.RegisterMany<DefaultDebugAdapterClientFacade>(
57+
serviceTypeCondition: type => type.IsClass || !type.Name.Contains("Proxy") && typeof(DefaultDebugAdapterClientFacade).GetInterfaces().Except(typeof(DefaultDebugAdapterClientFacade).BaseType!.GetInterfaces()).Any(z => type == z),
58+
reuse: Reuse.Singleton
5759
);
5860

5961
// container.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using DryIoc;
2+
using OmniSharp.Extensions.DebugAdapter.Protocol;
3+
using OmniSharp.Extensions.DebugAdapter.Protocol.Client;
4+
using OmniSharp.Extensions.DebugAdapter.Protocol.Server;
5+
using OmniSharp.Extensions.JsonRpc;
6+
7+
namespace OmniSharp.Extensions.DebugAdapter.Client
8+
{
9+
class DefaultDebugAdapterClientFacade : DebugAdapterProtocolProxy, IDebugAdapterClientFacade
10+
{
11+
public DefaultDebugAdapterClientFacade(IResponseRouter requestRouter, IResolverContext resolverContext, IDebugAdapterProtocolSettings debugAdapterProtocolSettings, IDebugAdapterClientProgressManager progressManager) : base(requestRouter, resolverContext, debugAdapterProtocolSettings)
12+
{
13+
ProgressManager = progressManager;
14+
}
15+
16+
public IDebugAdapterClientProgressManager ProgressManager { get; }
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace OmniSharp.Extensions.DebugAdapter.Protocol.Client
2+
{
3+
public interface IDebugAdapterClientFacade : IDebugAdapterClientProxy
4+
{
5+
IDebugAdapterClientProgressManager ProgressManager { get; }
6+
}
7+
}

0 commit comments

Comments
 (0)