diff --git a/PSReadLine/ReadLine.cs b/PSReadLine/ReadLine.cs index e76c78d6..fa7d93a9 100644 --- a/PSReadLine/ReadLine.cs +++ b/PSReadLine/ReadLine.cs @@ -33,15 +33,11 @@ public partial class PSConsoleReadLine : IPSConsoleReadLineMockableMethods { private const int ConsoleExiting = 1; - private const int CancellationRequested = 2; - // *must* be initialized in the static ctor // because the static member _clipboard depends upon it // for its own initialization private static readonly PSConsoleReadLine _singleton; - private static readonly CancellationToken _defaultCancellationToken = new CancellationTokenSource().Token; - // This is used by PowerShellEditorServices (the backend of the PowerShell VSCode extension) // so that it can call PSReadLine from a delegate and not hit nested pipeline issues. #pragma warning disable CS0649 @@ -143,17 +139,7 @@ private void ReadOneOrMoreKeys() } while (_charMap.KeyAvailable) { - ConsoleKeyInfo keyInfo = _charMap.ReadKey(); - if (_cancelReadCancellationToken.IsCancellationRequested) - { - // If PSReadLine is running under a host that can cancel it, the - // cancellation will come at a time when ReadKey is stuck waiting for input. - // The next key press will be used to force it to return, and so we want to - // discard this key since we were already canceled. - continue; - } - - var key = PSKeyInfo.FromConsoleKeyInfo(keyInfo); + var key = PSKeyInfo.FromConsoleKeyInfo(_charMap.ReadKey()); _lastNKeys.Enqueue(key); _queuedKeys.Enqueue(key); } @@ -170,10 +156,6 @@ private void ReadKeyThreadProc() break; ReadOneOrMoreKeys(); - if (_cancelReadCancellationToken.IsCancellationRequested) - { - continue; - } // One or more keys were read - let ReadKey know we're done. _keyReadWaitHandle.Set(); @@ -208,7 +190,6 @@ internal static PSKeyInfo ReadKey() // - a key is pressed // - the console is exiting // - 300ms timeout - to process events if we're idle - // - ReadLine cancellation is requested externally handleId = WaitHandle.WaitAny(_singleton._requestKeyWaitHandles, 300); if (handleId != WaitHandle.WaitTimeout) { @@ -292,10 +273,12 @@ internal static PSKeyInfo ReadKey() throw new OperationCanceledException(); } - if (handleId == CancellationRequested) + if (_singleton._cancelReadCancellationToken.IsCancellationRequested) { - // 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. + // ReadLine was cancelled. Dequeue the dummy input sent by the host, 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._queuedKeys.Dequeue(); _singleton.SaveCurrentLine(); _singleton._getNextHistoryIndex = _singleton._history.Count; _singleton._current = 0; @@ -331,9 +314,7 @@ private void PrependQueuedKeys(PSKeyInfo key) /// The complete command line. public static string ReadLine(Runspace runspace, EngineIntrinsics engineIntrinsics, bool? lastRunStatus) { - // Use a default cancellation token instead of CancellationToken.None because the - // WaitHandle is shared and could be triggered accidently. - return ReadLine(runspace, engineIntrinsics, _defaultCancellationToken, lastRunStatus); + return ReadLine(runspace, engineIntrinsics, CancellationToken.None, lastRunStatus); } /// @@ -396,7 +377,6 @@ public static string ReadLine( } _singleton._cancelReadCancellationToken = cancellationToken; - _singleton._requestKeyWaitHandles[2] = cancellationToken.WaitHandle; return _singleton.InputLoop(); } catch (OperationCanceledException) @@ -877,7 +857,7 @@ private void DelayedOneTimeInitialize() _readKeyWaitHandle = new AutoResetEvent(false); _keyReadWaitHandle = new AutoResetEvent(false); _closingWaitHandle = new ManualResetEvent(false); - _requestKeyWaitHandles = new WaitHandle[] {_keyReadWaitHandle, _closingWaitHandle, null}; + _requestKeyWaitHandles = new WaitHandle[] {_keyReadWaitHandle, _closingWaitHandle}; _threadProcWaitHandles = new WaitHandle[] {_readKeyWaitHandle, _closingWaitHandle}; // This is for a "being hosted in an alternate appdomain scenario" (the