diff --git a/src/PowerShellEditorServices.Host/EditorServicesHost.cs b/src/PowerShellEditorServices.Host/EditorServicesHost.cs index ccabf9ba8..0904b3c60 100644 --- a/src/PowerShellEditorServices.Host/EditorServicesHost.cs +++ b/src/PowerShellEditorServices.Host/EditorServicesHost.cs @@ -183,6 +183,14 @@ private async void OnLanguageServiceClientConnect( object sender, TcpSocketServerChannel serverChannel) { + MessageDispatcher messageDispatcher = new MessageDispatcher(this.logger); + + ProtocolEndpoint protocolEndpoint = + new ProtocolEndpoint( + serverChannel, + messageDispatcher, + this.logger); + this.editorSession = CreateSession( this.hostDetails, @@ -192,7 +200,8 @@ private async void OnLanguageServiceClientConnect( this.languageServer = new LanguageServer( this.editorSession, - serverChannel, + messageDispatcher, + protocolEndpoint, this.logger); await this.editorSession.PowerShellContext.ImportCommandsModule( @@ -200,7 +209,8 @@ await this.editorSession.PowerShellContext.ImportCommandsModule( Path.GetDirectoryName(this.GetType().GetTypeInfo().Assembly.Location), @"..\..\Commands")); - await this.languageServer.Start(); + this.languageServer.Start(); + protocolEndpoint.Start(); } /// @@ -214,7 +224,7 @@ public void StartDebugService( { this.debugServiceListener = new TcpSocketServerListener( - MessageProtocolType.LanguageServer, + MessageProtocolType.DebugAdapter, debugServicePort, this.logger); @@ -228,15 +238,24 @@ public void StartDebugService( debugServicePort)); } - private async void OnDebugServiceClientConnect(object sender, TcpSocketServerChannel serverChannel) + private void OnDebugServiceClientConnect(object sender, TcpSocketServerChannel serverChannel) { + MessageDispatcher messageDispatcher = new MessageDispatcher(this.logger); + + ProtocolEndpoint protocolEndpoint = + new ProtocolEndpoint( + serverChannel, + messageDispatcher, + this.logger); + if (this.enableConsoleRepl) { this.debugAdapter = new DebugAdapter( this.editorSession, - serverChannel, false, + messageDispatcher, + protocolEndpoint, this.logger); } else @@ -250,8 +269,9 @@ private async void OnDebugServiceClientConnect(object sender, TcpSocketServerCha this.debugAdapter = new DebugAdapter( debugSession, - serverChannel, true, + messageDispatcher, + protocolEndpoint, this.logger); } @@ -265,7 +285,8 @@ private async void OnDebugServiceClientConnect(object sender, TcpSocketServerCha this.debugServiceListener.Start(); }; - await this.debugAdapter.Start(); + this.debugAdapter.Start(); + protocolEndpoint.Start(); } /// @@ -273,10 +294,10 @@ private async void OnDebugServiceClientConnect(object sender, TcpSocketServerCha /// public void StopServices() { - this.languageServer?.Stop().Wait(); + // TODO: Need a new way to shut down the services + this.languageServer = null; - this.debugAdapter?.Stop().Wait(); this.debugAdapter = null; } diff --git a/src/PowerShellEditorServices.Protocol/Client/DebugAdapterClientBase.cs b/src/PowerShellEditorServices.Protocol/Client/DebugAdapterClientBase.cs index aeab98bd8..c90730a6d 100644 --- a/src/PowerShellEditorServices.Protocol/Client/DebugAdapterClientBase.cs +++ b/src/PowerShellEditorServices.Protocol/Client/DebugAdapterClientBase.cs @@ -6,20 +6,46 @@ using Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter; using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol; using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel; -using System.Threading.Tasks; using Microsoft.PowerShell.EditorServices.Utility; +using System.Threading.Tasks; +using System; namespace Microsoft.PowerShell.EditorServices.Protocol.Client { - public class DebugAdapterClient : ProtocolEndpoint + public class DebugAdapterClient : IMessageSender, IMessageHandlers { + private ILogger logger; + private ProtocolEndpoint protocolEndpoint; + private MessageDispatcher messageDispatcher; + public DebugAdapterClient(ChannelBase clientChannel, ILogger logger) - : base( + { + this.logger = logger; + this.messageDispatcher = new MessageDispatcher(logger); + this.protocolEndpoint = new ProtocolEndpoint( clientChannel, - new MessageDispatcher(logger), - MessageProtocolType.DebugAdapter, - logger) + messageDispatcher, + logger); + } + + public async Task Start() + { + this.protocolEndpoint.Start(); + + // Initialize the debug adapter + await this.SendRequest( + InitializeRequest.Type, + new InitializeRequestArguments + { + LinesStartAt1 = true, + ColumnsStartAt1 = true + }, + true); + } + + public void Stop() { + this.protocolEndpoint.Stop(); } public async Task LaunchScript(string scriptFilePath) @@ -28,21 +54,43 @@ await this.SendRequest( LaunchRequest.Type, new LaunchRequestArguments { Script = scriptFilePath - }); + }, + true); - await this.SendRequest(ConfigurationDoneRequest.Type, null); + await this.SendRequest( + ConfigurationDoneRequest.Type, + null, + true); } - protected override Task OnStart() + public Task SendEvent(NotificationType eventType, TParams eventParams) { - // Initialize the debug adapter - return this.SendRequest( - InitializeRequest.Type, - new InitializeRequestArguments - { - LinesStartAt1 = true, - ColumnsStartAt1 = true - }); + return ((IMessageSender)protocolEndpoint).SendEvent(eventType, eventParams); + } + + public Task SendRequest(RequestType requestType, TParams requestParams, bool waitForResponse) + { + return ((IMessageSender)protocolEndpoint).SendRequest(requestType, requestParams, waitForResponse); + } + + public Task SendRequest(RequestType0 requestType0) + { + return ((IMessageSender)protocolEndpoint).SendRequest(requestType0); + } + + public void SetRequestHandler(RequestType requestType, Func, Task> requestHandler) + { + ((IMessageHandlers)messageDispatcher).SetRequestHandler(requestType, requestHandler); + } + + public void SetRequestHandler(RequestType0 requestType0, Func, Task> requestHandler) + { + ((IMessageHandlers)messageDispatcher).SetRequestHandler(requestType0, requestHandler); + } + + public void SetEventHandler(NotificationType eventType, Func eventHandler) + { + ((IMessageHandlers)messageDispatcher).SetEventHandler(eventType, eventHandler); } } } diff --git a/src/PowerShellEditorServices.Protocol/Client/LanguageClientBase.cs b/src/PowerShellEditorServices.Protocol/Client/LanguageClientBase.cs index e7d780263..c8dc383dc 100644 --- a/src/PowerShellEditorServices.Protocol/Client/LanguageClientBase.cs +++ b/src/PowerShellEditorServices.Protocol/Client/LanguageClientBase.cs @@ -6,44 +6,97 @@ using Microsoft.PowerShell.EditorServices.Protocol.LanguageServer; using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol; using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel; -using System.Threading.Tasks; using Microsoft.PowerShell.EditorServices.Utility; +using System; +using System.Threading.Tasks; namespace Microsoft.PowerShell.EditorServices.Protocol.Client { /// /// Provides a base implementation for language server clients. /// - public abstract class LanguageClientBase : ProtocolEndpoint + public abstract class LanguageClientBase : IMessageHandlers, IMessageSender { + ILogger logger; + private ProtocolEndpoint protocolEndpoint; + private MessageDispatcher messageDispatcher; + /// /// Initializes an instance of the language client using the /// specified channel for communication. /// /// The channel to use for communication with the server. public LanguageClientBase(ChannelBase clientChannel, ILogger logger) - : base( - clientChannel, - new MessageDispatcher(logger), - MessageProtocolType.LanguageServer, - logger) { + this.logger = logger; + this.messageDispatcher = new MessageDispatcher(logger); + this.protocolEndpoint = new ProtocolEndpoint( + clientChannel, + messageDispatcher, + logger); } - protected override Task OnStart() + public Task Start() { + this.protocolEndpoint.Start(); + // Initialize the implementation class return this.Initialize(); } - protected override async Task OnStop() + public async Task Stop() { + await this.OnStop(); + // First, notify the language server that we're stopping - var response = await this.SendRequest(ShutdownRequest.Type); + var response = + await this.SendRequest( + ShutdownRequest.Type); + await this.SendEvent(ExitNotification.Type, new object()); + + this.protocolEndpoint.Stop(); + } + + protected virtual Task OnStop() + { + return Task.FromResult(true); + } + + protected virtual Task Initialize() + { + return Task.FromResult(true); + } + + public Task SendEvent(NotificationType eventType, TParams eventParams) + { + return ((IMessageSender)protocolEndpoint).SendEvent(eventType, eventParams); + } + + public Task SendRequest(RequestType requestType, TParams requestParams, bool waitForResponse) + { + return ((IMessageSender)protocolEndpoint).SendRequest(requestType, requestParams, waitForResponse); + } + + public Task SendRequest(RequestType0 requestType0) + { + return ((IMessageSender)protocolEndpoint).SendRequest(requestType0); } - protected abstract Task Initialize(); + public void SetRequestHandler(RequestType requestType, Func, Task> requestHandler) + { + ((IMessageHandlers)messageDispatcher).SetRequestHandler(requestType, requestHandler); + } + + public void SetRequestHandler(RequestType0 requestType0, Func, Task> requestHandler) + { + ((IMessageHandlers)messageDispatcher).SetRequestHandler(requestType0, requestHandler); + } + + public void SetEventHandler(NotificationType eventType, Func eventHandler) + { + ((IMessageHandlers)messageDispatcher).SetEventHandler(eventType, eventHandler); + } } } diff --git a/src/PowerShellEditorServices.Protocol/Client/LanguageServiceClient.cs b/src/PowerShellEditorServices.Protocol/Client/LanguageServiceClient.cs index 119fd09f8..8878814dd 100644 --- a/src/PowerShellEditorServices.Protocol/Client/LanguageServiceClient.cs +++ b/src/PowerShellEditorServices.Protocol/Client/LanguageServiceClient.cs @@ -38,7 +38,8 @@ protected override Task Initialize() return this.SendRequest( InitializeRequest.Type, - initializeParams); + initializeParams, + true); } #region Events diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/IMessageHandlers.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/IMessageHandlers.cs new file mode 100644 index 000000000..04e1ba9d6 --- /dev/null +++ b/src/PowerShellEditorServices.Protocol/MessageProtocol/IMessageHandlers.cs @@ -0,0 +1,25 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// + +using System; +using System.Threading.Tasks; + +namespace Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol +{ + public interface IMessageHandlers + { + void SetRequestHandler( + RequestType requestType, + Func, Task> requestHandler); + + void SetRequestHandler( + RequestType0 requestType0, + Func, Task> requestHandler); + + void SetEventHandler( + NotificationType eventType, + Func eventHandler); + } +} diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/IMessageSender.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/IMessageSender.cs index 5b4da2452..e32556cdc 100644 --- a/src/PowerShellEditorServices.Protocol/MessageProtocol/IMessageSender.cs +++ b/src/PowerShellEditorServices.Protocol/MessageProtocol/IMessageSender.cs @@ -7,7 +7,7 @@ namespace Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol { - internal interface IMessageSender + public interface IMessageSender { Task SendEvent( NotificationType eventType, @@ -17,6 +17,9 @@ Task SendRequest( RequestType requestType, TParams requestParams, bool waitForResponse); + + Task SendRequest( + RequestType0 requestType0); } } diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageDispatcher.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageDispatcher.cs index 4338ca3b3..8ad9557c7 100644 --- a/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageDispatcher.cs +++ b/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageDispatcher.cs @@ -13,7 +13,7 @@ namespace Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol { - public class MessageDispatcher + public class MessageDispatcher : IMessageHandlers { #region Fields @@ -79,6 +79,18 @@ public void SetRequestHandler( }); } + public void SetRequestHandler( + RequestType0 requestType0, + Func, Task> requestHandler) + { + this.SetRequestHandler( + RequestType.ConvertToRequestType(requestType0), + (param1, requestContext) => + { + return requestHandler(requestContext); + }); + } + public void SetEventHandler( NotificationType eventType, Func eventHandler) @@ -86,7 +98,7 @@ public void SetEventHandler( this.SetEventHandler( eventType, eventHandler, - false); + true); } public void SetEventHandler( diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/ProtocolEndpoint.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/ProtocolEndpoint.cs index 4a11d4eec..037d70290 100644 --- a/src/PowerShellEditorServices.Protocol/MessageProtocol/ProtocolEndpoint.cs +++ b/src/PowerShellEditorServices.Protocol/MessageProtocol/ProtocolEndpoint.cs @@ -30,7 +30,6 @@ private enum ProtocolEndpointState private int currentMessageId; private ChannelBase protocolChannel; private AsyncContextThread messageLoopThread; - private MessageProtocolType messageProtocolType; private TaskCompletionSource endpointExitedTask; private SynchronizationContext originalSynchronizationContext; private CancellationTokenSource messageLoopCancellationToken = @@ -74,12 +73,10 @@ private bool InMessageLoopThread public ProtocolEndpoint( ChannelBase protocolChannel, MessageDispatcher messageDispatcher, - MessageProtocolType messageProtocolType, ILogger logger) { this.protocolChannel = protocolChannel; this.MessageDispatcher = messageDispatcher; - this.messageProtocolType = messageProtocolType; this.originalSynchronizationContext = SynchronizationContext.Current; this.Logger = logger; } @@ -87,23 +84,16 @@ public ProtocolEndpoint( /// /// Starts the language server client and sends the Initialize method. /// - /// A Task that can be awaited for initialization to complete. - public async Task Start() + public void Start() { if (this.currentState == ProtocolEndpointState.NotStarted) { - // Start the provided protocol channel - this.protocolChannel.Start(this.messageProtocolType); - // Listen for unhandled exceptions from the message loop this.UnhandledException += MessageDispatcher_UnhandledException; // Start the message loop this.StartMessageLoop(); - // Notify implementation about endpoint start - await this.OnStart(); - // Endpoint is now started this.currentState = ProtocolEndpointState.Started; } @@ -115,22 +105,18 @@ public void WaitForExit() this.endpointExitedTask.Task.Wait(); } - public async Task Stop() + public void Stop() { if (this.currentState == ProtocolEndpointState.Started) { // Make sure no future calls try to stop the endpoint during shutdown this.currentState = ProtocolEndpointState.Shutdown; - // Stop the implementation first - await this.OnStop(); - // Stop the message loop and channel this.StopMessageLoop(); this.protocolChannel.Stop(); // Notify anyone waiting for exit - this.OnSessionEnded(); if (this.endpointExitedTask != null) { this.endpointExitedTask.SetResult(true); @@ -261,18 +247,6 @@ await this.protocolChannel.MessageWriter.WriteEvent( #region Message Handling - public void SetRequestHandler( - RequestType0 requestType0, - Func, Task> requestHandler) - { - SetRequestHandler( - RequestType.ConvertToRequestType(requestType0), - (param1, requestContext) => - { - return requestHandler(requestContext); - }); - } - public void SetRequestHandler( RequestType requestType, Func, Task> requestHandler) @@ -340,29 +314,8 @@ private void StopMessageLoop() #endregion - #region Subclass Lifetime Methods - - protected virtual Task OnStart() - { - return Task.FromResult(true); - } - - protected virtual Task OnStop() - { - return Task.FromResult(true); - } - - #endregion - #region Events - public event EventHandler SessionEnded; - - protected virtual void OnSessionEnded() - { - this.SessionEnded?.Invoke(this, null); - } - public event EventHandler UnhandledException; protected void OnUnhandledException(Exception unhandledException) @@ -442,11 +395,9 @@ private async Task ListenForMessages(CancellationToken cancellationToken) } catch (Exception e) { - Logger.Write( - LogLevel.Verbose, - "Caught unexpected exception '{0}' in MessageDispatcher loop:\r\n{1}", - e.GetType().Name, - e.Message); + Logger.WriteException( + "Caught unhandled exception in ProtocolEndpoint message loop", + e); } // The message could be null if there was an error parsing the diff --git a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs index 56200d1db..024b06dc2 100644 --- a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs +++ b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs @@ -20,12 +20,13 @@ namespace Microsoft.PowerShell.EditorServices.Protocol.Server { - public class DebugAdapter : DebugAdapterBase + public class DebugAdapter { private EditorSession editorSession; private OutputDebouncer outputDebouncer; private bool noDebug; + private ILogger Logger; private string arguments; private bool isRemoteAttach; private bool isAttachSession; @@ -34,47 +35,53 @@ public class DebugAdapter : DebugAdapterBase private bool enableConsoleRepl; private bool ownsEditorSession; private bool executionCompleted; + private IMessageSender messageSender; + private IMessageHandlers messageHandlers; private bool isInteractiveDebugSession; private RequestContext disconnectRequestContext = null; public DebugAdapter( EditorSession editorSession, - ChannelBase serverChannel, bool ownsEditorSession, + IMessageHandlers messageHandlers, + IMessageSender messageSender, ILogger logger) - : base(serverChannel, new MessageDispatcher(logger), logger) { + this.Logger = logger; this.editorSession = editorSession; + this.messageSender = messageSender; + this.messageHandlers = messageHandlers; this.ownsEditorSession = ownsEditorSession; this.enableConsoleRepl = editorSession.UsesConsoleHost; } - protected override void Initialize() + public void Start() { // Register all supported message types - - this.SetRequestHandler(LaunchRequest.Type, this.HandleLaunchRequest); - this.SetRequestHandler(AttachRequest.Type, this.HandleAttachRequest); - this.SetRequestHandler(ConfigurationDoneRequest.Type, this.HandleConfigurationDoneRequest); - this.SetRequestHandler(DisconnectRequest.Type, this.HandleDisconnectRequest); - - this.SetRequestHandler(SetBreakpointsRequest.Type, this.HandleSetBreakpointsRequest); - this.SetRequestHandler(SetExceptionBreakpointsRequest.Type, this.HandleSetExceptionBreakpointsRequest); - this.SetRequestHandler(SetFunctionBreakpointsRequest.Type, this.HandleSetFunctionBreakpointsRequest); - - this.SetRequestHandler(ContinueRequest.Type, this.HandleContinueRequest); - this.SetRequestHandler(NextRequest.Type, this.HandleNextRequest); - this.SetRequestHandler(StepInRequest.Type, this.HandleStepInRequest); - this.SetRequestHandler(StepOutRequest.Type, this.HandleStepOutRequest); - this.SetRequestHandler(PauseRequest.Type, this.HandlePauseRequest); - - this.SetRequestHandler(ThreadsRequest.Type, this.HandleThreadsRequest); - this.SetRequestHandler(StackTraceRequest.Type, this.HandleStackTraceRequest); - this.SetRequestHandler(ScopesRequest.Type, this.HandleScopesRequest); - this.SetRequestHandler(VariablesRequest.Type, this.HandleVariablesRequest); - this.SetRequestHandler(SetVariableRequest.Type, this.HandleSetVariablesRequest); - this.SetRequestHandler(SourceRequest.Type, this.HandleSourceRequest); - this.SetRequestHandler(EvaluateRequest.Type, this.HandleEvaluateRequest); + this.messageHandlers.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest); + + this.messageHandlers.SetRequestHandler(LaunchRequest.Type, this.HandleLaunchRequest); + this.messageHandlers.SetRequestHandler(AttachRequest.Type, this.HandleAttachRequest); + this.messageHandlers.SetRequestHandler(ConfigurationDoneRequest.Type, this.HandleConfigurationDoneRequest); + this.messageHandlers.SetRequestHandler(DisconnectRequest.Type, this.HandleDisconnectRequest); + + this.messageHandlers.SetRequestHandler(SetBreakpointsRequest.Type, this.HandleSetBreakpointsRequest); + this.messageHandlers.SetRequestHandler(SetExceptionBreakpointsRequest.Type, this.HandleSetExceptionBreakpointsRequest); + this.messageHandlers.SetRequestHandler(SetFunctionBreakpointsRequest.Type, this.HandleSetFunctionBreakpointsRequest); + + this.messageHandlers.SetRequestHandler(ContinueRequest.Type, this.HandleContinueRequest); + this.messageHandlers.SetRequestHandler(NextRequest.Type, this.HandleNextRequest); + this.messageHandlers.SetRequestHandler(StepInRequest.Type, this.HandleStepInRequest); + this.messageHandlers.SetRequestHandler(StepOutRequest.Type, this.HandleStepOutRequest); + this.messageHandlers.SetRequestHandler(PauseRequest.Type, this.HandlePauseRequest); + + this.messageHandlers.SetRequestHandler(ThreadsRequest.Type, this.HandleThreadsRequest); + this.messageHandlers.SetRequestHandler(StackTraceRequest.Type, this.HandleStackTraceRequest); + this.messageHandlers.SetRequestHandler(ScopesRequest.Type, this.HandleScopesRequest); + this.messageHandlers.SetRequestHandler(VariablesRequest.Type, this.HandleVariablesRequest); + this.messageHandlers.SetRequestHandler(SetVariableRequest.Type, this.HandleSetVariablesRequest); + this.messageHandlers.SetRequestHandler(SourceRequest.Type, this.HandleSourceRequest); + this.messageHandlers.SetRequestHandler(EvaluateRequest.Type, this.HandleEvaluateRequest); } protected Task LaunchScript(RequestContext requestContext) @@ -146,17 +153,17 @@ private async Task OnExecutionCompleted(Task executeTask) { // Respond to the disconnect request and stop the server await this.disconnectRequestContext.SendResult(null); - await this.Stop(); + this.Stop(); } else { - await this.SendEvent( + await this.messageSender.SendEvent( TerminatedEvent.Type, new TerminatedEvent()); } } - protected override void Shutdown() + protected void Stop() { Logger.Write(LogLevel.Normal, "Debug adapter is shutting down..."); @@ -179,10 +186,27 @@ protected override void Shutdown() this.editorSession = null; } + + this.OnSessionEnded(); } #region Built-in Message Handlers + private async Task HandleInitializeRequest( + object shutdownParams, + RequestContext requestContext) + { + // Now send the Initialize response to continue setup + await requestContext.SendResult( + new InitializeResponseBody { + SupportsConfigurationDoneRequest = true, + SupportsFunctionBreakpoints = true, + SupportsConditionalBreakpoints = true, + SupportsHitConditionalBreakpoints = true, + SupportsSetVariable = true + }); + } + protected async Task HandleConfigurationDoneRequest( object args, RequestContext requestContext) @@ -309,7 +333,7 @@ protected async Task HandleLaunchRequest( // Send the InitializedEvent so that the debugger will continue // sending configuration requests - await this.SendEvent( + await this.messageSender.SendEvent( InitializedEvent.Type, null); } @@ -449,7 +473,7 @@ protected async Task HandleDisconnectRequest( this.UnregisterEventHandlers(); await requestContext.SendResult(null); - await this.Stop(); + this.Stop(); } } } @@ -824,7 +848,7 @@ await requestContext.SendResult( private async Task WriteUseIntegratedConsoleMessage() { - await this.SendEvent( + await this.messageSender.SendEvent( OutputEvent.Type, new OutputEventBody { @@ -842,7 +866,7 @@ private void RegisterEventHandlers() if (!this.enableConsoleRepl) { this.editorSession.ConsoleService.OutputWritten += this.powerShellContext_OutputWritten; - this.outputDebouncer = new OutputDebouncer(this); + this.outputDebouncer = new OutputDebouncer(this.messageSender); } } @@ -888,7 +912,7 @@ e.OriginalEvent.Breakpoints[0] is CommandBreakpoint : "breakpoint"; } - await this.SendEvent( + await this.messageSender.SendEvent( StoppedEvent.Type, new StoppedEventBody { @@ -910,7 +934,7 @@ async void powerShellContext_RunspaceChanged(object sender, RunspaceChangedEvent // Send the InitializedEvent so that the debugger will continue // sending configuration requests this.waitingForAttach = false; - await this.SendEvent(InitializedEvent.Type, null); + await this.messageSender.SendEvent(InitializedEvent.Type, null); } else if ( e.ChangeAction == RunspaceChangeAction.Exit && @@ -920,7 +944,7 @@ async void powerShellContext_RunspaceChanged(object sender, RunspaceChangedEvent // Exited the session while the debugger is stopped, // send a ContinuedEvent so that the client changes the // UI to appear to be running again - await this.SendEvent( + await this.messageSender.SendEvent( ContinuedEvent.Type, new ContinuedEvent { @@ -932,7 +956,7 @@ await this.SendEvent( private async void powerShellContext_DebuggerResumed(object sender, DebuggerResumeAction e) { - await this.SendEvent( + await this.messageSender.SendEvent( ContinuedEvent.Type, new ContinuedEvent { @@ -942,5 +966,16 @@ await this.SendEvent( } #endregion + + #region Events + + public event EventHandler SessionEnded; + + protected virtual void OnSessionEnded() + { + this.SessionEnded?.Invoke(this, null); + } + + #endregion } } diff --git a/src/PowerShellEditorServices.Protocol/Server/DebugAdapterBase.cs b/src/PowerShellEditorServices.Protocol/Server/DebugAdapterBase.cs deleted file mode 100644 index b804ed0fe..000000000 --- a/src/PowerShellEditorServices.Protocol/Server/DebugAdapterBase.cs +++ /dev/null @@ -1,76 +0,0 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -using Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter; -using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol; -using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel; -using System.Threading.Tasks; -using Microsoft.PowerShell.EditorServices.Utility; - -namespace Microsoft.PowerShell.EditorServices.Protocol.Server -{ - public abstract class DebugAdapterBase : ProtocolEndpoint - { - public DebugAdapterBase( - ChannelBase serverChannel, - MessageDispatcher messageDispatcher, - ILogger logger) - : base( - serverChannel, - messageDispatcher, - MessageProtocolType.DebugAdapter, - logger) - { - } - - /// - /// Overridden by the subclass to provide initialization - /// logic after the server channel is started. - /// - protected abstract void Initialize(); - - /// - /// Can be overridden by the subclass to provide shutdown - /// logic before the server exits. - /// - protected virtual void Shutdown() - { - // No default implementation yet. - } - - protected override Task OnStart() - { - // Register handlers for server lifetime messages - this.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest); - - // Initialize the implementation class - this.Initialize(); - - return Task.FromResult(true); - } - - protected override Task OnStop() - { - this.Shutdown(); - - return Task.FromResult(true); - } - - private async Task HandleInitializeRequest( - object shutdownParams, - RequestContext requestContext) - { - // Now send the Initialize response to continue setup - await requestContext.SendResult( - new InitializeResponseBody { - SupportsConfigurationDoneRequest = true, - SupportsFunctionBreakpoints = true, - SupportsConditionalBreakpoints = true, - SupportsHitConditionalBreakpoints = true, - SupportsSetVariable = true - }); - } - } -} diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs index a528aad2f..f52b95661 100644 --- a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs +++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs @@ -25,13 +25,16 @@ namespace Microsoft.PowerShell.EditorServices.Protocol.Server { - public class LanguageServer : LanguageServerBase + public class LanguageServer { private static CancellationTokenSource existingRequestCancellation; + private ILogger Logger; private bool profilesLoaded; private bool consoleReplStarted; private EditorSession editorSession; + private IMessageSender messageSender; + private IMessageHandlers messageHandlers; private OutputDebouncer outputDebouncer; private LanguageServerEditorOperations editorOperations; private LanguageServerSettings currentSettings = new LanguageServerSettings(); @@ -49,10 +52,11 @@ public IEditorOperations EditorOperations /// public LanguageServer( EditorSession editorSession, - ChannelBase serverChannel, + IMessageHandlers messageHandlers, + IMessageSender messageSender, ILogger logger) - : base(serverChannel, new MessageDispatcher(logger), logger) { + this.Logger = logger; this.editorSession = editorSession; this.editorSession.PowerShellContext.RunspaceChanged += PowerShellContext_RunspaceChanged; @@ -61,11 +65,14 @@ public LanguageServer( this.editorSession.ExtensionService.CommandUpdated += ExtensionService_ExtensionUpdated; this.editorSession.ExtensionService.CommandRemoved += ExtensionService_ExtensionRemoved; + this.messageSender = messageSender; + this.messageHandlers = messageHandlers; + // Create the IEditorOperations implementation this.editorOperations = new LanguageServerEditorOperations( this.editorSession, - this); + this.messageSender); this.editorSession.StartDebugService(this.editorOperations); this.editorSession.DebugService.DebuggerStopped += DebugService_DebuggerStopped; @@ -78,67 +85,75 @@ public LanguageServer( // Always send console prompts through the UI in the language service this.editorSession.ConsoleService.PushPromptHandlerContext( new ProtocolPromptHandlerContext( - this, + this.messageSender, this.editorSession.ConsoleService)); } // Set up the output debouncer to throttle output event writes - this.outputDebouncer = new OutputDebouncer(this); + this.outputDebouncer = new OutputDebouncer(this.messageSender); } - protected override void Initialize() + /// + /// Starts the language server client and sends the Initialize method. + /// + /// A Task that can be awaited for initialization to complete. + public void Start() { // Register all supported message types - this.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest); + this.messageHandlers.SetRequestHandler(ShutdownRequest.Type, this.HandleShutdownRequest); + this.messageHandlers.SetEventHandler(ExitNotification.Type, this.HandleExitNotification); + + this.messageHandlers.SetRequestHandler(InitializeRequest.Type, this.HandleInitializeRequest); - this.SetEventHandler(DidOpenTextDocumentNotification.Type, this.HandleDidOpenTextDocumentNotification); - this.SetEventHandler(DidCloseTextDocumentNotification.Type, this.HandleDidCloseTextDocumentNotification); - this.SetEventHandler(DidSaveTextDocumentNotification.Type, this.HandleDidSaveTextDocumentNotification); - this.SetEventHandler(DidChangeTextDocumentNotification.Type, this.HandleDidChangeTextDocumentNotification); - this.SetEventHandler(DidChangeConfigurationNotification.Type, this.HandleDidChangeConfigurationNotification); + this.messageHandlers.SetEventHandler(DidOpenTextDocumentNotification.Type, this.HandleDidOpenTextDocumentNotification); + this.messageHandlers.SetEventHandler(DidCloseTextDocumentNotification.Type, this.HandleDidCloseTextDocumentNotification); + this.messageHandlers.SetEventHandler(DidSaveTextDocumentNotification.Type, this.HandleDidSaveTextDocumentNotification); + this.messageHandlers.SetEventHandler(DidChangeTextDocumentNotification.Type, this.HandleDidChangeTextDocumentNotification); + this.messageHandlers.SetEventHandler(DidChangeConfigurationNotification.Type, this.HandleDidChangeConfigurationNotification); - this.SetRequestHandler(DefinitionRequest.Type, this.HandleDefinitionRequest); - this.SetRequestHandler(ReferencesRequest.Type, this.HandleReferencesRequest); - this.SetRequestHandler(CompletionRequest.Type, this.HandleCompletionRequest); - this.SetRequestHandler(CompletionResolveRequest.Type, this.HandleCompletionResolveRequest); - this.SetRequestHandler(SignatureHelpRequest.Type, this.HandleSignatureHelpRequest); - this.SetRequestHandler(DocumentHighlightRequest.Type, this.HandleDocumentHighlightRequest); - this.SetRequestHandler(HoverRequest.Type, this.HandleHoverRequest); - this.SetRequestHandler(DocumentSymbolRequest.Type, this.HandleDocumentSymbolRequest); - this.SetRequestHandler(WorkspaceSymbolRequest.Type, this.HandleWorkspaceSymbolRequest); - this.SetRequestHandler(CodeActionRequest.Type, this.HandleCodeActionRequest); + this.messageHandlers.SetRequestHandler(DefinitionRequest.Type, this.HandleDefinitionRequest); + this.messageHandlers.SetRequestHandler(ReferencesRequest.Type, this.HandleReferencesRequest); + this.messageHandlers.SetRequestHandler(CompletionRequest.Type, this.HandleCompletionRequest); + this.messageHandlers.SetRequestHandler(CompletionResolveRequest.Type, this.HandleCompletionResolveRequest); + this.messageHandlers.SetRequestHandler(SignatureHelpRequest.Type, this.HandleSignatureHelpRequest); + this.messageHandlers.SetRequestHandler(DocumentHighlightRequest.Type, this.HandleDocumentHighlightRequest); + this.messageHandlers.SetRequestHandler(HoverRequest.Type, this.HandleHoverRequest); + this.messageHandlers.SetRequestHandler(DocumentSymbolRequest.Type, this.HandleDocumentSymbolRequest); + this.messageHandlers.SetRequestHandler(WorkspaceSymbolRequest.Type, this.HandleWorkspaceSymbolRequest); + this.messageHandlers.SetRequestHandler(CodeActionRequest.Type, this.HandleCodeActionRequest); - this.SetRequestHandler(ShowOnlineHelpRequest.Type, this.HandleShowOnlineHelpRequest); - this.SetRequestHandler(ExpandAliasRequest.Type, this.HandleExpandAliasRequest); + this.messageHandlers.SetRequestHandler(ShowOnlineHelpRequest.Type, this.HandleShowOnlineHelpRequest); + this.messageHandlers.SetRequestHandler(ExpandAliasRequest.Type, this.HandleExpandAliasRequest); - this.SetRequestHandler(FindModuleRequest.Type, this.HandleFindModuleRequest); - this.SetRequestHandler(InstallModuleRequest.Type, this.HandleInstallModuleRequest); + this.messageHandlers.SetRequestHandler(FindModuleRequest.Type, this.HandleFindModuleRequest); + this.messageHandlers.SetRequestHandler(InstallModuleRequest.Type, this.HandleInstallModuleRequest); - this.SetRequestHandler(InvokeExtensionCommandRequest.Type, this.HandleInvokeExtensionCommandRequest); + this.messageHandlers.SetRequestHandler(InvokeExtensionCommandRequest.Type, this.HandleInvokeExtensionCommandRequest); - this.SetRequestHandler(PowerShellVersionRequest.Type, this.HandlePowerShellVersionRequest); + this.messageHandlers.SetRequestHandler(PowerShellVersionRequest.Type, this.HandlePowerShellVersionRequest); - this.SetRequestHandler(NewProjectFromTemplateRequest.Type, this.HandleNewProjectFromTemplateRequest); - this.SetRequestHandler(GetProjectTemplatesRequest.Type, this.HandleGetProjectTemplatesRequest); + this.messageHandlers.SetRequestHandler(NewProjectFromTemplateRequest.Type, this.HandleNewProjectFromTemplateRequest); + this.messageHandlers.SetRequestHandler(GetProjectTemplatesRequest.Type, this.HandleGetProjectTemplatesRequest); - this.SetRequestHandler(DebugAdapterMessages.EvaluateRequest.Type, this.HandleEvaluateRequest); + this.messageHandlers.SetRequestHandler(DebugAdapterMessages.EvaluateRequest.Type, this.HandleEvaluateRequest); - this.SetRequestHandler(GetPSSARulesRequest.Type, this.HandleGetPSSARulesRequest); - this.SetRequestHandler(SetPSSARulesRequest.Type, this.HandleSetPSSARulesRequest); + this.messageHandlers.SetRequestHandler(GetPSSARulesRequest.Type, this.HandleGetPSSARulesRequest); + this.messageHandlers.SetRequestHandler(SetPSSARulesRequest.Type, this.HandleSetPSSARulesRequest); - this.SetRequestHandler(ScriptFileMarkersRequest.Type, this.HandleScriptFileMarkersRequest); - this.SetRequestHandler(ScriptRegionRequest.Type, this.HandleGetFormatScriptRegionRequest); + this.messageHandlers.SetRequestHandler(ScriptFileMarkersRequest.Type, this.HandleScriptFileMarkersRequest); + this.messageHandlers.SetRequestHandler(ScriptRegionRequest.Type, this.HandleGetFormatScriptRegionRequest); + + this.messageHandlers.SetRequestHandler(GetPSHostProcessesRequest.Type, this.HandleGetPSHostProcessesRequest); + this.messageHandlers.SetRequestHandler(CommentHelpRequest.Type, this.HandleCommentHelpRequest); - this.SetRequestHandler(GetPSHostProcessesRequest.Type, this.HandleGetPSHostProcessesRequest); - this.SetRequestHandler(CommentHelpRequest.Type, this.HandleCommentHelpRequest); // Initialize the extension service // TODO: This should be made awaited once Initialize is async! this.editorSession.ExtensionService.Initialize( this.editorOperations).Wait(); } - protected override async Task Shutdown() + protected async Task Stop() { // Stop the interactive terminal // TODO: This can happen at the host level @@ -149,15 +164,28 @@ protected override async Task Shutdown() Logger.Write(LogLevel.Normal, "Language service is shutting down..."); - if (this.editorSession != null) - { - this.editorSession.Dispose(); - this.editorSession = null; - } + // TODO: Raise an event so that the host knows to shut down } #region Built-in Message Handlers + private async Task HandleShutdownRequest( + RequestContext requestContext) + { + // Allow the implementor to shut down gracefully + await this.Stop(); + + await requestContext.SendResult(new object()); + } + + private async Task HandleExitNotification( + object exitParams, + EventContext eventContext) + { + // Stop the server channel + await this.Stop(); + } + protected async Task HandleInitializeRequest( InitializeParams initializeParams, RequestContext requestContext) @@ -239,7 +267,7 @@ private async Task HandleSetPSSARulesRequest( await RunScriptDiagnostics( new ScriptFile[] { scripFile }, editorSession, - this.SendEvent); + this.messageSender.SendEvent); await sendresult; } @@ -1193,7 +1221,7 @@ protected Task HandleEvaluateRequest( private async void PowerShellContext_RunspaceChanged(object sender, Session.RunspaceChangedEventArgs e) { - await this.SendEvent( + await this.messageSender.SendEvent( RunspaceChangedEvent.Type, new Protocol.LanguageServer.RunspaceDetails(e.NewRunspace)); } @@ -1206,7 +1234,7 @@ private async void powerShellContext_OutputWritten(object sender, OutputWrittenE private async void ExtensionService_ExtensionAdded(object sender, EditorCommand e) { - await this.SendEvent( + await this.messageSender.SendEvent( ExtensionCommandAddedNotification.Type, new ExtensionCommandAddedNotification { @@ -1217,7 +1245,7 @@ await this.SendEvent( private async void ExtensionService_ExtensionUpdated(object sender, EditorCommand e) { - await this.SendEvent( + await this.messageSender.SendEvent( ExtensionCommandUpdatedNotification.Type, new ExtensionCommandUpdatedNotification { @@ -1227,7 +1255,7 @@ await this.SendEvent( private async void ExtensionService_ExtensionRemoved(object sender, EditorCommand e) { - await this.SendEvent( + await this.messageSender.SendEvent( ExtensionCommandRemovedNotification.Type, new ExtensionCommandRemovedNotification { @@ -1239,7 +1267,7 @@ private async void DebugService_DebuggerStopped(object sender, DebuggerStoppedEv { if (!this.editorSession.DebugService.IsClientAttached) { - await this.SendEvent( + await this.messageSender.SendEvent( StartDebuggerEvent.Type, new StartDebuggerEvent()); } @@ -1294,7 +1322,7 @@ private Task RunScriptDiagnostics( EditorSession editorSession, EventContext eventContext) { - return RunScriptDiagnostics(filesToAnalyze, editorSession, this.SendEvent); + return RunScriptDiagnostics(filesToAnalyze, editorSession, this.messageSender.SendEvent); } private Task RunScriptDiagnostics( diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServerBase.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServerBase.cs deleted file mode 100644 index 869895e43..000000000 --- a/src/PowerShellEditorServices.Protocol/Server/LanguageServerBase.cs +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - -using Microsoft.PowerShell.EditorServices.Protocol.LanguageServer; -using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol; -using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel; -using Microsoft.PowerShell.EditorServices.Utility; -using System.Threading.Tasks; - -namespace Microsoft.PowerShell.EditorServices.Protocol.Server -{ - public abstract class LanguageServerBase : ProtocolEndpoint - { - private ChannelBase serverChannel; - - public LanguageServerBase( - ChannelBase serverChannel, - MessageDispatcher messageDispatcher, - ILogger logger) - : base( - serverChannel, - messageDispatcher, - MessageProtocolType.LanguageServer, - logger) - { - this.serverChannel = serverChannel; - } - - protected override Task OnStart() - { - // Register handlers for server lifetime messages - this.SetRequestHandler(ShutdownRequest.Type, this.HandleShutdownRequest); - this.SetEventHandler(ExitNotification.Type, this.HandleExitNotification); - - // Initialize the implementation class - this.Initialize(); - - return Task.FromResult(true); - } - - protected override async Task OnStop() - { - await this.Shutdown(); - } - - /// - /// Overridden by the subclass to provide initialization - /// logic after the server channel is started. - /// - protected abstract void Initialize(); - - /// - /// Can be overridden by the subclass to provide shutdown - /// logic before the server exits. Subclasses do not need - /// to invoke or return the value of the base implementation. - /// - protected virtual Task Shutdown() - { - // No default implementation yet. - return Task.FromResult(true); - } - - private async Task HandleShutdownRequest( - RequestContext requestContext) - { - // Allow the implementor to shut down gracefully - await this.Shutdown(); - - await requestContext.SendResult(new object()); - } - - private async Task HandleExitNotification( - object exitParams, - EventContext eventContext) - { - // Stop the server channel - await this.Stop(); - } - } -} - diff --git a/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs b/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs index 071a776d0..b6909a036 100644 --- a/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs +++ b/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs @@ -51,22 +51,26 @@ await this.LaunchService( waitForDebugger: false); //waitForDebugger: true); - this.protocolClient = - this.debugAdapterClient = - new DebugAdapterClient( - await TcpSocketClientChannel.Connect( - portNumbers.Item2, - MessageProtocolType.DebugAdapter, - this.logger), - this.logger); + this.debugAdapterClient = + new DebugAdapterClient( + await TcpSocketClientChannel.Connect( + portNumbers.Item2, + MessageProtocolType.DebugAdapter, + this.logger), + this.logger); + + this.messageSender = this.debugAdapterClient; + this.messageHandlers = this.debugAdapterClient; await this.debugAdapterClient.Start(); } - public async Task DisposeAsync() + public Task DisposeAsync() { - await this.debugAdapterClient.Stop(); + this.debugAdapterClient.Stop(); this.KillService(); + + return Task.FromResult(true); } [Fact] diff --git a/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs b/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs index ee32eb63f..abe684242 100644 --- a/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs +++ b/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs @@ -54,14 +54,16 @@ await this.LaunchService( waitForDebugger: false); //waitForDebugger: true); - this.protocolClient = - this.languageServiceClient = - new LanguageServiceClient( - await TcpSocketClientChannel.Connect( - portNumbers.Item1, - MessageProtocolType.LanguageServer, - this.logger), - this.logger); + this.languageServiceClient = + new LanguageServiceClient( + await TcpSocketClientChannel.Connect( + portNumbers.Item1, + MessageProtocolType.LanguageServer, + this.logger), + this.logger); + + this.messageSender = this.languageServiceClient; + this.messageHandlers = this.languageServiceClient; await this.languageServiceClient.Start(); } @@ -580,7 +582,7 @@ await this.SendRequest( [Fact] public async Task ServiceExecutesReplCommandAndReceivesOutput() { - OutputReader outputReader = new OutputReader(this.protocolClient); + OutputReader outputReader = new OutputReader(this.messageHandlers); await this.SendRequest( @@ -609,7 +611,7 @@ await this.SendRequest( [Fact] public async Task ServiceExecutesReplCommandAndReceivesChoicePrompt() { - OutputReader outputReader = new OutputReader(this.protocolClient); + OutputReader outputReader = new OutputReader(this.messageHandlers); string choiceScript = @" @@ -659,7 +661,7 @@ await requestContext.SendResult( [Fact] public async Task ServiceExecutesReplCommandAndReceivesInputPrompt() { - OutputReader outputReader = new OutputReader(this.protocolClient); + OutputReader outputReader = new OutputReader(this.messageHandlers); string promptScript = @" @@ -716,7 +718,7 @@ await requestContext.SendResult( [Fact(Skip = "Native command output in the legacy host has been disabled for now, may re-enable later")] public async Task ServiceExecutesNativeCommandAndReceivesCommand() { - OutputReader outputReader = new OutputReader(this.protocolClient); + OutputReader outputReader = new OutputReader(this.messageHandlers); // Execute the script but don't await the task yet because // the choice prompt will block execution from completing @@ -788,7 +790,7 @@ await this.languageServiceClient.SendEvent( } }); - OutputReader outputReader = new OutputReader(this.protocolClient); + OutputReader outputReader = new OutputReader(this.messageHandlers); Task evaluateTask = this.SendRequest( diff --git a/test/PowerShellEditorServices.Test.Host/OutputReader.cs b/test/PowerShellEditorServices.Test.Host/OutputReader.cs index 47caac118..989d193e5 100644 --- a/test/PowerShellEditorServices.Test.Host/OutputReader.cs +++ b/test/PowerShellEditorServices.Test.Host/OutputReader.cs @@ -23,9 +23,9 @@ internal class OutputReader private string currentOutputCategory; private Queue> bufferedOutput = new Queue>(); - public OutputReader(ProtocolEndpoint protocolClient) + public OutputReader(IMessageHandlers messageHandlers) { - protocolClient.SetEventHandler( + messageHandlers.SetEventHandler( OutputEvent.Type, this.OnOutputEvent); } @@ -66,7 +66,7 @@ public async Task ReadLine(string expectedOutputCategory = "stdout") this.currentOutputCategory = nextOutputEvent.Category; // Split up the output into multiple lines - outputLines = + outputLines = nextOutputEvent.Output.Split( new string[] { "\n", "\r\n" }, StringSplitOptions.None); diff --git a/test/PowerShellEditorServices.Test.Host/ServerTestsBase.cs b/test/PowerShellEditorServices.Test.Host/ServerTestsBase.cs index db6a7c56a..253f2da11 100644 --- a/test/PowerShellEditorServices.Test.Host/ServerTestsBase.cs +++ b/test/PowerShellEditorServices.Test.Host/ServerTestsBase.cs @@ -18,7 +18,8 @@ namespace Microsoft.PowerShell.EditorServices.Test.Host public class ServerTestsBase { private Process serviceProcess; - protected ProtocolEndpoint protocolClient; + protected IMessageSender messageSender; + protected IMessageHandlers messageHandlers; private ConcurrentDictionary> eventQueuePerType = new ConcurrentDictionary>(); @@ -136,15 +137,16 @@ protected Task SendRequest(NotificationType eventType, TParams eventParams) { return - this.protocolClient.SendEvent( + this.messageSender.SendEvent( eventType, eventParams); } @@ -157,7 +159,7 @@ protected void QueueEventsForType(NotificationTyp new AsyncQueue(), (key, queue) => queue); - this.protocolClient.SetEventHandler( + this.messageHandlers.SetEventHandler( eventType, (p, ctx) => { @@ -185,7 +187,7 @@ protected async Task WaitForEvent( { TaskCompletionSource eventTaskSource = new TaskCompletionSource(); - this.protocolClient.SetEventHandler( + this.messageHandlers.SetEventHandler( eventType, (p, ctx) => { @@ -195,8 +197,7 @@ protected async Task WaitForEvent( } return Task.FromResult(true); - }, - true); // Override any existing handler + }); eventTask = eventTaskSource.Task; } @@ -238,7 +239,7 @@ protected async Task>> WaitForRequest>>(); - this.protocolClient.SetRequestHandler( + this.messageHandlers.SetRequestHandler( requestType, (p, ctx) => { diff --git a/test/PowerShellEditorServices.Test.Protocol/Server/OutputDebouncerTests.cs b/test/PowerShellEditorServices.Test.Protocol/Server/OutputDebouncerTests.cs index bd15d1a75..50aad110e 100644 --- a/test/PowerShellEditorServices.Test.Protocol/Server/OutputDebouncerTests.cs +++ b/test/PowerShellEditorServices.Test.Protocol/Server/OutputDebouncerTests.cs @@ -120,6 +120,12 @@ public Task SendRequest // Legitimately not implemented for these tests. throw new NotImplementedException(); } + + public Task SendRequest(RequestType0 requestType0) + { + // Legitimately not implemented for these tests. + throw new NotImplementedException(); + } } }