/// <summary> /// Revert the list view suggestion. /// Namely, clear the list view and revert the buffer to the original user input. /// </summary> internal bool RevertSuggestion() { bool retValue = false; if (ActiveView is PredictionListView listView && listView.HasActiveSuggestion) { if (listView.SelectedItemIndex > -1 && _singleton._undoEditIndex > 0) { _singleton._edits[_singleton._undoEditIndex - 1].Undo(); _singleton._undoEditIndex--; } retValue = true; using var _ = DisableScoped(); _singleton.Render(); } return(retValue); }
private static ConsoleKeyInfo ReadKey() { // Reading a key is handled on a different thread. During process shutdown, // PowerShell will wait in it's ConsoleCtrlHandler until the pipeline has completed. // If we're running, we're most likely blocked waiting for user input. // This is a problem for two reasons. First, exiting takes a long time (5 seconds // on Win8) because PowerShell is waiting forever, but the OS will forcibly terminate // the console. Also - if there are any event handlers for the engine event // PowerShell.Exiting, those handlers won't get a chance to run. // // By waiting for a key on a different thread, our pipeline execution thread // (the thread Readline is called from) avoid being blocked in code that can't // be unblocked and instead blocks on events we control. // First, set an event so the thread to read a key actually attempts to read a key. _singleton._readKeyWaitHandle.Set(); int handleId; System.Management.Automation.PowerShell ps = null; try { while (true) { // Next, wait for one of three things: // - a key is pressed // - the console is exiting // - 300ms - to process events if we're idle handleId = WaitHandle.WaitAny(_singleton._requestKeyWaitHandles, 300); if (handleId != WaitHandle.WaitTimeout) { break; } if (_singleton._engineIntrinsics == null) { continue; } // If we timed out, check for event subscribers (which is just // a hint that there might be an event waiting to be processed.) var eventSubscribers = _singleton._engineIntrinsics.Events.Subscribers; if (eventSubscribers.Count > 0) { bool runPipelineForEventProcessing = false; foreach (var sub in eventSubscribers) { if (sub.SourceIdentifier.Equals("PowerShell.OnIdle", StringComparison.OrdinalIgnoreCase)) { // There is an OnIdle event. We're idle because we timed out. Normally // PowerShell generates this event, but PowerShell assumes the engine is not // idle because it called PSConsoleHostReadline which isn't returning. // So we generate the event intstead. _singleton._engineIntrinsics.Events.GenerateEvent("PowerShell.OnIdle", null, null, null); runPipelineForEventProcessing = true; break; } // If there are any event subscribers that have an action (which might // write to the console) and have a source object (i.e. aren't engine // events), run a tiny useless bit of PowerShell so that the events // can be processed. if (sub.Action != null && sub.SourceObject != null) { runPipelineForEventProcessing = true; break; } } if (runPipelineForEventProcessing) { if (ps == null) { ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace); ps.AddScript("0"); } // To detect output during possible event processing, see if the cursor moved // and rerender if so. var console = _singleton._console; var y = console.CursorTop; ps.Invoke(); if (y != console.CursorTop) { _singleton._initialY = console.CursorTop; _singleton.Render(); } } } } } finally { if (ps != null) { ps.Dispose(); } } if (handleId == 1) { // The console is exiting - throw an exception to unwind the stack to the point // where we can return from ReadLine. if (_singleton.Options.HistorySaveStyle == HistorySaveStyle.SaveAtExit) { _singleton.SaveHistoryAtExit(); } _singleton._historyFileMutex.Dispose(); throw new OperationCanceledException(); } var key = _singleton._queuedKeys.Dequeue(); return(key); }
internal static PSKeyInfo ReadKey() { // Reading a key is handled on a different thread. During process shutdown, // PowerShell will wait in it's ConsoleCtrlHandler until the pipeline has completed. // If we're running, we're most likely blocked waiting for user input. // This is a problem for two reasons. First, exiting takes a long time (5 seconds // on Win8) because PowerShell is waiting forever, but the OS will forcibly terminate // the console. Also - if there are any event handlers for the engine event // PowerShell.Exiting, those handlers won't get a chance to run. // // By waiting for a key on a different thread, our pipeline execution thread // (the thread ReadLine is called from) avoid being blocked in code that can't // be unblocked and instead blocks on events we control. // First, set an event so the thread to read a key actually attempts to read a key. _singleton._readKeyWaitHandle.Set(); int handleId; System.Management.Automation.PowerShell ps = null; try { while (true) { // Next, wait for one of three things: // - 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) { break; } if (_handleIdleOverride is not null) { _handleIdleOverride(_singleton._cancelReadCancellationToken); continue; } // If we timed out, check for event subscribers (which is just // a hint that there might be an event waiting to be processed.) var eventSubscribers = _singleton._engineIntrinsics?.Events.Subscribers; if (eventSubscribers?.Count > 0) { bool runPipelineForEventProcessing = false; foreach (var sub in eventSubscribers) { if (sub.SourceIdentifier.Equals(PSEngineEvent.OnIdle, StringComparison.OrdinalIgnoreCase)) { // If the buffer is not empty, let's not consider we are idle because the user is in the middle of typing something. if (_singleton._buffer.Length > 0) { continue; } // There is an OnIdle event subscriber and we are idle because we timed out and the buffer is empty. // Normally PowerShell generates this event, but PowerShell assumes the engine is not idle because // it called PSConsoleHostReadLine which isn't returning. So we generate the event instead. runPipelineForEventProcessing = true; _singleton._engineIntrinsics.Events.GenerateEvent(PSEngineEvent.OnIdle, null, null, null); // Break out so we don't genreate more than one 'OnIdle' event for a timeout. break; } runPipelineForEventProcessing = true; } // If there are any event subscribers, run a tiny useless PowerShell pipeline // so that the events can be processed. if (runPipelineForEventProcessing) { if (ps == null) { ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace); ps.AddScript("0", useLocalScope: true); } // To detect output during possible event processing, see if the cursor moved // and rerender if so. var console = _singleton._console; var y = console.CursorTop; ps.Invoke(); if (y != console.CursorTop) { _singleton._initialY = console.CursorTop; _singleton.Render(); } } } } } finally { ps?.Dispose(); } if (handleId == ConsoleExiting) { // The console is exiting - throw an exception to unwind the stack to the point // where we can return from ReadLine. if (_singleton.Options.HistorySaveStyle == HistorySaveStyle.SaveAtExit) { _singleton.SaveHistoryAtExit(); } _singleton._historyFileMutex.Dispose(); 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); }