|
| 1 | +using System; |
| 2 | +using System.Collections.Generic; |
| 3 | +using System.Linq; |
| 4 | +using System.Reactive.Disposables; |
| 5 | +using System.Reactive.Linq; |
| 6 | +using System.Reactive.Subjects; |
| 7 | +using System.Reactive.Threading.Tasks; |
| 8 | +using System.Threading; |
| 9 | +using System.Threading.Tasks; |
| 10 | +using MediatR; |
| 11 | +using Microsoft.Extensions.DependencyInjection; |
| 12 | +using Microsoft.Extensions.Logging; |
| 13 | +using OmniSharp.Extensions.DebugAdapter.Protocol; |
| 14 | +using OmniSharp.Extensions.DebugAdapter.Protocol.Events; |
| 15 | +using OmniSharp.Extensions.DebugAdapter.Protocol.Requests; |
| 16 | +using OmniSharp.Extensions.DebugAdapter.Shared; |
| 17 | +using OmniSharp.Extensions.JsonRpc; |
| 18 | +using IOutputHandler = OmniSharp.Extensions.JsonRpc.IOutputHandler; |
| 19 | +using OutputHandler = OmniSharp.Extensions.JsonRpc.OutputHandler; |
| 20 | + |
| 21 | +namespace OmniSharp.Extensions.DebugAdapter.Client |
| 22 | +{ |
| 23 | + public class DebugAdapterClient : JsonRpcServerBase, IDebugAdapterClient, IInitializedHandler |
| 24 | + { |
| 25 | + private readonly DebugAdapterHandlerCollection _collection; |
| 26 | + private readonly IEnumerable<OnClientStartedDelegate> _startedDelegates; |
| 27 | + private readonly CompositeDisposable _disposable = new CompositeDisposable(); |
| 28 | + private readonly Connection _connection; |
| 29 | + private readonly IClientProgressManager _progressManager; |
| 30 | + private readonly DapReceiver _receiver; |
| 31 | + private readonly ISubject<InitializedEvent> _initializedComplete = new AsyncSubject<InitializedEvent>(); |
| 32 | + |
| 33 | + public static Task<IDebugAdapterClient> From(Action<DebugAdapterClientOptions> optionsAction) |
| 34 | + { |
| 35 | + return From(optionsAction, CancellationToken.None); |
| 36 | + } |
| 37 | + |
| 38 | + public static Task<IDebugAdapterClient> From(DebugAdapterClientOptions options) |
| 39 | + { |
| 40 | + return From(options, CancellationToken.None); |
| 41 | + } |
| 42 | + |
| 43 | + public static Task<IDebugAdapterClient> From(Action<DebugAdapterClientOptions> optionsAction, CancellationToken token) |
| 44 | + { |
| 45 | + var options = new DebugAdapterClientOptions(); |
| 46 | + optionsAction(options); |
| 47 | + return From(options, token); |
| 48 | + } |
| 49 | + |
| 50 | + public static IDebugAdapterClient PreInit(Action<DebugAdapterClientOptions> optionsAction) |
| 51 | + { |
| 52 | + var options = new DebugAdapterClientOptions(); |
| 53 | + optionsAction(options); |
| 54 | + return PreInit(options); |
| 55 | + } |
| 56 | + |
| 57 | + public static async Task<IDebugAdapterClient> From(DebugAdapterClientOptions options, CancellationToken token) |
| 58 | + { |
| 59 | + var server = (DebugAdapterClient) PreInit(options); |
| 60 | + await server.Initialize(token); |
| 61 | + |
| 62 | + return server; |
| 63 | + } |
| 64 | + |
| 65 | + /// <summary> |
| 66 | + /// Create the server without connecting to the client |
| 67 | + /// |
| 68 | + /// Mainly used for unit testing |
| 69 | + /// </summary> |
| 70 | + /// <param name="options"></param> |
| 71 | + /// <returns></returns> |
| 72 | + public static IDebugAdapterClient PreInit(DebugAdapterClientOptions options) |
| 73 | + { |
| 74 | + return new DebugAdapterClient(options); |
| 75 | + } |
| 76 | + |
| 77 | + internal DebugAdapterClient(DebugAdapterClientOptions options) : base(options) |
| 78 | + { |
| 79 | + var services = options.Services; |
| 80 | + services.AddLogging(builder => options.LoggingBuilderAction(builder)); |
| 81 | + |
| 82 | + ClientSettings = new InitializeRequestArguments() { |
| 83 | + Locale = options.Locale, |
| 84 | + AdapterId = options.AdapterId, |
| 85 | + ClientId = options.ClientId, |
| 86 | + ClientName = options.ClientName, |
| 87 | + PathFormat = options.PathFormat, |
| 88 | + ColumnsStartAt1 = options.ColumnsStartAt1, |
| 89 | + LinesStartAt1 = options.LinesStartAt1, |
| 90 | + SupportsMemoryReferences = options.SupportsMemoryReferences, |
| 91 | + SupportsProgressReporting = options.SupportsProgressReporting, |
| 92 | + SupportsVariablePaging = options.SupportsVariablePaging, |
| 93 | + SupportsVariableType = options.SupportsVariableType, |
| 94 | + SupportsRunInTerminalRequest = options.SupportsRunInTerminalRequest, |
| 95 | + }; |
| 96 | + |
| 97 | + var serializer = options.Serializer; |
| 98 | + var collection = new DebugAdapterHandlerCollection(); |
| 99 | + services.AddSingleton<IHandlersManager>(collection); |
| 100 | + _collection = collection; |
| 101 | + // _initializeDelegates = initializeDelegates; |
| 102 | + // _initializedDelegates = initializedDelegates; |
| 103 | + _startedDelegates = options.StartedDelegates; |
| 104 | + |
| 105 | + var receiver = _receiver = new DapReceiver(); |
| 106 | + |
| 107 | + services.AddSingleton<IOutputHandler>(_ => |
| 108 | + new OutputHandler(options.Output, options.Serializer, receiver.ShouldFilterOutput, _.GetService<ILogger<OutputHandler>>())); |
| 109 | + services.AddSingleton(_collection); |
| 110 | + services.AddSingleton(serializer); |
| 111 | + services.AddSingleton(options.RequestProcessIdentifier); |
| 112 | + services.AddSingleton(receiver); |
| 113 | + services.AddSingleton<IDebugAdapterClient>(this); |
| 114 | + services.AddSingleton<DebugAdapterRequestRouter>(); |
| 115 | + services.AddSingleton<IRequestRouter<IHandlerDescriptor>>(_ => _.GetRequiredService<DebugAdapterRequestRouter>()); |
| 116 | + services.AddSingleton<IResponseRouter, DapResponseRouter>(); |
| 117 | + |
| 118 | + services.AddSingleton<IClientProgressManager, ClientProgressManager>(); |
| 119 | + services.AddSingleton(_ => _.GetRequiredService<IClientProgressManager>() as IJsonRpcHandler); |
| 120 | + |
| 121 | + EnsureAllHandlersAreRegistered(); |
| 122 | + |
| 123 | + var serviceProvider = services.BuildServiceProvider(); |
| 124 | + _disposable.Add(serviceProvider); |
| 125 | + IServiceProvider serviceProvider1 = serviceProvider; |
| 126 | + |
| 127 | + var responseRouter = serviceProvider1.GetRequiredService<IResponseRouter>(); |
| 128 | + ResponseRouter = responseRouter; |
| 129 | + _progressManager = serviceProvider1.GetRequiredService<IClientProgressManager>(); |
| 130 | + |
| 131 | + _connection = new Connection( |
| 132 | + options.Input, |
| 133 | + serviceProvider1.GetRequiredService<IOutputHandler>(), |
| 134 | + receiver, |
| 135 | + options.RequestProcessIdentifier, |
| 136 | + serviceProvider1.GetRequiredService<IRequestRouter<IHandlerDescriptor>>(), |
| 137 | + responseRouter, |
| 138 | + serviceProvider1.GetRequiredService<ILoggerFactory>(), |
| 139 | + options.OnUnhandledException ?? (e => { }), |
| 140 | + options.CreateResponseException, |
| 141 | + options.MaximumRequestTimeout, |
| 142 | + false, |
| 143 | + options.Concurrency |
| 144 | + ); |
| 145 | + |
| 146 | + var serviceHandlers = serviceProvider1.GetServices<IJsonRpcHandler>().ToArray(); |
| 147 | + _collection.Add(this); |
| 148 | + _disposable.Add(_collection.Add(serviceHandlers)); |
| 149 | + options.AddLinks(_collection); |
| 150 | + } |
| 151 | + |
| 152 | + public async Task Initialize(CancellationToken token) |
| 153 | + { |
| 154 | + RegisterCapabilities(ClientSettings); |
| 155 | + |
| 156 | + _connection.Open(); |
| 157 | + var serverParams = await this.RequestInitialize(ClientSettings, token); |
| 158 | + |
| 159 | + ServerSettings = serverParams; |
| 160 | + _receiver.Initialized(); |
| 161 | + |
| 162 | + await _initializedComplete.ToTask(token); |
| 163 | + |
| 164 | + await _startedDelegates.Select(@delegate => Observable.FromAsync(() => @delegate(this, serverParams, token))) |
| 165 | + .ToObservable() |
| 166 | + .Merge() |
| 167 | + .LastOrDefaultAsync() |
| 168 | + .ToTask(token); |
| 169 | + } |
| 170 | + |
| 171 | + Task<Unit> IRequestHandler<InitializedEvent, Unit>.Handle(InitializedEvent request, CancellationToken cancellationToken) |
| 172 | + { |
| 173 | + _initializedComplete.OnNext(request); |
| 174 | + _initializedComplete.OnCompleted(); |
| 175 | + return Unit.Task; |
| 176 | + } |
| 177 | + |
| 178 | + private void RegisterCapabilities(InitializeRequestArguments capabilities) |
| 179 | + { |
| 180 | + capabilities.SupportsRunInTerminalRequest ??= _collection.ContainsHandler(typeof(IRunInTerminalHandler)); |
| 181 | + capabilities.SupportsProgressReporting ??= _collection.ContainsHandler(typeof(IProgressStartHandler)) && |
| 182 | + _collection.ContainsHandler(typeof(IProgressUpdateHandler)) && |
| 183 | + _collection.ContainsHandler(typeof(IProgressEndHandler)); |
| 184 | + } |
| 185 | + |
| 186 | + protected override IResponseRouter ResponseRouter { get; } |
| 187 | + protected override IHandlersManager HandlersManager => _collection; |
| 188 | + public InitializeRequestArguments ClientSettings { get; } |
| 189 | + public InitializeResponse ServerSettings { get; private set; } |
| 190 | + public IClientProgressManager ProgressManager => _progressManager; |
| 191 | + |
| 192 | + public void Dispose() |
| 193 | + { |
| 194 | + _disposable?.Dispose(); |
| 195 | + _connection?.Dispose(); |
| 196 | + } |
| 197 | + } |
| 198 | +} |
0 commit comments