Skip to content

Commit

Permalink
Merge pull request #1584 from rjmholt/pt-legacy-readline
Browse files Browse the repository at this point in the history
Re-implement legacy readline support
  • Loading branch information
rjmholt committed Oct 15, 2021
2 parents a9bcc75 + ff77a68 commit 9fd0dfa
Show file tree
Hide file tree
Showing 7 changed files with 748 additions and 643 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,5 @@ internal interface IReadLine
string ReadLine(CancellationToken cancellationToken);

SecureString ReadSecureLine(CancellationToken cancellationToken);

bool TryOverrideReadKey(Func<bool, ConsoleKeyInfo> readKeyOverride);

bool TryOverrideIdleHandler(Action<CancellationToken> idleHandler);
}
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -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 Microsoft.PowerShell.EditorServices.Services.PowerShell.Execution;
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host;
using System.Management.Automation;
using System.Threading;

namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Console
{
using System;

internal class PsrlReadLine : TerminalReadLine
{
private readonly PSReadLineProxy _psrlProxy;

private readonly PsesInternalHost _psesHost;

private readonly EngineIntrinsics _engineIntrinsics;

#region Constructors

public PsrlReadLine(
PSReadLineProxy psrlProxy,
PsesInternalHost psesHost,
EngineIntrinsics engineIntrinsics,
Func<bool, ConsoleKeyInfo> readKeyFunc,
Action<CancellationToken> onIdleAction)
{
_psrlProxy = psrlProxy;
_psesHost = psesHost;
_engineIntrinsics = engineIntrinsics;
_psrlProxy.OverrideReadKey(readKeyFunc);
_psrlProxy.OverrideIdleHandler(onIdleAction);
}

#endregion

#region Public Methods

public override string ReadLine(CancellationToken cancellationToken)
{
return _psesHost.InvokeDelegate<string>(representation: "ReadLine", new ExecutionOptions { MustRunInForeground = true }, InvokePSReadLine, cancellationToken);
}

protected override ConsoleKeyInfo ReadKey(CancellationToken cancellationToken)
{
return ConsoleProxy.ReadKey(intercept: true, cancellationToken);
}

#endregion

#region Private Methods

private string InvokePSReadLine(CancellationToken cancellationToken)
{
EngineIntrinsics engineIntrinsics = _psesHost.IsRunspacePushed ? null : _engineIntrinsics;
return _psrlProxy.ReadLine(_psesHost.Runspace, engineIntrinsics, cancellationToken, /* lastExecutionStatus */ null);
}

#endregion
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.PowerShell.EditorServices.Services.PowerShell.Utility;
using System.Management.Automation;
using System.Security;
using System.Threading;

namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Console
{
using System;

internal abstract class TerminalReadLine : IReadLine
{
public abstract string ReadLine(CancellationToken cancellationToken);

protected abstract ConsoleKeyInfo ReadKey(CancellationToken cancellationToken);

public SecureString ReadSecureLine(CancellationToken cancellationToken)
{
Console.TreatControlCAsInput = true;
int previousInputLength = 0;
SecureString secureString = new SecureString();
try
{
bool enterPressed = false;
while (!enterPressed && !cancellationToken.IsCancellationRequested)
{
ConsoleKeyInfo keyInfo = ReadKey(cancellationToken);

if (keyInfo.IsCtrlC())
{
throw new PipelineStoppedException();
}

switch (keyInfo.Key)
{
case ConsoleKey.Enter:
// Stop the while loop so we can realign the cursor
// and then return the entered string
enterPressed = true;
continue;

case ConsoleKey.Tab:
break;

case ConsoleKey.Backspace:
if (secureString.Length > 0)
{
secureString.RemoveAt(secureString.Length - 1);
}
break;

default:
if (keyInfo.KeyChar != 0 && !char.IsControl(keyInfo.KeyChar))
{
secureString.AppendChar(keyInfo.KeyChar);
}
break;
}

// Re-render the secure string characters
int currentInputLength = secureString.Length;
int consoleWidth = Console.WindowWidth;

if (currentInputLength > previousInputLength)
{
Console.Write('*');
}
else if (previousInputLength > 0 && currentInputLength < previousInputLength)
{
int row = ConsoleProxy.GetCursorTop(cancellationToken);
int col = ConsoleProxy.GetCursorLeft(cancellationToken);

// Back up the cursor before clearing the character
col--;
if (col < 0)
{
col = consoleWidth - 1;
row--;
}

Console.SetCursorPosition(col, row);
Console.Write(' ');
Console.SetCursorPosition(col, row);
}

previousInputLength = currentInputLength;
}
}
finally
{
Console.TreatControlCAsInput = false;
}

return secureString;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -625,12 +625,14 @@ private static PowerShell CreatePowerShellForRunspace(Runspace runspace)

var engineIntrinsics = (EngineIntrinsics)runspace.SessionStateProxy.GetVariable("ExecutionContext");

if (hostStartupInfo.ConsoleReplEnabled && !hostStartupInfo.UsesLegacyReadLine)
if (hostStartupInfo.ConsoleReplEnabled)
{
var psrlProxy = PSReadLineProxy.LoadAndCreate(_loggerFactory, pwsh);
var readLine = new ConsoleReadLine(psrlProxy, this, engineIntrinsics);
readLine.TryOverrideReadKey(ReadKey);
readLine.TryOverrideIdleHandler(OnPowerShellIdle);
// If we've been configured to use it, or if we can't load PSReadLine, use the legacy readline
if (hostStartupInfo.UsesLegacyReadLine || !TryLoadPSReadLine(pwsh, engineIntrinsics, out IReadLine readLine))
{
readLine = new LegacyReadLine(this, ReadKey, OnPowerShellIdle);
}

readLineProvider.OverrideReadLine(readLine);
System.Console.CancelKeyPress += OnCancelKeyPress;
System.Console.InputEncoding = Encoding.UTF8;
Expand Down Expand Up @@ -747,9 +749,7 @@ private ConsoleKeyInfo ReadKey(bool intercept)
private bool LastKeyWasCtrlC()
{
return _lastKey.HasValue
&& _lastKey.Value.Key == ConsoleKey.C
&& (_lastKey.Value.Modifiers & ConsoleModifiers.Control) != 0
&& (_lastKey.Value.Modifiers & ConsoleModifiers.Alt) == 0;
&& _lastKey.Value.IsCtrlC();
}

private void OnDebuggerStopped(object sender, DebuggerStopEventArgs debuggerStopEventArgs)
Expand Down Expand Up @@ -825,6 +825,22 @@ private Task PopOrReinitializeRunspaceAsync()
CancellationToken.None);
}

private bool TryLoadPSReadLine(PowerShell pwsh, EngineIntrinsics engineIntrinsics, out IReadLine psrlReadLine)
{
psrlReadLine = null;
try
{
var psrlProxy = PSReadLineProxy.LoadAndCreate(_loggerFactory, pwsh);
psrlReadLine = new PsrlReadLine(psrlProxy, this, engineIntrinsics, ReadKey, OnPowerShellIdle);
return true;
}
catch (Exception e)
{
_logger.LogError(e, "Unable to load PSReadLine. Will fall back to legacy readline implementation.");
return false;
}
}

private record RunspaceFrame(
Runspace Runspace,
RunspaceInfo RunspaceInfo);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;

namespace Microsoft.PowerShell.EditorServices.Services.PowerShell.Utility
{
internal static class ConsoleKeyInfoExtensions
{
public static bool IsCtrlC(this ConsoleKeyInfo keyInfo)
{
if ((int)keyInfo.Key == 3)
{
return true;
}

return keyInfo.Key == ConsoleKey.C
&& (keyInfo.Modifiers & ConsoleModifiers.Control) != 0
&& (keyInfo.Modifiers & ConsoleModifiers.Shift) == 0
&& (keyInfo.Modifiers & ConsoleModifiers.Alt) == 0;
}
}
}

0 comments on commit 9fd0dfa

Please sign in to comment.