diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs index 0a0775a18..f925dbd7f 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleControl.cs @@ -2542,61 +2542,91 @@ namespace Microsoft.PowerShell /// Wrap Win32 WriteConsole. /// /// - /// handle for the console where the string is written + /// Handle for the console where the string is written. /// /// - /// string that is written + /// String that is written. + /// + /// + /// New line is written. /// /// - /// if the Win32's WriteConsole fails + /// If the Win32's WriteConsole fails. /// - - internal static void WriteConsole(ConsoleHandle consoleHandle, string output) + internal static void WriteConsole(ConsoleHandle consoleHandle, ReadOnlySpan output, bool newLine) { Dbg.Assert(!consoleHandle.IsInvalid, "ConsoleHandle is not valid"); Dbg.Assert(!consoleHandle.IsClosed, "ConsoleHandle is closed"); - if (string.IsNullOrEmpty(output)) + if (output.Length == 0) + { + if (newLine) + { + WriteConsole(consoleHandle, ConsoleHostUserInterface.Crlf); + } + return; + } // Native WriteConsole doesn't support output buffer longer than 64K. // We need to chop the output string if it is too long. - int cursor = 0; // This records the chopping position in output string - const int maxBufferSize = 16383; // this is 64K/4 - 1 to account for possible width of each character. + const int MaxBufferSize = 16383; // this is 64K/4 - 1 to account for possible width of each character. while (cursor < output.Length) { - string outBuffer; + ReadOnlySpan outBuffer; - if (cursor + maxBufferSize < output.Length) + if (cursor + MaxBufferSize < output.Length) { - outBuffer = output.Substring(cursor, maxBufferSize); - cursor += maxBufferSize; + outBuffer = output.Slice(cursor, MaxBufferSize); + cursor += MaxBufferSize; + + WriteConsole(consoleHandle, outBuffer); } else { - outBuffer = output.Substring(cursor); + outBuffer = output.Slice(cursor); cursor = output.Length; + + if (newLine) + { + var endOfLine = ConsoleHostUserInterface.Crlf.AsSpan(); + var endOfLineLength = endOfLine.Length; + Span outBufferLine = stackalloc char[outBuffer.Length + endOfLineLength]; + outBuffer.CopyTo(outBufferLine); + endOfLine.CopyTo(outBufferLine.Slice(outBufferLine.Length - endOfLineLength)); + WriteConsole(consoleHandle, outBufferLine); + } + else + { + WriteConsole(consoleHandle, outBuffer); + } } + } + } - DWORD charsWritten; - bool result = - NativeMethods.WriteConsole( - consoleHandle.DangerousGetHandle(), - outBuffer, - (DWORD)outBuffer.Length, - out charsWritten, - IntPtr.Zero); + private static void WriteConsole(ConsoleHandle consoleHandle, ReadOnlySpan buffer) + { + DWORD charsWritten; + bool result = + NativeMethods.WriteConsole( + consoleHandle.DangerousGetHandle(), + buffer, + (DWORD)buffer.Length, + out charsWritten, + IntPtr.Zero); - if (result == false) - { - int err = Marshal.GetLastWin32Error(); + if (result == false) + { + int err = Marshal.GetLastWin32Error(); - HostException e = CreateHostException(err, "WriteConsole", - ErrorCategory.WriteError, ConsoleControlStrings.WriteConsoleExceptionTemplate); - throw e; - } + HostException e = CreateHostException( + err, + "WriteConsole", + ErrorCategory.WriteError, + ConsoleControlStrings.WriteConsoleExceptionTemplate); + throw e; } } @@ -3009,15 +3039,30 @@ namespace Microsoft.PowerShell [DllImport(PinvokeDllNames.WriteConsoleDllName, SetLastError = true, CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool WriteConsole + private static extern unsafe bool WriteConsole ( NakedWin32Handle consoleOutput, - string buffer, + char* buffer, DWORD numberOfCharsToWrite, out DWORD numberOfCharsWritten, IntPtr reserved ); + internal static unsafe bool WriteConsole + ( + NakedWin32Handle consoleOutput, + ReadOnlySpan buffer, + DWORD numberOfCharsToWrite, + out DWORD numberOfCharsWritten, + IntPtr reserved + ) + { + fixed (char* bufferPtr = &MemoryMarshal.GetReference(buffer)) + { + return WriteConsole(consoleOutput, bufferPtr, numberOfCharsToWrite, out numberOfCharsWritten, reserved); + } + } + [DllImport(PinvokeDllNames.GetConsoleTitleDllName, SetLastError = true, CharSet = CharSet.Unicode)] internal static extern DWORD GetConsoleTitle(StringBuilder consoleTitle, DWORD size); diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs index 9026739db..5f1c63af9 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostRawUserInterface.cs @@ -705,9 +705,7 @@ namespace Microsoft.PowerShell if ((options & ReadKeyOptions.NoEcho) == 0) { - parent.WriteToConsole( - keyInfo.Character.ToString(), - true); + parent.WriteToConsole(keyInfo.Character, transcribeResult: true); } return keyInfo; diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostTranscript.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostTranscript.cs index f551f6bb4..fe3b37a77 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostTranscript.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostTranscript.cs @@ -10,15 +10,9 @@ using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell { - internal sealed partial - class ConsoleHost - : - PSHost, - IDisposable + internal sealed partial class ConsoleHost : PSHost, IDisposable { - internal - bool - IsTranscribing + internal bool IsTranscribing { get { @@ -69,9 +63,7 @@ namespace Microsoft.PowerShell */ private string _transcriptFileName = string.Empty; - internal - string - StopTranscribing() + internal string StopTranscribing() { lock (_transcriptionStateLock) { @@ -106,15 +98,30 @@ namespace Microsoft.PowerShell } } - internal - void - WriteToTranscript(string text) + internal void WriteToTranscript(ReadOnlySpan text) + { + WriteToTranscript(text, newLine: false); + } + + internal void WriteLineToTranscript(ReadOnlySpan text) + { + WriteToTranscript(text, newLine: true); + } + + internal void WriteToTranscript(ReadOnlySpan text, bool newLine) { lock (_transcriptionStateLock) { if (_isTranscribing && _transcriptionWriter != null) { - _transcriptionWriter.Write(text); + if (newLine) + { + _transcriptionWriter.WriteLine(text); + } + else + { + _transcriptionWriter.Write(text); + } } } } diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs index bc8fa54f8..53431f42d 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterface.cs @@ -2,16 +2,18 @@ // Licensed under the MIT License. using System; -using System.IO; -using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Linq; -using System.Management.Automation.Runspaces; -using System.Text; using System.Management.Automation; using System.Management.Automation.Internal; using System.Management.Automation.Host; +using System.Management.Automation.Runspaces; +using System.Runtime.CompilerServices; using System.Security; +using System.Text; + using Dbg = System.Management.Automation.Diagnostics; #if !UNIX using ConsoleHandle = Microsoft.Win32.SafeHandles.SafeFileHandle; @@ -268,7 +270,7 @@ namespace Microsoft.PowerShell #else // Ensure that we're in the proper line-input mode. - ConsoleControl.ConsoleModes desiredMode = + const ConsoleControl.ConsoleModes DesiredMode = ConsoleControl.ConsoleModes.Extended | ConsoleControl.ConsoleModes.QuickEdit; @@ -278,13 +280,13 @@ namespace Microsoft.PowerShell bool shouldUnsetMouseInput = shouldUnsetMode(ConsoleControl.ConsoleModes.MouseInput, ref m); bool shouldUnsetProcessInput = shouldUnsetMode(ConsoleControl.ConsoleModes.ProcessedInput, ref m); - if ((m & desiredMode) != desiredMode || + if ((m & DesiredMode) != DesiredMode || shouldUnsetMouseInput || shouldUnsetEchoInput || shouldUnsetLineInput || shouldUnsetProcessInput) { - m |= desiredMode; + m |= DesiredMode; ConsoleControl.SetMode(handle, m); } else @@ -542,23 +544,35 @@ namespace Microsoft.PowerShell #region WriteToConsole - internal void WriteToConsole(string value, bool transcribeResult) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WriteToConsole(char c, bool transcribeResult) + { + ReadOnlySpan value = stackalloc char[1] { c }; + WriteToConsole(value, transcribeResult); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WriteToConsole(ReadOnlySpan value, bool transcribeResult) + { + WriteToConsole(value, transcribeResult, newLine: false); + } + + private void WriteToConsole(ReadOnlySpan value, bool transcribeResult, bool newLine) { #if !UNIX ConsoleHandle handle = ConsoleControl.GetActiveScreenBufferHandle(); // Ensure that we're in the proper line-output mode. We don't lock here as it does not matter if we // attempt to set the mode from multiple threads at once. - ConsoleControl.ConsoleModes m = ConsoleControl.GetMode(handle); - const ConsoleControl.ConsoleModes desiredMode = - ConsoleControl.ConsoleModes.ProcessedOutput + const ConsoleControl.ConsoleModes DesiredMode = + ConsoleControl.ConsoleModes.ProcessedOutput | ConsoleControl.ConsoleModes.WrapEndOfLine; - if ((m & desiredMode) != desiredMode) + if ((m & DesiredMode) != DesiredMode) { - m |= desiredMode; + m |= DesiredMode; ConsoleControl.SetMode(handle, m); } #endif @@ -566,21 +580,20 @@ namespace Microsoft.PowerShell PreWrite(); // This is atomic, so we don't lock here... - #if !UNIX - ConsoleControl.WriteConsole(handle, value); + ConsoleControl.WriteConsole(handle, value, newLine); #else - Console.Out.Write(value); + ConsoleOutWriteHelper(value, newLine); #endif if (_isInteractiveTestToolListening && Console.IsOutputRedirected) { - Console.Out.Write(value); + ConsoleOutWriteHelper(value, newLine); } if (transcribeResult) { - PostWrite(value); + PostWrite(value, newLine); } else { @@ -588,34 +601,64 @@ namespace Microsoft.PowerShell } } - private void WriteToConsole(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string text) + private void WriteToConsole(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string text, bool newLine = false) { - ConsoleColor fg = RawUI.ForegroundColor; - ConsoleColor bg = RawUI.BackgroundColor; - - RawUI.ForegroundColor = foregroundColor; - RawUI.BackgroundColor = backgroundColor; - - try + // Sync access so that we don't race on color settings if called from multiple threads. + lock (_instanceLock) { - WriteToConsole(text, true); - } - finally - { - RawUI.ForegroundColor = fg; - RawUI.BackgroundColor = bg; + ConsoleColor fg = RawUI.ForegroundColor; + ConsoleColor bg = RawUI.BackgroundColor; + + RawUI.ForegroundColor = foregroundColor; + RawUI.BackgroundColor = backgroundColor; + + try + { + WriteToConsole(text, transcribeResult: true, newLine); + } + finally + { + RawUI.ForegroundColor = fg; + RawUI.BackgroundColor = bg; + } } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ConsoleOutWriteHelper(ReadOnlySpan value, bool newLine) + { + if (newLine) + { + Console.Out.WriteLine(value); + } + else + { + Console.Out.Write(value); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void WriteLineToConsole(ReadOnlySpan value, bool transcribeResult) + { + WriteToConsole(value, transcribeResult, newLine: true); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void WriteLineToConsole(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string text) + { + WriteToConsole(foregroundColor, backgroundColor, text, newLine: true); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void WriteLineToConsole(string text) { - WriteToConsole(text, true); - WriteToConsole(Crlf, true); + WriteLineToConsole(text, transcribeResult: true); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void WriteLineToConsole() { - WriteToConsole(Crlf, true); + WriteToConsole(Environment.NewLine, transcribeResult: true, newLine: false); } #endregion WriteToConsole @@ -636,15 +679,28 @@ namespace Microsoft.PowerShell public override void Write(string value) { - if (string.IsNullOrEmpty(value)) - { - // do nothing + WriteImpl(value, newLine: false); + } + private void WriteImpl(string value, bool newLine) + { + if (string.IsNullOrEmpty(value) && !newLine) + { return; } // If the test hook is set, write to it and continue. - if (s_h != null) s_h.Write(value); + if (s_h != null) + { + if (newLine) + { + s_h.WriteLine(value); + } + else + { + s_h.Write(value); + } + } TextWriter writer = Console.IsOutputRedirected ? Console.Out : _parent.ConsoleTextWriter; @@ -653,10 +709,22 @@ namespace Microsoft.PowerShell Dbg.Assert(writer == _parent.OutputSerializer.textWriter, "writers should be the same"); _parent.OutputSerializer.Serialize(value); + + if (newLine) + { + _parent.OutputSerializer.Serialize(Crlf); + } } else { - writer.Write(value); + if (newLine) + { + writer.WriteLine(value); + } + else + { + writer.Write(value); + } } } @@ -682,8 +750,36 @@ namespace Microsoft.PowerShell public override void Write(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value) { - // Sync access so that we don't race on color settings if called from multiple threads. + Write(foregroundColor, backgroundColor, value, newLine: false); + } + /// + /// See base class. + /// + /// + /// + /// + /// + /// If obtaining information about the buffer failed + /// OR + /// Win32's SetConsoleTextAttribute + /// OR + /// Win32's CreateFile fails + /// OR + /// Win32's GetConsoleMode fails + /// OR + /// Win32's SetConsoleMode fails + /// OR + /// Win32's WriteConsole fails + /// + public override void WriteLine(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value) + { + Write(foregroundColor, backgroundColor, value, newLine: true); + } + + private void Write(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value, bool newLine) + { + // Sync access so that we don't race on color settings if called from multiple threads. lock (_instanceLock) { ConsoleColor fg = RawUI.ForegroundColor; @@ -694,7 +790,7 @@ namespace Microsoft.PowerShell try { - this.Write(value); + this.WriteImpl(value, newLine); } finally { @@ -717,16 +813,26 @@ namespace Microsoft.PowerShell /// OR /// Win32's WriteConsole fails /// - public override void WriteLine(string value) { - // lock here so that the newline is written atomically with the value + this.WriteImpl(value, newLine: true); + } - lock (_instanceLock) - { - this.Write(value); - this.Write(Crlf); - } + /// + /// See base class. + /// + /// + /// Win32's CreateFile fails + /// OR + /// Win32's GetConsoleMode fails + /// OR + /// Win32's SetConsoleMode fails + /// OR + /// Win32's WriteConsole fails + /// + public override void WriteLine() + { + this.WriteImpl(Environment.NewLine, newLine: false); } #region Word Wrapping @@ -1227,8 +1333,6 @@ namespace Microsoft.PowerShell { if (string.IsNullOrEmpty(value)) { - // do nothing - return; } @@ -1247,7 +1351,7 @@ namespace Microsoft.PowerShell if (writer == _parent.ConsoleTextWriter) WriteLine(ErrorForegroundColor, ErrorBackgroundColor, value); else - Console.Error.Write(value + Crlf); + Console.Error.WriteLine(value); } } @@ -1419,15 +1523,15 @@ namespace Microsoft.PowerShell ConsoleHandle handle = ConsoleControl.GetConioDeviceHandle(); ConsoleControl.ConsoleModes m = ConsoleControl.GetMode(handle); - const ConsoleControl.ConsoleModes desiredMode = + const ConsoleControl.ConsoleModes DesiredMode = ConsoleControl.ConsoleModes.LineInput | ConsoleControl.ConsoleModes.EchoInput | ConsoleControl.ConsoleModes.ProcessedInput; - if ((m & desiredMode) != desiredMode || (m & ConsoleControl.ConsoleModes.MouseInput) > 0) + if ((m & DesiredMode) != DesiredMode || (m & ConsoleControl.ConsoleModes.MouseInput) > 0) { m &= ~ConsoleControl.ConsoleModes.MouseInput; - m |= desiredMode; + m |= DesiredMode; ConsoleControl.SetMode(handle, m); } #endif @@ -1941,7 +2045,7 @@ namespace Microsoft.PowerShell { // Reads always terminate with the enter key, so add that. - _parent.WriteToTranscript(input + Crlf); + _parent.WriteLineToTranscript(input); } return input; diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceProgress.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceProgress.cs index d63085751..88ad4e997 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceProgress.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceProgress.cs @@ -130,7 +130,7 @@ namespace Microsoft.PowerShell private void - PostWrite(string value) + PostWrite(ReadOnlySpan value, bool newLine) { PostWrite(); @@ -138,7 +138,7 @@ namespace Microsoft.PowerShell { try { - _parent.WriteToTranscript(value); + _parent.WriteToTranscript(value, newLine); } catch (Exception) { @@ -178,7 +178,7 @@ namespace Microsoft.PowerShell try { // Reads always terminate with the enter key, so add that. - _parent.WriteToTranscript(value + Crlf); + _parent.WriteLineToTranscript(value); } catch (Exception) { diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfacePrompt.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfacePrompt.cs index b82e09a8a..88eaab50c 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfacePrompt.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfacePrompt.cs @@ -118,8 +118,7 @@ namespace Microsoft.PowerShell // Should be a skin lookup WriteLineToConsole(); - WriteToConsole(PromptColor, RawUI.BackgroundColor, WrapToCurrentWindowWidth(caption)); - WriteLineToConsole(); + WriteLineToConsole(PromptColor, RawUI.BackgroundColor, WrapToCurrentWindowWidth(caption)); } if (!string.IsNullOrEmpty(message)) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfacePromptForChoice.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfacePromptForChoice.cs index 3f10679b9..fd2fc1db9 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfacePromptForChoice.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfacePromptForChoice.cs @@ -70,8 +70,7 @@ namespace Microsoft.PowerShell // Should be a skin lookup WriteLineToConsole(); - WriteToConsole(PromptColor, RawUI.BackgroundColor, WrapToCurrentWindowWidth(caption)); - WriteLineToConsole(); + WriteLineToConsole(PromptColor, RawUI.BackgroundColor, WrapToCurrentWindowWidth(caption)); } if (!string.IsNullOrEmpty(message)) @@ -216,8 +215,7 @@ namespace Microsoft.PowerShell { // Should be a skin lookup WriteLineToConsole(); - WriteToConsole(PromptColor, RawUI.BackgroundColor, WrapToCurrentWindowWidth(caption)); - WriteLineToConsole(); + WriteLineToConsole(PromptColor, RawUI.BackgroundColor, WrapToCurrentWindowWidth(caption)); } // write message if (!string.IsNullOrEmpty(message)) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceSecurity.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceSecurity.cs index ee44fe39d..18e5ce20f 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceSecurity.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceSecurity.cs @@ -72,8 +72,7 @@ namespace Microsoft.PowerShell // Should be a skin lookup WriteLineToConsole(); - WriteToConsole(PromptColor, RawUI.BackgroundColor, WrapToCurrentWindowWidth(caption)); - WriteLineToConsole(); + WriteLineToConsole(PromptColor, RawUI.BackgroundColor, WrapToCurrentWindowWidth(caption)); } if (!string.IsNullOrEmpty(message)) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleTextWriter.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleTextWriter.cs index 78f438970..888ba4bd7 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleTextWriter.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleTextWriter.cs @@ -39,38 +39,59 @@ namespace Microsoft.PowerShell void Write(string value) { - _ui.WriteToConsole(value, true); + _ui.WriteToConsole(value, transcribeResult: true); + } + + public override + void + Write(ReadOnlySpan value) + { + _ui.WriteToConsole(value, transcribeResult: true); } public override void WriteLine(string value) { - this.Write(value + ConsoleHostUserInterface.Crlf); + _ui.WriteLineToConsole(value, transcribeResult: true); } public override void - Write(Boolean b) + WriteLine(ReadOnlySpan value) { - this.Write(b.ToString()); + _ui.WriteLineToConsole(value, transcribeResult: true); + } + + public override + void + Write(bool b) + { + if (b) + { + _ui.WriteToConsole(bool.TrueString, transcribeResult: true); + } + else + { + _ui.WriteToConsole(bool.FalseString, transcribeResult: true); + } } public override void Write(char c) { - this.Write(new String(c, 1)); + ReadOnlySpan c1 = stackalloc char[1] { c }; + _ui.WriteToConsole(c1, transcribeResult: true); } public override void Write(char[] a) { - this.Write(new String(a)); + _ui.WriteToConsole(a, transcribeResult: true); } private ConsoleHostUserInterface _ui; } -} // namespace - +}