mirror of
https://github.com/PowerShell/PSReadLine
synced 2026-06-08 12:12:13 +00:00
Changes for Editor Hosts (VSCode/Atom) (#626)
This commit is contained in:
committed by
Jason Shirk
parent
2a3450d222
commit
46960b1fe4
@@ -9,6 +9,14 @@ namespace Microsoft.PowerShell.Internal
|
||||
{
|
||||
internal class VirtualTerminal : IConsole
|
||||
{
|
||||
// These two fields are used by PowerShellEditorServices to inject a
|
||||
// custom ReadKey implementation. This is not a public API, but it is
|
||||
// part of a private contract with that project.
|
||||
private static Func<bool, ConsoleKeyInfo> _readKeyOverride;
|
||||
|
||||
private static Lazy<Func<bool, ConsoleKeyInfo>> _readKeyMethod = new Lazy<Func<bool, ConsoleKeyInfo>>(
|
||||
() => _readKeyOverride == null ? Console.ReadKey : _readKeyOverride);
|
||||
|
||||
public int CursorLeft
|
||||
{
|
||||
get => Console.CursorLeft;
|
||||
@@ -97,7 +105,7 @@ namespace Microsoft.PowerShell.Internal
|
||||
set { try { Console.OutputEncoding = value; } catch { } }
|
||||
}
|
||||
|
||||
public ConsoleKeyInfo ReadKey() => Console.ReadKey(true);
|
||||
public ConsoleKeyInfo ReadKey() => _readKeyMethod.Value(true);
|
||||
public bool KeyAvailable => Console.KeyAvailable;
|
||||
public void SetWindowPosition(int left, int top) => Console.SetWindowPosition(left, top);
|
||||
public void SetCursorPosition(int left, int top) => Console.SetCursorPosition(left, top);
|
||||
|
||||
+60
-5
@@ -28,8 +28,16 @@ namespace Microsoft.PowerShell
|
||||
|
||||
public partial class PSConsoleReadLine : IPSConsoleReadLineMockableMethods
|
||||
{
|
||||
private const int ConsoleExiting = 1;
|
||||
|
||||
private const int CancellationRequested = 2;
|
||||
|
||||
private const int EventProcessingRequested = 3;
|
||||
|
||||
private static readonly PSConsoleReadLine _singleton = new PSConsoleReadLine();
|
||||
|
||||
private static readonly CancellationToken _defaultCancellationToken = new CancellationTokenSource().Token;
|
||||
|
||||
private bool _delayedOneTimeInitCompleted;
|
||||
|
||||
private IPSConsoleReadLineMockableMethods _mockableMethods;
|
||||
@@ -41,6 +49,8 @@ namespace Microsoft.PowerShell
|
||||
private Thread _readKeyThread;
|
||||
private AutoResetEvent _readKeyWaitHandle;
|
||||
private AutoResetEvent _keyReadWaitHandle;
|
||||
private AutoResetEvent _forceEventWaitHandle;
|
||||
private CancellationToken _cancelReadCancellationToken;
|
||||
internal ManualResetEvent _closingWaitHandle;
|
||||
private WaitHandle[] _threadProcWaitHandles;
|
||||
private WaitHandle[] _requestKeyWaitHandles;
|
||||
@@ -139,7 +149,12 @@ namespace Microsoft.PowerShell
|
||||
if (handleId == 1) // It was the _closingWaitHandle that was signaled.
|
||||
break;
|
||||
|
||||
var localCancellationToken = _singleton._cancelReadCancellationToken;
|
||||
ReadOneOrMoreKeys();
|
||||
if (localCancellationToken.IsCancellationRequested)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// One or more keys were read - let ReadKey know we're done.
|
||||
_keyReadWaitHandle.Set();
|
||||
@@ -174,9 +189,10 @@ namespace Microsoft.PowerShell
|
||||
// - a key is pressed
|
||||
// - the console is exiting
|
||||
// - 300ms - to process events if we're idle
|
||||
|
||||
// - processing of events is requested externally
|
||||
// - ReadLine cancellation is requested externally
|
||||
handleId = WaitHandle.WaitAny(_singleton._requestKeyWaitHandles, 300);
|
||||
if (handleId != WaitHandle.WaitTimeout)
|
||||
if (handleId != WaitHandle.WaitTimeout && handleId != EventProcessingRequested)
|
||||
break;
|
||||
|
||||
// If we timed out, check for event subscribers (which is just
|
||||
@@ -236,7 +252,7 @@ namespace Microsoft.PowerShell
|
||||
ps?.Dispose();
|
||||
}
|
||||
|
||||
if (handleId == 1)
|
||||
if (handleId == ConsoleExiting)
|
||||
{
|
||||
// The console is exiting - throw an exception to unwind the stack to the point
|
||||
// where we can return from ReadLine.
|
||||
@@ -249,6 +265,18 @@ namespace Microsoft.PowerShell
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
|
||||
if (handleId == CancellationRequested)
|
||||
{
|
||||
// ReadLine was cancelled. Save the current line to be restored next time ReadLine
|
||||
// is called, clear the buffer and throw an exception so we can return an empty string.
|
||||
_singleton.SaveCurrentLine();
|
||||
_singleton._getNextHistoryIndex = _singleton._history.Count;
|
||||
_singleton._current = 0;
|
||||
_singleton._buffer.Clear();
|
||||
_singleton.Render();
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
|
||||
var key = _singleton._queuedKeys.Dequeue();
|
||||
return key;
|
||||
}
|
||||
@@ -275,6 +303,18 @@ namespace Microsoft.PowerShell
|
||||
/// </summary>
|
||||
/// <returns>The complete command line.</returns>
|
||||
public static string ReadLine(Runspace runspace, EngineIntrinsics engineIntrinsics)
|
||||
{
|
||||
// Use a default cancellation token instead of CancellationToken.None because the
|
||||
// WaitHandle is shared and could be triggered accidently.
|
||||
return ReadLine(runspace, engineIntrinsics, _defaultCancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Entry point - called by custom PSHost implementations that require the
|
||||
/// ability to cancel ReadLine.
|
||||
/// </summary>
|
||||
/// <returns>The complete command line.</returns>
|
||||
public static string ReadLine(Runspace runspace, EngineIntrinsics engineIntrinsics, CancellationToken cancellationToken)
|
||||
{
|
||||
var console = _singleton._console;
|
||||
|
||||
@@ -313,11 +353,14 @@ namespace Microsoft.PowerShell
|
||||
_singleton.Initialize(runspace, engineIntrinsics);
|
||||
}
|
||||
|
||||
_singleton._cancelReadCancellationToken = cancellationToken;
|
||||
_singleton._requestKeyWaitHandles[2] = _singleton._cancelReadCancellationToken.WaitHandle;
|
||||
return _singleton.InputLoop();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// Console is exiting - return value isn't too critical - null or 'exit' could work equally well.
|
||||
// Console is either exiting or the cancellation of ReadLine has been requested
|
||||
// by a custom PSHost implementation.
|
||||
return "";
|
||||
}
|
||||
catch (ExitException)
|
||||
@@ -720,8 +763,9 @@ namespace Microsoft.PowerShell
|
||||
|
||||
_singleton._readKeyWaitHandle = new AutoResetEvent(false);
|
||||
_singleton._keyReadWaitHandle = new AutoResetEvent(false);
|
||||
_singleton._forceEventWaitHandle = new AutoResetEvent(false);
|
||||
_singleton._closingWaitHandle = new ManualResetEvent(false);
|
||||
_singleton._requestKeyWaitHandles = new WaitHandle[] {_singleton._keyReadWaitHandle, _singleton._closingWaitHandle};
|
||||
_singleton._requestKeyWaitHandles = new WaitHandle[] {_singleton._keyReadWaitHandle, _singleton._closingWaitHandle, _defaultCancellationToken.WaitHandle, _singleton._forceEventWaitHandle};
|
||||
_singleton._threadProcWaitHandles = new WaitHandle[] {_singleton._readKeyWaitHandle, _singleton._closingWaitHandle};
|
||||
|
||||
// This is for a "being hosted in an alternate appdomain scenario" (the
|
||||
@@ -741,6 +785,17 @@ namespace Microsoft.PowerShell
|
||||
_singleton._readKeyThread.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by PowerShellEditorServices to force immediate
|
||||
/// event handling during the <see cref="PSConsoleReadLine.ReadKey" />
|
||||
/// method. This is not a public API, but it is part of a private contract
|
||||
/// with that project.
|
||||
/// </summary>
|
||||
private static void ForcePSEventHandling()
|
||||
{
|
||||
_singleton._forceEventWaitHandle.Set();
|
||||
}
|
||||
|
||||
private static void Chord(ConsoleKeyInfo? key = null, object arg = null)
|
||||
{
|
||||
if (!key.HasValue)
|
||||
|
||||
Reference in New Issue
Block a user