diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index 24a5f3504..da0904197 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -27,7 +27,14 @@
"taskName": "Build",
"suppressTaskName": true,
"isBuildCommand": true,
- "args": [ "Invoke-Build Build" ]
+ "args": [ "Invoke-Build Build" ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "taskName": "Build Release Configuration",
+ "suppressTaskName": true,
+ "args": [ "Invoke-Build Build -Configuration Release" ],
+ "problemMatcher": "$msCompile"
},
{
"taskName": "Test",
diff --git a/src/PowerShellEditorServices.Host/EditorServicesHost.cs b/src/PowerShellEditorServices.Host/EditorServicesHost.cs
index 0904b3c60..182166732 100644
--- a/src/PowerShellEditorServices.Host/EditorServicesHost.cs
+++ b/src/PowerShellEditorServices.Host/EditorServicesHost.cs
@@ -1,383 +1,401 @@
-//
-// 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.Extensions;
-using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
-using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel;
-using Microsoft.PowerShell.EditorServices.Protocol.Server;
-using Microsoft.PowerShell.EditorServices.Session;
-using Microsoft.PowerShell.EditorServices.Utility;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Management.Automation.Runspaces;
-using System.Reflection;
-using System.Threading.Tasks;
-
-namespace Microsoft.PowerShell.EditorServices.Host
-{
- public enum EditorServicesHostStatus
- {
- Started,
- Failed,
- Ended
- }
-
- ///
- /// Provides a simplified interface for hosting the language and debug services
- /// over the named pipe server protocol.
- ///
- public class EditorServicesHost
- {
- #region Private Fields
-
- private ILogger logger;
- private bool enableConsoleRepl;
- private HostDetails hostDetails;
- private ProfilePaths profilePaths;
- private string bundledModulesPath;
- private DebugAdapter debugAdapter;
- private EditorSession editorSession;
- private HashSet featureFlags;
- private LanguageServer languageServer;
-
- private TcpSocketServerListener languageServiceListener;
- private TcpSocketServerListener debugServiceListener;
-
- private TaskCompletionSource serverCompletedTask;
-
- #endregion
-
- #region Properties
-
- public EditorServicesHostStatus Status { get; private set; }
-
- public int LanguageServicePort { get; private set; }
-
- public int DebugServicePort { get; private set; }
-
- #endregion
-
- #region Constructors
-
- ///
- /// Initializes a new instance of the EditorServicesHost class and waits for
- /// the debugger to attach if waitForDebugger is true.
- ///
- /// The details of the host which is launching PowerShell Editor Services.
- /// Provides a path to PowerShell modules bundled with the host, if any. Null otherwise.
- /// If true, causes the host to wait for the debugger to attach before proceeding.
- public EditorServicesHost(
- HostDetails hostDetails,
- string bundledModulesPath,
- bool enableConsoleRepl,
- bool waitForDebugger,
- string[] featureFlags)
- {
- Validate.IsNotNull(nameof(hostDetails), hostDetails);
-
- this.hostDetails = hostDetails;
- this.enableConsoleRepl = enableConsoleRepl;
- this.bundledModulesPath = bundledModulesPath;
- this.featureFlags = new HashSet(featureFlags ?? new string[0]);
-
-#if DEBUG
- if (waitForDebugger)
- {
- if (Debugger.IsAttached)
- {
- Debugger.Break();
- }
- else
- {
- Debugger.Launch();
- }
- }
-#endif
-
- // Catch unhandled exceptions for logging purposes
-#if !CoreCLR
- AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
-#endif
- }
-
- #endregion
-
- #region Public Methods
-
- ///
- /// Starts the Logger for the specified file path and log level.
- ///
- /// The path of the log file to be written.
- /// The minimum level of log messages to be written.
- public void StartLogging(string logFilePath, LogLevel logLevel)
- {
- this.logger = new FileLogger(logFilePath, logLevel);
- Logger.Initialize(this.logger);
-
-#if CoreCLR
- FileVersionInfo fileVersionInfo =
- FileVersionInfo.GetVersionInfo(this.GetType().GetTypeInfo().Assembly.Location);
-
- // TODO #278: Need the correct dependency package for this to work correctly
- //string osVersionString = RuntimeInformation.OSDescription;
- //string processArchitecture = RuntimeInformation.ProcessArchitecture == Architecture.X64 ? "64-bit" : "32-bit";
- //string osArchitecture = RuntimeInformation.OSArchitecture == Architecture.X64 ? "64-bit" : "32-bit";
-#else
- FileVersionInfo fileVersionInfo =
- FileVersionInfo.GetVersionInfo(this.GetType().Assembly.Location);
- string osVersionString = Environment.OSVersion.VersionString;
- string processArchitecture = Environment.Is64BitProcess ? "64-bit" : "32-bit";
- string osArchitecture = Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit";
-#endif
-
- string newLine = Environment.NewLine;
-
- this.logger.Write(
- LogLevel.Normal,
- string.Format(
- $"PowerShell Editor Services Host v{fileVersionInfo.FileVersion} starting (pid {Process.GetCurrentProcess().Id})..." + newLine + newLine +
- " Host application details:" + newLine + newLine +
- $" Name: {this.hostDetails.Name}" + newLine +
- $" ProfileId: {this.hostDetails.ProfileId}" + newLine +
- $" Version: {this.hostDetails.Version}" + newLine +
-#if !CoreCLR
- $" Arch: {processArchitecture}" + newLine + newLine +
- " Operating system details:" + newLine + newLine +
- $" Version: {osVersionString}" + newLine +
- $" Arch: {osArchitecture}"));
-#else
- ""));
-#endif
- }
-
- ///
- /// Starts the language service with the specified TCP socket port.
- ///
- /// The port number for the language service.
- /// The object containing the profile paths to load for this session.
- public void StartLanguageService(int languageServicePort, ProfilePaths profilePaths)
- {
- this.profilePaths = profilePaths;
-
- this.languageServiceListener =
- new TcpSocketServerListener(
- MessageProtocolType.LanguageServer,
- languageServicePort,
- this.logger);
-
- this.languageServiceListener.ClientConnect += this.OnLanguageServiceClientConnect;
- this.languageServiceListener.Start();
-
- this.logger.Write(
- LogLevel.Normal,
- string.Format(
- "Language service started, listening on port {0}",
- languageServicePort));
- }
-
- 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,
- this.profilePaths,
- this.enableConsoleRepl);
-
- this.languageServer =
- new LanguageServer(
- this.editorSession,
- messageDispatcher,
- protocolEndpoint,
- this.logger);
-
- await this.editorSession.PowerShellContext.ImportCommandsModule(
- Path.Combine(
- Path.GetDirectoryName(this.GetType().GetTypeInfo().Assembly.Location),
- @"..\..\Commands"));
-
- this.languageServer.Start();
- protocolEndpoint.Start();
- }
-
- ///
- /// Starts the debug service with the specified TCP socket port.
- ///
- /// The port number for the debug service.
- public void StartDebugService(
- int debugServicePort,
- ProfilePaths profilePaths,
- bool useExistingSession)
- {
- this.debugServiceListener =
- new TcpSocketServerListener(
- MessageProtocolType.DebugAdapter,
- debugServicePort,
- this.logger);
-
- this.debugServiceListener.ClientConnect += OnDebugServiceClientConnect;
- this.debugServiceListener.Start();
-
- this.logger.Write(
- LogLevel.Normal,
- string.Format(
- "Debug service started, listening on port {0}",
- debugServicePort));
- }
-
- 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,
- false,
- messageDispatcher,
- protocolEndpoint,
- this.logger);
- }
- else
- {
- EditorSession debugSession =
- this.CreateDebugSession(
- this.hostDetails,
- profilePaths,
- this.languageServer?.EditorOperations);
-
- this.debugAdapter =
- new DebugAdapter(
- debugSession,
- true,
- messageDispatcher,
- protocolEndpoint,
- this.logger);
- }
-
- this.debugAdapter.SessionEnded +=
- (obj, args) =>
- {
- this.logger.Write(
- LogLevel.Normal,
- "Previous debug session ended, restarting debug service listener...");
-
- this.debugServiceListener.Start();
- };
-
- this.debugAdapter.Start();
- protocolEndpoint.Start();
- }
-
- ///
- /// Stops the language or debug services if either were started.
- ///
- public void StopServices()
- {
- // TODO: Need a new way to shut down the services
-
- this.languageServer = null;
-
- this.debugAdapter = null;
- }
-
- ///
- /// Waits for either the language or debug service to shut down.
- ///
- public void WaitForCompletion()
- {
- // TODO: We need a way to know when to complete this task!
- this.serverCompletedTask = new TaskCompletionSource();
- this.serverCompletedTask.Task.Wait();
- }
-
- #endregion
-
- #region Private Methods
-
- private EditorSession CreateSession(
- HostDetails hostDetails,
- ProfilePaths profilePaths,
- bool enableConsoleRepl)
- {
- EditorSession editorSession = new EditorSession(this.logger);
- PowerShellContext powerShellContext = new PowerShellContext(this.logger);
-
- ConsoleServicePSHost psHost =
- new ConsoleServicePSHost(
- powerShellContext,
- hostDetails,
- enableConsoleRepl);
-
- Runspace initialRunspace = PowerShellContext.CreateRunspace(psHost);
- powerShellContext.Initialize(profilePaths, initialRunspace, true, psHost.ConsoleService);
-
- editorSession.StartSession(
- powerShellContext,
- psHost.ConsoleService);
-
- return editorSession;
- }
-
- private EditorSession CreateDebugSession(
- HostDetails hostDetails,
- ProfilePaths profilePaths,
- IEditorOperations editorOperations)
- {
- EditorSession editorSession = new EditorSession(this.logger);
- PowerShellContext powerShellContext = new PowerShellContext(this.logger);
-
- ConsoleServicePSHost psHost =
- new ConsoleServicePSHost(
- powerShellContext,
- hostDetails,
- enableConsoleRepl);
-
- Runspace initialRunspace = PowerShellContext.CreateRunspace(psHost);
- powerShellContext.Initialize(profilePaths, initialRunspace, true, psHost.ConsoleService);
-
- editorSession.StartDebugSession(
- powerShellContext,
- psHost.ConsoleService,
- editorOperations);
-
- return editorSession;
- }
-
-#if !CoreCLR
- private void CurrentDomain_UnhandledException(
- object sender,
- UnhandledExceptionEventArgs e)
- {
- // Log the exception
- this.logger.Write(
- LogLevel.Error,
- string.Format(
- "FATAL UNHANDLED EXCEPTION:\r\n\r\n{0}",
- e.ExceptionObject.ToString()));
- }
-#endif
-
- #endregion
- }
+//
+// 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.Extensions;
+using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
+using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel;
+using Microsoft.PowerShell.EditorServices.Protocol.Server;
+using Microsoft.PowerShell.EditorServices.Session;
+using Microsoft.PowerShell.EditorServices.Utility;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Management.Automation.Runspaces;
+using System.Reflection;
+using System.Threading.Tasks;
+
+namespace Microsoft.PowerShell.EditorServices.Host
+{
+ public enum EditorServicesHostStatus
+ {
+ Started,
+ Failed,
+ Ended
+ }
+
+ ///
+ /// Provides a simplified interface for hosting the language and debug services
+ /// over the named pipe server protocol.
+ ///
+ public class EditorServicesHost
+ {
+ #region Private Fields
+
+ private ILogger logger;
+ private bool enableConsoleRepl;
+ private HostDetails hostDetails;
+ private ProfilePaths profilePaths;
+ private string bundledModulesPath;
+ private DebugAdapter debugAdapter;
+ private EditorSession editorSession;
+ private HashSet featureFlags;
+ private LanguageServer languageServer;
+
+ private TcpSocketServerListener languageServiceListener;
+ private TcpSocketServerListener debugServiceListener;
+
+ private TaskCompletionSource serverCompletedTask;
+
+ #endregion
+
+ #region Properties
+
+ public EditorServicesHostStatus Status { get; private set; }
+
+ public int LanguageServicePort { get; private set; }
+
+ public int DebugServicePort { get; private set; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the EditorServicesHost class and waits for
+ /// the debugger to attach if waitForDebugger is true.
+ ///
+ /// The details of the host which is launching PowerShell Editor Services.
+ /// Provides a path to PowerShell modules bundled with the host, if any. Null otherwise.
+ /// If true, causes the host to wait for the debugger to attach before proceeding.
+ public EditorServicesHost(
+ HostDetails hostDetails,
+ string bundledModulesPath,
+ bool enableConsoleRepl,
+ bool waitForDebugger,
+ string[] featureFlags)
+ {
+ Validate.IsNotNull(nameof(hostDetails), hostDetails);
+
+ this.hostDetails = hostDetails;
+ this.enableConsoleRepl = enableConsoleRepl;
+ this.bundledModulesPath = bundledModulesPath;
+ this.featureFlags = new HashSet(featureFlags ?? new string[0]);
+
+#if DEBUG
+ if (waitForDebugger)
+ {
+ if (Debugger.IsAttached)
+ {
+ Debugger.Break();
+ }
+ else
+ {
+ Debugger.Launch();
+ }
+ }
+#endif
+
+ // Catch unhandled exceptions for logging purposes
+#if !CoreCLR
+ AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
+#endif
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ ///
+ /// Starts the Logger for the specified file path and log level.
+ ///
+ /// The path of the log file to be written.
+ /// The minimum level of log messages to be written.
+ public void StartLogging(string logFilePath, LogLevel logLevel)
+ {
+ this.logger = new FileLogger(logFilePath, logLevel);
+
+#if CoreCLR
+ FileVersionInfo fileVersionInfo =
+ FileVersionInfo.GetVersionInfo(this.GetType().GetTypeInfo().Assembly.Location);
+
+ // TODO #278: Need the correct dependency package for this to work correctly
+ //string osVersionString = RuntimeInformation.OSDescription;
+ //string processArchitecture = RuntimeInformation.ProcessArchitecture == Architecture.X64 ? "64-bit" : "32-bit";
+ //string osArchitecture = RuntimeInformation.OSArchitecture == Architecture.X64 ? "64-bit" : "32-bit";
+#else
+ FileVersionInfo fileVersionInfo =
+ FileVersionInfo.GetVersionInfo(this.GetType().Assembly.Location);
+ string osVersionString = Environment.OSVersion.VersionString;
+ string processArchitecture = Environment.Is64BitProcess ? "64-bit" : "32-bit";
+ string osArchitecture = Environment.Is64BitOperatingSystem ? "64-bit" : "32-bit";
+#endif
+
+ string newLine = Environment.NewLine;
+
+ this.logger.Write(
+ LogLevel.Normal,
+ string.Format(
+ $"PowerShell Editor Services Host v{fileVersionInfo.FileVersion} starting (pid {Process.GetCurrentProcess().Id})..." + newLine + newLine +
+ " Host application details:" + newLine + newLine +
+ $" Name: {this.hostDetails.Name}" + newLine +
+ $" ProfileId: {this.hostDetails.ProfileId}" + newLine +
+ $" Version: {this.hostDetails.Version}" + newLine +
+#if !CoreCLR
+ $" Arch: {processArchitecture}" + newLine + newLine +
+ " Operating system details:" + newLine + newLine +
+ $" Version: {osVersionString}" + newLine +
+ $" Arch: {osArchitecture}"));
+#else
+ ""));
+#endif
+ }
+
+ ///
+ /// Starts the language service with the specified TCP socket port.
+ ///
+ /// The port number for the language service.
+ /// The object containing the profile paths to load for this session.
+ public void StartLanguageService(int languageServicePort, ProfilePaths profilePaths)
+ {
+ this.profilePaths = profilePaths;
+
+ this.languageServiceListener =
+ new TcpSocketServerListener(
+ MessageProtocolType.LanguageServer,
+ languageServicePort,
+ this.logger);
+
+ this.languageServiceListener.ClientConnect += this.OnLanguageServiceClientConnect;
+ this.languageServiceListener.Start();
+
+ this.logger.Write(
+ LogLevel.Normal,
+ string.Format(
+ "Language service started, listening on port {0}",
+ languageServicePort));
+ }
+
+ 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,
+ this.profilePaths,
+ protocolEndpoint,
+ messageDispatcher,
+ this.enableConsoleRepl);
+
+ this.languageServer =
+ new LanguageServer(
+ this.editorSession,
+ messageDispatcher,
+ protocolEndpoint,
+ this.logger);
+
+ await this.editorSession.PowerShellContext.ImportCommandsModule(
+ Path.Combine(
+ Path.GetDirectoryName(this.GetType().GetTypeInfo().Assembly.Location),
+ @"..\..\Commands"));
+
+ this.languageServer.Start();
+ protocolEndpoint.Start();
+ }
+
+ ///
+ /// Starts the debug service with the specified TCP socket port.
+ ///
+ /// The port number for the debug service.
+ public void StartDebugService(
+ int debugServicePort,
+ ProfilePaths profilePaths,
+ bool useExistingSession)
+ {
+ this.debugServiceListener =
+ new TcpSocketServerListener(
+ MessageProtocolType.DebugAdapter,
+ debugServicePort,
+ this.logger);
+
+ this.debugServiceListener.ClientConnect += OnDebugServiceClientConnect;
+ this.debugServiceListener.Start();
+
+ this.logger.Write(
+ LogLevel.Normal,
+ string.Format(
+ "Debug service started, listening on port {0}",
+ debugServicePort));
+ }
+
+ 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,
+ false,
+ messageDispatcher,
+ protocolEndpoint,
+ this.logger);
+ }
+ else
+ {
+ EditorSession debugSession =
+ this.CreateDebugSession(
+ this.hostDetails,
+ profilePaths,
+ protocolEndpoint,
+ messageDispatcher,
+ this.languageServer?.EditorOperations,
+ false);
+
+ this.debugAdapter =
+ new DebugAdapter(
+ debugSession,
+ true,
+ messageDispatcher,
+ protocolEndpoint,
+ this.logger);
+ }
+
+ this.debugAdapter.SessionEnded +=
+ (obj, args) =>
+ {
+ this.logger.Write(
+ LogLevel.Normal,
+ "Previous debug session ended, restarting debug service listener...");
+
+ this.debugServiceListener.Start();
+ };
+
+ this.debugAdapter.Start();
+ protocolEndpoint.Start();
+ }
+
+ ///
+ /// Stops the language or debug services if either were started.
+ ///
+ public void StopServices()
+ {
+ // TODO: Need a new way to shut down the services
+
+ this.languageServer = null;
+
+ this.debugAdapter = null;
+ }
+
+ ///
+ /// Waits for either the language or debug service to shut down.
+ ///
+ public void WaitForCompletion()
+ {
+ // TODO: We need a way to know when to complete this task!
+ this.serverCompletedTask = new TaskCompletionSource();
+ this.serverCompletedTask.Task.Wait();
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private EditorSession CreateSession(
+ HostDetails hostDetails,
+ ProfilePaths profilePaths,
+ IMessageSender messageSender,
+ IMessageHandlers messageHandlers,
+ bool enableConsoleRepl)
+ {
+ EditorSession editorSession = new EditorSession(this.logger);
+ PowerShellContext powerShellContext = new PowerShellContext(this.logger);
+
+ EditorServicesPSHostUserInterface hostUserInterface =
+ enableConsoleRepl
+ ? (EditorServicesPSHostUserInterface) new TerminalPSHostUserInterface(powerShellContext, this.logger)
+ : new ProtocolPSHostUserInterface(powerShellContext, messageSender, messageHandlers, this.logger);
+
+ EditorServicesPSHost psHost =
+ new EditorServicesPSHost(
+ powerShellContext,
+ hostDetails,
+ hostUserInterface,
+ this.logger);
+
+ Runspace initialRunspace = PowerShellContext.CreateRunspace(psHost);
+ powerShellContext.Initialize(profilePaths, initialRunspace, true, hostUserInterface);
+
+ editorSession.StartSession(powerShellContext, hostUserInterface);
+
+ return editorSession;
+ }
+
+ private EditorSession CreateDebugSession(
+ HostDetails hostDetails,
+ ProfilePaths profilePaths,
+ IMessageSender messageSender,
+ IMessageHandlers messageHandlers,
+ IEditorOperations editorOperations,
+ bool enableConsoleRepl)
+ {
+ EditorSession editorSession = new EditorSession(this.logger);
+ PowerShellContext powerShellContext = new PowerShellContext(this.logger);
+
+ EditorServicesPSHostUserInterface hostUserInterface =
+ enableConsoleRepl
+ ? (EditorServicesPSHostUserInterface) new TerminalPSHostUserInterface(powerShellContext, this.logger)
+ : new ProtocolPSHostUserInterface(powerShellContext, messageSender, messageHandlers, this.logger);
+
+ EditorServicesPSHost psHost =
+ new EditorServicesPSHost(
+ powerShellContext,
+ hostDetails,
+ hostUserInterface,
+ this.logger);
+
+ Runspace initialRunspace = PowerShellContext.CreateRunspace(psHost);
+ powerShellContext.Initialize(profilePaths, initialRunspace, true, hostUserInterface);
+
+ editorSession.StartDebugSession(
+ powerShellContext,
+ editorOperations);
+
+ return editorSession;
+ }
+
+#if !CoreCLR
+ private void CurrentDomain_UnhandledException(
+ object sender,
+ UnhandledExceptionEventArgs e)
+ {
+ // Log the exception
+ this.logger.Write(
+ LogLevel.Error,
+ string.Format(
+ "FATAL UNHANDLED EXCEPTION:\r\n\r\n{0}",
+ e.ExceptionObject.ToString()));
+ }
+#endif
+
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/src/PowerShellEditorServices.Protocol/Server/PromptHandlers.cs b/src/PowerShellEditorServices.Host/PSHost/PromptHandlers.cs
similarity index 69%
rename from src/PowerShellEditorServices.Protocol/Server/PromptHandlers.cs
rename to src/PowerShellEditorServices.Host/PSHost/PromptHandlers.cs
index ff6097c5d..bae68616f 100644
--- a/src/PowerShellEditorServices.Protocol/Server/PromptHandlers.cs
+++ b/src/PowerShellEditorServices.Host/PSHost/PromptHandlers.cs
@@ -12,51 +12,24 @@
using System.Threading;
using System.Security;
-namespace Microsoft.PowerShell.EditorServices.Protocol.Server
+namespace Microsoft.PowerShell.EditorServices.Host
{
- internal class ProtocolPromptHandlerContext : IPromptHandlerContext
- {
- private IMessageSender messageSender;
- private ConsoleService consoleService;
-
- public ProtocolPromptHandlerContext(
- IMessageSender messageSender,
- ConsoleService consoleService)
- {
- this.messageSender = messageSender;
- this.consoleService = consoleService;
- }
-
- public ChoicePromptHandler GetChoicePromptHandler()
- {
- return new ProtocolChoicePromptHandler(
- this.messageSender,
- this.consoleService,
- Logger.CurrentLogger);
- }
-
- public InputPromptHandler GetInputPromptHandler()
- {
- return new ProtocolInputPromptHandler(
- this.messageSender,
- this.consoleService);
- }
- }
-
internal class ProtocolChoicePromptHandler : ConsoleChoicePromptHandler
{
+ private IHostInput hostInput;
private IMessageSender messageSender;
- private ConsoleService consoleService;
private TaskCompletionSource readLineTask;
public ProtocolChoicePromptHandler(
IMessageSender messageSender,
- ConsoleService consoleService,
+ IHostInput hostInput,
+ IHostOutput hostOutput,
ILogger logger)
- : base(consoleService, logger)
+ : base(hostOutput, logger)
{
+ this.hostInput = hostInput;
+ this.hostOutput = hostOutput;
this.messageSender = messageSender;
- this.consoleService = consoleService;
}
protected override void ShowPrompt(PromptStyle promptStyle)
@@ -93,7 +66,7 @@ private void HandlePromptResponse(
if (!response.PromptCancelled)
{
- this.consoleService.WriteOutput(
+ this.hostOutput.WriteOutput(
response.ResponseText,
OutputType.Normal);
@@ -102,7 +75,7 @@ private void HandlePromptResponse(
else
{
// Cancel the current prompt
- this.consoleService.SendControlC();
+ this.hostInput.SendControlC();
}
}
else
@@ -117,7 +90,7 @@ private void HandlePromptResponse(
}
// Cancel the current prompt
- this.consoleService.SendControlC();
+ this.hostInput.SendControlC();
}
this.readLineTask = null;
@@ -126,37 +99,24 @@ private void HandlePromptResponse(
internal class ProtocolInputPromptHandler : ConsoleInputPromptHandler
{
+ private IHostInput hostInput;
private IMessageSender messageSender;
- private ConsoleService consoleService;
private TaskCompletionSource readLineTask;
public ProtocolInputPromptHandler(
IMessageSender messageSender,
- ConsoleService consoleService)
- : base(
- consoleService,
- Microsoft.PowerShell.EditorServices.Utility.Logger.CurrentLogger)
+ IHostInput hostInput,
+ IHostOutput hostOutput,
+ ILogger logger)
+ : base(hostOutput, logger)
{
+ this.hostInput = hostInput;
+ this.hostOutput = hostOutput;
this.messageSender = messageSender;
- this.consoleService = consoleService;
- }
-
- protected override void ShowErrorMessage(Exception e)
- {
- // Use default behavior for writing the error message
- base.ShowErrorMessage(e);
- }
-
- protected override void ShowPromptMessage(string caption, string message)
- {
- // Use default behavior for writing the prompt message
- base.ShowPromptMessage(caption, message);
}
protected override void ShowFieldPrompt(FieldDetails fieldDetails)
{
- // Write the prompt to the console first so that there's a record
- // of it occurring
base.ShowFieldPrompt(fieldDetails);
messageSender
@@ -186,7 +146,7 @@ private void HandlePromptResponse(
if (!response.PromptCancelled)
{
- this.consoleService.WriteOutput(
+ this.hostOutput.WriteOutput(
response.ResponseText,
OutputType.Normal);
@@ -195,7 +155,7 @@ private void HandlePromptResponse(
else
{
// Cancel the current prompt
- this.consoleService.SendControlC();
+ this.hostInput.SendControlC();
}
}
else
@@ -210,11 +170,16 @@ private void HandlePromptResponse(
}
// Cancel the current prompt
- this.consoleService.SendControlC();
+ this.hostInput.SendControlC();
}
this.readLineTask = null;
}
+
+ protected override Task ReadSecureString(CancellationToken cancellationToken)
+ {
+ // TODO: Write a message to the console
+ throw new NotImplementedException();
+ }
}
}
-
diff --git a/src/PowerShellEditorServices.Host/PSHost/ProtocolPSHostUserInterface.cs b/src/PowerShellEditorServices.Host/PSHost/ProtocolPSHostUserInterface.cs
new file mode 100644
index 000000000..6b413798e
--- /dev/null
+++ b/src/PowerShellEditorServices.Host/PSHost/ProtocolPSHostUserInterface.cs
@@ -0,0 +1,188 @@
+//
+// 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.Console;
+using Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter;
+using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
+using Microsoft.PowerShell.EditorServices.Protocol.Server;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.PowerShell.EditorServices.Utility;
+
+namespace Microsoft.PowerShell.EditorServices.Host
+{
+ internal class ProtocolPSHostUserInterface : EditorServicesPSHostUserInterface
+ {
+ #region Private Fields
+
+ private IMessageSender messageSender;
+ private OutputDebouncer outputDebouncer;
+ private TaskCompletionSource commandLineInputTask;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Creates a new instance of the ConsoleServicePSHostUserInterface
+ /// class with the given IConsoleHost implementation.
+ ///
+ ///
+ public ProtocolPSHostUserInterface(
+ PowerShellContext powerShellContext,
+ IMessageSender messageSender,
+ IMessageHandlers messageHandlers,
+ ILogger logger)
+ : base(powerShellContext, new SimplePSHostRawUserInterface(logger), logger)
+ {
+ this.messageSender = messageSender;
+ this.outputDebouncer = new OutputDebouncer(messageSender);
+
+ messageHandlers.SetRequestHandler(
+ EvaluateRequest.Type,
+ this.HandleEvaluateRequest);
+ }
+
+ public void Dispose()
+ {
+ // TODO: Need a clear API path for this
+
+ // Make sure remaining output is flushed before exiting
+ if (this.outputDebouncer != null)
+ {
+ this.outputDebouncer.Flush().Wait();
+ this.outputDebouncer = null;
+ }
+ }
+
+ #endregion
+
+ ///
+ /// Writes output of the given type to the user interface with
+ /// the given foreground and background colors. Also includes
+ /// a newline if requested.
+ ///
+ ///
+ /// The output string to be written.
+ ///
+ ///
+ /// If true, a newline should be appended to the output's contents.
+ ///
+ ///
+ /// Specifies the type of output to be written.
+ ///
+ ///
+ /// Specifies the foreground color of the output to be written.
+ ///
+ ///
+ /// Specifies the background color of the output to be written.
+ ///
+ public override void WriteOutput(
+ string outputString,
+ bool includeNewLine,
+ OutputType outputType,
+ ConsoleColor foregroundColor,
+ ConsoleColor backgroundColor)
+ {
+ // TODO: This should use a synchronous method!
+ this.outputDebouncer.Invoke(
+ new OutputWrittenEventArgs(
+ outputString,
+ includeNewLine,
+ outputType,
+ foregroundColor,
+ backgroundColor)).Wait();
+ }
+
+ ///
+ /// Sends a progress update event to the user.
+ ///
+ /// The source ID of the progress event.
+ /// The details of the activity's current progress.
+ protected override void UpdateProgress(
+ long sourceId,
+ ProgressDetails progressDetails)
+ {
+ }
+
+ protected override async Task ReadCommandLine(CancellationToken cancellationToken)
+ {
+ this.commandLineInputTask = new TaskCompletionSource();
+ return await this.commandLineInputTask.Task;
+ }
+
+ protected override InputPromptHandler OnCreateInputPromptHandler()
+ {
+ return new ProtocolInputPromptHandler(this.messageSender, this, this, this.Logger);
+ }
+
+ protected override ChoicePromptHandler OnCreateChoicePromptHandler()
+ {
+ return new ProtocolChoicePromptHandler(this.messageSender, this, this, this.Logger);
+ }
+
+ protected async Task HandleEvaluateRequest(
+ EvaluateRequestArguments evaluateParams,
+ RequestContext requestContext)
+ {
+ // TODO: This needs to respect debug mode!
+
+ var evaluateResponse =
+ new EvaluateResponseBody
+ {
+ Result = "",
+ VariablesReference = 0
+ };
+
+ if (this.commandLineInputTask != null)
+ {
+ this.commandLineInputTask.SetResult(evaluateParams.Expression);
+ await requestContext.SendResult(evaluateResponse);
+ }
+ else
+ {
+ // Check for special commands
+ if (string.Equals("!ctrlc", evaluateParams.Expression, StringComparison.CurrentCultureIgnoreCase))
+ {
+ this.powerShellContext.AbortExecution();
+ await requestContext.SendResult(evaluateResponse);
+ }
+ else if (string.Equals("!break", evaluateParams.Expression, StringComparison.CurrentCultureIgnoreCase))
+ {
+ // TODO: Need debugger commands interface
+ //editorSession.DebugService.Break();
+ await requestContext.SendResult(evaluateResponse);
+ }
+ else
+ {
+ // We don't await the result of the execution here because we want
+ // to be able to receive further messages while the current script
+ // is executing. This important in cases where the pipeline thread
+ // gets blocked by something in the script like a prompt to the user.
+ var executeTask =
+ this.powerShellContext.ExecuteScriptString(
+ evaluateParams.Expression,
+ writeInputToHost: true,
+ writeOutputToHost: true,
+ addToHistory: true);
+
+ // Return the execution result after the task completes so that the
+ // caller knows when command execution completed.
+ Task unusedTask =
+ executeTask.ContinueWith(
+ (task) =>
+ {
+ // Return an empty result since the result value is irrelevant
+ // for this request in the LanguageServer
+ return
+ requestContext.SendResult(
+ evaluateResponse);
+ });
+ }
+ }
+ }
+ }
+}
diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageDispatcher.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/MessageDispatcher.cs
index 8ad9557c7..714857564 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 : IMessageHandlers
+ public class MessageDispatcher : IMessageHandlers, IMessageDispatcher
{
#region Fields
@@ -42,17 +42,8 @@ public void SetRequestHandler(
RequestType requestType,
Func, Task> requestHandler)
{
- this.SetRequestHandler(
- requestType,
- requestHandler,
- false);
- }
+ bool overrideExisting = true;
- public void SetRequestHandler(
- RequestType requestType,
- Func, Task> requestHandler,
- bool overrideExisting)
- {
if (overrideExisting)
{
// Remove the existing handler so a new one can be set
@@ -95,17 +86,8 @@ public void SetEventHandler(
NotificationType eventType,
Func eventHandler)
{
- this.SetEventHandler(
- eventType,
- eventHandler,
- true);
- }
+ bool overrideExisting = true;
- public void SetEventHandler(
- NotificationType eventType,
- Func eventHandler,
- bool overrideExisting)
- {
if (overrideExisting)
{
// Remove the existing handler so a new one can be set
diff --git a/src/PowerShellEditorServices.Protocol/MessageProtocol/ProtocolEndpoint.cs b/src/PowerShellEditorServices.Protocol/MessageProtocol/ProtocolEndpoint.cs
index 037d70290..66c2aba4b 100644
--- a/src/PowerShellEditorServices.Protocol/MessageProtocol/ProtocolEndpoint.cs
+++ b/src/PowerShellEditorServices.Protocol/MessageProtocol/ProtocolEndpoint.cs
@@ -26,9 +26,10 @@ private enum ProtocolEndpointState
Shutdown
}
- private ProtocolEndpointState currentState;
private int currentMessageId;
private ChannelBase protocolChannel;
+ private ProtocolEndpointState currentState;
+ private IMessageDispatcher messageDispatcher;
private AsyncContextThread messageLoopThread;
private TaskCompletionSource endpointExitedTask;
private SynchronizationContext originalSynchronizationContext;
@@ -53,13 +54,6 @@ private bool InMessageLoopThread
protected ILogger Logger { get; private set; }
- ///
- /// Gets the MessageDispatcher which allows registration of
- /// handlers for requests, responses, and events that are
- /// transmitted through the channel.
- ///
- private MessageDispatcher MessageDispatcher { get; set; }
-
///
/// Initializes an instance of the protocol server using the
/// specified channel for communication.
@@ -72,13 +66,14 @@ private bool InMessageLoopThread
///
public ProtocolEndpoint(
ChannelBase protocolChannel,
- MessageDispatcher messageDispatcher,
+ IMessageDispatcher messageDispatcher,
ILogger logger)
{
this.protocolChannel = protocolChannel;
- this.MessageDispatcher = messageDispatcher;
- this.originalSynchronizationContext = SynchronizationContext.Current;
+ this.messageDispatcher = messageDispatcher;
this.Logger = logger;
+
+ this.originalSynchronizationContext = SynchronizationContext.Current;
}
///
@@ -247,36 +242,6 @@ await this.protocolChannel.MessageWriter.WriteEvent(
#region Message Handling
- public void SetRequestHandler(
- RequestType requestType,
- Func, Task> requestHandler)
- {
- this.MessageDispatcher.SetRequestHandler(
- requestType,
- requestHandler);
- }
-
- public void SetEventHandler(
- NotificationType eventType,
- Func eventHandler)
- {
- this.MessageDispatcher.SetEventHandler(
- eventType,
- eventHandler,
- false);
- }
-
- public void SetEventHandler(
- NotificationType eventType,
- Func eventHandler,
- bool overrideExisting)
- {
- this.MessageDispatcher.SetEventHandler(
- eventType,
- eventHandler,
- overrideExisting);
- }
-
private void HandleResponse(Message responseMessage)
{
TaskCompletionSource pendingRequestTask = null;
@@ -411,7 +376,7 @@ private async Task ListenForMessages(CancellationToken cancellationToken)
else
{
// Process the message
- await this.MessageDispatcher.DispatchMessage(
+ await this.messageDispatcher.DispatchMessage(
newMessage,
this.protocolChannel.MessageWriter);
}
@@ -426,7 +391,7 @@ private void OnListenTaskCompleted(Task listenTask)
Logger.Write(
LogLevel.Error,
string.Format(
- "MessageDispatcher loop terminated due to unhandled exception:\r\n\r\n{0}",
+ "ProtocolEndpoint message loop terminated due to unhandled exception:\r\n\r\n{0}",
listenTask.Exception.ToString()));
this.OnUnhandledException(listenTask.Exception);
diff --git a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs
index 024b06dc2..675f404e9 100644
--- a/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs
+++ b/src/PowerShellEditorServices.Protocol/Server/DebugAdapter.cs
@@ -23,7 +23,6 @@ namespace Microsoft.PowerShell.EditorServices.Protocol.Server
public class DebugAdapter
{
private EditorSession editorSession;
- private OutputDebouncer outputDebouncer;
private bool noDebug;
private ILogger Logger;
@@ -32,7 +31,6 @@ public class DebugAdapter
private bool isAttachSession;
private bool waitingForAttach;
private string scriptToLaunch;
- private bool enableConsoleRepl;
private bool ownsEditorSession;
private bool executionCompleted;
private IMessageSender messageSender;
@@ -52,7 +50,6 @@ public DebugAdapter(
this.messageSender = messageSender;
this.messageHandlers = messageHandlers;
this.ownsEditorSession = ownsEditorSession;
- this.enableConsoleRepl = editorSession.UsesConsoleHost;
}
public void Start()
@@ -117,12 +114,6 @@ private async Task OnExecutionCompleted(Task executeTask)
this.executionCompleted = true;
- // Make sure remaining output is flushed before exiting
- if (this.outputDebouncer != null)
- {
- await this.outputDebouncer.Flush();
- }
-
this.UnregisterEventHandlers();
if (this.isAttachSession)
@@ -167,12 +158,6 @@ protected void Stop()
{
Logger.Write(LogLevel.Normal, "Debug adapter is shutting down...");
- // Make sure remaining output is flushed before exiting
- if (this.outputDebouncer != null)
- {
- this.outputDebouncer.Flush().Wait();
- }
-
if (this.editorSession != null)
{
this.editorSession.PowerShellContext.RunspaceChanged -= this.powerShellContext_RunspaceChanged;
@@ -325,12 +310,6 @@ protected async Task HandleLaunchRequest(
// debugging session
this.isInteractiveDebugSession = string.IsNullOrEmpty(this.scriptToLaunch);
- if (this.editorSession.ConsoleService.EnableConsoleRepl)
- {
- // TODO: Write this during DebugSession init
- await this.WriteUseIntegratedConsoleMessage();
- }
-
// Send the InitializedEvent so that the debugger will continue
// sending configuration requests
await this.messageSender.SendEvent(
@@ -788,31 +767,13 @@ protected async Task HandleEvaluateRequest(
if (isFromRepl)
{
- if (!this.editorSession.ConsoleService.EnableConsoleRepl)
- {
- // Check for special commands
- if (string.Equals("!ctrlc", evaluateParams.Expression, StringComparison.CurrentCultureIgnoreCase))
- {
- editorSession.PowerShellContext.AbortExecution();
- }
- else if (string.Equals("!break", evaluateParams.Expression, StringComparison.CurrentCultureIgnoreCase))
- {
- editorSession.DebugService.Break();
- }
- else
- {
- // Send the input through the console service
- var notAwaited =
- this.editorSession
- .PowerShellContext
- .ExecuteScriptString(evaluateParams.Expression, false, true)
- .ConfigureAwait(false);
- }
- }
- else
- {
- await this.WriteUseIntegratedConsoleMessage();
- }
+ // TODO: Do we send the input through the command handler?
+ // Send the input through the console service
+ var notAwaited =
+ this.editorSession
+ .PowerShellContext
+ .ExecuteScriptString(evaluateParams.Expression, false, true)
+ .ConfigureAwait(false);
}
else
{
@@ -862,12 +823,6 @@ private void RegisterEventHandlers()
this.editorSession.PowerShellContext.RunspaceChanged += this.powerShellContext_RunspaceChanged;
this.editorSession.DebugService.DebuggerStopped += this.DebugService_DebuggerStopped;
this.editorSession.PowerShellContext.DebuggerResumed += this.powerShellContext_DebuggerResumed;
-
- if (!this.enableConsoleRepl)
- {
- this.editorSession.ConsoleService.OutputWritten += this.powerShellContext_OutputWritten;
- this.outputDebouncer = new OutputDebouncer(this.messageSender);
- }
}
private void UnregisterEventHandlers()
@@ -875,26 +830,12 @@ private void UnregisterEventHandlers()
this.editorSession.PowerShellContext.RunspaceChanged -= this.powerShellContext_RunspaceChanged;
this.editorSession.DebugService.DebuggerStopped -= this.DebugService_DebuggerStopped;
this.editorSession.PowerShellContext.DebuggerResumed -= this.powerShellContext_DebuggerResumed;
-
- if (!this.enableConsoleRepl)
- {
- this.editorSession.ConsoleService.OutputWritten -= this.powerShellContext_OutputWritten;
- }
}
#endregion
#region Event Handlers
- private async void powerShellContext_OutputWritten(object sender, OutputWrittenEventArgs e)
- {
- if (this.outputDebouncer != null)
- {
- // Queue the output for writing
- await this.outputDebouncer.Invoke(e);
- }
- }
-
async void DebugService_DebuggerStopped(object sender, DebuggerStoppedEventArgs e)
{
// Provide the reason for why the debugger has stopped script execution.
diff --git a/src/PowerShellEditorServices.Protocol/Server/IMessageDispatcher.cs b/src/PowerShellEditorServices.Protocol/Server/IMessageDispatcher.cs
new file mode 100644
index 000000000..45342a21b
--- /dev/null
+++ b/src/PowerShellEditorServices.Protocol/Server/IMessageDispatcher.cs
@@ -0,0 +1,17 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System.Threading.Tasks;
+using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
+
+namespace Microsoft.PowerShell.EditorServices.Protocol
+{
+ public interface IMessageDispatcher
+ {
+ Task DispatchMessage(
+ Message messageToDispatch,
+ MessageWriter messageWriter);
+ }
+}
diff --git a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs
index f52b95661..978a9c080 100644
--- a/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs
+++ b/src/PowerShellEditorServices.Protocol/Server/LanguageServer.cs
@@ -8,7 +8,6 @@
using Microsoft.PowerShell.EditorServices.Protocol.LanguageServer;
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol;
using Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel;
-using Microsoft.PowerShell.EditorServices.Session;
using Microsoft.PowerShell.EditorServices.Templates;
using Microsoft.PowerShell.EditorServices.Utility;
using Newtonsoft.Json.Linq;
@@ -20,8 +19,6 @@
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
-using DebugAdapterMessages = Microsoft.PowerShell.EditorServices.Protocol.DebugAdapter;
-using System.Collections;
namespace Microsoft.PowerShell.EditorServices.Protocol.Server
{
@@ -35,7 +32,6 @@ public class LanguageServer
private EditorSession editorSession;
private IMessageSender messageSender;
private IMessageHandlers messageHandlers;
- private OutputDebouncer outputDebouncer;
private LanguageServerEditorOperations editorOperations;
private LanguageServerSettings currentSettings = new LanguageServerSettings();
@@ -76,21 +72,6 @@ public LanguageServer(
this.editorSession.StartDebugService(this.editorOperations);
this.editorSession.DebugService.DebuggerStopped += DebugService_DebuggerStopped;
-
- if (!this.editorSession.ConsoleService.EnableConsoleRepl)
- {
- // TODO: This should be handled in ProtocolPSHost
- this.editorSession.ConsoleService.OutputWritten += this.powerShellContext_OutputWritten;
-
- // Always send console prompts through the UI in the language service
- this.editorSession.ConsoleService.PushPromptHandlerContext(
- new ProtocolPromptHandlerContext(
- this.messageSender,
- this.editorSession.ConsoleService));
- }
-
- // Set up the output debouncer to throttle output event writes
- this.outputDebouncer = new OutputDebouncer(this.messageSender);
}
///
@@ -136,8 +117,6 @@ public void Start()
this.messageHandlers.SetRequestHandler(NewProjectFromTemplateRequest.Type, this.HandleNewProjectFromTemplateRequest);
this.messageHandlers.SetRequestHandler(GetProjectTemplatesRequest.Type, this.HandleGetProjectTemplatesRequest);
- this.messageHandlers.SetRequestHandler(DebugAdapterMessages.EvaluateRequest.Type, this.HandleEvaluateRequest);
-
this.messageHandlers.SetRequestHandler(GetPSSARulesRequest.Type, this.HandleGetPSSARulesRequest);
this.messageHandlers.SetRequestHandler(SetPSSARulesRequest.Type, this.HandleSetPSSARulesRequest);
@@ -153,18 +132,13 @@ public void Start()
this.editorOperations).Wait();
}
- protected async Task Stop()
+ protected Task Stop()
{
- // Stop the interactive terminal
- // TODO: This can happen at the host level
- this.editorSession.ConsoleService.CancelReadLoop();
-
- // Make sure remaining output is flushed before exiting
- await this.outputDebouncer.Flush();
-
Logger.Write(LogLevel.Normal, "Language service is shutting down...");
// TODO: Raise an event so that the host knows to shut down
+
+ return Task.FromResult(true);
}
#region Built-in Message Handlers
@@ -605,8 +579,7 @@ protected async Task HandleDidChangeConfigurationNotification(
if (!this.consoleReplStarted)
{
// Start the interactive terminal
- // TODO: This can happen at the host level
- this.editorSession.ConsoleService.StartReadLoop();
+ this.editorSession.HostInput.StartCommandLoop();
this.consoleReplStarted = true;
}
@@ -1177,44 +1150,6 @@ await requestContext.SendResult(
codeActionCommands.ToArray());
}
- protected Task HandleEvaluateRequest(
- DebugAdapterMessages.EvaluateRequestArguments evaluateParams,
- RequestContext requestContext)
- {
- // We don't await the result of the execution here because we want
- // to be able to receive further messages while the current script
- // is executing. This important in cases where the pipeline thread
- // gets blocked by something in the script like a prompt to the user.
- var executeTask =
- this.editorSession.PowerShellContext.ExecuteScriptString(
- evaluateParams.Expression,
- writeInputToHost: true,
- writeOutputToHost: true,
- addToHistory: true);
-
- // Return the execution result after the task completes so that the
- // caller knows when command execution completed.
- executeTask.ContinueWith(
- (task) =>
- {
- // Start the command loop again
- // TODO: This can happen inside the PSHost
- this.editorSession.ConsoleService.StartReadLoop();
-
- // Return an empty result since the result value is irrelevant
- // for this request in the LanguageServer
- return
- requestContext.SendResult(
- new DebugAdapterMessages.EvaluateResponseBody
- {
- Result = "",
- VariablesReference = 0
- });
- });
-
- return Task.FromResult(true);
- }
-
#endregion
#region Event Handlers
@@ -1226,12 +1161,6 @@ await this.messageSender.SendEvent(
new Protocol.LanguageServer.RunspaceDetails(e.NewRunspace));
}
- private async void powerShellContext_OutputWritten(object sender, OutputWrittenEventArgs e)
- {
- // Queue the output for writing
- await this.outputDebouncer.Invoke(e);
- }
-
private async void ExtensionService_ExtensionAdded(object sender, EditorCommand e)
{
await this.messageSender.SendEvent(
diff --git a/src/PowerShellEditorServices.Protocol/Server/OutputDebouncer.cs b/src/PowerShellEditorServices.Protocol/Server/OutputDebouncer.cs
index e65c83b1c..20866655a 100644
--- a/src/PowerShellEditorServices.Protocol/Server/OutputDebouncer.cs
+++ b/src/PowerShellEditorServices.Protocol/Server/OutputDebouncer.cs
@@ -14,7 +14,7 @@ namespace Microsoft.PowerShell.EditorServices.Protocol.Server
/// Throttles output written via OutputEvents by batching all output
/// written within a short time window and writing it all out at once.
///
- internal class OutputDebouncer : AsyncDebouncer
+ public class OutputDebouncer : AsyncDebouncer
{
#region Private Fields
@@ -72,7 +72,7 @@ protected override async Task OnInvoke(OutputWrittenEventArgs output)
// Add to string (and include newline)
this.currentOutputString +=
output.OutputText +
- (output.IncludeNewLine ?
+ (output.IncludeNewLine ?
System.Environment.NewLine :
string.Empty);
}
diff --git a/src/PowerShellEditorServices/Console/ConsoleChoicePromptHandler.cs b/src/PowerShellEditorServices/Console/ConsoleChoicePromptHandler.cs
index 347b6232b..74b51c876 100644
--- a/src/PowerShellEditorServices/Console/ConsoleChoicePromptHandler.cs
+++ b/src/PowerShellEditorServices/Console/ConsoleChoicePromptHandler.cs
@@ -14,11 +14,14 @@ namespace Microsoft.PowerShell.EditorServices.Console
/// Provides a standard implementation of ChoicePromptHandler
/// for use in the interactive console (REPL).
///
- public class ConsoleChoicePromptHandler : ChoicePromptHandler
+ public abstract class ConsoleChoicePromptHandler : ChoicePromptHandler
{
#region Private Fields
- private IConsoleHost consoleHost;
+ ///
+ /// The IHostOutput instance to use for this prompt.
+ ///
+ protected IHostOutput hostOutput;
#endregion
@@ -27,15 +30,17 @@ public class ConsoleChoicePromptHandler : ChoicePromptHandler
///
/// Creates an instance of the ConsoleChoicePromptHandler class.
///
- ///
- /// The IConsoleHost implementation to use for writing to the
+ ///
+ /// The IHostOutput implementation to use for writing to the
/// console.
///
/// An ILogger implementation used for writing log messages.
- public ConsoleChoicePromptHandler(IConsoleHost consoleHost, ILogger logger)
- : base(logger)
+ public ConsoleChoicePromptHandler(
+ IHostOutput hostOutput,
+ ILogger logger)
+ : base(logger)
{
- this.consoleHost = consoleHost;
+ this.hostOutput = hostOutput;
}
#endregion
@@ -52,12 +57,12 @@ protected override void ShowPrompt(PromptStyle promptStyle)
{
if (this.Caption != null)
{
- this.consoleHost.WriteOutput(this.Caption);
+ this.hostOutput.WriteOutput(this.Caption);
}
if (this.Message != null)
{
- this.consoleHost.WriteOutput(this.Message);
+ this.hostOutput.WriteOutput(this.Message);
}
}
@@ -68,7 +73,7 @@ protected override void ShowPrompt(PromptStyle promptStyle)
choice.Label[choice.HotKeyIndex].ToString().ToUpper() :
string.Empty;
- this.consoleHost.WriteOutput(
+ this.hostOutput.WriteOutput(
string.Format(
"[{0}] {1} ",
hotKeyString,
@@ -76,7 +81,7 @@ protected override void ShowPrompt(PromptStyle promptStyle)
false);
}
- this.consoleHost.WriteOutput("[?] Help", false);
+ this.hostOutput.WriteOutput("[?] Help", false);
var validDefaultChoices =
this.DefaultChoices.Where(
@@ -90,21 +95,12 @@ protected override void ShowPrompt(PromptStyle promptStyle)
this.DefaultChoices
.Select(choice => this.Choices[choice].Label));
- this.consoleHost.WriteOutput(
+ this.hostOutput.WriteOutput(
$" (default is \"{choiceString}\"): ",
false);
}
}
- ///
- /// Reads an input string from the user.
- ///
- /// A CancellationToken that can be used to cancel the prompt.
- /// A Task that can be awaited to get the user's response.
- protected override Task ReadInputString(CancellationToken cancellationToken)
- {
- return this.consoleHost.ReadSimpleLine(cancellationToken);
- }
///
/// Implements behavior to handle the user's response.
@@ -121,7 +117,7 @@ protected override int[] HandleResponse(string responseString)
// Print help text
foreach (var choice in this.Choices)
{
- this.consoleHost.WriteOutput(
+ this.hostOutput.WriteOutput(
string.Format(
"{0} - {1}",
(choice.HotKeyCharacter.HasValue ?
@@ -137,4 +133,3 @@ protected override int[] HandleResponse(string responseString)
}
}
}
-
diff --git a/src/PowerShellEditorServices/Console/ConsoleInputPromptHandler.cs b/src/PowerShellEditorServices/Console/ConsoleInputPromptHandler.cs
index 672c227a5..8124633a7 100644
--- a/src/PowerShellEditorServices/Console/ConsoleInputPromptHandler.cs
+++ b/src/PowerShellEditorServices/Console/ConsoleInputPromptHandler.cs
@@ -15,11 +15,14 @@ namespace Microsoft.PowerShell.EditorServices.Console
/// Provides a standard implementation of InputPromptHandler
/// for use in the interactive console (REPL).
///
- public class ConsoleInputPromptHandler : InputPromptHandler
+ public abstract class ConsoleInputPromptHandler : InputPromptHandler
{
#region Private Fields
- private IConsoleHost consoleHost;
+ ///
+ /// The IHostOutput instance to use for this prompt.
+ ///
+ protected IHostOutput hostOutput;
#endregion
@@ -28,15 +31,17 @@ public class ConsoleInputPromptHandler : InputPromptHandler
///
/// Creates an instance of the ConsoleInputPromptHandler class.
///
- ///
- /// The IConsoleHost implementation to use for writing to the
+ ///
+ /// The IHostOutput implementation to use for writing to the
/// console.
///
/// An ILogger implementation used for writing log messages.
- public ConsoleInputPromptHandler(IConsoleHost consoleHost, ILogger logger)
- : base(logger)
+ public ConsoleInputPromptHandler(
+ IHostOutput hostOutput,
+ ILogger logger)
+ : base(logger)
{
- this.consoleHost = consoleHost;
+ this.hostOutput = hostOutput;
}
#endregion
@@ -53,12 +58,12 @@ protected override void ShowPromptMessage(string caption, string message)
{
if (!string.IsNullOrEmpty(caption))
{
- this.consoleHost.WriteOutput(caption, true);
+ this.hostOutput.WriteOutput(caption, true);
}
if (!string.IsNullOrEmpty(message))
{
- this.consoleHost.WriteOutput(message, true);
+ this.hostOutput.WriteOutput(message, true);
}
}
@@ -73,7 +78,7 @@ protected override void ShowFieldPrompt(FieldDetails fieldDetails)
// In this case don't write anything
if (!string.IsNullOrEmpty(fieldDetails.Name))
{
- this.consoleHost.WriteOutput(
+ this.hostOutput.WriteOutput(
fieldDetails.Name + ": ",
false);
}
@@ -89,33 +94,12 @@ protected override void ShowFieldPrompt(FieldDetails fieldDetails)
///
protected override void ShowErrorMessage(Exception e)
{
- this.consoleHost.WriteOutput(
+ this.hostOutput.WriteOutput(
e.Message,
true,
OutputType.Error);
}
- ///
- /// Reads an input string from the user.
- ///
- /// A CancellationToken that can be used to cancel the prompt.
- /// A Task that can be awaited to get the user's response.
- protected override Task ReadInputString(CancellationToken cancellationToken)
- {
- return this.consoleHost.ReadSimpleLine(cancellationToken);
- }
-
- ///
- /// Reads a SecureString from the user.
- ///
- /// A CancellationToken that can be used to cancel the prompt.
- /// A Task that can be awaited to get the user's response.
- protected override Task ReadSecureString(CancellationToken cancellationToken)
- {
- return this.consoleHost.ReadSecureLine(cancellationToken);
- }
-
#endregion
}
}
-
diff --git a/src/PowerShellEditorServices/Console/ConsolePromptHandlerContext.cs b/src/PowerShellEditorServices/Console/ConsolePromptHandlerContext.cs
deleted file mode 100644
index f1c1a4808..000000000
--- a/src/PowerShellEditorServices/Console/ConsolePromptHandlerContext.cs
+++ /dev/null
@@ -1,74 +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 System;
-using Microsoft.PowerShell.EditorServices.Utility;
-
-namespace Microsoft.PowerShell.EditorServices.Console
-{
- ///
- /// Provides a standard IPromptHandlerContext implementation for
- /// use in the interactive console (REPL).
- ///
- public class ConsolePromptHandlerContext : IPromptHandlerContext
- {
- #region Private Fields
-
- private ILogger logger;
- private IConsoleHost consoleHost;
-
- #endregion
-
- #region Constructors
-
- ///
- /// Creates a new instance of the ConsolePromptHandlerContext
- /// class.
- ///
- ///
- /// The IConsoleHost implementation to use for writing to the
- /// console.
- ///
- /// An ILogger implementation used for writing log messages.
- public ConsolePromptHandlerContext(
- IConsoleHost consoleHost,
- ILogger logger)
- {
- this.consoleHost = consoleHost;
- this.logger = logger;
- }
-
- #endregion
-
- #region Public Methods
-
- ///
- /// Creates a new ChoicePromptHandler instance so that
- /// the caller can display a choice prompt to the user.
- ///
- ///
- /// A new ChoicePromptHandler instance.
- ///
- public ChoicePromptHandler GetChoicePromptHandler()
- {
- return new ConsoleChoicePromptHandler(this.consoleHost, this.logger);
- }
-
- ///
- /// Creates a new InputPromptHandler instance so that
- /// the caller can display an input prompt to the user.
- ///
- ///
- /// A new InputPromptHandler instance.
- ///
- public InputPromptHandler GetInputPromptHandler()
- {
- return new ConsoleInputPromptHandler(this.consoleHost, this.logger);
- }
-
- #endregion
- }
-}
-
diff --git a/src/PowerShellEditorServices/Console/ConsoleService.cs b/src/PowerShellEditorServices/Console/ConsoleService.cs
deleted file mode 100644
index 60184f93b..000000000
--- a/src/PowerShellEditorServices/Console/ConsoleService.cs
+++ /dev/null
@@ -1,524 +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.Utility;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using System.Threading;
-
-namespace Microsoft.PowerShell.EditorServices.Console
-{
- using Microsoft.PowerShell.EditorServices.Session;
- using System;
- using System.Globalization;
- using System.Linq;
- using System.Management.Automation;
- using System.Security;
-
- ///
- /// Provides a high-level service for exposing an interactive
- /// PowerShell console (REPL) to the user.
- ///
- public class ConsoleService : IConsoleHost
- {
- #region Fields
-
- private bool isReadLoopStarted;
- private ConsoleReadLine consoleReadLine;
- private PowerShellContext powerShellContext;
-
- CancellationTokenSource readLineCancellationToken;
-
- private PromptHandler activePromptHandler;
- private Stack promptHandlerContextStack =
- new Stack();
-
- #endregion
-
- #region Properties
-
- ///
- /// Gets or sets a boolean determining whether the console (terminal)
- /// REPL should be used in this session.
- ///
- public bool EnableConsoleRepl { get; set; }
-
- #endregion
-
- #region Constructors
-
- ///
- /// Creates a new instance of the ConsoleService class.
- ///
- ///
- /// The PowerShellContext that will be used for executing commands
- /// against a runspace.
- ///
- public ConsoleService(PowerShellContext powerShellContext)
- : this(powerShellContext, null)
- {
- }
-
- ///
- /// Creates a new instance of the ConsoleService class.
- ///
- ///
- /// The PowerShellContext that will be used for executing commands
- /// against a runspace.
- ///
- ///
- /// The default IPromptHandlerContext implementation to use for
- /// displaying prompts to the user.
- ///
- public ConsoleService(
- PowerShellContext powerShellContext,
- IPromptHandlerContext defaultPromptHandlerContext)
- {
- // Register this instance as the IConsoleHost for the PowerShellContext
- this.powerShellContext = powerShellContext;
- this.powerShellContext.DebuggerStop += PowerShellContext_DebuggerStop;
- this.powerShellContext.DebuggerResumed += PowerShellContext_DebuggerResumed;
- this.powerShellContext.ExecutionStatusChanged += PowerShellContext_ExecutionStatusChanged;
-
- // Set the default prompt handler factory or create
- // a default if one is not provided
- if (defaultPromptHandlerContext == null)
- {
- defaultPromptHandlerContext =
- new ConsolePromptHandlerContext(this, Logger.CurrentLogger);
- }
-
- this.promptHandlerContextStack.Push(
- defaultPromptHandlerContext);
-
- this.consoleReadLine = new ConsoleReadLine(powerShellContext);
-
- }
-
- #endregion
-
- #region Public Methods
-
- ///
- /// Starts a terminal-based interactive console loop in the current process.
- ///
- public void StartReadLoop()
- {
- if (this.EnableConsoleRepl)
- {
- this.isReadLoopStarted = true;
- this.InnerStartReadLoop();
- }
- }
-
- ///
- /// Cancels an active read loop.
- ///
- public void CancelReadLoop()
- {
- this.isReadLoopStarted = false;
- this.InnerCancelReadLoop();
- }
-
- ///
- /// Executes a script file at the specified path.
- ///
- /// The path to the script file to execute.
- /// Arguments to pass to the script.
- /// A Task that can be awaited for completion.
- public async Task ExecuteScriptAtPath(string scriptPath, string arguments = null)
- {
- this.InnerCancelReadLoop();
-
- // If we don't escape wildcard characters in the script path, the script can
- // fail to execute if say the script name was foo][.ps1.
- // Related to issue #123.
- string escapedScriptPath = PowerShellContext.EscapePath(scriptPath, escapeSpaces: true);
-
- await this.powerShellContext.ExecuteScriptString(
- $"{escapedScriptPath} {arguments}",
- true,
- true,
- false);
-
- this.InnerStartReadLoop();
- }
-
- ///
- /// Pushes a new IPromptHandlerContext onto the stack. This
- /// is used when a prompt handler context is only needed for
- /// a short series of command executions.
- ///
- ///
- /// The IPromptHandlerContext instance to push onto the stack.
- ///
- public void PushPromptHandlerContext(IPromptHandlerContext promptHandlerContext)
- {
- // Push a new prompt handler factory for future prompts
- this.promptHandlerContextStack.Push(promptHandlerContext);
- }
-
- ///
- /// Pops the most recent IPromptHandlerContext from the stack.
- /// This is called when execution requiring a specific type of
- /// prompt has completed and the previous prompt handler context
- /// should be restored.
- ///
- public void PopPromptHandlerContext()
- {
- // The last item on the stack is the default handler, never pop it
- if (this.promptHandlerContextStack.Count > 1)
- {
- this.promptHandlerContextStack.Pop();
- }
- }
-
- ///
- /// Cancels the currently executing command or prompt.
- ///
- public void SendControlC()
- {
- if (this.activePromptHandler != null)
- {
- this.activePromptHandler.CancelPrompt();
- }
- else
- {
- // Cancel the current execution
- this.powerShellContext.AbortExecution();
- }
- }
-
- ///
- /// Reads an input string from the user.
- ///
- /// A CancellationToken that can be used to cancel the prompt.
- /// A Task that can be awaited to get the user's response.
- public async Task ReadSimpleLine(CancellationToken cancellationToken)
- {
- string inputLine = await this.consoleReadLine.ReadSimpleLine(cancellationToken);
- this.WriteOutput(string.Empty, true);
- return inputLine;
- }
-
- ///
- /// Reads a SecureString from the user.
- ///
- /// A CancellationToken that can be used to cancel the prompt.
- /// A Task that can be awaited to get the user's response.
- public async Task ReadSecureLine(CancellationToken cancellationToken)
- {
- SecureString secureString = await this.consoleReadLine.ReadSecureLine(cancellationToken);
- this.WriteOutput(string.Empty, true);
- return secureString;
- }
-
- #endregion
-
- #region Private Methods
-
- private async Task WritePromptStringToHost()
- {
- PSCommand promptCommand = new PSCommand().AddScript("prompt");
-
- string promptString =
- (await this.powerShellContext.ExecuteCommand(promptCommand, false, false))
- .Select(pso => pso.BaseObject)
- .OfType()
- .FirstOrDefault() ?? "PS> ";
-
- // Add the [DBG] prefix if we're stopped in the debugger
- if (this.powerShellContext.IsDebuggerStopped)
- {
- promptString =
- string.Format(
- CultureInfo.InvariantCulture,
- "[DBG]: {0}",
- promptString);
- }
-
- // Update the stored prompt string if the session is remote
- if (this.powerShellContext.CurrentRunspace.Location == RunspaceLocation.Remote)
- {
- promptString =
- string.Format(
- CultureInfo.InvariantCulture,
- "[{0}]: {1}",
- this.powerShellContext.CurrentRunspace.Runspace.ConnectionInfo != null
- ? this.powerShellContext.CurrentRunspace.Runspace.ConnectionInfo.ComputerName
- : this.powerShellContext.CurrentRunspace.SessionDetails.ComputerName,
- promptString);
- }
-
- // Write the prompt string
- this.WriteOutput(promptString, false);
- }
-
- private void WriteDebuggerBanner(DebuggerStopEventArgs eventArgs)
- {
- // TODO: What do we display when we don't know why we stopped?
-
- if (eventArgs.Breakpoints.Count > 0)
- {
- // The breakpoint classes have nice ToString output so use that
- this.WriteOutput(
- Environment.NewLine + $"Hit {eventArgs.Breakpoints[0].ToString()}\n",
- true,
- OutputType.Normal,
- ConsoleColor.Blue);
- }
- }
-
- private void InnerStartReadLoop()
- {
- if (this.EnableConsoleRepl)
- {
- if (this.readLineCancellationToken == null)
- {
- this.readLineCancellationToken = new CancellationTokenSource();
-
- var terminalThreadTask =
- Task.Factory.StartNew(
- async () =>
- {
- await this.StartReplLoop(this.readLineCancellationToken.Token);
- });
- }
- else
- {
- Logger.CurrentLogger.Write(LogLevel.Verbose, "InnerStartReadLoop called while read loop is already running");
- }
- }
- }
-
- private void InnerCancelReadLoop()
- {
- if (this.readLineCancellationToken != null)
- {
- // Set this to false so that Ctrl+C isn't trapped by any
- // lingering ReadKey
- Console.TreatControlCAsInput = false;
-
- this.readLineCancellationToken.Cancel();
- this.readLineCancellationToken = null;
- }
- }
-
- private async Task StartReplLoop(CancellationToken cancellationToken)
- {
- do
- {
- string commandString = null;
-
- await this.WritePromptStringToHost();
-
- try
- {
- commandString =
- await this.consoleReadLine.ReadCommandLine(
- cancellationToken);
- }
- catch (PipelineStoppedException)
- {
- this.WriteOutput(
- "^C",
- true,
- OutputType.Normal,
- foregroundColor: ConsoleColor.Red);
- }
- catch (TaskCanceledException)
- {
- // Do nothing here, the while loop condition will exit.
- }
- catch (Exception e) // Narrow this if possible
- {
- this.WriteOutput(
- $"\n\nAn error occurred while reading input:\n\n{e.ToString()}\n",
- true,
- OutputType.Error);
-
- Logger.CurrentLogger.WriteException("Caught exception while reading command line", e);
- }
-
- if (commandString != null)
- {
- Console.Write(Environment.NewLine);
-
- if (!string.IsNullOrWhiteSpace(commandString))
- {
- var unusedTask =
- this.powerShellContext
- .ExecuteScriptString(
- commandString,
- false,
- true,
- true)
- .ConfigureAwait(false);
-
- break;
- }
- }
- }
- while (!cancellationToken.IsCancellationRequested);
- }
-
- #endregion
-
- #region Events
-
- ///
- /// An event that is raised when textual output of any type is
- /// written to the session.
- ///
- public event EventHandler OutputWritten;
-
- #endregion
-
- #region IConsoleHost Implementation
-
- void IConsoleHost.WriteOutput(string outputString, bool includeNewLine, OutputType outputType, ConsoleColor foregroundColor, ConsoleColor backgroundColor)
- {
- if (this.EnableConsoleRepl)
- {
- ConsoleColor oldForegroundColor = Console.ForegroundColor;
- ConsoleColor oldBackgroundColor = Console.BackgroundColor;
-
- Console.ForegroundColor = foregroundColor;
- Console.BackgroundColor = backgroundColor;
-
- Console.Write(outputString + (includeNewLine ? Environment.NewLine : ""));
-
- Console.ForegroundColor = oldForegroundColor;
- Console.BackgroundColor = oldBackgroundColor;
- }
- else
- {
- this.OutputWritten?.Invoke(
- this,
- new OutputWrittenEventArgs(
- outputString,
- includeNewLine,
- outputType,
- foregroundColor,
- backgroundColor));
- }
- }
-
- void IConsoleHost.UpdateProgress(long sourceId, ProgressDetails progressDetails)
- {
- //throw new NotImplementedException();
- }
-
- void IConsoleHost.ExitSession(int exitCode)
- {
- //throw new NotImplementedException();
- }
-
- ChoicePromptHandler IConsoleHost.GetChoicePromptHandler()
- {
- return this.GetPromptHandler(
- factory => factory.GetChoicePromptHandler());
- }
-
- InputPromptHandler IConsoleHost.GetInputPromptHandler()
- {
- return this.GetPromptHandler(
- factory => factory.GetInputPromptHandler());
- }
-
- private TPromptHandler GetPromptHandler(
- Func factoryInvoker)
- where TPromptHandler : PromptHandler
- {
- if (this.activePromptHandler != null)
- {
- Logger.CurrentLogger.Write(
- LogLevel.Error,
- "Prompt handler requested while another prompt is already active.");
- }
-
- // Get the topmost prompt handler factory
- IPromptHandlerContext promptHandlerContext =
- this.promptHandlerContextStack.Peek();
-
- TPromptHandler promptHandler = factoryInvoker(promptHandlerContext);
- this.activePromptHandler = promptHandler;
- this.activePromptHandler.PromptCancelled += activePromptHandler_PromptCancelled;
-
- return promptHandler;
- }
-
- #endregion
-
- #region Event Handlers
-
- private void activePromptHandler_PromptCancelled(object sender, EventArgs e)
- {
- // Clean up the existing prompt
- this.activePromptHandler.PromptCancelled -= activePromptHandler_PromptCancelled;
- this.activePromptHandler = null;
- }
-
- private void PowerShellContext_DebuggerStop(object sender, System.Management.Automation.DebuggerStopEventArgs e)
- {
- // Cancel any existing prompt first
- this.InnerCancelReadLoop();
-
- this.WriteDebuggerBanner(e);
- this.InnerStartReadLoop();
- }
-
- private void PowerShellContext_DebuggerResumed(object sender, System.Management.Automation.DebuggerResumeAction e)
- {
- this.InnerCancelReadLoop();
- }
-
- private void PowerShellContext_ExecutionStatusChanged(object sender, ExecutionStatusChangedEventArgs eventArgs)
- {
- if (this.EnableConsoleRepl && this.isReadLoopStarted)
- {
- if (eventArgs.ExecutionStatus == ExecutionStatus.Aborted)
- {
- // When aborted, cancel any lingering prompts
- if (this.activePromptHandler != null)
- {
- this.activePromptHandler.CancelPrompt();
- this.WriteOutput(string.Empty);
- }
- }
- else if (
- eventArgs.ExecutionOptions.WriteOutputToHost ||
- eventArgs.ExecutionOptions.InterruptCommandPrompt)
- {
- // Any command which writes output to the host will affect
- // the display of the prompt
- if (eventArgs.ExecutionStatus != ExecutionStatus.Running)
- {
- // Execution has completed, start the input prompt
- this.InnerStartReadLoop();
- }
- else
- {
- // A new command was started, cancel the input prompt
- this.InnerCancelReadLoop();
- this.WriteOutput(string.Empty);
- }
- }
- else if (
- eventArgs.ExecutionOptions.WriteErrorsToHost &&
- (eventArgs.ExecutionStatus == ExecutionStatus.Failed ||
- eventArgs.HadErrors))
- {
- this.InnerCancelReadLoop();
- this.WriteOutput(string.Empty);
- this.InnerStartReadLoop();
- }
- }
- }
-
- #endregion
- }
-}
-
diff --git a/src/PowerShellEditorServices/Console/IPromptHandlerContext.cs b/src/PowerShellEditorServices/Console/IPromptHandlerContext.cs
deleted file mode 100644
index f967ee0d8..000000000
--- a/src/PowerShellEditorServices/Console/IPromptHandlerContext.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-//
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-
-namespace Microsoft.PowerShell.EditorServices.Console
-{
- ///
- /// Defines an interface for requesting prompt handlers in
- /// a given user interface context.
- ///
- public interface IPromptHandlerContext
- {
- ///
- /// Creates a new ChoicePromptHandler instance so that
- /// the caller can display a choice prompt to the user.
- ///
- ///
- /// A new ChoicePromptHandler instance.
- ///
- ChoicePromptHandler GetChoicePromptHandler();
-
- ///
- /// Creates a new InputPromptHandler instance so that
- /// the caller can display an input prompt to the user.
- ///
- ///
- /// A new InputPromptHandler instance.
- ///
- InputPromptHandler GetInputPromptHandler();
- }
-}
-
diff --git a/src/PowerShellEditorServices/Console/TerminalChoicePromptHandler.cs b/src/PowerShellEditorServices/Console/TerminalChoicePromptHandler.cs
new file mode 100644
index 000000000..b99b959cd
--- /dev/null
+++ b/src/PowerShellEditorServices/Console/TerminalChoicePromptHandler.cs
@@ -0,0 +1,63 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.PowerShell.EditorServices.Utility;
+
+namespace Microsoft.PowerShell.EditorServices.Console
+{
+ ///
+ /// Provides a standard implementation of ChoicePromptHandler
+ /// for use in the interactive console (REPL).
+ ///
+ internal class TerminalChoicePromptHandler : ConsoleChoicePromptHandler
+ {
+ #region Private Fields
+
+ private ConsoleReadLine consoleReadLine;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Creates an instance of the ConsoleChoicePromptHandler class.
+ ///
+ ///
+ /// The ConsoleReadLine instance to use for interacting with the terminal.
+ ///
+ ///
+ /// The IHostOutput implementation to use for writing to the
+ /// console.
+ ///
+ /// An ILogger implementation used for writing log messages.
+ public TerminalChoicePromptHandler(
+ ConsoleReadLine consoleReadLine,
+ IHostOutput hostOutput,
+ ILogger logger)
+ : base(hostOutput, logger)
+ {
+ this.hostOutput = hostOutput;
+ this.consoleReadLine = consoleReadLine;
+ }
+
+ #endregion
+
+ ///
+ /// Reads an input string from the user.
+ ///
+ /// A CancellationToken that can be used to cancel the prompt.
+ /// A Task that can be awaited to get the user's response.
+ protected override async Task ReadInputString(CancellationToken cancellationToken)
+ {
+ string inputString = await this.consoleReadLine.ReadSimpleLine(cancellationToken);
+ this.hostOutput.WriteOutput(string.Empty);
+
+ return inputString;
+ }
+ }
+}
diff --git a/src/PowerShellEditorServices/Console/TerminalInputPromptHandler.cs b/src/PowerShellEditorServices/Console/TerminalInputPromptHandler.cs
new file mode 100644
index 000000000..e77bf2f9a
--- /dev/null
+++ b/src/PowerShellEditorServices/Console/TerminalInputPromptHandler.cs
@@ -0,0 +1,80 @@
+//
+// 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.Security;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.PowerShell.EditorServices.Utility;
+
+namespace Microsoft.PowerShell.EditorServices.Console
+{
+ ///
+ /// Provides a standard implementation of InputPromptHandler
+ /// for use in the interactive console (REPL).
+ ///
+ internal class TerminalInputPromptHandler : ConsoleInputPromptHandler
+ {
+ #region Private Fields
+
+ private ConsoleReadLine consoleReadLine;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Creates an instance of the ConsoleInputPromptHandler class.
+ ///
+ ///
+ /// The ConsoleReadLine instance to use for interacting with the terminal.
+ ///
+ ///
+ /// The IHostOutput implementation to use for writing to the
+ /// console.
+ ///
+ /// An ILogger implementation used for writing log messages.
+ public TerminalInputPromptHandler(
+ ConsoleReadLine consoleReadLine,
+ IHostOutput hostOutput,
+ ILogger logger)
+ : base(hostOutput, logger)
+ {
+ this.consoleReadLine = consoleReadLine;
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ ///
+ /// Reads an input string from the user.
+ ///
+ /// A CancellationToken that can be used to cancel the prompt.
+ /// A Task that can be awaited to get the user's response.
+ protected override async Task ReadInputString(CancellationToken cancellationToken)
+ {
+ string inputString = await this.consoleReadLine.ReadSimpleLine(cancellationToken);
+ this.hostOutput.WriteOutput(string.Empty);
+
+ return inputString;
+ }
+
+ ///
+ /// Reads a SecureString from the user.
+ ///
+ /// A CancellationToken that can be used to cancel the prompt.
+ /// A Task that can be awaited to get the user's response.
+ protected override async Task ReadSecureString(CancellationToken cancellationToken)
+ {
+ SecureString secureString = await this.consoleReadLine.ReadSecureLine(cancellationToken);
+ this.hostOutput.WriteOutput(string.Empty);
+
+ return secureString;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/PowerShellEditorServices/Session/EditorSession.cs b/src/PowerShellEditorServices/Session/EditorSession.cs
index e72e26588..6c00581b4 100644
--- a/src/PowerShellEditorServices/Session/EditorSession.cs
+++ b/src/PowerShellEditorServices/Session/EditorSession.cs
@@ -26,6 +26,11 @@ public class EditorSession
#region Properties
+ ///
+ /// Gets the IHostInput implementation to use for this session.
+ ///
+ public IHostInput HostInput { get; private set; }
+
///
/// Gets the Workspace instance for this session.
///
@@ -51,11 +56,6 @@ public class EditorSession
///
public DebugService DebugService { get; private set; }
- ///
- /// Gets the ConsoleService instance for this session.
- ///
- public ConsoleService ConsoleService { get; private set; }
-
///
/// Gets the ExtensionService instance for this session.
///
@@ -71,18 +71,12 @@ public class EditorSession
///
public RemoteFileManager RemoteFileManager { get; private set; }
- ///
- /// Gets a boolean which is true if the integrated console host is
- /// active in this session.
- ///
- public bool UsesConsoleHost { get; private set; }
-
#endregion
#region Constructors
///
- ///
+ ///
///
/// An ILogger implementation used for writing log messages.
public EditorSession(ILogger logger)
@@ -99,16 +93,15 @@ public EditorSession(ILogger logger)
/// for the ConsoleService.
///
///
- ///
+ ///
public void StartSession(
PowerShellContext powerShellContext,
- ConsoleService consoleService)
+ IHostInput hostInput)
{
- // Initialize all services
this.PowerShellContext = powerShellContext;
- this.ConsoleService = consoleService;
- this.UsesConsoleHost = this.ConsoleService.EnableConsoleRepl;
+ this.HostInput = hostInput;
+ // Initialize all services
this.LanguageService = new LanguageService(this.PowerShellContext, this.logger);
this.ExtensionService = new ExtensionService(this.PowerShellContext);
this.TemplateService = new TemplateService(this.PowerShellContext, this.logger);
@@ -124,19 +117,16 @@ public void StartSession(
/// for the ConsoleService.
///
///
- ///
///
/// An IEditorOperations implementation used to interact with the editor.
///
public void StartDebugSession(
PowerShellContext powerShellContext,
- ConsoleService consoleService,
IEditorOperations editorOperations)
{
- // Initialize all services
this.PowerShellContext = powerShellContext;
- this.ConsoleService = consoleService;
+ // Initialize all services
this.RemoteFileManager = new RemoteFileManager(this.PowerShellContext, editorOperations, logger);
this.DebugService = new DebugService(this.PowerShellContext, this.RemoteFileManager, logger);
diff --git a/src/PowerShellEditorServices/Session/SessionPSHost.cs b/src/PowerShellEditorServices/Session/Host/EditorServicesPSHost.cs
similarity index 71%
rename from src/PowerShellEditorServices/Session/SessionPSHost.cs
rename to src/PowerShellEditorServices/Session/Host/EditorServicesPSHost.cs
index cc54438ec..1210f8284 100644
--- a/src/PowerShellEditorServices/Session/SessionPSHost.cs
+++ b/src/PowerShellEditorServices/Session/Host/EditorServicesPSHost.cs
@@ -17,38 +17,18 @@ namespace Microsoft.PowerShell.EditorServices
/// ConsoleService and routes its calls to an IConsoleHost
/// implementation.
///
- public class ConsoleServicePSHost : PSHost, IHostSupportsInteractiveSession
+ public class EditorServicesPSHost : PSHost, IHostSupportsInteractiveSession
{
#region Private Fields
+ private ILogger Logger;
private HostDetails hostDetails;
- private IConsoleHost consoleHost;
- private bool isNativeApplicationRunning;
private Guid instanceId = Guid.NewGuid();
- private ConsoleServicePSHostUserInterface hostUserInterface;
+ private EditorServicesPSHostUserInterface hostUserInterface;
private IHostSupportsInteractiveSession hostSupportsInteractiveSession;
#endregion
- #region Properties
-
- internal IConsoleHost ConsoleHost
- {
- get { return this.consoleHost; }
- set
- {
- this.consoleHost = value;
- this.hostUserInterface.ConsoleHost = value;
- }
- }
-
- ///
- /// Gets the ConsoleServices owned by this host.
- ///
- public ConsoleService ConsoleService { get; private set; }
-
- #endregion
-
#region Constructors
///
@@ -61,35 +41,20 @@ internal IConsoleHost ConsoleHost
///
/// Provides details about the host application.
///
- ///
- /// Enables a terminal-based REPL for this session.
+ ///
+ /// The EditorServicesPSHostUserInterface implementation to use for this host.
///
- public ConsoleServicePSHost(
+ /// An ILogger implementation to use for this host.
+ public EditorServicesPSHost(
PowerShellContext powerShellContext,
HostDetails hostDetails,
- bool enableConsoleRepl)
+ EditorServicesPSHostUserInterface hostUserInterface,
+ ILogger logger)
{
+ this.Logger = logger;
this.hostDetails = hostDetails;
- this.hostUserInterface = new ConsoleServicePSHostUserInterface(enableConsoleRepl);
+ this.hostUserInterface = hostUserInterface;
this.hostSupportsInteractiveSession = powerShellContext;
-
- this.ConsoleService = new ConsoleService(powerShellContext);
- this.ConsoleService.EnableConsoleRepl = enableConsoleRepl;
- this.ConsoleHost = this.ConsoleService;
-
- System.Console.CancelKeyPress +=
- (obj, args) =>
- {
- if (!this.isNativeApplicationRunning)
- {
- // We'll handle Ctrl+C
- if (this.ConsoleHost != null)
- {
- args.Cancel = true;
- this.consoleHost.SendControlC();
- }
- }
- };
}
#endregion
@@ -151,7 +116,7 @@ public override PSHostUserInterface UI
///
public override void EnterNestedPrompt()
{
- Logger.CurrentLogger.Write(LogLevel.Verbose, "EnterNestedPrompt() called.");
+ Logger.Write(LogLevel.Verbose, "EnterNestedPrompt() called.");
}
///
@@ -159,7 +124,7 @@ public override void EnterNestedPrompt()
///
public override void ExitNestedPrompt()
{
- Logger.CurrentLogger.Write(LogLevel.Verbose, "ExitNestedPrompt() called.");
+ Logger.Write(LogLevel.Verbose, "ExitNestedPrompt() called.");
}
///
@@ -167,8 +132,8 @@ public override void ExitNestedPrompt()
///
public override void NotifyBeginApplication()
{
- Logger.CurrentLogger.Write(LogLevel.Verbose, "NotifyBeginApplication() called.");
- this.isNativeApplicationRunning = true;
+ Logger.Write(LogLevel.Verbose, "NotifyBeginApplication() called.");
+ this.hostUserInterface.IsNativeApplicationRunning = true;
}
///
@@ -176,8 +141,8 @@ public override void NotifyBeginApplication()
///
public override void NotifyEndApplication()
{
- Logger.CurrentLogger.Write(LogLevel.Verbose, "NotifyEndApplication() called.");
- this.isNativeApplicationRunning = false;
+ Logger.Write(LogLevel.Verbose, "NotifyEndApplication() called.");
+ this.hostUserInterface.IsNativeApplicationRunning = false;
}
///
@@ -186,11 +151,6 @@ public override void NotifyEndApplication()
///
public override void SetShouldExit(int exitCode)
{
- if (this.consoleHost != null)
- {
- this.consoleHost.ExitSession(exitCode);
- }
-
if (this.IsRunspacePushed)
{
this.PopRunspace();
diff --git a/src/PowerShellEditorServices/Session/Host/EditorServicesPSHostUserInterface.cs b/src/PowerShellEditorServices/Session/Host/EditorServicesPSHostUserInterface.cs
new file mode 100644
index 000000000..a818c9446
--- /dev/null
+++ b/src/PowerShellEditorServices/Session/Host/EditorServicesPSHostUserInterface.cs
@@ -0,0 +1,893 @@
+//
+// 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.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Management.Automation;
+using System.Management.Automation.Host;
+using System.Linq;
+using System.Security;
+using System.Threading.Tasks;
+using Microsoft.PowerShell.EditorServices.Console;
+using System.Threading;
+using Microsoft.PowerShell.EditorServices.Utility;
+using Microsoft.PowerShell.EditorServices.Session;
+using System.Globalization;
+
+namespace Microsoft.PowerShell.EditorServices
+{
+ ///
+ /// Provides an implementation of the PSHostUserInterface class
+ /// for the ConsoleService and routes its calls to an IConsoleHost
+ /// implementation.
+ ///
+ public abstract class EditorServicesPSHostUserInterface :
+ PSHostUserInterface,
+ IHostInput,
+ IHostOutput,
+ IHostUISupportsMultipleChoiceSelection
+ {
+ #region Private Fields
+
+ private PromptHandler activePromptHandler;
+ private PSHostRawUserInterface rawUserInterface;
+ private CancellationTokenSource commandLoopCancellationToken;
+
+ ///
+ /// The PowerShellContext to use for executing commands.
+ ///
+ protected PowerShellContext powerShellContext;
+
+ #endregion
+
+ #region Public Constants
+
+ ///
+ /// Gets a const string for the console's debug message prefix.
+ ///
+ public const string DebugMessagePrefix = "DEBUG: ";
+
+ ///
+ /// Gets a const string for the console's warning message prefix.
+ ///
+ public const string WarningMessagePrefix = "WARNING: ";
+
+ ///
+ /// Gets a const string for the console's verbose message prefix.
+ ///
+ public const string VerboseMessagePrefix = "VERBOSE: ";
+
+ #endregion
+
+ #region Properties
+
+#if !PowerShellv3 && !PowerShellv4 && !PowerShellv5r1 // Only available in Windows 10 Update 1 or higher
+ ///
+ /// Returns true if the host supports VT100 output codes.
+ ///
+ public override bool SupportsVirtualTerminal => true;
+#endif
+
+ ///
+ /// Returns true if a native application is currently running.
+ ///
+ public bool IsNativeApplicationRunning { get; internal set; }
+
+ private bool IsCommandLoopRunning { get; set; }
+
+ ///
+ /// Gets the ILogger implementation used for this host.
+ ///
+ protected ILogger Logger { get; private set; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Creates a new instance of the ConsoleServicePSHostUserInterface
+ /// class with the given IConsoleHost implementation.
+ ///
+ /// The PowerShellContext to use for executing commands.
+ /// The PSHostRawUserInterface implementation to use for this host.
+ /// An ILogger implementation to use for this host.
+ public EditorServicesPSHostUserInterface(
+ PowerShellContext powerShellContext,
+ PSHostRawUserInterface rawUserInterface,
+ ILogger logger)
+ {
+ this.Logger = logger;
+ this.powerShellContext = powerShellContext;
+ this.rawUserInterface = rawUserInterface;
+
+ this.powerShellContext.DebuggerStop += PowerShellContext_DebuggerStop;
+ this.powerShellContext.DebuggerResumed += PowerShellContext_DebuggerResumed;
+ this.powerShellContext.ExecutionStatusChanged += PowerShellContext_ExecutionStatusChanged;
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ void IHostInput.StartCommandLoop()
+ {
+ if (!this.IsCommandLoopRunning)
+ {
+ this.IsCommandLoopRunning = true;
+ this.ShowCommandPrompt();
+ }
+ }
+
+ void IHostInput.StopCommandLoop()
+ {
+ if (this.IsCommandLoopRunning)
+ {
+ this.IsCommandLoopRunning = false;
+ this.CancelCommandPrompt();
+ }
+ }
+
+ private void ShowCommandPrompt()
+ {
+ if (this.commandLoopCancellationToken == null)
+ {
+ this.commandLoopCancellationToken = new CancellationTokenSource();
+
+ var commandLoopThreadTask =
+ Task.Factory.StartNew(
+ async () =>
+ {
+ await this.StartReplLoop(this.commandLoopCancellationToken.Token);
+ });
+ }
+ else
+ {
+ Logger.Write(LogLevel.Verbose, "StartReadLoop called while read loop is already running");
+ }
+ }
+
+ private void CancelCommandPrompt()
+ {
+ if (this.commandLoopCancellationToken != null)
+ {
+ // Set this to false so that Ctrl+C isn't trapped by any
+ // lingering ReadKey
+ // TOOD: Move this to Terminal impl!
+ //Console.TreatControlCAsInput = false;
+
+ this.commandLoopCancellationToken.Cancel();
+ this.commandLoopCancellationToken = null;
+ }
+ }
+
+ ///
+ /// Cancels the currently executing command or prompt.
+ ///
+ public void SendControlC()
+ {
+ if (this.activePromptHandler != null)
+ {
+ this.activePromptHandler.CancelPrompt();
+ }
+ else
+ {
+ // Cancel the current execution
+ this.powerShellContext.AbortExecution();
+ }
+ }
+
+ #endregion
+
+ #region Abstract Methods
+
+ ///
+ /// Requests that the HostUI implementation read a command line
+ /// from the user to be executed in the integrated console command
+ /// loop.
+ ///
+ ///
+ /// A CancellationToken used to cancel the command line request.
+ ///
+ /// A Task that can be awaited for the resulting input string.
+ protected abstract Task ReadCommandLine(CancellationToken cancellationToken);
+
+ ///
+ /// Creates an InputPrompt handle to use for displaying input
+ /// prompts to the user.
+ ///
+ /// A new InputPromptHandler instance.
+ protected abstract InputPromptHandler OnCreateInputPromptHandler();
+
+ ///
+ /// Creates a ChoicePromptHandler to use for displaying a
+ /// choice prompt to the user.
+ ///
+ /// A new ChoicePromptHandler instance.
+ protected abstract ChoicePromptHandler OnCreateChoicePromptHandler();
+
+ ///
+ /// Writes output of the given type to the user interface with
+ /// the given foreground and background colors. Also includes
+ /// a newline if requested.
+ ///
+ ///
+ /// The output string to be written.
+ ///
+ ///
+ /// If true, a newline should be appended to the output's contents.
+ ///
+ ///
+ /// Specifies the type of output to be written.
+ ///
+ ///
+ /// Specifies the foreground color of the output to be written.
+ ///
+ ///
+ /// Specifies the background color of the output to be written.
+ ///
+ public abstract void WriteOutput(
+ string outputString,
+ bool includeNewLine,
+ OutputType outputType,
+ ConsoleColor foregroundColor,
+ ConsoleColor backgroundColor);
+
+ ///
+ /// Sends a progress update event to the user.
+ ///
+ /// The source ID of the progress event.
+ /// The details of the activity's current progress.
+ protected abstract void UpdateProgress(
+ long sourceId,
+ ProgressDetails progressDetails);
+
+ #endregion
+
+ #region IHostInput Implementation
+
+ #endregion
+
+ #region PSHostUserInterface Implementation
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override Dictionary Prompt(
+ string promptCaption,
+ string promptMessage,
+ Collection fieldDescriptions)
+ {
+ FieldDetails[] fields =
+ fieldDescriptions
+ .Select(f => { return FieldDetails.Create(f, this.Logger); })
+ .ToArray();
+
+ CancellationTokenSource cancellationToken = new CancellationTokenSource();
+ Task> promptTask =
+ this.CreateInputPromptHandler()
+ .PromptForInput(
+ promptCaption,
+ promptMessage,
+ fields,
+ cancellationToken.Token);
+
+ // Run the prompt task and wait for it to return
+ this.WaitForPromptCompletion(
+ promptTask,
+ "Prompt",
+ cancellationToken);
+
+ // Convert all values to PSObjects
+ var psObjectDict = new Dictionary();
+
+ // The result will be null if the prompt was cancelled
+ if (promptTask.Result != null)
+ {
+ // Convert all values to PSObjects
+ foreach (var keyValuePair in promptTask.Result)
+ {
+ psObjectDict.Add(
+ keyValuePair.Key,
+ keyValuePair.Value != null
+ ? PSObject.AsPSObject(keyValuePair.Value)
+ : null);
+ }
+ }
+
+ // Return the result
+ return psObjectDict;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override int PromptForChoice(
+ string promptCaption,
+ string promptMessage,
+ Collection choiceDescriptions,
+ int defaultChoice)
+ {
+ ChoiceDetails[] choices =
+ choiceDescriptions
+ .Select(ChoiceDetails.Create)
+ .ToArray();
+
+ CancellationTokenSource cancellationToken = new CancellationTokenSource();
+ Task promptTask =
+ this.CreateChoicePromptHandler()
+ .PromptForChoice(
+ promptCaption,
+ promptMessage,
+ choices,
+ defaultChoice,
+ cancellationToken.Token);
+
+ // Run the prompt task and wait for it to return
+ this.WaitForPromptCompletion(
+ promptTask,
+ "PromptForChoice",
+ cancellationToken);
+
+ // Return the result
+ return promptTask.Result;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override PSCredential PromptForCredential(
+ string promptCaption,
+ string promptMessage,
+ string userName,
+ string targetName,
+ PSCredentialTypes allowedCredentialTypes,
+ PSCredentialUIOptions options)
+ {
+ CancellationTokenSource cancellationToken = new CancellationTokenSource();
+
+ Task> promptTask =
+ this.CreateInputPromptHandler()
+ .PromptForInput(
+ promptCaption,
+ promptMessage,
+ new FieldDetails[] { new CredentialFieldDetails("Credential", "Credential", userName) },
+ cancellationToken.Token);
+
+ Task unpackTask =
+ promptTask.ContinueWith(
+ task =>
+ {
+ if (task.IsFaulted)
+ {
+ throw task.Exception;
+ }
+ else if (task.IsCanceled)
+ {
+ throw new TaskCanceledException(task);
+ }
+
+ // Return the value of the sole field
+ return (PSCredential)task.Result?["Credential"];
+ });
+
+ // Run the prompt task and wait for it to return
+ this.WaitForPromptCompletion(
+ unpackTask,
+ "PromptForCredential",
+ cancellationToken);
+
+ return unpackTask.Result;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override PSCredential PromptForCredential(
+ string caption,
+ string message,
+ string userName,
+ string targetName)
+ {
+ return this.PromptForCredential(
+ caption,
+ message,
+ userName,
+ targetName,
+ PSCredentialTypes.Default,
+ PSCredentialUIOptions.Default);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public override PSHostRawUserInterface RawUI
+ {
+ get { return this.rawUserInterface; }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public override string ReadLine()
+ {
+ CancellationTokenSource cancellationToken = new CancellationTokenSource();
+
+ Task promptTask =
+ this.CreateInputPromptHandler()
+ .PromptForInput(cancellationToken.Token);
+
+ // Run the prompt task and wait for it to return
+ this.WaitForPromptCompletion(
+ promptTask,
+ "ReadLine",
+ cancellationToken);
+
+ return promptTask.Result;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public override SecureString ReadLineAsSecureString()
+ {
+ CancellationTokenSource cancellationToken = new CancellationTokenSource();
+
+ Task promptTask =
+ this.CreateInputPromptHandler()
+ .PromptForSecureInput(cancellationToken.Token);
+
+ // Run the prompt task and wait for it to return
+ this.WaitForPromptCompletion(
+ promptTask,
+ "ReadLineAsSecureString",
+ cancellationToken);
+
+ return promptTask.Result;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override void Write(
+ ConsoleColor foregroundColor,
+ ConsoleColor backgroundColor,
+ string value)
+ {
+ this.WriteOutput(
+ value,
+ false,
+ OutputType.Normal,
+ foregroundColor,
+ backgroundColor);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public override void Write(string value)
+ {
+ this.WriteOutput(
+ value,
+ false,
+ OutputType.Normal,
+ this.rawUserInterface.ForegroundColor,
+ this.rawUserInterface.BackgroundColor);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public override void WriteLine(string value)
+ {
+ this.WriteOutput(
+ value,
+ true,
+ OutputType.Normal,
+ this.rawUserInterface.ForegroundColor,
+ this.rawUserInterface.BackgroundColor);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public override void WriteDebugLine(string message)
+ {
+ this.WriteOutput(
+ DebugMessagePrefix + message,
+ true,
+ OutputType.Debug,
+ foregroundColor: ConsoleColor.Yellow);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public override void WriteVerboseLine(string message)
+ {
+ this.WriteOutput(
+ VerboseMessagePrefix + message,
+ true,
+ OutputType.Verbose,
+ foregroundColor: ConsoleColor.Blue);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public override void WriteWarningLine(string message)
+ {
+ this.WriteOutput(
+ WarningMessagePrefix + message,
+ true,
+ OutputType.Warning,
+ foregroundColor: ConsoleColor.Yellow);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public override void WriteErrorLine(string value)
+ {
+ this.WriteOutput(
+ value,
+ true,
+ OutputType.Error,
+ ConsoleColor.Red);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override void WriteProgress(
+ long sourceId,
+ ProgressRecord record)
+ {
+ this.UpdateProgress(
+ sourceId,
+ ProgressDetails.Create(record));
+ }
+
+ #endregion
+
+ #region IHostUISupportsMultipleChoiceSelection Implementation
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public Collection PromptForChoice(
+ string promptCaption,
+ string promptMessage,
+ Collection choiceDescriptions,
+ IEnumerable defaultChoices)
+ {
+ ChoiceDetails[] choices =
+ choiceDescriptions
+ .Select(ChoiceDetails.Create)
+ .ToArray();
+
+ CancellationTokenSource cancellationToken = new CancellationTokenSource();
+ Task promptTask =
+ this.CreateChoicePromptHandler()
+ .PromptForChoice(
+ promptCaption,
+ promptMessage,
+ choices,
+ defaultChoices.ToArray(),
+ cancellationToken.Token);
+
+ // Run the prompt task and wait for it to return
+ this.WaitForPromptCompletion(
+ promptTask,
+ "PromptForChoice",
+ cancellationToken);
+
+ // Return the result
+ return new Collection(promptTask.Result.ToList());
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private async Task WritePromptStringToHost()
+ {
+ PSCommand promptCommand = new PSCommand().AddScript("prompt");
+
+ string promptString =
+ (await this.powerShellContext.ExecuteCommand(promptCommand, false, false))
+ .Select(pso => pso.BaseObject)
+ .OfType()
+ .FirstOrDefault() ?? "PS> ";
+
+ // Add the [DBG] prefix if we're stopped in the debugger
+ if (this.powerShellContext.IsDebuggerStopped)
+ {
+ promptString =
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "[DBG]: {0}",
+ promptString);
+ }
+
+ // Update the stored prompt string if the session is remote
+ if (this.powerShellContext.CurrentRunspace.Location == RunspaceLocation.Remote)
+ {
+ promptString =
+ string.Format(
+ CultureInfo.InvariantCulture,
+ "[{0}]: {1}",
+ this.powerShellContext.CurrentRunspace.Runspace.ConnectionInfo != null
+ ? this.powerShellContext.CurrentRunspace.Runspace.ConnectionInfo.ComputerName
+ : this.powerShellContext.CurrentRunspace.SessionDetails.ComputerName,
+ promptString);
+ }
+
+ // Write the prompt string
+ this.WriteOutput(promptString, false);
+ }
+
+ private void WriteDebuggerBanner(DebuggerStopEventArgs eventArgs)
+ {
+ // TODO: What do we display when we don't know why we stopped?
+
+ if (eventArgs.Breakpoints.Count > 0)
+ {
+ // The breakpoint classes have nice ToString output so use that
+ this.WriteOutput(
+ Environment.NewLine + $"Hit {eventArgs.Breakpoints[0].ToString()}\n",
+ true,
+ OutputType.Normal,
+ ConsoleColor.Blue);
+ }
+ }
+
+ private async Task StartReplLoop(CancellationToken cancellationToken)
+ {
+ do
+ {
+ string commandString = null;
+
+ await this.WritePromptStringToHost();
+
+ try
+ {
+ commandString = await this.ReadCommandLine(cancellationToken);
+ }
+ catch (PipelineStoppedException)
+ {
+ this.WriteOutput(
+ "^C",
+ true,
+ OutputType.Normal,
+ foregroundColor: ConsoleColor.Red);
+ }
+ catch (TaskCanceledException)
+ {
+ // Do nothing here, the while loop condition will exit.
+ }
+ catch (Exception e) // Narrow this if possible
+ {
+ this.WriteOutput(
+ $"\n\nAn error occurred while reading input:\n\n{e.ToString()}\n",
+ true,
+ OutputType.Error);
+
+ Logger.WriteException("Caught exception while reading command line", e);
+ }
+
+ if (commandString != null)
+ {
+ this.WriteOutput(string.Empty);
+
+ if (!string.IsNullOrWhiteSpace(commandString))
+ {
+ var unusedTask =
+ this.powerShellContext
+ .ExecuteScriptString(
+ commandString,
+ false,
+ true,
+ true)
+ .ConfigureAwait(false);
+
+ break;
+ }
+ }
+ }
+ while (!cancellationToken.IsCancellationRequested);
+ }
+
+ private InputPromptHandler CreateInputPromptHandler()
+ {
+ if (this.activePromptHandler != null)
+ {
+ Logger.Write(
+ LogLevel.Error,
+ "Prompt handler requested while another prompt is already active.");
+ }
+
+ InputPromptHandler inputPromptHandler = this.OnCreateInputPromptHandler();
+ this.activePromptHandler = inputPromptHandler;
+ this.activePromptHandler.PromptCancelled += activePromptHandler_PromptCancelled;
+
+ return inputPromptHandler;
+ }
+
+ private ChoicePromptHandler CreateChoicePromptHandler()
+ {
+ if (this.activePromptHandler != null)
+ {
+ Logger.Write(
+ LogLevel.Error,
+ "Prompt handler requested while another prompt is already active.");
+ }
+
+ ChoicePromptHandler choicePromptHandler = this.OnCreateChoicePromptHandler();
+ this.activePromptHandler = choicePromptHandler;
+ this.activePromptHandler.PromptCancelled += activePromptHandler_PromptCancelled;
+
+ return choicePromptHandler;
+ }
+
+ private void activePromptHandler_PromptCancelled(object sender, EventArgs e)
+ {
+ // Clean up the existing prompt
+ this.activePromptHandler.PromptCancelled -= activePromptHandler_PromptCancelled;
+ this.activePromptHandler = null;
+ }
+ private void WaitForPromptCompletion(
+ Task promptTask,
+ string promptFunctionName,
+ CancellationTokenSource cancellationToken)
+ {
+ try
+ {
+ // This will synchronously block on the prompt task
+ // method which gets run on another thread.
+ promptTask.Wait();
+
+ if (promptTask.Status == TaskStatus.WaitingForActivation)
+ {
+ // The Wait() call has timed out, cancel the prompt
+ cancellationToken.Cancel();
+
+ this.WriteOutput("\r\nPrompt has been cancelled due to a timeout.\r\n");
+ throw new PipelineStoppedException();
+ }
+ }
+ catch (AggregateException e)
+ {
+ // Find the right InnerException
+ Exception innerException = e.InnerException;
+ while (innerException is AggregateException)
+ {
+ innerException = innerException.InnerException;
+ }
+
+ // Was the task cancelled?
+ if (innerException is TaskCanceledException)
+ {
+ // Stop the pipeline if the prompt was cancelled
+ throw new PipelineStoppedException();
+ }
+ else if (innerException is PipelineStoppedException)
+ {
+ // The prompt is being cancelled, rethrow the exception
+ throw innerException;
+ }
+ else
+ {
+ // Rethrow the exception
+ throw new Exception(
+ string.Format(
+ "{0} failed, check inner exception for details",
+ promptFunctionName),
+ innerException);
+ }
+ }
+ }
+
+ private void PowerShellContext_DebuggerStop(object sender, System.Management.Automation.DebuggerStopEventArgs e)
+ {
+ // Cancel any existing prompt first
+ this.CancelCommandPrompt();
+
+ this.WriteDebuggerBanner(e);
+ this.ShowCommandPrompt();
+ }
+
+ private void PowerShellContext_DebuggerResumed(object sender, System.Management.Automation.DebuggerResumeAction e)
+ {
+ this.CancelCommandPrompt();
+ }
+
+ private void PowerShellContext_ExecutionStatusChanged(object sender, ExecutionStatusChangedEventArgs eventArgs)
+ {
+ // The command loop should only be manipulated if it's already started
+ if (this.IsCommandLoopRunning)
+ {
+ if (eventArgs.ExecutionStatus == ExecutionStatus.Aborted)
+ {
+ // When aborted, cancel any lingering prompts
+ if (this.activePromptHandler != null)
+ {
+ this.activePromptHandler.CancelPrompt();
+ this.WriteOutput(string.Empty);
+ }
+ }
+ else if (
+ eventArgs.ExecutionOptions.WriteOutputToHost ||
+ eventArgs.ExecutionOptions.InterruptCommandPrompt)
+ {
+ // Any command which writes output to the host will affect
+ // the display of the prompt
+ if (eventArgs.ExecutionStatus != ExecutionStatus.Running)
+ {
+ // Execution has completed, start the input prompt
+ this.ShowCommandPrompt();
+ }
+ else
+ {
+ // A new command was started, cancel the input prompt
+ this.CancelCommandPrompt();
+ this.WriteOutput(string.Empty);
+ }
+ }
+ else if (
+ eventArgs.ExecutionOptions.WriteErrorsToHost &&
+ (eventArgs.ExecutionStatus == ExecutionStatus.Failed ||
+ eventArgs.HadErrors))
+ {
+ this.CancelCommandPrompt();
+ this.WriteOutput(string.Empty);
+ this.ShowCommandPrompt();
+ }
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/PowerShellEditorServices/Session/Host/IHostInput.cs b/src/PowerShellEditorServices/Session/Host/IHostInput.cs
new file mode 100644
index 000000000..28c79839d
--- /dev/null
+++ b/src/PowerShellEditorServices/Session/Host/IHostInput.cs
@@ -0,0 +1,28 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+namespace Microsoft.PowerShell.EditorServices
+{
+ ///
+ /// Provides methods for integrating with the host's input system.
+ ///
+ public interface IHostInput
+ {
+ ///
+ /// Starts the host's interactive command loop.
+ ///
+ void StartCommandLoop();
+
+ ///
+ /// Stops the host's interactive command loop.
+ ///
+ void StopCommandLoop();
+
+ ///
+ /// Cancels the currently executing command or prompt.
+ ///
+ void SendControlC();
+ }
+}
\ No newline at end of file
diff --git a/src/PowerShellEditorServices/Console/IConsoleHost.cs b/src/PowerShellEditorServices/Session/Host/IHostOutput.cs
similarity index 57%
rename from src/PowerShellEditorServices/Console/IConsoleHost.cs
rename to src/PowerShellEditorServices/Session/Host/IHostOutput.cs
index 950fb3fd8..31389043d 100644
--- a/src/PowerShellEditorServices/Console/IConsoleHost.cs
+++ b/src/PowerShellEditorServices/Session/Host/IHostOutput.cs
@@ -4,17 +4,14 @@
//
using System;
-using System.Security;
-using System.Threading;
-using System.Threading.Tasks;
-namespace Microsoft.PowerShell.EditorServices.Console
+namespace Microsoft.PowerShell.EditorServices
{
///
- /// Provides a simplified interface for implementing a PowerShell
- /// host that will be used for an interactive console.
+ /// Provides a simplified interface for writing output to a
+ /// PowerShell host implementation.
///
- public interface IConsoleHost
+ public interface IHostOutput
{
///
/// Writes output of the given type to the user interface with
@@ -42,83 +39,34 @@ void WriteOutput(
OutputType outputType,
ConsoleColor foregroundColor,
ConsoleColor backgroundColor);
-
- ///
- /// Creates a ChoicePromptHandler to use for displaying a
- /// choice prompt to the user.
- ///
- /// A new ChoicePromptHandler instance.
- ChoicePromptHandler GetChoicePromptHandler();
-
- ///
- /// Creates an InputPrompt handle to use for displaying input
- /// prompts to the user.
- ///
- /// A new InputPromptHandler instance.
- InputPromptHandler GetInputPromptHandler();
-
- ///
- /// Reads an input string from the user.
- ///
- /// A CancellationToken that can be used to cancel the prompt.
- /// A Task that can be awaited to get the user's response.
- Task ReadSimpleLine(CancellationToken cancellationToken);
-
- ///
- /// Reads a SecureString from the user.
- ///
- /// A CancellationToken that can be used to cancel the prompt.
- /// A Task that can be awaited to get the user's response.
- Task ReadSecureLine(CancellationToken cancellationToken);
-
- ///
- /// Cancels the currently executing command or prompt.
- ///
- void SendControlC();
-
- ///
- /// Sends a progress update event to the user.
- ///
- /// The source ID of the progress event.
- /// The details of the activity's current progress.
- void UpdateProgress(
- long sourceId,
- ProgressDetails progressDetails);
-
- ///
- /// Notifies the IConsoleHost implementation that the PowerShell
- /// session is exiting.
- ///
- /// The error code that identifies the session exit result.
- void ExitSession(int exitCode);
}
///
- /// Provides helpful extension methods for the IConsoleHost interface.
+ /// Provides helpful extension methods for the IHostOutput interface.
///
- public static class IConsoleHostExtensions
+ public static class IHostOutputExtensions
{
///
/// Writes normal output with a newline to the user interface.
///
- ///
- /// The IConsoleHost implementation to use for WriteOutput calls.
+ ///
+ /// The IHostOutput implementation to use for WriteOutput calls.
///
///
/// The output string to be written.
///
public static void WriteOutput(
- this IConsoleHost consoleHost,
+ this IHostOutput hostOutput,
string outputString)
{
- consoleHost.WriteOutput(outputString, true);
+ hostOutput.WriteOutput(outputString, true);
}
///
/// Writes normal output to the user interface.
///
- ///
- /// The IConsoleHost implementation to use for WriteOutput calls.
+ ///
+ /// The IHostOutput implementation to use for WriteOutput calls.
///
///
/// The output string to be written.
@@ -127,11 +75,11 @@ public static void WriteOutput(
/// If true, a newline should be appended to the output's contents.
///
public static void WriteOutput(
- this IConsoleHost consoleHost,
+ this IHostOutput hostOutput,
string outputString,
bool includeNewLine)
{
- consoleHost.WriteOutput(
+ hostOutput.WriteOutput(
outputString,
includeNewLine,
OutputType.Normal);
@@ -141,8 +89,8 @@ public static void WriteOutput(
/// Writes output of a particular type to the user interface
/// with a newline ending.
///
- ///
- /// The IConsoleHost implementation to use for WriteOutput calls.
+ ///
+ /// The IHostOutput implementation to use for WriteOutput calls.
///
///
/// The output string to be written.
@@ -151,11 +99,11 @@ public static void WriteOutput(
/// Specifies the type of output to be written.
///
public static void WriteOutput(
- this IConsoleHost consoleHost,
+ this IHostOutput hostOutput,
string outputString,
OutputType outputType)
{
- consoleHost.WriteOutput(
+ hostOutput.WriteOutput(
outputString,
true,
OutputType.Normal);
@@ -164,8 +112,8 @@ public static void WriteOutput(
///
/// Writes output of a particular type to the user interface.
///
- ///
- /// The IConsoleHost implementation to use for WriteOutput calls.
+ ///
+ /// The IHostOutput implementation to use for WriteOutput calls.
///
///
/// The output string to be written.
@@ -177,12 +125,12 @@ public static void WriteOutput(
/// Specifies the type of output to be written.
///
public static void WriteOutput(
- this IConsoleHost consoleHost,
+ this IHostOutput hostOutput,
string outputString,
bool includeNewLine,
OutputType outputType)
{
- consoleHost.WriteOutput(
+ hostOutput.WriteOutput(
outputString,
includeNewLine,
outputType,
@@ -194,8 +142,8 @@ public static void WriteOutput(
/// Writes output of a particular type to the user interface using
/// a particular foreground color.
///
- ///
- /// The IConsoleHost implementation to use for WriteOutput calls.
+ ///
+ /// The IHostOutput implementation to use for WriteOutput calls.
///
///
/// The output string to be written.
@@ -210,13 +158,13 @@ public static void WriteOutput(
/// Specifies the foreground color of the output to be written.
///
public static void WriteOutput(
- this IConsoleHost consoleHost,
+ this IHostOutput hostOutput,
string outputString,
bool includeNewLine,
OutputType outputType,
ConsoleColor foregroundColor)
{
- consoleHost.WriteOutput(
+ hostOutput.WriteOutput(
outputString,
includeNewLine,
outputType,
diff --git a/src/PowerShellEditorServices/Session/SimplePSHostRawUserInterface.cs b/src/PowerShellEditorServices/Session/Host/SimplePSHostRawUserInterface.cs
similarity index 92%
rename from src/PowerShellEditorServices/Session/SimplePSHostRawUserInterface.cs
rename to src/PowerShellEditorServices/Session/Host/SimplePSHostRawUserInterface.cs
index 85993107f..f12b03919 100644
--- a/src/PowerShellEditorServices/Session/SimplePSHostRawUserInterface.cs
+++ b/src/PowerShellEditorServices/Session/Host/SimplePSHostRawUserInterface.cs
@@ -3,7 +3,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
-using Microsoft.PowerShell.EditorServices.Console;
using Microsoft.PowerShell.EditorServices.Utility;
using System;
using System.Management.Automation.Host;
@@ -13,24 +12,16 @@ namespace Microsoft.PowerShell.EditorServices
///
/// Provides an simple implementation of the PSHostRawUserInterface class.
///
- internal class SimplePSHostRawUserInterface : PSHostRawUserInterface
+ public class SimplePSHostRawUserInterface : PSHostRawUserInterface
{
#region Private Fields
private const int DefaultConsoleHeight = 100;
private const int DefaultConsoleWidth = 120;
- private Size currentBufferSize = new Size(DefaultConsoleWidth, DefaultConsoleHeight);
-
- #endregion
+ private ILogger Logger;
- #region Properties
-
- internal IConsoleHost ConsoleHost
- {
- get;
- set;
- }
+ private Size currentBufferSize = new Size(DefaultConsoleWidth, DefaultConsoleHeight);
#endregion
@@ -40,8 +31,10 @@ internal IConsoleHost ConsoleHost
/// Creates a new instance of the SimplePSHostRawUserInterface
/// class with the given IConsoleHost implementation.
///
- public SimplePSHostRawUserInterface()
+ /// The ILogger implementation to use for this instance.
+ public SimplePSHostRawUserInterface(ILogger logger)
{
+ this.Logger = logger;
this.ForegroundColor = ConsoleColor.White;
this.BackgroundColor = ConsoleColor.Black;
}
@@ -159,7 +152,7 @@ public override Size MaxWindowSize
/// A KeyInfo struct with details about the current keypress.
public override KeyInfo ReadKey(ReadKeyOptions options)
{
- Logger.CurrentLogger.Write(
+ Logger.Write(
LogLevel.Warning,
"PSHostRawUserInterface.ReadKey was called");
@@ -171,7 +164,7 @@ public override KeyInfo ReadKey(ReadKeyOptions options)
///
public override void FlushInputBuffer()
{
- Logger.CurrentLogger.Write(
+ Logger.Write(
LogLevel.Warning,
"PSHostRawUserInterface.FlushInputBuffer was called");
}
@@ -183,7 +176,7 @@ public override void FlushInputBuffer()
/// A BufferCell array with the requested buffer contents.
public override BufferCell[,] GetBufferContents(Rectangle rectangle)
{
- Logger.CurrentLogger.Write(
+ Logger.Write(
LogLevel.Warning,
"PSHostRawUserInterface.GetBufferContents was called");
@@ -203,7 +196,7 @@ public override void ScrollBufferContents(
Rectangle clip,
BufferCell fill)
{
- Logger.CurrentLogger.Write(
+ Logger.Write(
LogLevel.Warning,
"PSHostRawUserInterface.ScrollBufferContents was called");
}
@@ -217,7 +210,7 @@ public override void SetBufferContents(
Rectangle rectangle,
BufferCell fill)
{
- Logger.CurrentLogger.Write(
+ Logger.Write(
LogLevel.Warning,
"PSHostRawUserInterface.SetBufferContents was called");
}
@@ -231,7 +224,7 @@ public override void SetBufferContents(
Coordinates origin,
BufferCell[,] contents)
{
- Logger.CurrentLogger.Write(
+ Logger.Write(
LogLevel.Warning,
"PSHostRawUserInterface.SetBufferContents was called");
}
diff --git a/src/PowerShellEditorServices/Session/SessionPSHostRawUserInterface.cs b/src/PowerShellEditorServices/Session/Host/TerminalPSHostRawUserInterface.cs
similarity index 92%
rename from src/PowerShellEditorServices/Session/SessionPSHostRawUserInterface.cs
rename to src/PowerShellEditorServices/Session/Host/TerminalPSHostRawUserInterface.cs
index 6f4d5bfae..29daec059 100644
--- a/src/PowerShellEditorServices/Session/SessionPSHostRawUserInterface.cs
+++ b/src/PowerShellEditorServices/Session/Host/TerminalPSHostRawUserInterface.cs
@@ -14,13 +14,29 @@ namespace Microsoft.PowerShell.EditorServices
/// for the ConsoleService and routes its calls to an IConsoleHost
/// implementation.
///
- internal class ConsoleServicePSHostRawUserInterface : PSHostRawUserInterface
+ internal class TerminalPSHostRawUserInterface : PSHostRawUserInterface
{
#region Private Fields
private const int DefaultConsoleHeight = 100;
private const int DefaultConsoleWidth = 120;
+ private ILogger Logger;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Creates a new instance of the TerminalPSHostRawUserInterface
+ /// class with the given IConsoleHost implementation.
+ ///
+ /// The ILogger implementation to use for this instance.
+ public TerminalPSHostRawUserInterface(ILogger logger)
+ {
+ this.Logger = logger;
+ }
+
#endregion
#region PSHostRawUserInterface Implementation
@@ -168,7 +184,7 @@ public override Size MaxWindowSize
/// A KeyInfo struct with details about the current keypress.
public override KeyInfo ReadKey(ReadKeyOptions options)
{
- Logger.CurrentLogger.Write(
+ Logger.Write(
LogLevel.Warning,
"PSHostRawUserInterface.ReadKey was called");
@@ -180,7 +196,7 @@ public override KeyInfo ReadKey(ReadKeyOptions options)
///
public override void FlushInputBuffer()
{
- Logger.CurrentLogger.Write(
+ Logger.Write(
LogLevel.Warning,
"PSHostRawUserInterface.FlushInputBuffer was called");
}
@@ -192,7 +208,7 @@ public override void FlushInputBuffer()
/// A BufferCell array with the requested buffer contents.
public override BufferCell[,] GetBufferContents(Rectangle rectangle)
{
- Logger.CurrentLogger.Write(
+ Logger.Write(
LogLevel.Warning,
"PSHostRawUserInterface.GetBufferContents was called");
@@ -212,7 +228,7 @@ public override void ScrollBufferContents(
Rectangle clip,
BufferCell fill)
{
- Logger.CurrentLogger.Write(
+ Logger.Write(
LogLevel.Warning,
"PSHostRawUserInterface.ScrollBufferContents was called");
}
@@ -236,7 +252,7 @@ public override void SetBufferContents(
}
else
{
- Logger.CurrentLogger.Write(
+ Logger.Write(
LogLevel.Warning,
"PSHostRawUserInterface.SetBufferContents was called with a specific region");
}
@@ -251,7 +267,7 @@ public override void SetBufferContents(
Coordinates origin,
BufferCell[,] contents)
{
- Logger.CurrentLogger.Write(
+ Logger.Write(
LogLevel.Warning,
"PSHostRawUserInterface.SetBufferContents was called");
}
diff --git a/src/PowerShellEditorServices/Session/Host/TerminalPSHostUserInterface.cs b/src/PowerShellEditorServices/Session/Host/TerminalPSHostUserInterface.cs
new file mode 100644
index 000000000..e64321205
--- /dev/null
+++ b/src/PowerShellEditorServices/Session/Host/TerminalPSHostUserInterface.cs
@@ -0,0 +1,154 @@
+//
+// 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.Console;
+
+namespace Microsoft.PowerShell.EditorServices
+{
+ using System;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Microsoft.PowerShell.EditorServices.Utility;
+
+ ///
+ /// Provides an EditorServicesPSHostUserInterface implementation
+ /// that integrates with the user's terminal UI.
+ ///
+ public class TerminalPSHostUserInterface : EditorServicesPSHostUserInterface
+ {
+ #region Private Fields
+
+ private ConsoleReadLine consoleReadLine;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Creates a new instance of the ConsoleServicePSHostUserInterface
+ /// class with the given IConsoleHost implementation.
+ ///
+ /// The PowerShellContext to use for executing commands.
+ /// An ILogger implementation to use for this host.
+ public TerminalPSHostUserInterface(
+ PowerShellContext powerShellContext,
+ ILogger logger)
+ : base(
+ powerShellContext,
+ new TerminalPSHostRawUserInterface(logger),
+ logger)
+ {
+ this.consoleReadLine = new ConsoleReadLine(powerShellContext);
+
+ // Set the output encoding to UTF-8 so that special
+ // characters are written to the console correctly
+ System.Console.OutputEncoding = System.Text.Encoding.UTF8;
+
+ System.Console.CancelKeyPress +=
+ (obj, args) =>
+ {
+ if (!this.IsNativeApplicationRunning)
+ {
+ // We'll handle Ctrl+C
+ args.Cancel = true;
+ this.SendControlC();
+ }
+ };
+ }
+
+ #endregion
+
+ ///
+ /// Requests that the HostUI implementation read a command line
+ /// from the user to be executed in the integrated console command
+ /// loop.
+ ///
+ ///
+ /// A CancellationToken used to cancel the command line request.
+ ///
+ /// A Task that can be awaited for the resulting input string.
+ protected override Task ReadCommandLine(CancellationToken cancellationToken)
+ {
+ return this.consoleReadLine.ReadCommandLine(cancellationToken);
+ }
+
+ ///
+ /// Creates an InputPrompt handle to use for displaying input
+ /// prompts to the user.
+ ///
+ /// A new InputPromptHandler instance.
+ protected override InputPromptHandler OnCreateInputPromptHandler()
+ {
+ return new TerminalInputPromptHandler(
+ this.consoleReadLine,
+ this,
+ this.Logger);
+ }
+
+ ///
+ /// Creates a ChoicePromptHandler to use for displaying a
+ /// choice prompt to the user.
+ ///
+ /// A new ChoicePromptHandler instance.
+ protected override ChoicePromptHandler OnCreateChoicePromptHandler()
+ {
+ return new TerminalChoicePromptHandler(
+ this.consoleReadLine,
+ this,
+ this.Logger);
+ }
+
+ ///
+ /// Writes output of the given type to the user interface with
+ /// the given foreground and background colors. Also includes
+ /// a newline if requested.
+ ///
+ ///
+ /// The output string to be written.
+ ///
+ ///
+ /// If true, a newline should be appended to the output's contents.
+ ///
+ ///
+ /// Specifies the type of output to be written.
+ ///
+ ///
+ /// Specifies the foreground color of the output to be written.
+ ///
+ ///
+ /// Specifies the background color of the output to be written.
+ ///
+ public override void WriteOutput(
+ string outputString,
+ bool includeNewLine,
+ OutputType outputType,
+ ConsoleColor foregroundColor,
+ ConsoleColor backgroundColor)
+ {
+ ConsoleColor oldForegroundColor = System.Console.ForegroundColor;
+ ConsoleColor oldBackgroundColor = System.Console.BackgroundColor;
+
+ System.Console.ForegroundColor = foregroundColor;
+ System.Console.BackgroundColor = backgroundColor;
+
+ System.Console.Write(outputString + (includeNewLine ? Environment.NewLine : ""));
+
+ System.Console.ForegroundColor = oldForegroundColor;
+ System.Console.BackgroundColor = oldBackgroundColor;
+ }
+
+ ///
+ /// Sends a progress update event to the user.
+ ///
+ /// The source ID of the progress event.
+ /// The details of the activity's current progress.
+ protected override void UpdateProgress(
+ long sourceId,
+ ProgressDetails progressDetails)
+ {
+
+ }
+ }
+}
diff --git a/src/PowerShellEditorServices/Session/PowerShellContext.cs b/src/PowerShellEditorServices/Session/PowerShellContext.cs
index a0c962b80..303a404a6 100644
--- a/src/PowerShellEditorServices/Session/PowerShellContext.cs
+++ b/src/PowerShellEditorServices/Session/PowerShellContext.cs
@@ -89,10 +89,10 @@ public PowerShellVersionDetails LocalPowerShellVersion
}
///
- /// Gets or sets an IConsoleHost implementation for use in
+ /// Gets or sets an IHostOutput implementation for use in
/// writing output to the console.
///
- private IConsoleHost ConsoleHost { get; set; }
+ private IHostOutput ConsoleWriter { get; set; }
///
/// Gets details pertaining to the current runspace.
@@ -108,7 +108,7 @@ public RunspaceDetails CurrentRunspace
#region Constructors
///
- ///
+ ///
///
/// An ILogger implementation used for writing log messages.
public PowerShellContext(ILogger logger)
@@ -121,15 +121,19 @@ public PowerShellContext(ILogger logger)
///
///
///
- ///
+ ///
+ /// The EditorServicesPSHostUserInterface to use for this instance.
+ ///
+ /// An ILogger implementation to use for this instance.
///
public static Runspace CreateRunspace(
HostDetails hostDetails,
PowerShellContext powerShellContext,
- bool enableConsoleRepl)
+ EditorServicesPSHostUserInterface hostUserInterface,
+ ILogger logger)
{
- var psHost = new ConsoleServicePSHost(powerShellContext, hostDetails, enableConsoleRepl);
- powerShellContext.ConsoleHost = psHost.ConsoleService;
+ var psHost = new EditorServicesPSHost(powerShellContext, hostDetails, hostUserInterface, logger);
+ powerShellContext.ConsoleWriter = hostUserInterface;
return CreateRunspace(psHost);
}
@@ -174,18 +178,18 @@ public void Initialize(
/// An object containing the profile paths for the session.
/// The initial runspace to use for this instance.
/// If true, the PowerShellContext owns this runspace.
- /// An IConsoleHost implementation. Optional.
+ /// An IHostOutput implementation. Optional.
public void Initialize(
ProfilePaths profilePaths,
Runspace initialRunspace,
bool ownsInitialRunspace,
- IConsoleHost consoleHost)
+ IHostOutput consoleHost)
{
Validate.IsNotNull("initialRunspace", initialRunspace);
this.ownsInitialRunspace = ownsInitialRunspace;
this.SessionState = PowerShellContextState.NotStarted;
- this.ConsoleHost = consoleHost;
+ this.ConsoleWriter = consoleHost;
// Get the PowerShell runtime version
this.LocalPowerShellVersion =
@@ -1211,9 +1215,9 @@ internal void WriteOutput(
bool includeNewLine,
OutputType outputType)
{
- if (this.ConsoleHost != null)
+ if (this.ConsoleWriter != null)
{
- this.ConsoleHost.WriteOutput(
+ this.ConsoleWriter.WriteOutput(
outputString,
includeNewLine,
outputType);
@@ -1281,9 +1285,9 @@ private void WriteError(
private void WriteError(string errorMessage)
{
- if (this.ConsoleHost != null)
+ if (this.ConsoleWriter != null)
{
- this.ConsoleHost.WriteOutput(
+ this.ConsoleWriter.WriteOutput(
errorMessage,
true,
OutputType.Error,
diff --git a/src/PowerShellEditorServices/Session/SessionPSHostUserInterface.cs b/src/PowerShellEditorServices/Session/SessionPSHostUserInterface.cs
deleted file mode 100644
index 968a98110..000000000
--- a/src/PowerShellEditorServices/Session/SessionPSHostUserInterface.cs
+++ /dev/null
@@ -1,509 +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 System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Management.Automation;
-using System.Management.Automation.Host;
-using System.Linq;
-using System.Security;
-using System.Threading.Tasks;
-using Microsoft.PowerShell.EditorServices.Console;
-using System.Threading;
-using Microsoft.PowerShell.EditorServices.Utility;
-
-namespace Microsoft.PowerShell.EditorServices
-{
- ///
- /// Provides an implementation of the PSHostUserInterface class
- /// for the ConsoleService and routes its calls to an IConsoleHost
- /// implementation.
- ///
- internal class ConsoleServicePSHostUserInterface : PSHostUserInterface, IHostUISupportsMultipleChoiceSelection
- {
- #region Private Fields
-
- private IConsoleHost consoleHost;
- private PSHostRawUserInterface rawUserInterface;
-
- #endregion
-
- #region Public Constants
-
- public const string DebugMessagePrefix = "DEBUG: ";
- public const string WarningMessagePrefix = "WARNING: ";
- public const string VerboseMessagePrefix = "VERBOSE: ";
-
- #endregion
-
- #region Properties
-
- internal IConsoleHost ConsoleHost
- {
- get { return this.consoleHost; }
- set
- {
- this.consoleHost = value;
- }
- }
-
-#if !PowerShellv3 && !PowerShellv4 && !PowerShellv5r1 // Only available in Windows 10 Update 1 or higher
- public override bool SupportsVirtualTerminal => true;
-#endif
-
- #endregion
-
- #region Constructors
-
- ///
- /// Creates a new instance of the ConsoleServicePSHostUserInterface
- /// class with the given IConsoleHost implementation.
- ///
- public ConsoleServicePSHostUserInterface(bool enableConsoleRepl)
- {
- if (enableConsoleRepl)
- {
- // Set the output encoding to UTF-8 so that special
- // characters are written to the console correctly
- System.Console.OutputEncoding = System.Text.Encoding.UTF8;
- }
-
- this.rawUserInterface =
- enableConsoleRepl
- ? (PSHostRawUserInterface)new ConsoleServicePSHostRawUserInterface()
- : new SimplePSHostRawUserInterface();
- }
-
- #endregion
-
- #region PSHostUserInterface Implementation
-
- public override Dictionary Prompt(
- string promptCaption,
- string promptMessage,
- Collection fieldDescriptions)
- {
- if (this.consoleHost != null)
- {
- FieldDetails[] fields =
- fieldDescriptions
- .Select(f => { return FieldDetails.Create(f, Logger.CurrentLogger); })
- .ToArray();
-
- CancellationTokenSource cancellationToken = new CancellationTokenSource();
- Task> promptTask =
- this.consoleHost
- .GetInputPromptHandler()
- .PromptForInput(
- promptCaption,
- promptMessage,
- fields,
- cancellationToken.Token);
-
- // Run the prompt task and wait for it to return
- this.WaitForPromptCompletion(
- promptTask,
- "Prompt",
- cancellationToken);
-
- // Convert all values to PSObjects
- var psObjectDict = new Dictionary();
-
- // The result will be null if the prompt was cancelled
- if (promptTask.Result != null)
- {
- // Convert all values to PSObjects
- foreach (var keyValuePair in promptTask.Result)
- {
- psObjectDict.Add(
- keyValuePair.Key,
- keyValuePair.Value != null
- ? PSObject.AsPSObject(keyValuePair.Value)
- : null);
- }
- }
-
- // Return the result
- return psObjectDict;
- }
- else
- {
- // Notify the caller that there's no implementation
- throw new NotImplementedException();
- }
- }
-
- public override int PromptForChoice(
- string promptCaption,
- string promptMessage,
- Collection choiceDescriptions,
- int defaultChoice)
- {
- if (this.consoleHost != null)
- {
- ChoiceDetails[] choices =
- choiceDescriptions
- .Select(ChoiceDetails.Create)
- .ToArray();
-
- CancellationTokenSource cancellationToken = new CancellationTokenSource();
- Task promptTask =
- this.consoleHost
- .GetChoicePromptHandler()
- .PromptForChoice(
- promptCaption,
- promptMessage,
- choices,
- defaultChoice,
- cancellationToken.Token);
-
- // Run the prompt task and wait for it to return
- this.WaitForPromptCompletion(
- promptTask,
- "PromptForChoice",
- cancellationToken);
-
- // Return the result
- return promptTask.Result;
- }
- else
- {
- // Notify the caller that there's no implementation
- throw new NotImplementedException();
- }
- }
-
- public override PSCredential PromptForCredential(
- string promptCaption,
- string promptMessage,
- string userName,
- string targetName,
- PSCredentialTypes allowedCredentialTypes,
- PSCredentialUIOptions options)
- {
- if (this.consoleHost != null)
- {
- CancellationTokenSource cancellationToken = new CancellationTokenSource();
-
- Task> promptTask =
- this.consoleHost
- .GetInputPromptHandler()
- .PromptForInput(
- promptCaption,
- promptMessage,
- new FieldDetails[] { new CredentialFieldDetails("Credential", "Credential", userName) },
- cancellationToken.Token);
-
- Task unpackTask =
- promptTask.ContinueWith(
- task =>
- {
- if (task.IsFaulted)
- {
- throw task.Exception;
- }
- else if (task.IsCanceled)
- {
- throw new TaskCanceledException(task);
- }
-
- // Return the value of the sole field
- return (PSCredential)task.Result?["Credential"];
- });
-
- // Run the prompt task and wait for it to return
- this.WaitForPromptCompletion(
- unpackTask,
- "PromptForCredential",
- cancellationToken);
-
- return unpackTask.Result;
- }
- else
- {
- // Notify the caller that there's no implementation
- throw new NotImplementedException(
- "'Get-Credential' is not yet supported in this editor.");
- }
- }
-
- public override PSCredential PromptForCredential(
- string caption,
- string message,
- string userName,
- string targetName)
- {
- return this.PromptForCredential(
- caption,
- message,
- userName,
- targetName,
- PSCredentialTypes.Default,
- PSCredentialUIOptions.Default);
- }
-
- public override PSHostRawUserInterface RawUI
- {
- get { return this.rawUserInterface; }
- }
-
- public override string ReadLine()
- {
- if (this.consoleHost != null)
- {
- CancellationTokenSource cancellationToken = new CancellationTokenSource();
-
- Task promptTask =
- this.consoleHost
- .GetInputPromptHandler()
- .PromptForInput(cancellationToken.Token);
-
- // Run the prompt task and wait for it to return
- this.WaitForPromptCompletion(
- promptTask,
- "ReadLine",
- cancellationToken);
-
- return promptTask.Result;
- }
- else
- {
- // Notify the caller that there's no implementation
- throw new NotImplementedException();
- }
- }
-
- public override SecureString ReadLineAsSecureString()
- {
- if (this.consoleHost != null)
- {
- CancellationTokenSource cancellationToken = new CancellationTokenSource();
-
- Task promptTask =
- this.consoleHost
- .GetInputPromptHandler()
- .PromptForSecureInput(cancellationToken.Token);
-
- // Run the prompt task and wait for it to return
- this.WaitForPromptCompletion(
- promptTask,
- "ReadLineAsSecureString",
- cancellationToken);
-
- return promptTask.Result;
- }
- else
- {
- // Notify the caller that there's no implementation
- throw new NotImplementedException();
- }
- }
-
- public override void Write(
- ConsoleColor foregroundColor,
- ConsoleColor backgroundColor,
- string value)
- {
- if (this.consoleHost != null)
- {
- this.consoleHost.WriteOutput(
- value,
- false,
- OutputType.Normal,
- foregroundColor,
- backgroundColor);
- }
- }
-
- public override void Write(string value)
- {
- if (this.consoleHost != null)
- {
- this.consoleHost.WriteOutput(
- value,
- false,
- OutputType.Normal,
- this.rawUserInterface.ForegroundColor,
- this.rawUserInterface.BackgroundColor);
- }
- }
-
- public override void WriteLine(string value)
- {
- if (this.consoleHost != null)
- {
- this.consoleHost.WriteOutput(
- value,
- true,
- OutputType.Normal,
- this.rawUserInterface.ForegroundColor,
- this.rawUserInterface.BackgroundColor);
- }
- }
-
- public override void WriteDebugLine(string message)
- {
- if (this.consoleHost != null)
- {
- this.consoleHost.WriteOutput(
- DebugMessagePrefix + message,
- true,
- OutputType.Debug,
- foregroundColor: ConsoleColor.Yellow);
- }
- }
-
- public override void WriteVerboseLine(string message)
- {
- if (this.consoleHost != null)
- {
- this.consoleHost.WriteOutput(
- VerboseMessagePrefix + message,
- true,
- OutputType.Verbose,
- foregroundColor: ConsoleColor.Blue);
- }
- }
-
- public override void WriteWarningLine(string message)
- {
- if (this.consoleHost != null)
- {
- this.consoleHost.WriteOutput(
- WarningMessagePrefix + message,
- true,
- OutputType.Warning,
- foregroundColor: ConsoleColor.Yellow);
- }
- }
-
- public override void WriteErrorLine(string value)
- {
- if (this.consoleHost != null)
- {
- this.consoleHost.WriteOutput(
- value,
- true,
- OutputType.Error,
- ConsoleColor.Red);
- }
- }
-
- public override void WriteProgress(
- long sourceId,
- ProgressRecord record)
- {
- if (this.consoleHost != null)
- {
- this.consoleHost.UpdateProgress(
- sourceId,
- ProgressDetails.Create(record));
- }
- }
-
- #endregion
-
- #region IHostUISupportsMultipleChoiceSelection Implementation
-
- public Collection PromptForChoice(
- string promptCaption,
- string promptMessage,
- Collection choiceDescriptions,
- IEnumerable defaultChoices)
- {
- if (this.consoleHost != null)
- {
- ChoiceDetails[] choices =
- choiceDescriptions
- .Select(ChoiceDetails.Create)
- .ToArray();
-
- CancellationTokenSource cancellationToken = new CancellationTokenSource();
- Task promptTask =
- this.consoleHost
- .GetChoicePromptHandler()
- .PromptForChoice(
- promptCaption,
- promptMessage,
- choices,
- defaultChoices.ToArray(),
- cancellationToken.Token);
-
- // Run the prompt task and wait for it to return
- this.WaitForPromptCompletion(
- promptTask,
- "PromptForChoice",
- cancellationToken);
-
- // Return the result
- return new Collection(promptTask.Result.ToList());
- }
- else
- {
- // Notify the caller that there's no implementation
- throw new NotImplementedException();
- }
- }
-
- #endregion
-
- #region Private Methods
-
- private void WaitForPromptCompletion(
- Task promptTask,
- string promptFunctionName,
- CancellationTokenSource cancellationToken)
- {
- try
- {
- // This will synchronously block on the prompt task
- // method which gets run on another thread.
- promptTask.Wait();
-
- if (promptTask.Status == TaskStatus.WaitingForActivation)
- {
- // The Wait() call has timed out, cancel the prompt
- cancellationToken.Cancel();
-
- this.consoleHost.WriteOutput("\r\nPrompt has been cancelled due to a timeout.\r\n");
- throw new PipelineStoppedException();
- }
- }
- catch (AggregateException e)
- {
- // Find the right InnerException
- Exception innerException = e.InnerException;
- if (innerException is AggregateException)
- {
- innerException = innerException.InnerException;
- }
-
- // Was the task cancelled?
- if (innerException is TaskCanceledException)
- {
- // Stop the pipeline if the prompt was cancelled
- throw new PipelineStoppedException();
- }
- else if (innerException is PipelineStoppedException)
- {
- // The prompt is being cancelled, rethrow the exception
- throw innerException;
- }
- else
- {
- // Rethrow the exception
- throw new Exception(
- string.Format(
- "{0} failed, check inner exception for details",
- promptFunctionName),
- innerException);
- }
- }
- }
-
- #endregion
- }
-}
diff --git a/src/PowerShellEditorServices/Utility/Logger.cs b/src/PowerShellEditorServices/Utility/Logger.cs
deleted file mode 100644
index 930fcc369..000000000
--- a/src/PowerShellEditorServices/Utility/Logger.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-//
-// Copyright (c) Microsoft. All rights reserved.
-// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-//
-
-namespace Microsoft.PowerShell.EditorServices.Utility
-{
- ///
- /// Provides a simple logging interface. May be replaced with a
- /// more robust solution at a later date.
- ///
- public static class Logger
- {
- ///
- /// Gets the current static ILogger instance. This property
- /// is temporary and will be removed in an upcoming commit.
- ///
- public static ILogger CurrentLogger { get; private set; }
-
- ///
- /// Initializes the Logger for the current session.
- ///
- ///
- /// Specifies the ILogger implementation to use for the static interface.
- ///
- public static void Initialize(ILogger logger)
- {
- if (CurrentLogger != null)
- {
- CurrentLogger.Dispose();
- }
-
- CurrentLogger = logger;
- }
-
- ///
- /// Closes the Logger.
- ///
- public static void Close()
- {
- if (CurrentLogger != null)
- {
- CurrentLogger.Dispose();
- }
- }
- }
-}
diff --git a/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs b/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs
index b6909a036..16b7ef62d 100644
--- a/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs
+++ b/test/PowerShellEditorServices.Test.Host/DebugAdapterTests.cs
@@ -40,8 +40,6 @@ public async Task InitializeAsync()
testLogPath + "-client.log",
LogLevel.Verbose);
- Logger.Initialize(this.logger);
-
testLogPath += "-server.log";
System.Console.WriteLine(" Output log at path: {0}", testLogPath);
diff --git a/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs b/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs
index abe684242..532808bd9 100644
--- a/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs
+++ b/test/PowerShellEditorServices.Test.Host/LanguageServerTests.cs
@@ -43,8 +43,6 @@ public async Task InitializeAsync()
testLogPath + "-client.log",
LogLevel.Verbose);
- Logger.Initialize(this.logger);
-
testLogPath += "-server.log";
System.Console.WriteLine(" Output log at path: {0}", testLogPath);
@@ -772,6 +770,8 @@ public async Task ServiceLoadsProfilesOnDemand()
File.Exists(currentUserCurrentHostPath),
"Copied profile path does not exist!");
+ OutputReader outputReader = new OutputReader(this.messageHandlers);
+
// Send the configuration change to cause profiles to be loaded
await this.languageServiceClient.SendEvent(
DidChangeConfigurationNotification.Type,
@@ -790,7 +790,8 @@ await this.languageServiceClient.SendEvent(
}
});
- OutputReader outputReader = new OutputReader(this.messageHandlers);
+ // Wait for the prompt to be written once the profile loads
+ Assert.StartsWith("PS ", await outputReader.ReadLine(waitForNewLine: false));
Task evaluateTask =
this.SendRequest(
diff --git a/test/PowerShellEditorServices.Test.Host/OutputReader.cs b/test/PowerShellEditorServices.Test.Host/OutputReader.cs
index 989d193e5..91bd980e8 100644
--- a/test/PowerShellEditorServices.Test.Host/OutputReader.cs
+++ b/test/PowerShellEditorServices.Test.Host/OutputReader.cs
@@ -8,8 +8,6 @@
using Microsoft.PowerShell.EditorServices.Utility;
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
@@ -30,7 +28,7 @@ public OutputReader(IMessageHandlers messageHandlers)
this.OnOutputEvent);
}
- public async Task ReadLine(string expectedOutputCategory = "stdout")
+ public async Task ReadLine(string expectedOutputCategory = "stdout", bool waitForNewLine = true)
{
try
{
@@ -100,6 +98,10 @@ public async Task ReadLine(string expectedOutputCategory = "stdout")
// At this point, the state of lineHasNewLine will determine
// whether the loop continues to wait for another output
// event that completes the current line.
+ if (!waitForNewLine)
+ {
+ break;
+ }
}
return nextOutputString;
diff --git a/test/PowerShellEditorServices.Test/Console/ConsoleServiceTests.cs b/test/PowerShellEditorServices.Test/Console/ConsoleServiceTests.cs
index 37356d5fc..14dda1fa0 100644
--- a/test/PowerShellEditorServices.Test/Console/ConsoleServiceTests.cs
+++ b/test/PowerShellEditorServices.Test/Console/ConsoleServiceTests.cs
@@ -1,674 +1,674 @@
-//
-// 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.Console;
-using Microsoft.PowerShell.EditorServices.Utility;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Security;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using Xunit;
-
-namespace Microsoft.PowerShell.EditorServices.Test.Console
-{
- public class ConsoleServiceTests : IDisposable
- {
- private ConsoleService consoleService;
- private PowerShellContext powerShellContext;
- private TestConsolePromptHandlerContext promptHandlerContext;
-
- private Dictionary outputPerType =
- new Dictionary();
-
- const string TestOutputString = "This is a test.";
-
- const string PromptCaption = "Test Prompt";
- const string PromptMessage = "Make a selection";
- const int PromptDefault = 1;
-
- static readonly Tuple[] PromptChoices =
- new Tuple[]
- {
- new Tuple("&Apple", "Help for Apple"),
- new Tuple("Ba&nana", "Help for Banana"),
- new Tuple("Orange", "Help for Orange")
- };
-
- static readonly Tuple[] PromptFields =
- new Tuple[]
- {
- new Tuple("Name", typeof(string)),
- new Tuple("Age", typeof(int)),
- new Tuple("Books", typeof(string[])),
- };
-
- public ConsoleServiceTests()
- {
- this.powerShellContext = new PowerShellContext(new NullLogger());
- ConsoleServicePSHost psHost =
- new ConsoleServicePSHost(
- powerShellContext,
- PowerShellContextTests.TestHostDetails,
- false);
-
- this.consoleService = psHost.ConsoleService;
-
- this.powerShellContext.Initialize(
- null,
- PowerShellContext.CreateRunspace(psHost),
- true);
-
- this.promptHandlerContext =
- new TestConsolePromptHandlerContext();
-
- this.consoleService.PushPromptHandlerContext(this.promptHandlerContext);
- this.consoleService.OutputWritten += OnOutputWritten;
- promptHandlerContext.ConsoleHost = this.consoleService;
- }
-
- public void Dispose()
- {
- this.powerShellContext.Dispose();
- }
-
- [Fact]
- public async Task ReceivesNormalOutput()
- {
- await this.powerShellContext.ExecuteScriptString(
- string.Format(
- "\"{0}\"",
- TestOutputString));
-
- // Prompt strings are returned as normal output, ignore the prompt
- string[] normalOutputLines =
- this.GetOutputForType(OutputType.Normal)
- .Split(
- new string[] { Environment.NewLine },
- StringSplitOptions.None);
-
- // The output should be 2 lines: the expected string and
- // an empty line.
- Assert.Equal(2, normalOutputLines.Length);
- Assert.Equal(
- TestOutputString,
- normalOutputLines[0]);
- }
-
- [Fact]
- public async Task ReceivesErrorOutput()
- {
- await this.powerShellContext.ExecuteScriptString(
- string.Format(
- "Write-Error \"{0}\"",
- TestOutputString));
-
- string errorString = this.GetOutputForType(OutputType.Error).Split('\r')[0];
-
- Assert.Equal(
- string.Format("Write-Error \"{0}\" : {0}", TestOutputString),
- errorString);
- }
-
- [Fact]
- public async Task ReceivesVerboseOutput()
- {
- // Since setting VerbosePreference causes other message to
- // be written out when we run our test, run a command preemptively
- // to flush out unwanted verbose messages
- await this.powerShellContext.ExecuteScriptString("Write-Verbose \"Preloading\"");
-
- await this.powerShellContext.ExecuteScriptString(
- string.Format(
- "$VerbosePreference = \"Continue\"; Write-Verbose \"{0}\"",
- TestOutputString));
-
- Assert.Equal(
- ConsoleServicePSHostUserInterface.VerboseMessagePrefix + TestOutputString + Environment.NewLine,
- this.GetOutputForType(OutputType.Verbose));
- }
-
- [Fact]
- public async Task ReceivesDebugOutput()
- {
- // Since setting VerbosePreference causes other message to
- // be written out when we run our test, run a command preemptively
- // to flush out unwanted verbose messages
- await this.powerShellContext.ExecuteScriptString("Write-Verbose \"Preloading\"");
-
- await this.powerShellContext.ExecuteScriptString(
- string.Format(
- "$DebugPreference = \"Continue\"; Write-Debug \"{0}\"",
- TestOutputString));
-
- Assert.Equal(
- ConsoleServicePSHostUserInterface.DebugMessagePrefix + TestOutputString + Environment.NewLine,
- this.GetOutputForType(OutputType.Debug));
- }
-
- [Fact]
- public async Task ReceivesWarningOutput()
- {
- await this.powerShellContext.ExecuteScriptString(
- string.Format(
- "Write-Warning \"{0}\"",
- TestOutputString));
-
- Assert.Equal(
- ConsoleServicePSHostUserInterface.WarningMessagePrefix + TestOutputString + Environment.NewLine,
- this.GetOutputForType(OutputType.Warning));
- }
-
- [Fact]
- public async Task ReceivesChoicePrompt()
- {
- string choiceScript =
- this.GetChoicePromptString(
- PromptCaption,
- PromptMessage,
- PromptChoices,
- PromptDefault);
-
- var promptTask = this.promptHandlerContext.WaitForChoicePrompt();
- var executeTask = this.powerShellContext.ExecuteScriptString(choiceScript);
-
- // Wait for the prompt to be shown
- var promptHandler = await promptTask;
-
- // Respond to the prompt and wait for the prompt to complete
- await promptHandler.ReturnInputString("apple");
- await executeTask;
-
- string[] outputLines =
- this.GetOutputForType(OutputType.Normal)
- .Split(
- new string[] { Environment.NewLine },
- StringSplitOptions.None);
-
- Assert.Equal(PromptCaption, outputLines[0]);
- Assert.Equal(PromptMessage, outputLines[1]);
- Assert.Equal("[A] Apple [N] Banana [] Orange [?] Help (default is \"Banana\"): apple", outputLines[2]);
- Assert.Equal("0", outputLines[3]);
- }
-
- [Fact]
- public async Task CancelsChoicePrompt()
- {
- string choiceScript =
- this.GetChoicePromptString(
- PromptCaption,
- PromptMessage,
- PromptChoices,
- PromptDefault);
-
- var promptTask = this.promptHandlerContext.WaitForChoicePrompt();
- var executeTask = this.powerShellContext.ExecuteScriptString(choiceScript);
-
- // Wait for the prompt to be shown
- await promptTask;
-
- // Cancel the prompt and wait for the execution to complete
- this.consoleService.SendControlC();
- await executeTask;
-
- string[] outputLines =
- this.GetOutputForType(OutputType.Normal)
- .Split(
- new string[] { Environment.NewLine },
- StringSplitOptions.None);
-
- Assert.Equal(PromptCaption, outputLines[0]);
- Assert.Equal(PromptMessage, outputLines[1]);
- Assert.Equal("[A] Apple [N] Banana [] Orange [?] Help (default is \"Banana\"): ", outputLines[2]);
- }
-
- [Fact]
- public async Task ReceivesChoicePromptHelp()
- {
- string choiceScript =
- this.GetChoicePromptString(
- PromptCaption,
- PromptMessage,
- PromptChoices,
- PromptDefault);
-
- var promptTask = this.promptHandlerContext.WaitForChoicePrompt();
- var executeTask = this.powerShellContext.ExecuteScriptString(choiceScript);
-
- // Wait for the prompt to be shown
- var promptHandler = await promptTask;
-
- // Respond to the prompt and wait for the help prompt to appear
- await promptHandler.ReturnInputString("?");
- await promptHandler.ReturnInputString("A");
- await executeTask;
-
- string[] outputLines =
- this.GetOutputForType(OutputType.Normal)
- .Split(
- new string[] { Environment.NewLine },
- StringSplitOptions.None);
-
- // Help lines start after initial prompt, skip 3 lines
- Assert.Equal("A - Help for Apple", outputLines[3]);
- Assert.Equal("N - Help for Banana", outputLines[4]);
- Assert.Equal("Orange - Help for Orange", outputLines[5]);
- }
-
- [Fact]
- public async Task ReceivesInputPrompt()
- {
- string inputScript =
- this.GetInputPromptString(
- PromptCaption,
- PromptMessage,
- PromptFields);
-
- var promptTask = this.promptHandlerContext.WaitForInputPrompt();
- var executeTask = this.powerShellContext.ExecuteScriptString(inputScript);
-
- // Wait for the prompt to be shown
- var promptHandler = await promptTask;
-
- // Respond to the prompt and wait for execution to complete
- await promptHandler.ReturnInputString("John");
- await promptHandler.ReturnInputString("40");
- await promptHandler.ReturnInputString("Windows PowerShell In Action");
- await promptHandler.ReturnInputString("");
- await executeTask;
-
- string[] outputLines =
- this.GetOutputForType(OutputType.Normal)
- .Split(
- new string[] { Environment.NewLine },
- StringSplitOptions.None);
-
- Assert.Equal(PromptCaption, outputLines[0]);
- Assert.Equal(PromptMessage, outputLines[1]);
- Assert.Equal("Name: John", outputLines[2]);
- Assert.Equal("Age: 40", outputLines[3]);
- Assert.Equal("Books[0]: Windows PowerShell In Action", outputLines[4]);
- Assert.Equal("Books[1]: ", outputLines[5]);
- Assert.Equal("Name John", outputLines[9].Trim());
- Assert.Equal("Age 40", outputLines[10].Trim());
- Assert.Equal("Books {Windows PowerShell In Action}", outputLines[11].Trim());
- }
-
- [Fact]
- public async Task CancelsInputPrompt()
- {
- string inputScript =
- this.GetInputPromptString(
- PromptCaption,
- PromptMessage,
- PromptFields);
-
- var promptTask = this.promptHandlerContext.WaitForInputPrompt();
- var executeTask = this.powerShellContext.ExecuteScriptString(inputScript);
-
- // Wait for the prompt to be shown
- await promptTask;
-
- // Cancel the prompt and wait for execution to complete
- this.consoleService.SendControlC();
- await executeTask;
-
- string[] outputLines =
- this.GetOutputForType(OutputType.Normal)
- .Split(
- new string[] { Environment.NewLine },
- StringSplitOptions.None);
-
- Assert.Equal(PromptCaption, outputLines[0]);
- Assert.Equal(PromptMessage, outputLines[1]);
- Assert.Equal("Name: ", outputLines[2]);
- }
-
- [Fact]
- public async Task ReceivesReadHostPrompt()
- {
- var promptTask = this.promptHandlerContext.WaitForInputPrompt();
- var executeTask = this.powerShellContext.ExecuteScriptString("Read-Host");
-
- // Wait for the prompt to be shown
- TestConsoleInputPromptHandler promptHandler = await promptTask;
-
- // Respond to the prompt and wait for execution to complete
- await promptHandler.ReturnInputString("John");
- await executeTask;
-
- string[] outputLines =
- this.GetOutputForType(OutputType.Normal)
- .Split(
- new string[] { Environment.NewLine },
- StringSplitOptions.None);
-
- Assert.Equal("John", outputLines[0]);
- Assert.Equal("John", outputLines[1]);
- }
-
- [Fact]
- public async Task CancelsReadHostPrompt()
- {
- var promptTask = this.promptHandlerContext.WaitForInputPrompt();
- var executeTask = this.powerShellContext.ExecuteScriptString("Read-Host");
-
- // Wait for the prompt to be shown
- await promptTask;
-
- // Cancel the prompt and wait for execution to complete
- this.consoleService.SendControlC();
- await executeTask;
-
- // No output will be written from a cancelled Read-Host prompt
- Assert.Null(this.GetOutputForType(OutputType.Normal));
- }
-
- [Fact]
- public async Task ReceivesReadHostPromptWithFieldName()
- {
- var promptTask = this.promptHandlerContext.WaitForInputPrompt();
- var executeTask = this.powerShellContext.ExecuteScriptString("Read-Host -Prompt \"Name\"");
-
- // Wait for the prompt to be shown
- TestConsoleInputPromptHandler promptHandler = await promptTask;
-
- // Respond to the prompt and wait for execution to complete
- await promptHandler.ReturnInputString("John");
- await executeTask;
-
- string[] outputLines =
- this.GetOutputForType(OutputType.Normal)
- .Split(
- new string[] { Environment.NewLine },
- StringSplitOptions.None);
-
- Assert.Equal("Name: John", outputLines[0]);
- Assert.Equal("John", outputLines[1]);
- }
-
- #region Helper Methods
-
- void OnOutputWritten(object sender, OutputWrittenEventArgs e)
- {
- string storedOutputString = null;
- if (!this.outputPerType.TryGetValue(e.OutputType, out storedOutputString))
- {
- this.outputPerType.Add(e.OutputType, null);
- }
-
- if (storedOutputString == null)
- {
- storedOutputString = e.OutputText;
- }
- else
- {
- storedOutputString += e.OutputText;
- }
-
- if (e.IncludeNewLine)
- {
- storedOutputString += Environment.NewLine;
- }
-
- this.outputPerType[e.OutputType] = storedOutputString;
- }
-
- private string GetOutputForType(OutputType outputLineType)
- {
- string outputString = null;
-
- this.outputPerType.TryGetValue(outputLineType, out outputString);
-
- return outputString;
- }
-
- private string GetChoicePromptString(
- string caption,
- string message,
- Tuple[] choices,
- int defaultChoice)
- {
- StringBuilder scriptBuilder = new StringBuilder();
-
- scriptBuilder.AppendFormat(
- "$caption = {0}\r\n",
- caption != null ?
- "\"" + caption + "\"" :
- "$null");
-
- scriptBuilder.AppendFormat(
- "$message = {0}\r\n",
- message != null ?
- "\"" + message + "\"" :
- "$null");
-
- scriptBuilder.AppendLine("$choices = [System.Management.Automation.Host.ChoiceDescription[]](");
-
- List choiceItems = new List();
- foreach (var choice in choices)
- {
- choiceItems.Add(
- string.Format(
- " (new-Object System.Management.Automation.Host.ChoiceDescription \"{0}\",\"{1}\")",
- choice.Item1,
- choice.Item2));
- }
-
- scriptBuilder.AppendFormat(
- "{0})\r\n",
- string.Join(",\r\n", choiceItems));
-
- scriptBuilder.AppendFormat(
- "$host.ui.PromptForChoice($caption, $message, $choices, {0})\r\n",
- defaultChoice);
-
- return scriptBuilder.ToString();
- }
-
- private string GetInputPromptString(
- string caption,
- string message,
- Tuple[] fields)
- {
- StringBuilder scriptBuilder = new StringBuilder();
-
- scriptBuilder.AppendFormat(
- "$caption = {0}\r\n",
- caption != null ?
- "\"" + caption + "\"" :
- "$null");
-
- scriptBuilder.AppendFormat(
- "$message = {0}\r\n",
- message != null ?
- "\"" + message + "\"" :
- "$null");
-
- foreach (var field in fields)
- {
- scriptBuilder.AppendFormat(
- "${0}Field = New-Object System.Management.Automation.Host.FieldDescription \"{0}\"\r\n${0}Field.SetParameterType([{1}])\r\n",
- field.Item1,
- field.Item2.FullName);
- }
-
- scriptBuilder.AppendFormat(
- "$fields = [System.Management.Automation.Host.FieldDescription[]]({0})\r\n",
- string.Join(
- ", ",
- fields.Select(
- f => string.Format("${0}Field", f.Item1))));
-
- scriptBuilder.AppendLine(
- "$host.ui.Prompt($caption, $message, $fields)");
-
- return scriptBuilder.ToString();
- }
-
- #endregion
- }
-
- internal class TestConsolePromptHandlerContext : IPromptHandlerContext
- {
- private TaskCompletionSource choicePromptShownTask;
- private TaskCompletionSource inputPromptShownTask;
-
- public IConsoleHost ConsoleHost { get; set; }
-
- public ChoicePromptHandler GetChoicePromptHandler()
- {
- return new TestConsoleChoicePromptHandler(
- this.ConsoleHost,
- this.choicePromptShownTask);
- }
-
- public InputPromptHandler GetInputPromptHandler()
- {
- return new TestConsoleInputPromptHandler(
- this.ConsoleHost,
- this.inputPromptShownTask);
- }
-
- public Task WaitForChoicePrompt()
- {
- this.choicePromptShownTask = new TaskCompletionSource();
- return this.choicePromptShownTask.Task;
- }
-
- public Task WaitForInputPrompt()
- {
- this.inputPromptShownTask = new TaskCompletionSource();
- return this.inputPromptShownTask.Task;
- }
- }
-
- internal class TestConsoleChoicePromptHandler : ConsoleChoicePromptHandler
- {
- private IConsoleHost consoleHost;
- private TaskCompletionSource promptShownTask;
- private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
-
- private TaskCompletionSource linePromptTask;
- private AsyncQueue> linePromptQueue =
- new AsyncQueue>();
-
- public TestConsoleChoicePromptHandler(
- IConsoleHost consoleHost,
- TaskCompletionSource promptShownTask)
- : base(consoleHost, new NullLogger())
- {
- this.consoleHost = consoleHost;
- this.promptShownTask = promptShownTask;
- }
-
- public async Task ReturnInputString(string inputString)
- {
- var promptTask = await this.linePromptQueue.DequeueAsync();
- this.consoleHost.WriteOutput(inputString);
- promptTask.SetResult(inputString);
- }
-
- protected override async Task ReadInputString(CancellationToken cancellationToken)
- {
- TaskCompletionSource promptTask = new TaskCompletionSource();
- await this.linePromptQueue.EnqueueAsync(promptTask);
-
- if (this.cancellationTokenSource.IsCancellationRequested)
- {
- this.linePromptTask.TrySetCanceled();
- }
-
- this.linePromptTask = promptTask;
- return await promptTask.Task;
- }
-
- protected override void ShowPrompt(PromptStyle promptStyle)
- {
- base.ShowPrompt(promptStyle);
-
- if (this.promptShownTask != null &&
- this.promptShownTask.Task.Status != TaskStatus.RanToCompletion)
- {
- this.promptShownTask.SetResult(this);
- }
- }
-
- protected override void OnPromptCancelled()
- {
- this.cancellationTokenSource.Cancel();
-
- if (this.linePromptTask != null)
- {
- this.linePromptTask.TrySetCanceled();
- }
- }
- }
-
- internal class TestConsoleInputPromptHandler : ConsoleInputPromptHandler
- {
- private IConsoleHost consoleHost;
- private TaskCompletionSource promptShownTask;
- private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
-
- private TaskCompletionSource linePromptTask;
- private AsyncQueue> linePromptQueue =
- new AsyncQueue>();
-
- public TestConsoleInputPromptHandler(
- IConsoleHost consoleHost,
- TaskCompletionSource promptShownTask)
- : base(consoleHost, new NullLogger())
- {
- this.consoleHost = consoleHost;
- this.promptShownTask = promptShownTask;
- }
-
- public async Task ReturnInputString(string inputString)
- {
- var promptTask = await this.linePromptQueue.DequeueAsync();
- this.consoleHost.WriteOutput(inputString);
- promptTask.SetResult(inputString);
- }
-
- protected override async Task ReadInputString(CancellationToken cancellationToken)
- {
- TaskCompletionSource promptTask = new TaskCompletionSource();
- await this.linePromptQueue.EnqueueAsync(promptTask);
-
- if (this.cancellationTokenSource.IsCancellationRequested)
- {
- this.linePromptTask.TrySetCanceled();
- }
-
- this.linePromptTask = promptTask;
- return await promptTask.Task;
- }
-
- protected override void ShowFieldPrompt(FieldDetails fieldDetails)
- {
- base.ShowFieldPrompt(fieldDetails);
-
- // Raise the task for the first field prompt shown
- if (this.promptShownTask != null &&
- this.promptShownTask.Task.Status == TaskStatus.WaitingForActivation)
- {
- this.promptShownTask.SetResult(this);
- }
- }
-
- protected override void OnPromptCancelled()
- {
- this.cancellationTokenSource.Cancel();
-
- if (this.linePromptTask != null)
- {
- this.linePromptTask.TrySetCanceled();
- }
- }
- }
-}
+// //
+// // 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.Console;
+// using Microsoft.PowerShell.EditorServices.Utility;
+// using System;
+// using System.Collections.Generic;
+// using System.Linq;
+// using System.Security;
+// using System.Text;
+// using System.Threading;
+// using System.Threading.Tasks;
+// using Xunit;
+
+// namespace Microsoft.PowerShell.EditorServices.Test.Console
+// {
+// public class ConsoleServiceTests : IDisposable
+// {
+// private ConsoleService consoleService;
+// private PowerShellContext powerShellContext;
+// private TestConsolePromptHandlerContext promptHandlerContext;
+
+// private Dictionary outputPerType =
+// new Dictionary();
+
+// const string TestOutputString = "This is a test.";
+
+// const string PromptCaption = "Test Prompt";
+// const string PromptMessage = "Make a selection";
+// const int PromptDefault = 1;
+
+// static readonly Tuple[] PromptChoices =
+// new Tuple[]
+// {
+// new Tuple("&Apple", "Help for Apple"),
+// new Tuple("Ba&nana", "Help for Banana"),
+// new Tuple("Orange", "Help for Orange")
+// };
+
+// static readonly Tuple[] PromptFields =
+// new Tuple[]
+// {
+// new Tuple("Name", typeof(string)),
+// new Tuple("Age", typeof(int)),
+// new Tuple("Books", typeof(string[])),
+// };
+
+// public ConsoleServiceTests()
+// {
+// this.powerShellContext = new PowerShellContext();
+// EditorServicesPSHost psHost =
+// new EditorServicesPSHost(
+// powerShellContext,
+// PowerShellContextTests.TestHostDetails,
+// false);
+
+// this.consoleService = psHost.ConsoleService;
+
+// this.powerShellContext.Initialize(
+// null,
+// PowerShellContext.CreateRunspace(psHost),
+// true);
+
+// this.promptHandlerContext =
+// new TestConsolePromptHandlerContext();
+
+// this.consoleService.PushPromptHandlerContext(this.promptHandlerContext);
+// this.consoleService.OutputWritten += OnOutputWritten;
+// promptHandlerContext.ConsoleHost = this.consoleService;
+// }
+
+// public void Dispose()
+// {
+// this.powerShellContext.Dispose();
+// }
+
+// [Fact]
+// public async Task ReceivesNormalOutput()
+// {
+// await this.powerShellContext.ExecuteScriptString(
+// string.Format(
+// "\"{0}\"",
+// TestOutputString));
+
+// // Prompt strings are returned as normal output, ignore the prompt
+// string[] normalOutputLines =
+// this.GetOutputForType(OutputType.Normal)
+// .Split(
+// new string[] { Environment.NewLine },
+// StringSplitOptions.None);
+
+// // The output should be 2 lines: the expected string and
+// // an empty line.
+// Assert.Equal(2, normalOutputLines.Length);
+// Assert.Equal(
+// TestOutputString,
+// normalOutputLines[0]);
+// }
+
+// [Fact]
+// public async Task ReceivesErrorOutput()
+// {
+// await this.powerShellContext.ExecuteScriptString(
+// string.Format(
+// "Write-Error \"{0}\"",
+// TestOutputString));
+
+// string errorString = this.GetOutputForType(OutputType.Error).Split('\r')[0];
+
+// Assert.Equal(
+// string.Format("Write-Error \"{0}\" : {0}", TestOutputString),
+// errorString);
+// }
+
+// [Fact]
+// public async Task ReceivesVerboseOutput()
+// {
+// // Since setting VerbosePreference causes other message to
+// // be written out when we run our test, run a command preemptively
+// // to flush out unwanted verbose messages
+// await this.powerShellContext.ExecuteScriptString("Write-Verbose \"Preloading\"");
+
+// await this.powerShellContext.ExecuteScriptString(
+// string.Format(
+// "$VerbosePreference = \"Continue\"; Write-Verbose \"{0}\"",
+// TestOutputString));
+
+// Assert.Equal(
+// EditorServicesPSHostUserInterface.VerboseMessagePrefix + TestOutputString + Environment.NewLine,
+// this.GetOutputForType(OutputType.Verbose));
+// }
+
+// [Fact]
+// public async Task ReceivesDebugOutput()
+// {
+// // Since setting VerbosePreference causes other message to
+// // be written out when we run our test, run a command preemptively
+// // to flush out unwanted verbose messages
+// await this.powerShellContext.ExecuteScriptString("Write-Verbose \"Preloading\"");
+
+// await this.powerShellContext.ExecuteScriptString(
+// string.Format(
+// "$DebugPreference = \"Continue\"; Write-Debug \"{0}\"",
+// TestOutputString));
+
+// Assert.Equal(
+// EditorServicesPSHostUserInterface.DebugMessagePrefix + TestOutputString + Environment.NewLine,
+// this.GetOutputForType(OutputType.Debug));
+// }
+
+// [Fact]
+// public async Task ReceivesWarningOutput()
+// {
+// await this.powerShellContext.ExecuteScriptString(
+// string.Format(
+// "Write-Warning \"{0}\"",
+// TestOutputString));
+
+// Assert.Equal(
+// EditorServicesPSHostUserInterface.WarningMessagePrefix + TestOutputString + Environment.NewLine,
+// this.GetOutputForType(OutputType.Warning));
+// }
+
+// [Fact]
+// public async Task ReceivesChoicePrompt()
+// {
+// string choiceScript =
+// this.GetChoicePromptString(
+// PromptCaption,
+// PromptMessage,
+// PromptChoices,
+// PromptDefault);
+
+// var promptTask = this.promptHandlerContext.WaitForChoicePrompt();
+// var executeTask = this.powerShellContext.ExecuteScriptString(choiceScript);
+
+// // Wait for the prompt to be shown
+// var promptHandler = await promptTask;
+
+// // Respond to the prompt and wait for the prompt to complete
+// await promptHandler.ReturnInputString("apple");
+// await executeTask;
+
+// string[] outputLines =
+// this.GetOutputForType(OutputType.Normal)
+// .Split(
+// new string[] { Environment.NewLine },
+// StringSplitOptions.None);
+
+// Assert.Equal(PromptCaption, outputLines[0]);
+// Assert.Equal(PromptMessage, outputLines[1]);
+// Assert.Equal("[A] Apple [N] Banana [] Orange [?] Help (default is \"Banana\"): apple", outputLines[2]);
+// Assert.Equal("0", outputLines[3]);
+// }
+
+// [Fact]
+// public async Task CancelsChoicePrompt()
+// {
+// string choiceScript =
+// this.GetChoicePromptString(
+// PromptCaption,
+// PromptMessage,
+// PromptChoices,
+// PromptDefault);
+
+// var promptTask = this.promptHandlerContext.WaitForChoicePrompt();
+// var executeTask = this.powerShellContext.ExecuteScriptString(choiceScript);
+
+// // Wait for the prompt to be shown
+// await promptTask;
+
+// // Cancel the prompt and wait for the execution to complete
+// this.consoleService.SendControlC();
+// await executeTask;
+
+// string[] outputLines =
+// this.GetOutputForType(OutputType.Normal)
+// .Split(
+// new string[] { Environment.NewLine },
+// StringSplitOptions.None);
+
+// Assert.Equal(PromptCaption, outputLines[0]);
+// Assert.Equal(PromptMessage, outputLines[1]);
+// Assert.Equal("[A] Apple [N] Banana [] Orange [?] Help (default is \"Banana\"): ", outputLines[2]);
+// }
+
+// [Fact]
+// public async Task ReceivesChoicePromptHelp()
+// {
+// string choiceScript =
+// this.GetChoicePromptString(
+// PromptCaption,
+// PromptMessage,
+// PromptChoices,
+// PromptDefault);
+
+// var promptTask = this.promptHandlerContext.WaitForChoicePrompt();
+// var executeTask = this.powerShellContext.ExecuteScriptString(choiceScript);
+
+// // Wait for the prompt to be shown
+// var promptHandler = await promptTask;
+
+// // Respond to the prompt and wait for the help prompt to appear
+// await promptHandler.ReturnInputString("?");
+// await promptHandler.ReturnInputString("A");
+// await executeTask;
+
+// string[] outputLines =
+// this.GetOutputForType(OutputType.Normal)
+// .Split(
+// new string[] { Environment.NewLine },
+// StringSplitOptions.None);
+
+// // Help lines start after initial prompt, skip 3 lines
+// Assert.Equal("A - Help for Apple", outputLines[3]);
+// Assert.Equal("N - Help for Banana", outputLines[4]);
+// Assert.Equal("Orange - Help for Orange", outputLines[5]);
+// }
+
+// [Fact]
+// public async Task ReceivesInputPrompt()
+// {
+// string inputScript =
+// this.GetInputPromptString(
+// PromptCaption,
+// PromptMessage,
+// PromptFields);
+
+// var promptTask = this.promptHandlerContext.WaitForInputPrompt();
+// var executeTask = this.powerShellContext.ExecuteScriptString(inputScript);
+
+// // Wait for the prompt to be shown
+// var promptHandler = await promptTask;
+
+// // Respond to the prompt and wait for execution to complete
+// await promptHandler.ReturnInputString("John");
+// await promptHandler.ReturnInputString("40");
+// await promptHandler.ReturnInputString("Windows PowerShell In Action");
+// await promptHandler.ReturnInputString("");
+// await executeTask;
+
+// string[] outputLines =
+// this.GetOutputForType(OutputType.Normal)
+// .Split(
+// new string[] { Environment.NewLine },
+// StringSplitOptions.None);
+
+// Assert.Equal(PromptCaption, outputLines[0]);
+// Assert.Equal(PromptMessage, outputLines[1]);
+// Assert.Equal("Name: John", outputLines[2]);
+// Assert.Equal("Age: 40", outputLines[3]);
+// Assert.Equal("Books[0]: Windows PowerShell In Action", outputLines[4]);
+// Assert.Equal("Books[1]: ", outputLines[5]);
+// Assert.Equal("Name John", outputLines[9].Trim());
+// Assert.Equal("Age 40", outputLines[10].Trim());
+// Assert.Equal("Books {Windows PowerShell In Action}", outputLines[11].Trim());
+// }
+
+// [Fact]
+// public async Task CancelsInputPrompt()
+// {
+// string inputScript =
+// this.GetInputPromptString(
+// PromptCaption,
+// PromptMessage,
+// PromptFields);
+
+// var promptTask = this.promptHandlerContext.WaitForInputPrompt();
+// var executeTask = this.powerShellContext.ExecuteScriptString(inputScript);
+
+// // Wait for the prompt to be shown
+// await promptTask;
+
+// // Cancel the prompt and wait for execution to complete
+// this.consoleService.SendControlC();
+// await executeTask;
+
+// string[] outputLines =
+// this.GetOutputForType(OutputType.Normal)
+// .Split(
+// new string[] { Environment.NewLine },
+// StringSplitOptions.None);
+
+// Assert.Equal(PromptCaption, outputLines[0]);
+// Assert.Equal(PromptMessage, outputLines[1]);
+// Assert.Equal("Name: ", outputLines[2]);
+// }
+
+// [Fact]
+// public async Task ReceivesReadHostPrompt()
+// {
+// var promptTask = this.promptHandlerContext.WaitForInputPrompt();
+// var executeTask = this.powerShellContext.ExecuteScriptString("Read-Host");
+
+// // Wait for the prompt to be shown
+// TestConsoleInputPromptHandler promptHandler = await promptTask;
+
+// // Respond to the prompt and wait for execution to complete
+// await promptHandler.ReturnInputString("John");
+// await executeTask;
+
+// string[] outputLines =
+// this.GetOutputForType(OutputType.Normal)
+// .Split(
+// new string[] { Environment.NewLine },
+// StringSplitOptions.None);
+
+// Assert.Equal("John", outputLines[0]);
+// Assert.Equal("John", outputLines[1]);
+// }
+
+// [Fact]
+// public async Task CancelsReadHostPrompt()
+// {
+// var promptTask = this.promptHandlerContext.WaitForInputPrompt();
+// var executeTask = this.powerShellContext.ExecuteScriptString("Read-Host");
+
+// // Wait for the prompt to be shown
+// await promptTask;
+
+// // Cancel the prompt and wait for execution to complete
+// this.consoleService.SendControlC();
+// await executeTask;
+
+// // No output will be written from a cancelled Read-Host prompt
+// Assert.Null(this.GetOutputForType(OutputType.Normal));
+// }
+
+// [Fact]
+// public async Task ReceivesReadHostPromptWithFieldName()
+// {
+// var promptTask = this.promptHandlerContext.WaitForInputPrompt();
+// var executeTask = this.powerShellContext.ExecuteScriptString("Read-Host -Prompt \"Name\"");
+
+// // Wait for the prompt to be shown
+// TestConsoleInputPromptHandler promptHandler = await promptTask;
+
+// // Respond to the prompt and wait for execution to complete
+// await promptHandler.ReturnInputString("John");
+// await executeTask;
+
+// string[] outputLines =
+// this.GetOutputForType(OutputType.Normal)
+// .Split(
+// new string[] { Environment.NewLine },
+// StringSplitOptions.None);
+
+// Assert.Equal("Name: John", outputLines[0]);
+// Assert.Equal("John", outputLines[1]);
+// }
+
+// #region Helper Methods
+
+// void OnOutputWritten(object sender, OutputWrittenEventArgs e)
+// {
+// string storedOutputString = null;
+// if (!this.outputPerType.TryGetValue(e.OutputType, out storedOutputString))
+// {
+// this.outputPerType.Add(e.OutputType, null);
+// }
+
+// if (storedOutputString == null)
+// {
+// storedOutputString = e.OutputText;
+// }
+// else
+// {
+// storedOutputString += e.OutputText;
+// }
+
+// if (e.IncludeNewLine)
+// {
+// storedOutputString += Environment.NewLine;
+// }
+
+// this.outputPerType[e.OutputType] = storedOutputString;
+// }
+
+// private string GetOutputForType(OutputType outputLineType)
+// {
+// string outputString = null;
+
+// this.outputPerType.TryGetValue(outputLineType, out outputString);
+
+// return outputString;
+// }
+
+// private string GetChoicePromptString(
+// string caption,
+// string message,
+// Tuple[] choices,
+// int defaultChoice)
+// {
+// StringBuilder scriptBuilder = new StringBuilder();
+
+// scriptBuilder.AppendFormat(
+// "$caption = {0}\r\n",
+// caption != null ?
+// "\"" + caption + "\"" :
+// "$null");
+
+// scriptBuilder.AppendFormat(
+// "$message = {0}\r\n",
+// message != null ?
+// "\"" + message + "\"" :
+// "$null");
+
+// scriptBuilder.AppendLine("$choices = [System.Management.Automation.Host.ChoiceDescription[]](");
+
+// List choiceItems = new List();
+// foreach (var choice in choices)
+// {
+// choiceItems.Add(
+// string.Format(
+// " (new-Object System.Management.Automation.Host.ChoiceDescription \"{0}\",\"{1}\")",
+// choice.Item1,
+// choice.Item2));
+// }
+
+// scriptBuilder.AppendFormat(
+// "{0})\r\n",
+// string.Join(",\r\n", choiceItems));
+
+// scriptBuilder.AppendFormat(
+// "$host.ui.PromptForChoice($caption, $message, $choices, {0})\r\n",
+// defaultChoice);
+
+// return scriptBuilder.ToString();
+// }
+
+// private string GetInputPromptString(
+// string caption,
+// string message,
+// Tuple[] fields)
+// {
+// StringBuilder scriptBuilder = new StringBuilder();
+
+// scriptBuilder.AppendFormat(
+// "$caption = {0}\r\n",
+// caption != null ?
+// "\"" + caption + "\"" :
+// "$null");
+
+// scriptBuilder.AppendFormat(
+// "$message = {0}\r\n",
+// message != null ?
+// "\"" + message + "\"" :
+// "$null");
+
+// foreach (var field in fields)
+// {
+// scriptBuilder.AppendFormat(
+// "${0}Field = New-Object System.Management.Automation.Host.FieldDescription \"{0}\"\r\n${0}Field.SetParameterType([{1}])\r\n",
+// field.Item1,
+// field.Item2.FullName);
+// }
+
+// scriptBuilder.AppendFormat(
+// "$fields = [System.Management.Automation.Host.FieldDescription[]]({0})\r\n",
+// string.Join(
+// ", ",
+// fields.Select(
+// f => string.Format("${0}Field", f.Item1))));
+
+// scriptBuilder.AppendLine(
+// "$host.ui.Prompt($caption, $message, $fields)");
+
+// return scriptBuilder.ToString();
+// }
+
+// #endregion
+// }
+
+// internal class TestConsolePromptHandlerContext : IPromptHandlerContext
+// {
+// private TaskCompletionSource choicePromptShownTask;
+// private TaskCompletionSource inputPromptShownTask;
+
+// public IHostOutput ConsoleHost { get; set; }
+
+// public ChoicePromptHandler GetChoicePromptHandler()
+// {
+// return new TestConsoleChoicePromptHandler(
+// this.ConsoleHost,
+// this.choicePromptShownTask);
+// }
+
+// public InputPromptHandler GetInputPromptHandler()
+// {
+// return new TestConsoleInputPromptHandler(
+// this.ConsoleHost,
+// this.inputPromptShownTask);
+// }
+
+// public Task WaitForChoicePrompt()
+// {
+// this.choicePromptShownTask = new TaskCompletionSource();
+// return this.choicePromptShownTask.Task;
+// }
+
+// public Task WaitForInputPrompt()
+// {
+// this.inputPromptShownTask = new TaskCompletionSource();
+// return this.inputPromptShownTask.Task;
+// }
+// }
+
+// internal class TestConsoleChoicePromptHandler : ConsoleChoicePromptHandler
+// {
+// private IHostOutput hostOutput;
+// private TaskCompletionSource promptShownTask;
+// private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
+
+// private TaskCompletionSource linePromptTask;
+// private AsyncQueue> linePromptQueue =
+// new AsyncQueue>();
+
+// public TestConsoleChoicePromptHandler(
+// IHostOutput hostOutput,
+// TaskCompletionSource promptShownTask)
+// : base(hostOutput)
+// {
+// this.hostOutput = hostOutput;
+// this.promptShownTask = promptShownTask;
+// }
+
+// public async Task ReturnInputString(string inputString)
+// {
+// var promptTask = await this.linePromptQueue.DequeueAsync();
+// this.hostOutput.WriteOutput(inputString);
+// promptTask.SetResult(inputString);
+// }
+
+// protected override async Task ReadInputString(CancellationToken cancellationToken)
+// {
+// TaskCompletionSource promptTask = new TaskCompletionSource();
+// await this.linePromptQueue.EnqueueAsync(promptTask);
+
+// if (this.cancellationTokenSource.IsCancellationRequested)
+// {
+// this.linePromptTask.TrySetCanceled();
+// }
+
+// this.linePromptTask = promptTask;
+// return await promptTask.Task;
+// }
+
+// protected override void ShowPrompt(PromptStyle promptStyle)
+// {
+// base.ShowPrompt(promptStyle);
+
+// if (this.promptShownTask != null &&
+// this.promptShownTask.Task.Status != TaskStatus.RanToCompletion)
+// {
+// this.promptShownTask.SetResult(this);
+// }
+// }
+
+// protected override void OnPromptCancelled()
+// {
+// this.cancellationTokenSource.Cancel();
+
+// if (this.linePromptTask != null)
+// {
+// this.linePromptTask.TrySetCanceled();
+// }
+// }
+// }
+
+// internal class TestConsoleInputPromptHandler : ConsoleInputPromptHandler
+// {
+// private IHostOutput hostOutput;
+// private TaskCompletionSource promptShownTask;
+// private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
+
+// private TaskCompletionSource linePromptTask;
+// private AsyncQueue> linePromptQueue =
+// new AsyncQueue>();
+
+// public TestConsoleInputPromptHandler(
+// IHostOutput hostOutput,
+// TaskCompletionSource promptShownTask)
+// : base(hostOutput)
+// {
+// this.hostOutput = hostOutput;
+// this.promptShownTask = promptShownTask;
+// }
+
+// public async Task ReturnInputString(string inputString)
+// {
+// var promptTask = await this.linePromptQueue.DequeueAsync();
+// this.hostOutput.WriteOutput(inputString);
+// promptTask.SetResult(inputString);
+// }
+
+// protected override async Task ReadInputString(CancellationToken cancellationToken)
+// {
+// TaskCompletionSource promptTask = new TaskCompletionSource();
+// await this.linePromptQueue.EnqueueAsync(promptTask);
+
+// if (this.cancellationTokenSource.IsCancellationRequested)
+// {
+// this.linePromptTask.TrySetCanceled();
+// }
+
+// this.linePromptTask = promptTask;
+// return await promptTask.Task;
+// }
+
+// protected override void ShowFieldPrompt(FieldDetails fieldDetails)
+// {
+// base.ShowFieldPrompt(fieldDetails);
+
+// // Raise the task for the first field prompt shown
+// if (this.promptShownTask != null &&
+// this.promptShownTask.Task.Status == TaskStatus.WaitingForActivation)
+// {
+// this.promptShownTask.SetResult(this);
+// }
+// }
+
+// protected override void OnPromptCancelled()
+// {
+// this.cancellationTokenSource.Cancel();
+
+// if (this.linePromptTask != null)
+// {
+// this.linePromptTask.TrySetCanceled();
+// }
+// }
+// }
+// }
\ No newline at end of file
diff --git a/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs b/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs
index 61d429a91..8afea7c46 100644
--- a/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs
+++ b/test/PowerShellEditorServices.Test/PowerShellContextFactory.cs
@@ -8,21 +8,64 @@
using System;
using System.IO;
using Microsoft.PowerShell.EditorServices.Utility;
+using Microsoft.PowerShell.EditorServices.Console;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Management.Automation.Host;
namespace Microsoft.PowerShell.EditorServices.Test
{
internal static class PowerShellContextFactory
{
-
public static PowerShellContext Create(ILogger logger)
{
PowerShellContext powerShellContext = new PowerShellContext(logger);
powerShellContext.Initialize(
PowerShellContextTests.TestProfilePaths,
- PowerShellContext.CreateRunspace(PowerShellContextTests.TestHostDetails, powerShellContext, false),
+ PowerShellContext.CreateRunspace(
+ PowerShellContextTests.TestHostDetails,
+ powerShellContext,
+ new TestPSHostUserInterface(powerShellContext, logger),
+ logger),
true);
return powerShellContext;
}
}
+
+ public class TestPSHostUserInterface : EditorServicesPSHostUserInterface
+ {
+ public TestPSHostUserInterface(
+ PowerShellContext powerShellContext,
+ ILogger logger)
+ : base(
+ powerShellContext,
+ new SimplePSHostRawUserInterface(logger),
+ new NullLogger())
+ {
+ }
+
+ public override void WriteOutput(string outputString, bool includeNewLine, OutputType outputType, ConsoleColor foregroundColor, ConsoleColor backgroundColor)
+ {
+ }
+
+ protected override ChoicePromptHandler OnCreateChoicePromptHandler()
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override InputPromptHandler OnCreateInputPromptHandler()
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override Task ReadCommandLine(CancellationToken cancellationToken)
+ {
+ return Task.FromResult("USER COMMAND");
+ }
+
+ protected override void UpdateProgress(long sourceId, ProgressDetails progressDetails)
+ {
+ }
+ }
}
diff --git a/test/PowerShellEditorServices.Test/Utility/LoggerTests.cs b/test/PowerShellEditorServices.Test/Utility/LoggerTests.cs
index a343ad4a9..5ab202675 100644
--- a/test/PowerShellEditorServices.Test/Utility/LoggerTests.cs
+++ b/test/PowerShellEditorServices.Test/Utility/LoggerTests.cs
@@ -118,8 +118,6 @@ private string GetLogLevelName(LogLevel logLevel)
private string ReadLogContents()
{
- Logger.Close();
-
return
string.Join(
"\r\n",